Section 8 Home page Section 10

9. Multi-Window Programming

So far, we have managed the display of a single window. Managing more than one window is not much different and, thanks to the structure we’ve used above, requires relatively few changes to the earlier code.

9.1. The Window List

The critical change is to make the win_data structure a list of instances of such structures. We achieve this by adding a single field to win_data, which is a pointer to the next window in the list:

struct win_data {
        int handle;     /* identifying handle of the window */

        /* OTHER SLOTS */

        struct win_data * next; /* pointer to next item in list   <1> */
};
  1. The added slot provides a pointer to the next item in the list.

As we create each window, we add its pointer to the end of the current window list. In our example I create the three windows inside start_program; for a larger program, you may want to put this in a separate window-creation function.

void start_program (void) {
        struct win_data wd1;
        struct win_data wd2;
        struct win_data wd3;
        int dum, fullx, fully, fullw, fullh;

        graf_mouse (ARROW, 0L); /* ensure mouse is an arrow */
        wind_get (0, WF_WORKXYWH, &fullx, &fully, &fullw, &fullh);

        /* 1. set up and open our first window */
        wd1.handle = wind_create (NAME|CLOSER|FULLER|MOVER|SIZER, fullx, fully, fullw, fullh);
        wind_set (wd1.handle, WF_NAME, "Example: Version 6 - Blake", 0, 0);
        wind_open (wd1.handle, fullx, fully, 300, 200);
        wd1.poem = poem1;
        wd1.next = NULL;

        wd1.horz_posn = 0;
        wd1.vert_posn = 0;
        vst_point (app_handle, 11, &dum, &dum, &wd1.cell_w, &wd1.cell_h);

        /* set up and open our second window */
        wd2.handle = wind_create (NAME|CLOSER|FULLER|MOVER|SIZER, fullx, fully, fullw, fullh);
        wind_set (wd2.handle, WF_NAME, "Example: Version 6 - Keats", 0, 0);
        wind_open (wd2.handle, fullx, fully, 300, 200);
        wd2.poem = poem2;
        wd2.next = NULL;

        wd2.horz_posn = 0;
        wd2.vert_posn = 0;
        vst_point (app_handle, 11, &dum, &dum, &wd2.cell_w, &wd2.cell_h);

        wd3.horz_posn = 0;
        wd3.vert_posn = 0;
        vst_point (app_handle, 11, &dum, &dum, &wd3.cell_w, &wd3.cell_h);

        /* add second window to end of list */
        wd1.next = &wd2;                                // <1>

        /* set up and open our third window */
        wd3.handle = wind_create (NAME|CLOSER|FULLER|MOVER|SIZER, fullx, fully, fullw, fullh);
        wind_set (wd3.handle, WF_NAME, "Example: Version 6 - Wordsworth", 0, 0);
        wind_open (wd3.handle, fullx, fully, 300, 200);
        wd3.poem = poem3;
        wd3.next = NULL;

        /* add second window to end of list */
        wd1.next->next = &wd3;                          // <2>

        /* 2. process events for our window */
        event_loop (&wd1);

        /* 3. close and remove our windows */
        wind_close (wd1.handle);
        wind_delete (wd1.handle);
        wind_close (wd2.handle);
        wind_delete (wd2.handle);
        wind_close (wd3.handle);
        wind_delete (wd3.handle);
}
  1. Use wd1 as the head of the list, so attach wd2 to it.

  2. Attach wd3 as the third element of the list, beginning with wd1.

We still pass the start of the list of windows to event_loop. However now, because each event could apply to any one of our windows, we must find the correct instance of win_data for each event. This is done in its own function, by looking for the window’s handle. The following function simply walks along the list of win_data instances, until it finds the one with a handle matching the target handle:

struct win_data * get_win_data (struct win_data * wd, int handle) {
        while (wd != NULL) {                            // <1>
                if (wd->handle == handle) break;        // <2>
                wd = wd->next;                          // <3>
        }
        return wd;
}
  1. Repeat the search until the end of the list.

  2. When the current handle matches the target handle, end the search.

  3. Move on to the next window in the list.

Finally, each event which requires an instance of win_data must first find the correct win_data based on the window handle which triggered the event. For example, the REDRAW event now looks like:

case WM_REDRAW:
        do_redraw (get_win_data(wd, msg_buf[3]),  // <1>
                   (GRECT *)&msg_buf[4]);
        break;
  1. Find the win_data corresponding to the window handle which triggered this event.

The close event may need some care. In this example, clicking close for any window will quit the application. If you wish to have a more dynamic system, with windows that can open and close at arbitrary times, you will need to process the closed event based on the window handle. For an example of doing this see Version 7 of the sample program.

9.2. Sample Program: Version 6

Version 6 provides a display with three displayed poems. Notice how few changes were required to the source code, but the final system is very flexible: each window can be moved, resized and handled separately.