Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C Programming


NOV91: C PROGRAMMING

C PROGRAMMING

D-Flat Window Classes

This article contains the following executables: DFLAT9.ARC DF9TXT.ARC

Al Stevens

In past months, I discussed and published most of the D-Flat code that underpins the design of window classes and their management and display. What remain are code modules that implement each of the standard D-Flat classes in the hierarchy. By studying these, you will see how window classes work. This knowledge will be valuable when you begin to derive your own classes from the ones that D-Flat provides. This column begins the discussion of specific window classes by reviewing the classes that D-Flat provides, discussing how the class hierarchy works, and describing the NORMAL class, which is the base class from which all others derive.

Table 1 lists the standard D-Flat window classes. Figure 1 shows the windows classes in their hierarchy. I discussed most of these classes in the June 1991 column.

Table 1: The Standard D-Flat window classes

  Class        Description
  ------------------------------------------------------------------------
  NORMAL       Base window for all window classes
  APPLICATION  Application window -- has the menu
  TEXTBOX      Contains text.  Base window for listbox, editbox, and so on
  LISTBOX      Contains a list of text -- base window for menubar
  EDITBOX      Text that a user can edit
  MENUBAR      The application's menu bar
  POPDOWNMENU  Pop-down menu
  BUTTON       Command button in a dialog box
  DIALOG       Dialog box -- parent to editbox, button, listbox, and so on
  ERRORBOX     For displaying an error message
  MESSAGEBOX   For displaying a message
  HELPBOX      Help window
  TEXT         Static text on a dialog box
  RADIOBUTTON  Radio button on a dialog box
  CHECKBOX     Check box on a dialog box
  STATUSBAR    Status bar at the bottom of application window

Figure 1 shows how all window classes derive from the NORMAL base class. In September, you learned how to add new window classes to the hierarchy by adding entries to classes.h and config.c.

Window-Processing Module

Each window class needs a window processing module to which D-Flat will send messages. If a class does not have its own window processing module, D-Flat will send the messages to the processing module of the window's base class. A window class's processing module takes the form shown in Example 1.

Example 1: A window class's processing module

  int ClassProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
  {
      switch (msg)     {
          /* ---- process the window's messages ---- */
          default:
              break;
      }
      return BaseWndProc (CLASS, wnd, msg, p1, p2);
  }

The ClassProc identifier is unique for each class, and the CLASS constant in the last statement is the window's class name, for example TEXTBOX or MENUBAR.

D-Flat calls the window processing module to send the window a message. The message might have one or two parameters that extend its meaning. For example, the LEFT_BUTTON message occurs when the user presses the left button. The message parameters contain the x and y coordinates where the mouse cursor was positioned when the user pressed the button.

D-Flat decides which window gets a message based on the current operating environment. Keyboard messages go to the window that has the focus. Mouse messages go to the window where the mouse cursor is located at the time of the message. Messages from other windows go to the windows to which they are addressed. For example, a window might send a message to its parent window or to one of its child windows.

A window processing module can ignore a message. In fact, most window processing modules process only a small subset of the messages. By calling the BaseWndProc module, the window processing module guarantees that its base window class will process the messages it needs. For example, no window class needs to concern itself with the details of moving itself around the screen. The window processing module for the NORMAL class takes care of window movements. Because the NORMAL class is the base for all other classes, the other classes all share the window movement code that the NORMAL class provides. On the other hand, most window classes have unique operations for the PAINT message. Some of them will process the message entirely, some will superimpose their PAINT operations on top of those of the base classes, and some will ignore the message and allow the base class to handle the entire process. In a few cases they will intercept and reject the message.

A window processing module can prevent a message from going to the base window's processing module simply by returning without calling the BaseWndProc function. Sometimes the window processing module wants the base class processes to occur first, so it calls BaseWndProc before doing its own stuff rather than after.

You will remember from the discussion on the CreateWindow function that you can add a window processing module to an instance of a window. This is not the same as a derived window's processing module, although it has some of the same features. An overriding window processing module for an existing class gets first crack at the messages, employs the same strategies for intercepting, ignoring, and augmenting the class's processing, and then calls the DefaultWndProc function instead of the BaseWndProc function. The memopad.c example application from last month created an application window that used its own window processing module in addition to the default one that D-Flat provides.

The Normal Class

You will learn about D-Flat windows from the top down, using the chart in Figure 1 as a guide. This month we will begin with the NORMAL window class, which contains the code common to all windows. The NormalProc window processing module manages the following operations: Showing, hiding, and maintaining the window in the order of focused windows; moving and resizing windows; painting blank windows for classes that do not paint themselves; displaying the window border and title bar; minimizing, maximizing, and restoring the window; calling and managing the commands from the system menu; and closing the window. The NormalProc function is included in Listing One, page 136, normal.c. That source file also includes the code for deciding which parts of which windows should be repainted when an overlapping window is closed or moved out of the way, and which parts of a window should be repainted when it comes to the front of the focus.

We will look at NormalProc first. It uses the format given in Example 1 for window processing modules. A window processing module is essentially one big switch statement with cases for each of the messages. We will discuss the messages one at a time. Remember that by the time the NormalProc function receives a message, it has passed up through every window processing module in the class hierarchy, starting at the bottom. So, if a message gets sent to a POPDOWNMENU window, for example, the window processing modules for the POPDOWNMENU, LISTBOX, and TEXTBOX classes get it first in turn before the NormalProc function sees it. Furthermore, if the instance of the window has a window processing module, that one gets it first.

The Messages

The CreateWindow function sends the CREATE_WINDOW message after the function has built the basic window structure. NormalProc eventually gets the message and adds the window to the two linked lists I described last month. Then, if the mouse is not installed, NormalProc removes the window's scroll bar attributes. There is no point in having scroll bars if you don't have a mouse. Some windows have the SAVESELF attribute, which means that they will never be overlapped by another window. These include pop-down menus, message boxes, and modal dialog boxes. These windows do not require the overhead involved in repainting the contents of the windows that they overlap because they will never lose the focus as long as they exist. Instead, they simply save the video memory that they will overwrite. NormalProc calls the GetVideoBuffer function for such windows if they have the VISIBLE attribute when they are created.

The SHOW_WINDOW message is to paint the entire window. If the window has a parent, and the parent is not visible, this message does nothing. Otherwise, if the window has the SAVESELF attribute and its video save buffer has not been built, the message calls the GetVideoBuffer function. The message then sets the VISIBLE attribute for the window and sends PAINT and BORDER messages to the window.

Take a moment and consider the operation just described. The window processing module for a window class just sent a message to a window that is either of or is derived from the class. Why couldn't the module simply avoid the message-passing overhead and do whatever it does when it receives the message? You will see a lot of this kind of activity in D-Flat and other message-based systems. It is one reason why they are generally less efficient than other programming models. The NormalProc function cannot simply pass control to its own code to process the PAINT and BORDER messages. The window class is most certainly derived -- directly or indirectly -- from the NORMAL class. The derived class might -- probably will -- have its own processing for the PAINT message, if not for the BORDER message.

After sending itself PAINT and BORDER messages, this window sends the SHOW_WINDOW message to each of its child windows so that they will be displayed as well.

The HIDE_WINDOW message processes only if the window has the VISIBLE attribute. First the message clears the VISIBLE attribute from the window and all child windows. Then it must redisplay the other windows that the window covers. If the window has a video save buffer, the message calls the RestoreVideoBuffer function. Otherwise, it calls the PaintOverLappers function -- which we will discuss soon -- which removes the window by sending PAINT and BORDER messages to whatever the window covered. Not quite that simple, but conceptually correct.

The DISPLAY_HELP message is posted by a HELPBOX window when it is changing help window displays. NormalProc calls the DisplayHelp function when it gets this message.

The INSIDE_WINDOW message is sent to find out if the screen coordinates in the parameters are inside the window.

The KEYBOARD message processes the keystrokes that the derived classes do not intercept. The message translates the F1 key into a COMMAND ID_HELP message. If the window is being moved or resized, the KEYBOARD message processes the arrow keys, the Enter key and the Esc key for these operations by translating them into MOUSE_CURSOR and MOUSE_MOVED messages. If the keystroke is the F1 help key, the Alt+F6 window stepper key, the Alt+space bar system menu key, or the Ctrl+F4 close window key, the program processes those keys here because their functions are common to all windows. If a particular window class does not use one or more of these operations, or uses them differently, the class's window processing module will intercept and process or ignore the key.

All other keystrokes that make it as far as NormalProc get posted as messages to the parent of the window. This procedure assures that system-wide keystrokes, such as menu accelerator keys, get processed by the application window, which is the parent of all other windows. The ADDSTATUS and SHIFT_CHANGED messages are similarly passed by NormalProc to the parent window of whatever window originally receives them. If that is not supposed to happen for a particular derived window class, the window processing module of one of the windows in the hierarchy will have intercepted and processed the message.

Take another moment to consider the difference between the hierarchy of window classes and the parent-child relationship of instances of windows. These are two different logical chains, and it is easy to confuse them. An instance of a window is created, and that window's definition and behavior are functions of its class and the base classes from which its class is derived. The same window can be a child of another window of the same or a different class, and it can have one or more child windows of its own, each of which may be of classes that are the same as or different from each other and that of their parent.

The PAINT message for the NORMAL window class simply calls ClearWindow to paint a blank window. A NORMAL window has no data display in its client data space.

The BORDER message calls functions to display the window's border and title bar, and, if the window has a status bar, sends the status bar window a PAINT message.

The COMMAND message includes a parameter that identifies the command that is to be processed. As a general rule, command messages result when the user selects a command from a menu or performs some activity on a dialog box. D-Flat sends a COMMAND message to the current in-focus document window, dialog box, or the window that is the parent of the menu. The COMMAND message's first parameter contains the command's code. NormalProc manages the ID_HELP command and the commands in Example 2, sent by the system menu.

Example 2: These commands, managed by NormalProc, are sent by the system menu.

  ID_SYSRESTORE to restore a minimized or
     maximized window
  ID_SYSMOVE to move a window
  ID_SYSSIZE to resize a window
  ID_SYSMINIMIZE to minimize a window
  ID_SYSMAXIMIZE to maximize a window
  ID_SYSCLOSE to close a window

A window receives the SETFOCUS message when it is to receive or relinquish the user's input focus. For the window to receive the input focus, the message has a true value in the first parameter. To relinquish the focus, the first parameter is false.

Clearing the focus from a window consists of setting the global inFocus WINDOW type to NULL and sending a BORDER message to the window. The current in-focus window identifies itself to the user with a double-line border and a highlighted title bar, so the border must be repainted when the window loses the focus.

Setting the focus to a window is more complicated. The Redraw flag will indicate if the window needs to be selectively repainted for those parts of it that are overlapped by other windows. If the window is presently visible and does not have the SAVESELF attribute, it might need to be redrawn. If the window is the child of an APPLICATION window and has no children itself, however, it will not need to be repainted selectively. Once that decision is made, the program sends the SETFOCUS message with a false parameter to the currently in-focus window to tell it to relinquish the focus. If the Redraw indicator has been set, the program calls the PaintUnderLappers function to paint those portions of the window that are overlapped by other windows. Then it puts the window at the top of the infocus linked list and puts its WINDOW handle into the inFocus global variable. If the Redraw indicator is set, the program sends the window the BORDER message to tell it to change its border to the in-focus configuration just described. Otherwise, the SHOW_WINDOW message will tell the window to completely repaint itself. If the window has no parent or its parent is an application window or dialog box, the SHOW_WINDOW message goes to the window itself. Otherwise, it goes to the window's parent window. You might set the focus to a child window when all or part of its parent is overlapped by other windows.

If the DOUBLE_CLICK message hits the window's control box, the program sends the CLOSE_WINDOW message to the window. If the LEFT_BUTTON message hits the window's control box, the program posts the LEFT_BUTTON message to the window's parent. The LEFT_BUTTON can also hit the minimize, maximize, or restore boxes, in which case the program sends the corresponding message to the window. If the message hits the title bar, the program sets up a window move operation by capturing the mouse and calling the dragborder function. If the message hits the lower right corner of the window, the program sets up the window resize operation the same way.

The MOUSE_MOVED message handles moving and sizing windows by calling the dragborder and sizeborder functions each time the mouse movement occurs. BUTTON_RELEASED terminates any window move or resize that is in effect.

The MAXIMIZE, MINIMIZE, RESTORE, MOVE, and SIZE messages come next. These messages perform those operations on the window. The MOVE message sends corresponding MOVE messages to all the window's child windows. If you resize a window that has a maximized child window in it, the program sends a corresponding SIZE message to the child.

The CLOSE_WINDOW message sends the HIDE_WINDOW message to the window and then sends CLOSE_WINDOW messages to all the window's child windows. If the window has the focus, the program calls the SetPrevFocus function to set the focus to the one that had it just before this window got it. Then the program removes the window structure from the window linked lists and frees the memory used by the window structure and its video save buffer.

Other Functions

The normal.c source file includes functions to compute the space for D-Flat's version of minimized window icons. When the user minimizes a window, D-Flat simply resizes it to a tiny window and finds room for it somewhere in the application window's client data space. The LowerRight function computes the rectangle occupied by the first minimized window, which will display in the lower right corner of the application window. The PositionIcon function computes subsequent adjacent icon positions to the left and above the first one.

The TerminateMoveSize, dragborder, and sizeborder functions support window moving and sizing. When you move or size a window with the keyboard or mouse, D-Flat draws the border of a dummy window frame around the original window position. You interactively move or resize that dummy frame with the dragborder and resizeborder functions. When you are done, the TerminateMoveSize function gets rid of the dummy frame.

The next several functions manage the redisplay of document windows when there are several on the screen and one of them gets hidden or brought to the top. When a window becomes hidden, either because you move it to another position or close it, the windows it overlaps need to be repainted. D-Flat begins at the bottom of the in-focus list and sends each window a PAINT message. That process would work on its own, but it would be time-consuming and visually distracting if a lot of windows were involved.

Rather than completely repaint every overlapped window, NormalProc calls the PaintOverLappers function to compute and repaint the overlapping rectangle space within each overlapped window. The PAINT messages for each window class and the BORDER message for all window classes acknowledge the first parameter as a RECT structure that is the relative rectangle within themselves to be repainted. PaintOverLappers stores the WINDOW handle of the overlapping window in the HiddenWindow variable and calls PaintOverParents. This function calls itself recursively for its parent and then calls PaintOver to repaint the subject window and PaintOverChildren to repaint the window's child windows. The recursive calls are to assure that the most distant ancestor of the window and its children are dealt with first. The PaintOverChildren function processes all the child windows for a window. It calls PaintOver to paint each child, and then calls itself recursively in case the child window has child windows. The PaintOver function computes the rectangle that is the intersection of the one to paint and the one being hidden--the one whose handle is in the HiddenWindow variable. Then it calls the PaintOverLap function, which decides from the rectangle which of the component parts of the window need to be painted.

The PaintUnderLappers function is the other side of the coin. The program calls the function when a window gets the focus and is coming to the front of the display. Rather than unnecessarily repainting the entire window, PaintUnderLappers computes the rectangle that includes all overlapped parts of the window. First, it locates the oldest ancestor of the window and works with it by calling the PaintUnder function. This function looks at every other window in the system. If a window is descended from the one being processed, the function skips it. The descendents will be repainted as a result of the repainting of their ancestor. If a window is an ancestor, it too is skipped. This approach assures that only siblings and unrelated windows will be looked at. Once the function has found such a window, it looks to see if the found window is ahead of the subject window in the focus chain. If so, the function saves the handle of the found window in the HiddenWindow variable and calls the PaintOver function for the subject window. After PaintUnder has processed all the other windows against the subject window this way, it calls itself recursively for each of the subject window's child windows.

Considering the previous two paragraphs, do you wonder why event-driven, message-based windowing systems are generally less snappy than some of their forebears?

How to Get D-Flat Now

The D-Flat source code is on CompuServe in Library 0 of DDJ Forum and on M&T Online. Its name is DFLATn. ARC, where the n is an integer that represents a loosely-assigned version number. There is another file, named DFnTXT.ARC. It includes a README file that describes the changes and how to build the software. The file also contains the Help system database and the documentation for the programmer's API. D-Flat compiles with Turbo C 2.0, Borland C++ 2.0, Microsoft C 6.0, and Watcom C 8.0. There are makefiles for the TC, MSC, and Watcom compilers. There is an example program, MEMOPAD, which is a multiple-document notepad.

If you cannot get to either online service, send me a formatted diskette--360K or 720K--and an addressed, stamped diskette mailer. Send it to me in care of DDJ. I'll send you the latest copy of the library. The software is free, but if you care to, stick a dollar bill in the mailer. I'll match the dollar and give it to the Brevard County Food Bank. They take care of homeless and hungry children. This "careware" program of mine has netted about $200 so far for the Food Bank. They are grateful for your generosity. If you want to discuss D-Flat with me, use CompuServe. My CompuServe ID is 71101,1262, and I monitor DDJ Forum daily.


_C PROGRAMMING COLUMN_
by Al Stevens



[LISTING ONE]
<a name="0266_000d">

/* ------------- normal.c ------------ */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include "dflat.h"

#ifdef INCLUDE_MULTIDOCS
static void near PaintOverLappers(WINDOW wnd);
static void near PaintUnderLappers(WINDOW wnd);
#endif
static int InsideWindow(WINDOW, int, int);
#ifdef INCLUDE_SYSTEM_MENUS
static void TerminateMoveSize(void);
static void SaveBorder(RECT);
static void RestoreBorder(RECT);
static RECT PositionIcon(WINDOW);
static void near dragborder(WINDOW, int, int);
static void near sizeborder(WINDOW, int, int);
static int px = -1, py = -1;
static int diff;
static int conditioning;
static struct window dwnd = {DUMMY, NULL, NULL, NormalProc, {-1,-1,-1,-1}};
static int *Bsave;
static int Bht, Bwd;
int WindowMoving;
int WindowSizing;
#endif
/* -------- array of class definitions -------- */
CLASSDEFS classdefs[] = {
    #undef ClassDef
    #define ClassDef(c,b,p,a) {b,p,a},
    #include "classes.h"
};
WINDOW HiddenWindow;

int NormalProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int mx = (int) p1 - GetLeft(wnd);
    int my = (int) p2 - GetTop(wnd);
    int DoneClosing = FALSE;
    switch (msg)    {
        case CREATE_WINDOW:
            AppendBuiltWindow(wnd);  /* add to the lists */
            AppendFocusWindow(wnd);
#ifdef INCLUDE_SCROLLBARS
            if (!SendMessage(NULL, MOUSE_INSTALLED, 0, 0))
                ClearAttribute(wnd, VSCROLLBAR | HSCROLLBAR);
#endif
            if (TestAttribute(wnd, SAVESELF) && isVisible(wnd))
                GetVideoBuffer(wnd);
            break;
        case SHOW_WINDOW:
            if ((GetParent(wnd) == NULL || isVisible(GetParent(wnd)))
#ifdef INCLUDE_SYSTEM_MENUS
                            && !conditioning
#endif
                                                    )    {
                WINDOW cwnd = Focus.FirstWindow;
                if (TestAttribute(wnd, SAVESELF) &&
                                wnd->videosave == NULL)
                    GetVideoBuffer(wnd);
                SetVisible(wnd);
                SendMessage(wnd, PAINT, 0, 0);
                SendMessage(wnd, BORDER, 0, 0);
                /* --- show the children of this window --- */
                while (cwnd != NULL)    {
                    if (GetParent(cwnd) == wnd &&
                            cwnd->condition != ISCLOSING)
                        SendMessage(cwnd, msg, p1, p2);
                    cwnd = NextWindow(cwnd);
                }
            }
            break;
        case HIDE_WINDOW:
            if (isVisible(wnd)
#ifdef INCLUDE_SYSTEM_MENUS
                && !conditioning
#endif
                                )    {
                WINDOW cwnd = Focus.LastWindow;
                /* --- hide the children of this window --- */
                while (cwnd != NULL)    {
                    if (GetParent(cwnd) == wnd)
                        ClearVisible(cwnd);
                    cwnd = PrevWindow(cwnd);
                }
                ClearVisible(wnd);
                /* --- paint what this window covered --- */
                if (wnd->videosave != NULL)
                    RestoreVideoBuffer(wnd);
#ifdef INCLUDE_MULTIDOCS
                else
                    PaintOverLappers(wnd);
#endif
            }
            break;
#ifdef INCLUDE_HELP
        case DISPLAY_HELP:
            DisplayHelp(wnd, (char *)p1);
            break;
#endif
        case INSIDE_WINDOW:
            return InsideWindow(wnd, (int) p1, (int) p2);
        case KEYBOARD:
#ifdef INCLUDE_SYSTEM_MENUS
            if (WindowMoving || WindowSizing)    {
                /* -- move or size a window with keyboard -- */
                int x, y;
                x=WindowMoving?GetLeft(&dwnd):GetRight(&dwnd);
                y=WindowMoving?GetTop(&dwnd):GetBottom(&dwnd);
                switch ((int)p1)    {
                    case ESC:
                        TerminateMoveSize();
                        return TRUE;
                    case UP:
                        if (y)
                            --y;
                        break;
                    case DN:
                        if (y < SCREENHEIGHT-1)
                            y++;
                        break;
                    case FWD:
                        if (x < SCREENWIDTH-1)
                            x++;
                        break;
                    case BS:
                        if (x)
                            --x;
                        break;
                    case '\r':
                        SendMessage(wnd,BUTTON_RELEASED,x,y);
                    default:
                        return TRUE;
                }
                /* -- use the mouse functions to move/size - */
                SendMessage(wnd, MOUSE_CURSOR, x, y);
                SendMessage(wnd, MOUSE_MOVED, x, y);
                break;
            }
#endif
            switch ((int)p1)    {
#ifdef INCLUDE_HELP
                case F1:
                    SendMessage(wnd, COMMAND, ID_HELP, 0);
                    return TRUE;
#endif
                case ALT_F6:
                    SetNextFocus(inFocus);
                    SkipSystemWindows(FALSE);
                    return TRUE;
#ifdef INCLUDE_SYSTEM_MENUS
                case ' ':
                    if ((int)p2 & ALTKEY)
                        if (TestAttribute(wnd, HASTITLEBAR))
                            if (TestAttribute(wnd, CONTROLBOX))
                                BuildSystemMenu(wnd);
                    return TRUE;
#endif
                case CTRL_F4:
                    SendMessage(wnd, CLOSE_WINDOW, 0, 0);
                    SkipSystemWindows(FALSE);
                    return TRUE;
                default:
                    break;
            }
            /* ------- fall through ------- */
        case ADDSTATUS:
        case SHIFT_CHANGED:
            if (GetParent(wnd) != NULL)
                PostMessage(GetParent(wnd), msg, p1, p2);
            break;
        case PAINT:
            if (isVisible(wnd))
                ClearWindow(wnd, (RECT *)p1, ' ');
            break;
        case BORDER:
            if (isVisible(wnd))    {
                if (TestAttribute(wnd, HASBORDER))
                    RepaintBorder(wnd, (RECT *)p1);
                else if (TestAttribute(wnd, HASTITLEBAR))
                    DisplayTitle(wnd, (RECT *)p1);
                if (wnd->StatusBar != NULL)
                    SendMessage(wnd->StatusBar, PAINT, p1, 0);
            }
            break;
#ifdef INCLUDE_SYSTEM_MENUS
        case COMMAND:
            switch ((int)p1)    {
#ifdef INCLUDE_HELP
                case ID_HELP:
                    DisplayHelp(wnd,ClassNames[GetClass(wnd)]);
                    break;
#endif
                case ID_SYSRESTORE:
                    SendMessage(wnd, RESTORE, 0, 0);
                    break;
                case ID_SYSMOVE:
                    SendMessage(wnd, CAPTURE_MOUSE, TRUE, (PARAM) &dwnd);
                    SendMessage(wnd, CAPTURE_KEYBOARD, TRUE, (PARAM) &dwnd);
                    SendMessage(wnd, MOUSE_CURSOR, GetLeft(wnd), GetTop(wnd));
                    WindowMoving = TRUE;
                    dragborder(wnd, GetLeft(wnd), GetTop(wnd));
                    break;
                case ID_SYSSIZE:
                 SendMessage(wnd, CAPTURE_MOUSE, TRUE, (PARAM) &dwnd);
                 SendMessage(wnd, CAPTURE_KEYBOARD, TRUE, (PARAM) &dwnd);
                 SendMessage(wnd, MOUSE_CURSOR, GetRight(wnd), GetBottom(wnd));
                 WindowSizing = TRUE;
                 dragborder(wnd, GetLeft(wnd), GetTop(wnd));
                 break;
                case ID_SYSMINIMIZE:
                    SendMessage(wnd, MINIMIZE, 0, 0);
                    break;
                case ID_SYSMAXIMIZE:
                    SendMessage(wnd, MAXIMIZE, 0, 0);
                    break;
                case ID_SYSCLOSE:
                    SendMessage(wnd, CLOSE_WINDOW, 0, 0);
                    break;
                default:
                    break;
            }
            break;
#endif
        case SETFOCUS:
            if (p1 && inFocus != wnd)    {
                WINDOW pwnd = GetParent(wnd);
                int Redraw = isVisible(wnd) &&
                       !TestAttribute(wnd, SAVESELF);
                if (GetClass(pwnd) == APPLICATION)    {
                    WINDOW cwnd = Focus.FirstWindow;
                    /* -- if no children, do not need selective
                        redraw --- */
                    while (cwnd != NULL)    {
                        if (GetParent(cwnd) == wnd)
                            break;
                        cwnd = NextWindow(cwnd);
                    }
                    if (cwnd == NULL)
                        Redraw = FALSE;
                }
                /* ---- setting focus ------ */
                SendMessage(inFocus, SETFOCUS, FALSE, 0);

                if (Redraw)
                    PaintUnderLappers(wnd);

                /* remove window from list */
                RemoveFocusWindow(wnd);
                /* move window to end of list */
                AppendFocusWindow(wnd);
                inFocus = wnd;

                if (Redraw)
                    SendMessage(wnd, BORDER, 0, 0);
                else    {
                    if (pwnd == NULL ||
                        GetClass(pwnd) == DIALOG ||
                            DerivedClass(GetClass(pwnd)) ==
                                DIALOG || GetClass(pwnd) ==
                                        APPLICATION)
                        SendMessage(wnd, SHOW_WINDOW, 0, 0);
                    else
                        SendMessage(pwnd, SHOW_WINDOW, 0, 0);
                }
            }
            else if (!p1 && inFocus == wnd)    {
                /* -------- clearing focus --------- */
                inFocus = NULL;
                SendMessage(wnd, BORDER, 0, 0);
            }
            break;
        case DOUBLE_CLICK:
#ifdef INCLUDE_SYSTEM_MENUS
            if (!WindowSizing && !WindowMoving)
#endif
                if (HitControlBox(wnd, mx, my))
                    PostMessage(wnd, CLOSE_WINDOW, 0, 0);
            break;
        case LEFT_BUTTON:
#ifdef INCLUDE_SYSTEM_MENUS
            if (WindowSizing || WindowMoving)
                break;
#endif
            if (HitControlBox(wnd, mx, my))    {
                BuildSystemMenu(wnd);
                break;
            }
#ifdef INCLUDE_SYSTEM_MENUS
            if (my == 0 && mx > -1 && mx < WindowWidth(wnd))  {
                /* ---------- hit the top border -------- */
                if (TestAttribute(wnd, MINMAXBOX) &&
                        TestAttribute(wnd, HASTITLEBAR))  {
                    if (mx == WindowWidth(wnd)-2)    {
                        if (wnd->condition == ISRESTORED)
                            /* --- hit the maximize box --- */
                            SendMessage(wnd, MAXIMIZE, 0, 0);
                        else
                            /* --- hit the restore box --- */
                            SendMessage(wnd, RESTORE, 0, 0);
                        break;
                    }
                    if (mx == WindowWidth(wnd)-3)    {
                        /* --- hit the minimize box --- */
                        if (wnd->condition != ISMINIMIZED)
                            SendMessage(wnd, MINIMIZE, 0, 0);
                        break;
                    }
                }
                if (wnd->condition != ISMAXIMIZED &&
                            TestAttribute(wnd, MOVEABLE))    {
                    WindowMoving = TRUE;
                    px = mx;
                    py = my;
                    diff = (int) mx;
                    SendMessage(wnd, CAPTURE_MOUSE, TRUE,
                        (PARAM) &dwnd);
                    dragborder(wnd, GetLeft(wnd), GetTop(wnd));
                }
                break;
            }
            if (mx == WindowWidth(wnd)-1 &&
                    my == WindowHeight(wnd)-1)    {
                /* ------- hit the resize corner ------- */
                if (wnd->condition == ISMINIMIZED ||
                        !TestAttribute(wnd, SIZEABLE))
                    break;
                if (wnd->condition == ISMAXIMIZED)    {
                    if (TestAttribute(GetParent(wnd),HASBORDER))
                        break;
                    /* ----- resizing a maximized window over a
                            borderless parent ----- */
                    wnd = GetParent(wnd);
                }
                WindowSizing = TRUE;
                SendMessage(wnd, CAPTURE_MOUSE,
                    TRUE, (PARAM) &dwnd);
                dragborder(wnd, GetLeft(wnd), GetTop(wnd));
            }
#endif
            break;
#ifdef INCLUDE_SYSTEM_MENUS
        case MOUSE_MOVED:
            if (WindowMoving)    {
                int leftmost = 0, topmost = 0,
                    bottommost = SCREENHEIGHT-2,
                    rightmost = SCREENWIDTH-2;
                int x = (int) p1 - diff;
                int y = (int) p2;
                if (GetParent(wnd) != NULL &&
                        !TestAttribute(wnd, NOCLIP))    {
                    WINDOW wnd1 = GetParent(wnd);
                    topmost    = GetClientTop(wnd1);
                    leftmost   = GetClientLeft(wnd1);
                    bottommost = GetClientBottom(wnd1);
                    rightmost  = GetClientRight(wnd1);
                }
                if (x < leftmost || x > rightmost ||
                        y < topmost || y > bottommost)    {
                    x = max(x, leftmost);
                    x = min(x, rightmost);
                    y = max(y, topmost);
                    y = min(y, bottommost);
                    SendMessage(NULL,MOUSE_CURSOR,x+diff,y);
                }
                if (x != px || y != py)    {
                    px = x;
                    py = y;
                    dragborder(wnd, x, y);
                }
                return TRUE;
            }
            if (WindowSizing)    {
                sizeborder(wnd, (int) p1, (int) p2);
                return TRUE;
            }
            break;
        case BUTTON_RELEASED:
            if (WindowMoving || WindowSizing)    {
                if (WindowMoving)
                    PostMessage(wnd,MOVE,dwnd.rc.lf,dwnd.rc.tp);
                else
                    PostMessage(wnd,SIZE,dwnd.rc.rt,dwnd.rc.bt);
                TerminateMoveSize();
            }
            break;
        case MAXIMIZE:
            if (wnd->condition != ISMAXIMIZED)    {
                RECT rc = {0, 0, 0, 0};
                RECT holdrc;
                holdrc = wnd->RestoredRC;
                rc.rt = SCREENWIDTH-1;
                rc.bt = SCREENHEIGHT-1;
                if (GetParent(wnd))
                    rc = ClientRect(GetParent(wnd));
                wnd->condition = ISMAXIMIZED;
                SendMessage(wnd, HIDE_WINDOW, 0, 0);
                conditioning = TRUE;
                SendMessage(wnd, MOVE, RectLeft(rc), RectTop(rc));
                SendMessage(wnd, SIZE, RectRight(rc), RectBottom(rc));
                conditioning = FALSE;
                if (wnd->restored_attrib == 0)
                    wnd->restored_attrib = wnd->attrib;
#ifdef INCLUDE_SHADOWS
                ClearAttribute(wnd, SHADOW);
#endif
                SendMessage(wnd, SHOW_WINDOW, 0, 0);
                wnd->RestoredRC = holdrc;
            }
            break;
        case MINIMIZE:
            if (wnd->condition != ISMINIMIZED)    {
                RECT rc;
                RECT holdrc;

                holdrc = wnd->RestoredRC;
                rc = PositionIcon(wnd);
                wnd->condition = ISMINIMIZED;
                SendMessage(wnd, HIDE_WINDOW, 0, 0);
                conditioning = TRUE;
                SendMessage(wnd, MOVE, RectLeft(rc), RectTop(rc));
                SendMessage(wnd, SIZE, RectRight(rc), RectBottom(rc));
                SetPrevFocus(wnd);
                conditioning = FALSE;
                if (wnd->restored_attrib == 0)
                    wnd->restored_attrib = wnd->attrib;
                ClearAttribute(wnd,
                    SHADOW | SIZEABLE | HASMENUBAR |
                    VSCROLLBAR | HSCROLLBAR);
                SendMessage(wnd, SHOW_WINDOW, 0, 0);
                wnd->RestoredRC = holdrc;
            }
            break;
        case RESTORE:
            if (wnd->condition != ISRESTORED)    {
                RECT holdrc;
                holdrc = wnd->RestoredRC;
                wnd->condition = ISRESTORED;
                SendMessage(wnd, HIDE_WINDOW, 0, 0);
                wnd->attrib = wnd->restored_attrib;
                wnd->restored_attrib = 0;
                conditioning = TRUE;
                SendMessage(wnd, MOVE, wnd->RestoredRC.lf, wnd->RestoredRC.tp);
                wnd->RestoredRC = holdrc;
                SendMessage(wnd, SIZE, wnd->RestoredRC.rt, wnd->RestoredRC.bt);
                SendMessage(wnd, SETFOCUS, TRUE, 0);
                conditioning = FALSE;
                SendMessage(wnd, SHOW_WINDOW, 0, 0);
            }
            break;
        case MOVE:    {
            WINDOW wnd1 = Focus.FirstWindow;
            int wasVisible = isVisible(wnd);
            int xdif = (int) p1 - wnd->rc.lf;
            int ydif = (int) p2 - wnd->rc.tp;

            if (xdif == 0 && ydif == 0)
                return FALSE;
            if (wasVisible)
                SendMessage(wnd, HIDE_WINDOW, 0, 0);
            wnd->rc.lf = (int) p1;
            wnd->rc.tp = (int) p2;
            wnd->rc.rt = GetLeft(wnd)+WindowWidth(wnd)-1;
            wnd->rc.bt = GetTop(wnd)+WindowHeight(wnd)-1;
            if (wnd->condition == ISRESTORED)
                wnd->RestoredRC = wnd->rc;
            while (wnd1 != NULL)    {
                if (GetParent(wnd1) == wnd)
                  SendMessage(wnd1, MOVE, wnd1->rc.lf+xdif, wnd1->rc.tp+ydif);
                wnd1 = NextWindow(wnd1);
            }
            if (wasVisible)
                SendMessage(wnd, SHOW_WINDOW, 0, 0);
            break;
        }
        case SIZE:    {
            int wasVisible = isVisible(wnd);
            WINDOW wnd1 = Focus.FirstWindow;
            RECT rc;
            int xdif = (int) p1 - wnd->rc.rt;
            int ydif = (int) p2 - wnd->rc.bt;

            if (xdif == 0 && ydif == 0)
                return FALSE;
            if (wasVisible)
                SendMessage(wnd, HIDE_WINDOW, 0, 0);
            wnd->rc.rt = (int) p1;
            wnd->rc.bt = (int) p2;
            wnd->ht = GetBottom(wnd)-GetTop(wnd)+1;
            wnd->wd = GetRight(wnd)-GetLeft(wnd)+1;

            if (wnd->condition == ISRESTORED)
                wnd->RestoredRC = WindowRect(wnd);

            rc = ClientRect(wnd);
            while (wnd1 != NULL)    {
                if (GetParent(wnd1) == wnd &&
                        wnd1->condition == ISMAXIMIZED)
                    SendMessage(wnd1, SIZE, RectRight(rc), RectBottom(rc));
                wnd1 = NextWindow(wnd1);
            }

            if (wasVisible)
                SendMessage(wnd, SHOW_WINDOW, 0, 0);
            break;
        }
#endif
        case CLOSE_WINDOW:
            wnd->condition = ISCLOSING;
            if (wnd->PrevMouse != NULL)
                SendMessage(wnd, RELEASE_MOUSE, 0, 0);
            if (wnd->PrevKeyboard != NULL)
                SendMessage(wnd, RELEASE_KEYBOARD, 0, 0);
            /* ----------- hide this window ------------ */
            SendMessage(wnd, HIDE_WINDOW, 0, 0);
            /* --- close the children of this window --- */
            while (!DoneClosing)    {
                WINDOW wnd1 = Focus.LastWindow;
                DoneClosing = TRUE;
                while (wnd1 != NULL)    {
                    WINDOW prwnd = PrevWindow(wnd1);
                    if (GetParent(wnd1) == wnd)    {
                        if (inFocus == wnd1)    {
                            RemoveFocusWindow(wnd);
                            AppendFocusWindow(wnd);
                            inFocus = wnd;
                        }
                        SendMessage(wnd1,CLOSE_WINDOW,0,0);
                        DoneClosing = FALSE;
                        break;
                    }
                    wnd1 = prwnd;
                }
            }
            /* --- change focus if this window had it -- */
            SetPrevFocus(wnd);
            /* ------- remove this window from the
                    list of open windows ------------- */
            RemoveBuiltWindow(wnd);
            /* ------- remove this window from the
                    list of in-focus windows ---------- */
            RemoveFocusWindow(wnd);
            /* -- free memory allocated to this window - */
            if (wnd->title != NULL)
                free(wnd->title);
            if (wnd->videosave != NULL)
                free(wnd->videosave);
            free(wnd);
            break;
        default:
            break;
    }
    return TRUE;
}
/* ---- compute lower left icon space in a rectangle ---- */
#ifdef INCLUDE_SYSTEM_MENUS
static RECT LowerRight(RECT prc)
{
    RECT rc;
    RectLeft(rc) = RectRight(prc) - ICONWIDTH;
    RectTop(rc) = RectBottom(prc) - ICONHEIGHT;
    RectRight(rc) = RectLeft(rc)+ICONWIDTH-1;
    RectBottom(rc) = RectTop(rc)+ICONHEIGHT-1;
    return rc;
}
/* ----- compute a position for a minimized window icon ---- */
static RECT PositionIcon(WINDOW wnd)
{
    RECT rc;
    RectLeft(rc) = SCREENWIDTH-ICONWIDTH;
    RectTop(rc) = SCREENHEIGHT-ICONHEIGHT;
    RectRight(rc) = SCREENWIDTH-1;
    RectBottom(rc) = SCREENHEIGHT-1;
    if (GetParent(wnd))    {
        WINDOW wnd1 = (WINDOW) -1;
        RECT prc;
        prc = WindowRect(GetParent(wnd));
        rc = LowerRight(prc);
        /* - search for icon available location - */
        while (wnd1 != NULL)    {
            wnd1 = GetFirstChild(GetParent(wnd));
            while (wnd1 != NULL)    {
                if (wnd1->condition == ISMINIMIZED)    {
                    RECT rc1;
                    rc1 = WindowRect(wnd1);
                    if (RectLeft(rc1) == RectLeft(rc) &&
                            RectTop(rc1) == RectTop(rc))    {
                        RectLeft(rc) -= ICONWIDTH;
                        RectRight(rc) -= ICONWIDTH;
                        if (RectLeft(rc) < RectLeft(prc)+1)   {
                            RectLeft(rc) =
                                RectRight(prc)-ICONWIDTH;
                            RectRight(rc) =
                                RectLeft(rc)+ICONWIDTH-1;
                            RectTop(rc) -= ICONHEIGHT;
                            RectBottom(rc) -= ICONHEIGHT;
                            if (RectTop(rc) < RectTop(prc)+1)
                                return LowerRight(prc);
                        }
                        break;
                    }
                }
                wnd1 = GetNextChild(GetParent(wnd), wnd1);
            }
        }
    }
    return rc;
}
/* ----- terminate the move or size operation ----- */
static void TerminateMoveSize(void)
{
    px = py = -1;
    diff = 0;
    SendMessage(&dwnd, RELEASE_MOUSE, TRUE, 0);
    SendMessage(&dwnd, RELEASE_KEYBOARD, TRUE, 0);
    RestoreBorder(dwnd.rc);
    WindowMoving = WindowSizing = FALSE;
}
/* ---- build a dummy window border for moving or sizing --- */
static void near dragborder(WINDOW wnd, int x, int y)
{
    RestoreBorder(dwnd.rc);
    /* ------- build the dummy window -------- */
    dwnd.rc.lf = x;
    dwnd.rc.tp = y;
    dwnd.rc.rt = dwnd.rc.lf+WindowWidth(wnd)-1;
    dwnd.rc.bt = dwnd.rc.tp+WindowHeight(wnd)-1;
    dwnd.ht = WindowHeight(wnd);
    dwnd.wd = WindowWidth(wnd);
    dwnd.parent = GetParent(wnd);
    dwnd.attrib = VISIBLE | HASBORDER | NOCLIP;
    InitWindowColors(&dwnd);
    SaveBorder(dwnd.rc);
    RepaintBorder(&dwnd, NULL);
}
/* ---- write the dummy window border for sizing ---- */
static void near sizeborder(WINDOW wnd, int rt, int bt)
{
    int leftmost = GetLeft(wnd)+10;
    int topmost = GetTop(wnd)+3;
    int bottommost = SCREENHEIGHT-1;
    int rightmost  = SCREENWIDTH-1;
    if (GetParent(wnd))    {
        bottommost = min(bottommost, GetClientBottom(GetParent(wnd)));
        rightmost  = min(rightmost, GetClientRight(GetParent(wnd)));
    }
    rt = min(rt, rightmost);
    bt = min(bt, bottommost);
    rt = max(rt, leftmost);
    bt = max(bt, topmost);
    SendMessage(NULL, MOUSE_CURSOR, rt, bt);

    if (rt != px || bt != py)
        RestoreBorder(dwnd.rc);

    /* ------- change the dummy window -------- */
    dwnd.ht = bt-dwnd.rc.tp+1;
    dwnd.wd = rt-dwnd.rc.lf+1;
    dwnd.rc.rt = rt;
    dwnd.rc.bt = bt;
    if (rt != px || bt != py)    {
        px = rt;
        py = bt;
        SaveBorder(dwnd.rc);
        RepaintBorder(&dwnd, NULL);
    }
}
#endif
/* ----- adjust a rectangle to include the shadow ----- */
#ifdef INCLUDE_SHADOWS
static RECT adjShadow(WINDOW wnd)
{
    RECT rc;
    rc = wnd->rc;
    if (TestAttribute(wnd, SHADOW))    {
        if (RectRight(rc) < SCREENWIDTH-1)
            RectRight(rc)++;
        if (RectBottom(rc) < SCREENHEIGHT-1)
            RectBottom(rc)++;
    }
    return rc;
}
#endif
/* --- repaint a rectangular subsection of a window --- */
#ifdef INCLUDE_MULTIDOCS
static void near PaintOverLap(WINDOW wnd, RECT rc)
{
    int isBorder, isTitle, isData;
    isBorder = isTitle = FALSE;
    isData = TRUE;
    if (TestAttribute(wnd, HASBORDER))    {
        isBorder =  RectLeft(rc) == 0 &&
                    RectTop(rc) < WindowHeight(wnd);
        isBorder |= RectLeft(rc) < WindowWidth(wnd) &&
                    RectRight(rc) >= WindowWidth(wnd)-1 &&
                    RectTop(rc) < WindowHeight(wnd);
        isBorder |= RectTop(rc) == 0 &&
                    RectLeft(rc) < WindowWidth(wnd);
        isBorder |= RectTop(rc) < WindowHeight(wnd) &&
                    RectBottom(rc) >= WindowHeight(wnd)-1 &&
                    RectLeft(rc) < WindowWidth(wnd);
    }
    else if (TestAttribute(wnd, HASTITLEBAR))
        isTitle = RectTop(rc) == 0 &&
                  RectLeft(rc) > 0 &&
                  RectLeft(rc)<WindowWidth(wnd)-BorderAdj(wnd);

    if (RectLeft(rc) >= WindowWidth(wnd)-BorderAdj(wnd))
        isData = FALSE;
    if (RectTop(rc) >= WindowHeight(wnd)-BottomBorderAdj(wnd))
        isData = FALSE;
    if (TestAttribute(wnd, HASBORDER))    {
        if (RectRight(rc) == 0)
            isData = FALSE;
        if (RectBottom(rc) == 0)
            isData = FALSE;
    }
#ifdef INCLUDE_SHADOWS
    if (TestAttribute(wnd, SHADOW))
        isBorder |= RectRight(rc) == WindowWidth(wnd) ||
                    RectBottom(rc) == WindowHeight(wnd);
#endif
    if (isData)
        SendMessage(wnd, PAINT, (PARAM) &rc, 0);
    if (isBorder)
        SendMessage(wnd, BORDER, (PARAM) &rc, 0);
    else if (isTitle)
        DisplayTitle(wnd, &rc);
}
/* ------ paint the part of a window that is overlapped
            by another window that is being hidden ------- */
static void PaintOver(WINDOW wnd)
{
        RECT wrc, rc;
#ifdef INCLUDE_SHADOWS
        wrc = adjShadow(HiddenWindow);
        rc = adjShadow(wnd);
#else
        wrc = HiddenWindow->rc;
        rc = wnd->rc;
#endif
        rc = subRectangle(rc, wrc);
        if (ValidRect(rc))
            PaintOverLap(wnd, RelativeWindowRect(wnd, rc));
}
/* --- paint the overlapped parts of all children --- */
static void PaintOverChildren(WINDOW pwnd)
{
    WINDOW cwnd = GetFirstFocusChild(pwnd);
    while (cwnd != NULL)    {
        if (cwnd != HiddenWindow)    {
            PaintOver(cwnd);
            PaintOverChildren(cwnd);
        }
        cwnd = GetNextFocusChild(pwnd, cwnd);
    }
}
/* -- recursive overlapping paint of parents -- */
static void PaintOverParents(WINDOW wnd)
{
    WINDOW pwnd = GetParent(wnd);
    if (pwnd != NULL)    {
        PaintOverParents(pwnd);
        PaintOver(pwnd);
        PaintOverChildren(pwnd);
    }
}
/* - paint the parts of all windows that a window is over - */
static void near PaintOverLappers(WINDOW wnd)
{
    HiddenWindow = wnd;
    PaintOverParents(wnd);
}
/* --- paint those parts of a window that are overlapped --- */
static void near PaintUnder(WINDOW wnd)
{
    WINDOW hwnd = Focus.FirstWindow;
    while (hwnd != NULL)    {
        /* ---- don't bother testing self ----- */
        if (hwnd != wnd)    {
            /* --- see if other window is descendent --- */
            WINDOW pwnd = GetParent(hwnd);
            while (pwnd != NULL)    {
                if (pwnd == wnd)
                    break;
                pwnd = GetParent(pwnd);
            }
            /* ----- don't test descendent overlaps ----- */
            if (pwnd == NULL)    {
                /* -- see if other window is ancestor --- */
                pwnd = GetParent(wnd);
                while (pwnd != NULL)    {
                    if (pwnd == hwnd)
                        break;
                    pwnd = GetParent(pwnd);
                }
                /* --- don't test ancestor overlaps --- */
                if (pwnd == NULL)    {
                    /* ---- other window must be ahead in
                        focus chain ----- */
                    WINDOW fwnd = NextWindow(wnd);
                    while (fwnd != NULL)    {
                        if (fwnd == hwnd)
                            break;
                        fwnd = NextWindow(fwnd);
                    }
                    if (fwnd != NULL)    {
                        HiddenWindow = hwnd;
                        PaintOver(wnd);
                    }
                }
            }
        }
        hwnd = NextWindow(hwnd);
    }
    /* --------- repaint all children of this window
        the same way ----------- */
    hwnd = Focus.FirstWindow;
    while (hwnd != NULL)    {
        if (GetParent(hwnd) == wnd)
            PaintUnder(hwnd);
        hwnd = NextWindow(hwnd);
    }
}
/* paint the parts of a window that are under other windows */
static void near PaintUnderLappers(WINDOW wnd)
{
    WINDOW pwnd = wnd;
    /* find oldest ancestor younger than application window */
    while (pwnd != NULL && GetClass(pwnd) != APPLICATION) {
        if (TestAttribute(wnd, SAVESELF))
            break;
        wnd = pwnd;
        pwnd = GetParent(pwnd);
    }
    PaintUnder(wnd);
}
#endif
#ifdef INCLUDE_SYSTEM_MENUS
/* --- save video area to be used by dummy window border --- */
static void SaveBorder(RECT rc)
{
    Bht = RectBottom(rc) - RectTop(rc) + 1;
    Bwd = RectRight(rc) - RectLeft(rc) + 1;
    if ((Bsave = realloc(Bsave, (Bht + Bwd) * 4)) != NULL)    {
        RECT lrc;
        int i;
        int *cp;

        lrc = rc;
        RectBottom(lrc) = RectTop(lrc);
        getvideo(lrc, Bsave);
        RectTop(lrc) = RectBottom(lrc) = RectBottom(rc);
        getvideo(lrc, Bsave + Bwd);
        cp = Bsave + Bwd * 2;
        for (i = 1; i < Bht-1; i++)    {
            *cp++ = GetVideoChar(RectLeft(rc),RectTop(rc)+i);
            *cp++ = GetVideoChar(RectRight(rc),RectTop(rc)+i);
        }
    }
}
/* ---- restore video area used by dummy window border ---- */
static void RestoreBorder(RECT rc)
{
    if (Bsave != NULL)    {
        RECT lrc;
        int i;
        int *cp;
        lrc = rc;
        RectBottom(lrc) = RectTop(lrc);
        storevideo(lrc, Bsave);
        RectTop(lrc) = RectBottom(lrc) = RectBottom(rc);
        storevideo(lrc, Bsave + Bwd);
        cp = Bsave + Bwd * 2;
        for (i = 1; i < Bht-1; i++)    {
            PutVideoChar(RectLeft(rc),RectTop(rc)+i, *cp++);
            PutVideoChar(RectRight(rc),RectTop(rc)+i, *cp++);
        }
        free(Bsave);
        Bsave = NULL;
    }
}
#endif
/* ----- test if screen coordinates are in a window ---- */
static int InsideWindow(WINDOW wnd, int x, int y)
{
    RECT rc;
    rc = WindowRect(wnd);
    if (!TestAttribute(wnd, NOCLIP))    {
        WINDOW pwnd = GetParent(wnd);
        while (pwnd != NULL)    {
            rc = subRectangle(rc, ClientRect(pwnd));
            pwnd = GetParent(pwnd);
        }
    }
    return InsideRect(x, y, rc);
}
/* ----- find window that screen coordinates are in --- */
WINDOW inWindow(int x, int y)
{
    WINDOW wnd = Focus.LastWindow;
    while (wnd != NULL)    {
        if (SendMessage(wnd, INSIDE_WINDOW, x, y))    {
            WINDOW wnd1 = GetLastChild(wnd);
            while (wnd1 != NULL)    {
                if (SendMessage(wnd1, INSIDE_WINDOW, x, y)) {
                    if (isVisible(wnd))  {
                        wnd = wnd1;
                        break;
                    }
                }
                wnd1 = GetPrevChild(wnd, wnd1);
            }
            break;
        }
        wnd = PrevWindow(wnd);
    }
    return wnd;
}


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.