Introduction to PM Programming - Feb 1996
Written by Larry Salomon Jr.
The purpose of this column is to provide the readers out there who are not familiar with PM application development the information necessary to satisfy their curiosity, educate themselves, and give them an advantage over the documentation supplied by IBM. Of course, much of this stuff could probably be found in one of the many books out there, but the problem with books in general is that they don't answer the questions you have after you read the book the first time through.
I will gladly entertain feedback from the readers about what was "glossed over" or what was detailed well, what tangential topics need to be covered and what superfluous crap should have been removed. This feedback is essential in guaranteeing that you get what you pay for. [grin]
It should be said that you must not depend solely on this column to teach you how to develop PM applications; instead, this should be viewed as a supplement to your other information storehouses (books, the network conferences, etc.). Because this column must take a general approach, there will be some topics that you would like to see discussed that really do not belong here. Specific questions can be directed to me via email and I will do my best to answer them in a timely fashion.
The results are in! You want more "meat" with your potatoes. Heck, I could have figured that out, since anyone can read an online reference to find out what a particular message does. Anyway, we will look at the slider control, how it is used, and dissect a sample application which uses three sliders to display a fillet.
What Is a Slider, Anyway?
Back in the days when OS/2 1.2 was around, a lot of people found that they were using the scroll bars to display various types of things. However, when these uses were viewed collectively, it was noticed that the things that were being done had similar characteristics:
- Most had feedback to display a number corresponding to the scrollbar position - whether it was the size of an object, the length of time to process a calculation, or something else, the user had to know what they were selecting.
- Most used the number from the scrollbar to control some aspect of the application - I keep thinking about the myriad of applications that allowed to you to define a color. All would display three scroll bars which controlled the red, green, and blue components of the color being defined and this color was updated as you moved the scroll bars.
When combined with other, commonly-seen user interface components, e.g. progress indicators, it was decided that these applications would be better served by a window class that was tailored somewhat for these types of things. Thus, the slider was born. (The circular slider made its debut in 2.0 and the programming interface in 2.1.)
The slider (shown below) has many components:
Figure 1: A linear slider.
- Slider arm - this is the part that the user moves with the mouse or keyboard. It properly indicates that it has the input focus by displaying a dot in the center of itself. The programmer may additionally specify "snap to scale," meaning that the slider arm is always on an integral part of the scale - no intermediate values are allowed.
- Slider buttons - these are alternate ways of moving the slider arm one unit lower or higher. (I hesitate to use "up" / "down" or "left" / "right" since the slider really doesn't care if it is oriented horizontally or vertically.)
- Ribbon strip - this is the "shaft" on which the slider arm travels. This is frequently "ownerdrawn."
- Tick marks - these are indicators of the scale used in the slider. They may be sized (shown) collectively or individually. You could, for example, only show every fifth item on the scale.
- Detent - these markers are inserted by the application to mark points of interest on the slider.
The slider has some concepts associated with it, which you need to know before we may continue.
- Slider type - we haven't mentioned it, but there are two types of sliders: linear and circular. We will initially deal exclusively with the linear slider, but we will get to the circular one afterwards.
- Home position - this is the position of the slider arm corresponding to point "0". This may be up or down, left or right, depending on the style you use to create the slider.
- Scales - the linear slider provides two scales for you to use and some messages will return to you information based on a particular scale. The primary scale is the one you will reference the most, but you may have use for the secondary scale.
I won't bore you with the parameters and such of each message. Instead, I will list each message, briefly describe the message's purpose, and annotate as necessary with any thoughts that come into my head.
SLM_ADDDETENT - this message adds a detent to the slider. Since you may add more than one, you must save the identifier returned so that you can send it back to the slider when you send future messages related to this detent.
SLM_QUERYDETENTPOS - this message returns the position of a detent relative to the home position of the slider and on which scale the detent resides. There are two problems, however: 1) there is no way that I know of to add a detent to the secondary scale, and 2) the position is returned in pels and not increments of the associated scale. The second item isn't such a bad thing because a detent will usually refer to points of reference that are not integral multiples of the scale unit. The first item seems to point out an oversight, but maybe I'm just missing something.
SLM_QUERYSCALETEXT - this message returns the text associated with a specific tick mark on the primary scale.
SLM_QUERYSLIDERINFO - this message allows you to query any of four pieces of information about the slider, including the slider arm position and the size of the ribbon strip. For the slider arm position, you may request it either in pels or units along the primary scale. Again, I find it difficult to understand why the secondary scale isn't allowed here.
SLM_QUERYTICKPOS - this message returns the (x,y) position of a particular tick mark.
SLM_QUERYTICKSIZE - this message returns the length of a particular tick mark. Note that all ticks have a width of 1 pel.
SLM_REMOVEDETENT - this message removes a specific detent from the slider. Besides the lack of a scale specifier, the slider lacks a way to remove all detents as well.
SLM_SETSCALETEXT - this messages sets the text for a specific tick mark.
SLM_SETSLIDERINFO - this message allows you to specify the size and position of the components of the slider. I would have liked to see a way to set the position of the slider buttons.
SLM_SETTICKSIZE - this message allows you to set the size of a specific tick mark or all tick marks. Tick marks have an initial size of 0, so you must send this message if you want to see any ticks.
About The Sample Application
Now that we've taken an admittedly accelerated look at the linear slider, let us now move forward to a sample application. The application doesn't utilitize every feature of the slider control, but it shows enough of the feature set to be useful to us.
The slider sample application
What this program does is display a fillet. A fillet is a mathematical construct composed of a curve drawn in relation to the lines connecting three points. (I am not a guru on this topic, so I will stop the discussion here.) The vertical position of any of the three points may be changed via a slider which corresponds to a specific point. Additionally, after the position is changed, a detent is added to indicate the current position of the point along the slider. To graphically demonstrate the effects of moving the points, a call to GpiPolyFillet is made to draw the construct as well.
Before we get into the code, a few other points of interest need to be mentioned:
Modeless dialogs - I'm not sure if we've discussed these beasts in this column, and I'm too lazy to do an exhaustive search, so I'll describe them anyway. A dialog, as we know, is a window temporarily created to obtain information from the user so that the application may continue its processing. These are also referred to as modal dialogs.
A modeless dialog, however, is not created temporarily, but is instead utilized for a longer period of time and is used concurrently with the application (versus stopping the application until a modal dialog is completed). Modeless dialogs are commonly used for palettes in graphics applications, such as a color palette or tool palette.
Another common use of modeless dialogs is to create a main window with many child windows in them. We could, of course, repeatedly call WinCreateWindow() within our WM_CREATE message to create the child windows, but why go through all of the typing when there is an easier way? Additionally, dialogs were never meant to be minimized and there are some nasty caveats when trying to get the icon to display properly if you minimize a dialog. Instead, we'll call WinCreateStdWindow() to create a "regular" window (without the minimize problems) and then call WinLoadDlg() to create a modeless dialog as a child of the client window. WinLoadDlg() will create all of the child windows and we can go on our merry way.
Subclassing within dialogs - if you need to provide some customized painting within a dialog, you can do things the hard way or the easy way. The hard way is to register a window class in the initialization of your application and then specify this window class in the DIALOGTEMPLATE definition of your resource file. However, support for user- defined classes within the Dialog Editor is dismal, so the easy way is usually used instead.
The simplest of all window classes is a simple text control, aligned left and top to take advantage of default styles. Wouldn't it be easier to create one of these and subclass it instead? This way, you can easily see what's going on within the Dialog Editor and you can let the WC_STATIC class handle the petty details of being a window while you concentrate on the animated logo that you want to display in your "Product information" dialog. We use subclassing to display the fillet in our window.
Instead of going through the source code line-for-line, I commented it liberally and will leave it up to you to read through it for comprehension. Believe me when I say that the application is indeed a simple one. Should you have any questions, however, feel free to email me.