Drawing your own listboxes

By Roger Orr

Introduction
One of the strengths of PM is that it encourages re-use of code with the attendant benefits this brings. This is true of both pure application code and also of the system's own window classes - in particular the standard controls.

For those whose PM is a bit rusty, a PM control is a window for user command/control input. A push button and a menu are both examples of controls. They provide the consistent look and feel of PM across all applications.

This re-use of code is supported by the PM API in a variety of ways.

For example, a previous article in this magazine by Adrian Thompson described writing a DLL to add line numbers to the standard multi-line edit (MLE) control by re-registering a system defined window class.

Another common way is to use WinSubClassWindow to replace the usual window procedure for a specific control window with your own, and call the usual window procedure after your own application-specific actions.

However it appears to be less well known that some of the system controls have standard methods to allow you to replace some or all of the DRAWING of the control, while leaving the other behaviour of the control, such as the focussing and mouse behaviour, unchanged.

The advantages of this method is that since the behaviour of the control is unchanged the PM user will have an intuitive understanding of how it will behave. In addition you do not have to have exhaustive knowledge of all the various actions the control takes for the possible input events such a mouse movement, etc. Finally, if enhancements are made to the way the control operates in subsequent versions of OS/2 you inherit them for nothing.

Overview of the example
I have chosen to describe a simple example of using this technique in a list box. The example program draws a window containing a list of five of the system icons and their name, demonstrating how you can mix text, colour and graphics in a list box.

The basic method is to create a list box with the LS_OWNERDRAW style. PM will then send you two message values: A very similar method is provided for the menu control, whereby the menu item attribute MIS_OWNERDRAW can be specified for some or all of the items in a menu.
 * a WM_MEASUREITEM message to obtain the height (and optionally the width) of each item
 * a WM_DRAWITEM message when a specific item in the list box is to be drawn. The message contains a structure describing the item to draw.

The push button control provides a similar feature by providing a style of BS_USERBUTTON. A WM_CONTROL message of type BN_PAINT is sent when the button needs to be drawn.

Programming notes
I am using Microsoft C 6.00 and OS/2 1.20 & 1.30. The compilation command I am using is: cl /G2s drawlist.c -link /pm:pm The program is pared to the minimum for this article. Note in particular that there is no processing of the LN_SELECT or LN_ENTER commands from the list box control so the example doesn't actually DO anything!

The list box is created in the client area of a size such that the border of the list box does not appear, and the WM_SIZE message is processed to keep the window sizes in step.

The WM_MEASUREITEM message returns the height of each item in the box. I used the height of the icon plus some spacing for readability.

The WM_DRAWITEM message processing is the bulk of the interest.

PM passes a OWNERITEM pointer containing (among other things) a window handle, a presentation space to use for drawing, a rectangle in which to draw, some flags and the application defined item handle.

The drawitem procedure uses the item handle as the address of the data structure which contains a system icon number and a string. (I used system icons because everyone would have them on their machine!)

The item handle is set using the LM_SETITEMHANDLE message once the item has been added to the list box.

The first point to note is that the rectangle may contain only PART of the list box item being drawn, in particular when the item being drawn is the last visible item and is not complete. For this reason the local rectangle structure is filled in relative to the TOP of the passed rectangle.

The second point to note is that the rectangle may need blanking first, and this is done by the WinFillrect call.

Then the icon and text are drawn using the presentation space provided. I have picked different colours for the text to differentiate selected and unselected, note these are not the standard colours!

The usual system colours for a list box are SYSCLR_WINDOWTEXT on SYSCLR_WINDOW for unselected items and SYSCLR_HILITEFOREGROUND on SYSCLR_HILITEBACKGROUND for selected items. (Under OS/2 1.10 the selected item was shown with the colours inverted.) I recommend using these for some or all of the list box item unless there are strong reasons for another colour choice or highlight method.

The final point to note is that the fsState AND fsStateOld flags in the passed structure are cleared. These flags are used by the list box control to allow you do the drawing but to leave the highlighting to the list box. Unfortunately this is rarely possible since the default highlighting is not usually compatable with the non-default drawing! In this example the highlighting is done by changing the colours used for the text based on the state of the fsState flag. Setting the flags to 0 before returning tells the list box control that all the drawing and highlighting have been performed.


 * Note:I have noticed problems with list boxes in the client area when WM_ERASEBACKGROUND message processing is non-default. The scroll bar sometimes disappears or is only partly drawn.