PMCX - What's zat?

by Matt Smith, Prominare, Inc.

In the last issue of The Developer Connections News, we introduced the Universal Resource Editor (URE) and briefly touched upon PMCX controls. Well, what are these PMCX controls all about?

PMCX stands for PM Control Extensions. In a nutshell, it's a mechanism that lets you integrate and use custom controls within applications and design tools such as the Universal Resource Editor. It is very easy to create a custom control to use within an application, but it has been very difficult to add or maintain your resource script files to be able to use the custom control within your dialogs.

PMCX makes this whole process much easier. It basically allows for a custom control to be 'wrappered' in a manner that can be then clipped into tools like URE so you can treat the control almost like a standard Presentation Manager (PM) control. This includes the visual display of the control within a window or dialog and, for a styles dialog, allows the changing of the text and styles of the control.

The Architecture
PMCX controls are made up of three exported functions plus the code that constitutes the actual custom control:
 * The first function registers the control with OS/2 PM. This function is used both by your final application and by URE.
 * The second function is used by URE to query the capabilities of the control. It allows URE to reserve space for the text and control data. It also provides the mechanism for defining the symbolic styles through a lookup that loads the style from the PMCX control's DLL string table resources.
 * The final function handles the styles dialog. This dialog allows the user to enter text, ID symbol and value, control-specific styles, and to use the bi-directional as well as font and colors (also known as presentation parameters).

Where to Start
A few simple conventions are generally used to aid the creation and usage of PMCX controls. For example, URE uses the base name of the DLL to form the names of the exported functions. Quite simply, if you name your DLL OPUS.DLL, the names of the three functions would be OpusRegister, OpusQuery, and OpusStyles. The exported ordinals of these three functions are 1 for the OpusRegister, 2 for the OpusQuery, and 3 for the OpusStyles.

For the most part, URE is responsible for most aspects of the internal housekeeping when the control is being used in a design (such as allocating the memory for control data and text during the styles dialog display). URE also keeps track of bi-directional, font, and color selections. It provides the interface to areas that allow the selection of ID symbols and values, the display of Bi-directional Options dialog, and the Font Colors dialog. It also has helper functions that check for CUA compliance within the text of the control.

An Example
The best way to show you how this all works is through an example. The sample PMCX control, BANNER.DLL, was created specifically for this article. URE provides a wealth of sample PMCX controls with full source. This PMCX control is specifically designed to display the text within a colored ellipse. It also lets you display the text either horizontally or vertically and with an optional shadow. Figure 1 shows what the final control looks like:

The first function for the banner control is BannerRegister:  BOOL EXPENTRY BannerRegister(HAB hAB) { /* Register the control class with OS/2 */ /* Presentation Manager and return registration */ /* result */

return(WinRegisterClass(hAB, 'Banner', BannerWndProc, CS_SYNCPAINT | CS_SIZEREDRAW, USER_CWINDOWWORDS)); }  ''Sample Code 1. BannerRegister function''

As you can see, the function registers the control class with OS/2 PM. The function is used within URE to register the control with PM and it is also used in the final application so when the control is referenced through a dialog resource, it is created and displayed properly.

The second function, BannerQuery, is used to define the capabilities of the control to URE:  BOOL EXPENTRY BannerQuery(PUSERINFO pUserInfo) { /* Complete the version and number of control */ /* types. In Version 1 of PMCX, only one */ /* control type is used. */ pUserInfo->ulMajor = 1; pUserInfo->ulMinor = 0; /* Complete the author and control classname */

strcpy(pUserInfo->szAuthor, 'Prominare Inc.'); strcpy(pUserInfo->szClassname, 'Banner'); strcpy(pUserInfo->szName, 'Banner'); /* Complete the default size and style of the */ /* first user control type */

pUserInfo->utDefined[0].cx = 50; pUserInfo->utDefined[0].cy = 14; pUserInfo->utDefined[0].flStyle = WS_VISIBLE;

/* Set the maximum amount of text control can */ /* accept including NULL termination byte */

pUserInfo->utDefined[0].cMaxText = CCHTEXTMAX;

/* Save the style's dialog ID, type, control */ /* data size and count of style masks */

pUserInfo->utDefined[0].idDlg = DLG_BANNERSTYLES; pUserInfo->utDefined[0].flOptions = PMCXOPT_REFRESH | PMCXOPT_STYLECHG; pUserInfo->utDefined[0].ulType = UTYPE_PRIVATE; pUserInfo->utDefined[0].cCtlData = 0UL; pUserInfo->utDefined[0].flStyleType = STYLETYPE_BITFLAGS; pUserInfo->utDefined[0].cMasks = 3UL; pUserInfo->utDefined[0].stMasks[0].flStyleMask = BNS_HORIZONTAL; pUserInfo->utDefined[0].stMasks[0].idStyle = IDS_BNS_HORIZONTAL; pUserInfo->utDefined[0].stMasks[1].flStyleMask = BNS_VERTICAL; pUserInfo->utDefined[0].stMasks[1].idStyle = IDS_BNS_VERTICAL; pUserInfo->utDefined[0].stMasks[2].flStyleMask = BNS_SHADOWTEXT; pUserInfo->utDefined[0].stMasks[2].idStyle = IDS_BNS_SHADOWTEXT;

/* Save the description of the control */

strcpy(pUserInfo->utDefined[0].szDescription, 'PMCX Example');

/* Return the success flag back to Universal */ /* Resource Editor */ return(TRUE); }  ''Sample Code 2. BannerQuery function''

The basic role of the BannerQuery function is to provide the details for the control that let URE properly interact with the control during the design phase. This includes defining the capabilities of the control in terms of how it should be displayed after a style change, the resource ID of the styles dialog, the IDs of the strings for the styles, the default creation size, the amount of text that it can handle, and the amount of control data required. The third function is the BannerStyles dialog. Figure 2 shows the dialog being designed within URE.

As you can see, the dialog is very straightforward. The BannerStyles dialog itself uses a PMCX control, IDBOX.DLL. This control is provided to allow the proper display of the ID symbols and values through the ID field. The actual source for the function is straightforward as well. (Because the source for the styles dialog is quite large, we have put it on disc 1 of your accompanying CD-ROMs in \SOURCE\DEVNEWS\VOL9\BANNER.)

The most significant aspect of how the dialog really works is that a structure is passed to the dialog through mp2 in the WM_INITDLG message. This structure, USERSTYLE, provides all the necessary information for the control that will allow the styles dialog to operate as though it were a built-in styles dialog from URE. The first thing the dialog does is squirrel away the pointer into the window words of the dialog. This allows the data to be retrieved during normal message processing. A macro, PDATAFROMDLG, gets the pointer from the window words.

URE provides helper functions for the style dialog to allow it to be used in a manner consistent with the URE style dialogs. These helper functions give the style dialog access to ID field, the Font Colors dialog, symbol verification, and CUA compliance. URE provides the addresses of these routines within the USERSTYLE structure in the following elements:
 * opfnSetSymbol - ID field symbol and value set helper function
 * opfnGetSymbol - Symbol ID and value validation and retrieval helper function
 * opfnGetFontClr - Font and colors helper function
 * opfnCUACheck - CUA compliance helper function
 * opfnRealloc - Memory reallocation function similar to C library function realloc, used for allocating memory to store variable sized CTLDATA

The final resource script file for the PMCX control is: 
 * 1) define INCL_WINSYS
 * 2) define INCL_WINSTDDLGS
 * 3) define INCL_WINSTDSPIN
 * 4) define INCL_NLS
 * 5) define INCL_SW


 * 1) include 

DLGTEMPLATE DLG_BANNERSTYLES 850 MOVEABLE DISCARDABLE BEGIN DIALOG 'PMCX Banner Styles', DLG_BANNERSTYLES, 6, 28, 220, 107, FS_NOBYTEALIGN | FS_DLGBORDER | WS_CLIPSIBLINGS | WS_SAVEBITS | WS_VISIBLE, FCF_TITLEBAR | FCF_SYSMENU PRESPARAMS PP_FONTNAMESIZE, '8.Helv' BEGIN CONTROL 'Enter text and selection style options.', -1, 5, 95, 118, 8, WC_STATIC, SS_TEXT | DT_VCENTER | WS_VISIBLE PRESPARAMS PP_FOREGROUNDCOLORINDEX, CLR_BLACK CONTROL 'Text:', -1, 5, 83, 18, 8, WC_STATIC, SS_TEXT | DT_RIGHT | DT_VCENTER | WS_VISIBLE CONTROL '', MLE_TEXT, 25, 60, 190, 32, WC_MLE, MLS_BORDER | MLS_HSCROLL | MLS_IGNORETAB | MLS_VSCROLL | MLS_DISABLEUNDO | WS_TABSTOP | WS_VISIBLE CONTROL 'ID:', -1, 5, 50, 18, 8, WC_STATIC, SS_TEXT | DT_RIGHT | DT_VCENTER | WS_VISIBLE CONTROL '', IDBX_SYMBOLID, 25, 50, 190, 8, 'ID.Box', WS_TABSTOP | WS_VISIBLE CONTROL 'Styles', -1, 5, 20, 105, 26, WC_STATIC, SS_GROUPBOX | WS_VISIBLE CONTROL '~Horizontal', RB_HORIZONTAL, 8, 30, 44, 8, WC_BUTTON, BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL '~Vertical', RB_VERTICAL, 8, 22, 38, 8, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL '~Shadow text', CB_SHADOWTEXT, 55, 30, 50, 8, WC_BUTTON, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL 'Base styles', -1, 115, 20, 100, 26, WC_STATIC, SS_GROUPBOX | WS_VISIBLE CONTROL '~Visible', CB_WS_VISIBLE, 118, 30, 38, 8, WC_BUTTON, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL '~Group', CB_WS_GROUP, 118, 22, 38, 8, WC_BUTTON, BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE CONTROL '~Disabled', CB_WS_DISABLED, 165, 30, 38, 8, WC_BUTTON, BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE CONTROL '~Tab stop', CB_WS_TABSTOP, 165, 22, 39, 8, WC_BUTTON, BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE CONTROL 'Enter', DID_OK, 33, 5, 60, 12, WC_BUTTON, BS_PUSHBUTTON | BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL 'Cancel', DID_CANCEL, 94, 5, 60, 12, WC_BUTTON, BS_PUSHBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL 'Font & color...', DID_PRESPARAM, 155, 5, 60, 12, WC_BUTTON, BS_PUSHBUTTON | WS_TABSTOP | WS_VISIBLE END END
 * 1) include 'banner.h'

CODEPAGE 850 STRINGTABLE MOVEABLE DISCARDABLE BEGIN IDS_BNS_HORIZONTAL, 'BNS_HORIZONTAL' IDS_BNS_SHADOWTEXT, 'BNS_SHADOWTEXT' IDS_BNS_VERTICAL, 'BNS_VERTICAL' END  Sample Code 3.

The definitions file for the PMCX control is:  LIBRARY Banner INITGLOBAL TERMGLOBAL DESCRIPTION 'PMCX Example'

CODE LOADONCALL EXECUTEREAD NOIOPL NONCONFORMING DATA LOADONCALL READWRITE NOIOPL MULTIPLE NONSHARED

PROTMODE

EXETYPE OS2

EXPORTS BannerRegister @ 1 BannerQuery @ 2 BannerStyles @ 3  Sample Code 4. Once the final PMCX control is compiled and linked, it is made accessible to URE through the PMCX/Temporary Control Definition dialog, shown in Figure 3.

Then the control can be used in the design of an application. You select it from the PMCX/Temporary Control Selection dialog (see Figure 4), where it is listed along with other PMCX controls registered within URE.

When you add the control to the dialog being designed, the PMCX Banner Styles dialog for the control is displayed (see Figure 5). It lets you define the text, ID symbol, and value along with the setting of the control's styles, fonts, and colors.

You can see that the control appears as intended within the dialog being designed. This makes the process of visualizing the final dialog easy, because you can see the final relationships between controls.

PMCX finally gives you a way of easily incorporating your custom controls within your dialogs and resource scripts. The best part is that you can see what you are trying to do. And if you are prototyping, it makes it easier to show your ideas to someone else.