The Design and Implementation of VIOWIN - Part 6

Written by Larry Salomon Jr.

Introduction
For my job, I once had to write an application that ran only when OS/2 booted from the floppy diskettes. Because I had no access to the functionality PM provides, I resorted to a line-oriented interface, where messages were displayed on the screen and scrolled up when necessary. It was a good interface, I thought; it was fully NLS enabled and had intelligent defaults so the user basically only had to type in the name of the application. Unfortunately, the Quality Assurance team didn't concur with my opinion. "We want a nice interface!" one exclaimed. "Yeah, one with different windows and such!" another shouted.

I was backed into a corner that I could only get out of one way.

This series describes the design and implementation of VIOWIN, a library that implements a subset of the Win APIs provided by PM for full-screen sessions. The reasoning behind writing this series is that it provided me and will hopefully provide you with some unique insights into how a windowing system is developed; and since it is based on PM, your familiarity with the already defined interface will increase your capability to fully understand what is being described.

Obviously, this series assumes you have PM application development experience, but it isn't required.

This Month
This month, we will begin our look at the windows classes that are implemented in VIOWIN.

Scaffolding
First, we must look at how and where the classes are registered. In issue 2-9, we briefly looked at how ordinal 1 was loaded by vwInitialize and called to give the DLL an opportunity to perform any initialization it needed. We could have just as easily used the DLL initialization routine, which IBM's C-Set++ product allows you to redefine. It needs to be mentioned that the various window procedures are exported in the .DEF file, but this is probably unnecessary since the vwSendMsg and vwPostMsg routines simply call the procedure directly using the function address that was passed to vwRegisterClass.

Entryfields
Let us now begin to look at the VWWC_ENTRYFIELD class. Since we should all be familiar with its PM counterpart, we will skip the introduction to the class and will instead dive into the code itself. The first thing that should be discovered is the instance data used by the instantiations of the class.
 * ulSzStruct is the size of the structure.
 * bChanged is TRUE when the entry field's contents have changed since the last EM_QUERYCHANGED message.
 * bDirty is TRUE when a call to vwSetWindowText exceeded the text limit and, thus, the text the VIOWIN system thinks the entry field has is different from what the entry field really has.
 * bSetParms is used in conjunction with bDirty to synchronize the differences in window text.
 * sAnchor is the anchor point.
 * sCursor is the cursor point.
 * sFirstChar is the first visible character index.
 * pchBuf points to the buffer containing the text.
 * usSzBuf is the size of the buffer.

Utility Functions
As you can well imagine, there are a number of tasks which are used frequently or are complex enough to warrant a separate function. These functions are listed below:
 * scrollText - scrolls the text of the window by one-half of the window's width either left or right.
 * deltaCursor - moves the cursor one position to the left or right.
 * textChanged - sets bChanged and bDirty and sends an EN_CHANGED notification to the owner.
 * handleVkey - processes virtual keystrokes.
 * handleChr - processes non-virtual keystrokes.

The scrollText Function
The code for the function is presented below. This function is fairly straightforward; it calculates the new "first visible character", performs a bounds check, sends the EN_SCROLL notification, and repaints the window.

The deltaCursor Function
The code for the function is presented below. This function is a bit more complication, but should still be easy to understand. It calculates the new cursor position and calls scrollText if the cursor goes off-screen.

The textChanged Function
The code for the function is presented below. This code is trivial.

The handleVkey Function
The code for the function is presented below. This function is one of the workhorses (the other being the handleChr function). It classifies the keystroke according to the modifiers (Ctrl, Alt, or Shift) and processes the keys individually. It relies heavily on deltaCursor.

The handleChr Function
The code for the function is presented below. This function inserts the character pressed into the buffer or overwrites the character at the current cursor position with the character pressed.

The Window Procedure
The window procedure is shown below. The window procedure, too, is fairly trivial, but there are two noteworthy pieces: the processing for the WM_SETWINDOWPARAMS and WM_QUERYWINDOWPARAMS messages. Here is where the bDirty and bSetParms flags are used. Since it would be costly, in terms of performance, to call vwSetWindowText every time a character is pressed, the entryfield maintains its own copy of the text and only updates the system's version of the text when someone requests it via vwQueryWindowText. When this function is called, the system sends us a WM_QUERYWINDOWPARAMS message, during which we check bDirty. If it is TRUE, we set bSetParms and call vwSetWindowText. This results in us receiving a WM_SETWINDOWPARAMS message, but we don't want to update ourselves because of the annoying flicker that will take place. So, only if bSetParms is not set do we update the internal buffer.

Conclusion
This month, we looked at the code for the entryfield. Next month, we will look at the next window class, VWWC_STATIC. Please send me your thoughts on the approach I've taken this month, so that I can modify it as necessary.

Please note that the source is not included in any of the .ZIP files that accompany this issue. When all of the window classes have been discussed, the source for the entire class library will be given as well as the source for the VIOWIN library.