home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
contro.zip
/
CONTROL.TXT
< prev
next >
Wrap
Text File
|
1994-12-28
|
18KB
|
606 lines
Creating user controls- not for the faint of heart
There are two basic types of user controls, simple and complex.
The simple type does not require one to create any screens, as
Gpf supplies a default. The tougher one has the "extended"
feature of the default screen turned on, and one must create the
entire infrastructure to display and make use of such a
dialogue.
In both cases the developer is responsible for the entire
behavior of the user control, which can be very simple for
static-like entities, or a real pain for interactive objects
(for example, building a genuine multi-column list box, or
formatted data entry fields, or SQL-wise controls, etc.)
This package contains:
a. a minimal user control, similar to STATIC TEXT controls, but
auto sizing and with fancy shadowing. No extended dialog is
required.
b. the same control with an extended dialog, permitting the Gpf
developer to specify some extra properties.
A. The Minimal Control
We will call this control "SHADOW". You will need the following
files:
GPFUC.H do not write, included with the Gpf product
SHADOW.H you write this, though not strictly required
SHADOW.C includes GPFUC.H, SHADOW.H, and is the driver code.
It must contain at least (2) entry points expected by Gpf, and
an entire window procedure for the control. This window
procedure will be fairly simple.
SHADOW.DEF linker information, and exports the required entry
points
SHADOW.MAK a makefile, necessary if you hate to type a lot
When all is said and done you will have created:
SHADOW.DLL this will need to be moved to one of your LIBPATH
locations
SHADOW.LIB IMPLIBed, nice to make, but again not strictly
necessary
SHADOW.OBJ which can be discarded after the DLL is generated
For this simple example we will not use a SHADOW.H, all source
will be in SHADOW.C. What is required in this file?
For the purposes of Gpf, two and only two exported functions
PUCC APIENTRY RegisterXXXXXX (VOID)
FNWP fnwpXXXXXX(HWND,ULONG,MPARAM,MPARAM)
where XXXXXX is replaced with your control name. In this example
we would therefore create these two functions:
RegisterShadow
fnwpShadow
and can now write the entire SHADOW.DEF file as follows
LIBRARY SHADOW INITINSTANCE TERMINSTANCE
DESCRIPTION 'any text you want'
PROTMODE
DATA PRELOAD MULTIPLE NONSHARED
CODE PRELOAD
EXPORTS
RegisterShadow @1
fnwpShadow @2
SHADOW.DEF is now complete, so now let's write and file away
SHADOW.MAK. [In this example we are using IBM C\SET 2 in "c"
mode]. The entire text follows
---------------------------------------------
Lib = Os2386
Objects = shadow.obj
LinkOpt = /A:4 /BASE:0x12000000
Options = /W3 /c /Gd- /Ge- /Gm+ /Re
Shadow.dll: $(Objects) Shadow.def
Link386 $(Objects), Shadow.dll $(LinkOpt), ,$(Lib), Shadow.def
Shadow.obj: Shadow.c
ICC $(Options) Shadow.C
----------------------------------------------
Nothing left to do except the "C" file.
Notes about the two exported functions:
1. RegisterShadow()
Three major things are done here:
- get a HAB
- register this class, including its name and window procedure
(fnwpShadow). The last parameter reserves some extra storage
which is to be tacked on to each instance.
- fill in the fields in struct "Ucc", typedef'd in GPFUC.H. In
this file there are several 'defines' of the form GPF_CAPS_wxzy.
These are used to control the capabilities which are enabled
during the presentation of the default User Control dialog.
Since we want the developer to define text, we enable the text
entry field this way:
Ucc.Capability= GPF_CAPS_TEXT;
/* more stuff could be ORed in */
2. fnwpShadow()
Each instance of this control will need some storage in which we
can store, as a minimum, the text defined by the developer
during control creation. In this case it will be of type PSZ,
for which memory will be allocated during the WM_CREATE message.
[Note that the call to WM_CREATE will be made from Gpf, after
the CREATESTRUCT fields have been filled in. The text specified
by the developer will be pointed to by the "pszText" field of
this structure.] Once allocated we will place a pointer to this
area into the first slot of the EXTRAWORDS asked for during
class registration.
This storage will be free(d) during the WM_DESTROY message.
Now jump back to the start of fnwpShadow and notice that we
initially try to retrieve the pointer stored with the instance.
If it is NULL we know the WM_CREATE function has not been
processed (or invoked) as of yet, hence we immediately return
unless the message is WM_CREATE! It is usually safe to ignore
any messages flying around before WM_CREATE.
The other message of real interest is WM_PAINT. The pointer to
the instance text has already been retrieved, so an HPS is
opened, and along with the PSZ and HWND, is passed to a helper
function called Paint(). The details of what Paint() does will
not be explained here, kindly look at the source code. In fact,
modify it to your heart's content.... but don't call me if it
blows up after your 'improvements' are installed. Many
optimizations are possible, such as re-sizing the control to
exactly fit the passed-in text. But that would obscure the point
of this AppNote. The next control, having an extended dialog,
will indeed have some obfuscating code.
How does one use this control?
1. From Gpf click on menu "Objects- User Controls"
2. In new dialog, press "Add"
3. In new dialog
enter "shadow" in name field
enter "shadow" in DLL name field
the link via should be left at "import"
4. press Ok. If Gpf is unable to find it, move SHADOW.DLL to a
directory seen by the LIBPATH variable in CONFIG.SYS, then
repeat the above
5. exit to top level
6. open or create a window
7. click on the "Create- User Control" menu
8. drop the tracking rectangle anywhere on the client area.
9. the User Control default dialog should come up (if you have
this turned off, simply double click anywhere within the
tracking rectangle for this control)
10. enter the desired text and return with OK
11. drag/drop or re-size as desired
12. if you want to modify the SHADOW.DLL sources, close Gpf so
that it releases the DLL!
Al Charov, November 1994
*****************************************************************
B. A more complex example
Examining file "Gpfuc.h" reveals a definition of
GPF_CAPS_EXTENDED. Including this bit during the registration
phase enables the "Extended..." pushbutton in the User Control
default dialog. Simple enough, but a bunch of extra baggage must
be generated to support this capability:
- one or more dialog templates must be made, using the toolkit
dialog editor, and (resource) compiled.
- an extra registration procedure must be written, with a
specific format, for the extended dialog
- a complete window procedure must be written for each and
every new dialog
- a parameter/data collection and passing stategy must be
implemented within the new procedures, and made known to the two
original procedures (made in the simple case).
- the .DEF and MAK files need to be expanded a bit.
- for general ease, an .RC file is made; its only function is
to pull in the .DLG file created for the extended dialogs.
Sounds a bit messy, but it all becomes perfectly clear after
making a few dozen of these beasts. In this example we will
create a User Control with the name "SHADOWX", since it is
derived from the previous control but with a few bells and
whistles thrown in.
As before, let's talk about the bare minimums:
- you must create source code for these functions
/* the next two are just as in the simple example */
/* except for a slight name modification */
PUCC RegisterShadowx(VOID);
MRESULT EXPENTRY fnwpShawdowx(HWND,ULONG,MPARAM,MPARAM)
/* the next two are new, when using the EXTENDED feature */
LONG APIENTRY ExtendedShadowx(LONG, PVOID);
MRESULT EXPENTRY fnwpExtShadowx(HWND,ULONG,MPARAM,MPARAM);
/* the new prefixes, 'Extended' and 'fnwpExt' must be exact,
even their case !! */
/* and the suffix for all (4) functions must be the name of
this control: Shadowx */
- invoke the dialog editor (not Gpf!!) and create a .DLG and .H
file. In this example I created SHADEXT.DLG and SHADEXT.H. No
instructions for using this particular editor will be provided
here, except an admonition to specify useful ID names.
As before we can immediately write the complete .DEF file, and
we'll call it SHADOWX.DEF. The entire source follows:
LIBRARY SHADOWX INITINSTANCE TERMINSTANCE
DESCRIPTION 'any text you want'
PROTMODE
DATA PRELOAD MULTIPLE NONSHARED
CODE PRELOAD
EXPORTS
RegisterShadowx @1
ExtendedShadowx @2
fnwpShadowx
fnwpExtShadowx
and now the entire MAK file, which I've named SHADOWX.MAK
-------------------------------------------
Lib = Os2386
Objects = shadowx.obj shadext.obj
LinkOpt = /A:4 /BASE:0x12000000
Options = /W3 /c /Gd- /Ge- /Gm+ /Re
Shadowx.Dll: $(Objects) shadext.res Shadowx.Def
Link386 $(Objects) , Shadowx.Dll $(LinkOpt),,$(Lib) ,Shadowx.Def;
rc shadext.res shadowx.dll
copy shadowx.dll c:\os2\dll
Shadowx.Obj: shadowx.c shadowx.h shadext.h
ICC $(Options) shadowx.C
Shadext.obj: shadext.c shadowx.h shadext.h
ICC $(Options) shadext.c
shadext.res: shadext.rc shadext.dlg shadext.h
rc -r shadext.rc
For no particular reason, except perhaps to keep all source
files on the small side, I chose to split my code into two "C"
files. SHADOWX.C contains the same functions as in the simple
example, and SHADEXT.C contains the required new functions which
support the extended dialog. Aside from ensuring the
compile/link of two source files, the only real addition to the
.MAK file is some extra processing for the resource compiler.
SHADEXT.DLG and SHADEXT.H files were generated by the Dialog
Editor, and then a simple .RC file was created which simply does
an 'RCINCLUDE' of these two. With the production details out of
the way it is time to discuss the source changes and additions.
What are the new features of this control?
- Presentation Parameters, familiar to most Gpf users, can now
be specified for this control in the same fashion. Note: only
foreground/background colors have any effect. Font specifications are
ignored, as the source code is hard-wired to 'Times New Roman".
- the "Extended..." pushbutton is enabled, leading to a dialog
in which we can
a. specify a left or right tilt to our text, 0-30 degrees from
vertical.
b. specify the left shadow color
c. specify the right shadow color
d. override the background color
e. override the foreground color
The only change to the registration procedure was the addition
of two bits to 'Ucc.capability', turning ON the PresParams and
Extended pushbutton in the default User Control dialog. The
changes to the window procedure are a bit more involved, and
depend on what happens in the extended dialog (if anything; the
user should not be required to invoke it). The key to
understanding this code is knowing what, and when, gets passed
between our four required functions.
All Gpf User Controls make use of a structure of type UCC,
contained in GPFUC.H:
#pragma pack(1) /* force structure alignment packing */
typedef struct
{
ULONG Capability; /* Caps Style */
ULONG WsStyle; /* Initial Ws Style */
USHORT Cx; /* Initial Cx */
USHORT Cy; /* Initial Cy */
LONG Reserved[60]; /* Reserved */
} UCC;
typedef UCC FAR *PUCC; /* Far Pointer */
Previously we used the first four members of this structure and
now we will use the fifth in addition. We can define any
structure we wish, provided it is 'packed' and does not exceed
60 bytes in length. Gpf will be providing us with a pointer to
the 'Reserved' member at appropriate times, and we can type cast
it to suit our needs. So I created a structure to hold the
information this control will be interested in retaining. In
file SHADOWX.H:
#define MAX_SHADOW_TEXT 26
/* due to Gpf definitions this structure must be <= 60 bytes */
#pragma pack(1)
typedef struct{
USHORT cBytes;
CHAR chText[MAX_SHADOW_TEXT];
ULONG BgColor;
SHORT BgIndex;
ULONG FgColor;
SHORT FgIndex;
ULONG LeftColor;
SHORT LeftIndex;
ULONG RightColor;
SHORT RightIndex;
SHORT sAngle;
BOOL fOverride;
} UCPARAMS;
typedef UCPARAMS * PUCPARAMS;
#pragma pack()
Member 'cBytes' will be used as a test of whether or not the
structure has been initialized. After initialization the
sizeof(UCPARAMS) number will be placed here.
All members suffixed with 'Color' will contain the OS/2 PM
definition for a color, such as CLR_BLACK. All colors known to
this control are in structure 'Colors', found at the top of file
SHADEXT.C.
All members suffixed with 'Index' are used to hold the offset
into the 'Colors' structure for the related 'Color' member. This
is for convenience only, and comes in handy when communicating
with the combo boxes which display the current and the available
choices.
Member 'sAngle' will contain the tilt angle, read from a spin
button having a range of +/- 30.
Member 'fOverride' will follow a check button in the extended
dialog, and when true will cause the WM_PAINT procedure to
ignore Presentation Parameters and use the colors specified by
the user from the extended dialog.
It is important to understand that Gpf itself will know nothing
about this structure, and will care even less. But it will
maintain a pointer, set to NULL or to the enclosing area
(UCC.Reserved). A NULL value is the initial value and will stay
that way unless and until the extended dialog is invoked at
least once. Then it will be fixed to point to the reserved
block. This pointer is available at these times:
- for Gpf's default User Control dialog functions
In RegisterShadowx one can, if one really desires, derive
&Ucc.Reserved
In fnwpShadowx, message WM_CREATE, it is passed in as 'mp1'.
It may or may not be NULL, subject to the explanation above.
- for the extended dialog functions
In fnwpExtShadowx, message WM_INITDLG, it is passed in as
'mp2'. Here it is never NULL and we will immediately capture it
into a static variable, available then to all other messages
including any further WM_INITDLG. Keep in mind that the entire
UCC structure is primarily for the use of Gpf, and we will keep
a local copy of the reserved area in the 'window words' of each
control instance. How? Let's examine the modifications to
WM_CREATE in fnwpShadowx().
As in the simple case, 'mp2' is a pointer to a CREATESTRUCT
defined by OS/2 PM header files. The member of interest to us is
'pszText', into which Gpf will stuff any text entered by the Gpf
user during the default dialog. The pointer to the reserved
block will be in 'mp1', providing that the extended dialog has
been invoked at least once. The first time that WM_CREATE is
called there is no possibility of having gone through the
extended dialog, hence Gpf will have set this to NULL.
Meanwhile, we allocate storage for a UCPARAMS structure and
initialize a pointer to this block. Next we check 'mp1', here
type cast to a PUCPARAMS. If it is NULL we init two of the color
fields in our local structure; if it is non-NULL, we copy the
entire contents into our local instance. The text is then
copied, in both cases, to the newly allocated structure. It is
then tucked away for future use with the WinSetWindowPtr()
function. WM_PAINT will have access to this pointer, and will
modify itself according to the information. Now to the extended
dialog...
The window procedure, fnwpExtShadowx, will always be given a
true and valid pointer to the reserved, 60-byte area (via
'mp2'). As this window is constantly being created and
destroyed, we must implement a mechanism to determine the
validity of the structure contents. It would be nice, for
example, to set all the controls to their last-invoked state.
But testing for a NULL pointer will not work, as it is never
NULL. Hence we do something else, we set member 'cBytes' to a
certain value at the first opportunity. Now we can test the
contents of 'cBytes' for this value. If it is incorrect we know
this is the first time it is seen by our window procedure, and
we initialize ALL relevant members to desired values. The
remaining controls can then be initialized with respect to the
structure contents, new or existing, and this completes our
WM_INITDLG phase.
The only remaining job is to catch user interactions with the
extended dialog controls. Since they will all generate a
WM_COMMAND message, that is where we lump them. Parameter 'mp1'
tells us the control ID and the nature of the message. A big
switch statement is built to take care of all possibilities of
interest to us. Those of no interest should not be ignored, but
rather passed to WinDefDlgProc(). Bizarre things can happen if
you forget to do this.
Using this control is similar to the simple example, except that
you must change the name to 'shadowx'. Obviously nothing
prevents you from using both types of control, in the same
application.
Hope this helps.
Al Charov, December 1994