home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / contro.zip / CONTROL.TXT < prev    next >
Text File  |  1994-12-28  |  18KB  |  606 lines

  1. Creating user controls- not for the faint of heart
  2.  
  3.  
  4.  
  5. There are two basic types of user controls, simple and complex.
  6. The simple type does not require one to create any screens, as
  7. Gpf supplies a default. The tougher one has the "extended"
  8. feature of the default screen turned on, and one must create the
  9. entire infrastructure to display and make use of such a
  10. dialogue.
  11.  
  12.  
  13.  
  14. In both cases the developer is responsible for the entire
  15. behavior of the user control, which can be very simple for
  16. static-like entities, or a real pain for interactive objects
  17. (for example, building a genuine multi-column list box, or
  18. formatted data entry fields, or SQL-wise controls, etc.)
  19.  
  20.  
  21.  
  22. This package contains:
  23.  
  24.     a. a minimal user control, similar to STATIC TEXT controls, but
  25. auto sizing and with fancy shadowing. No extended dialog is
  26. required.
  27.  
  28.     b. the same control with an extended dialog, permitting the Gpf
  29. developer to specify some extra properties.
  30.  
  31.  
  32.  
  33. A. The Minimal Control
  34.  
  35. We will call this control "SHADOW". You will need the following
  36. files:
  37.  
  38.     GPFUC.H     do not write, included with the Gpf product
  39.  
  40.     SHADOW.H    you write this, though not strictly required
  41.  
  42.     SHADOW.C    includes GPFUC.H, SHADOW.H, and is the driver code.
  43. It must contain  at least (2) entry points expected by Gpf, and
  44. an entire window procedure for the control. This window
  45. procedure will be fairly simple.
  46.  
  47.     SHADOW.DEF    linker information, and exports the required entry
  48. points
  49.  
  50.     SHADOW.MAK    a makefile, necessary if you hate to type a lot
  51.  
  52.  
  53.  
  54. When all is said and done you will have created:
  55.  
  56.     SHADOW.DLL    this will need to be moved to one of your LIBPATH
  57. locations
  58.  
  59.     SHADOW.LIB    IMPLIBed, nice to make, but again not strictly
  60. necessary
  61.  
  62.     SHADOW.OBJ    which can be discarded after the DLL is generated
  63.  
  64.  
  65.  
  66. For this simple example we will not use a SHADOW.H, all source
  67. will be in SHADOW.C. What is required in this file?
  68.  
  69. For the purposes of Gpf, two and only two exported functions
  70.  
  71.     PUCC APIENTRY RegisterXXXXXX (VOID)
  72.  
  73.     FNWP       fnwpXXXXXX(HWND,ULONG,MPARAM,MPARAM)
  74.  
  75. where XXXXXX is replaced with your control name. In this example
  76. we would therefore create these two functions:
  77.  
  78.     RegisterShadow
  79.  
  80.     fnwpShadow
  81.  
  82. and can now write the entire SHADOW.DEF file as follows
  83.  
  84. LIBRARY SHADOW INITINSTANCE TERMINSTANCE
  85.  
  86. DESCRIPTION 'any text you want'
  87.  
  88. PROTMODE
  89.  
  90. DATA        PRELOAD MULTIPLE NONSHARED
  91.  
  92. CODE        PRELOAD
  93.  
  94. EXPORTS
  95.  
  96.     RegisterShadow    @1
  97.  
  98.     fnwpShadow    @2
  99.  
  100.  
  101.  
  102. SHADOW.DEF is now complete, so now  let's write and file away
  103. SHADOW.MAK. [In this example we are using IBM C\SET 2 in "c"
  104. mode]. The entire text follows
  105.  
  106.  
  107.  
  108. ---------------------------------------------
  109.  
  110. Lib    =    Os2386
  111.  
  112. Objects =    shadow.obj
  113.  
  114. LinkOpt =    /A:4 /BASE:0x12000000
  115.  
  116. Options =    /W3 /c /Gd- /Ge- /Gm+ /Re
  117.  
  118.  
  119.  
  120. Shadow.dll: $(Objects) Shadow.def
  121.  
  122.     Link386 $(Objects), Shadow.dll $(LinkOpt), ,$(Lib), Shadow.def
  123.  
  124.  
  125.  
  126. Shadow.obj: Shadow.c
  127.  
  128.     ICC $(Options) Shadow.C
  129.  
  130.  
  131.  
  132. ----------------------------------------------
  133.  
  134.  
  135.  
  136. Nothing left to do except the "C" file.
  137.  
  138. Notes about the two exported functions:
  139.  
  140. 1. RegisterShadow()
  141.  
  142. Three major things are done here:
  143.  
  144.     - get a HAB
  145.  
  146.     - register this class, including its name and window procedure
  147. (fnwpShadow). The last parameter reserves some extra storage
  148. which is to be tacked on to each instance.
  149.  
  150.     - fill in the fields in struct "Ucc", typedef'd in GPFUC.H. In
  151. this file there are several 'defines' of the form GPF_CAPS_wxzy.
  152. These are used to control the capabilities which are enabled
  153. during the presentation of the default User Control dialog.
  154. Since we want the developer to define text, we enable the text
  155. entry field this way:
  156.  
  157.     Ucc.Capability= GPF_CAPS_TEXT;
  158.  
  159.     /* more stuff could be ORed in */
  160.  
  161.  
  162.  
  163. 2. fnwpShadow()
  164.  
  165. Each instance of this control will need some storage in which we
  166. can store, as a minimum, the text defined by the developer
  167. during control creation. In this case it will be of type PSZ,
  168. for which memory will be allocated during the WM_CREATE message.
  169. [Note that the call to WM_CREATE will be made from Gpf, after
  170. the CREATESTRUCT fields have been filled in. The text specified
  171. by the developer will be pointed to by the "pszText" field of
  172. this structure.] Once allocated we will place a pointer to this
  173. area into the first slot of the EXTRAWORDS asked for during
  174. class registration.
  175.  
  176. This storage will be free(d) during the WM_DESTROY message.
  177.  
  178. Now jump back to the start of fnwpShadow and notice that we
  179. initially try to retrieve the pointer stored with the instance.
  180. If it is NULL we know the WM_CREATE function has not been
  181. processed (or invoked) as of yet, hence we immediately return
  182. unless the message is WM_CREATE! It is usually safe to ignore
  183. any messages flying around before WM_CREATE.
  184.  
  185.  
  186.  
  187. The other message of real interest is WM_PAINT. The pointer to
  188. the instance text has already been retrieved, so an HPS is
  189. opened, and along with the PSZ and HWND, is passed to a helper
  190. function called Paint(). The details of what Paint() does will
  191. not be explained here, kindly look at the source code. In fact,
  192. modify it to your heart's content.... but don't call me if it
  193. blows up after your 'improvements' are installed.  Many
  194. optimizations are possible, such as re-sizing the control to
  195. exactly fit the passed-in text. But that would obscure the point
  196. of this AppNote. The next control, having an extended dialog,
  197. will indeed have some obfuscating code.
  198.  
  199.  
  200.  
  201. How does one use this control?
  202.  
  203. 1. From Gpf click on menu "Objects- User Controls"
  204.  
  205. 2. In new dialog, press "Add"
  206.  
  207. 3. In new dialog
  208.  
  209.     enter "shadow" in name field
  210.  
  211.     enter "shadow" in DLL name field
  212.  
  213.      the link via should be left at "import"
  214.  
  215. 4. press Ok. If Gpf is unable to find it, move SHADOW.DLL to a
  216. directory seen by the LIBPATH variable in CONFIG.SYS, then
  217. repeat the above
  218.  
  219. 5. exit to top level
  220.  
  221. 6. open or create a window
  222.  
  223. 7. click on the "Create- User Control" menu
  224.  
  225. 8. drop the tracking rectangle anywhere on the client area.
  226.  
  227. 9. the User Control default dialog should come up (if you have
  228. this turned off, simply double click anywhere within the
  229. tracking rectangle for this control)
  230.  
  231. 10. enter the desired text and return with OK
  232.  
  233. 11. drag/drop or re-size as desired
  234.  
  235. 12. if you want to modify the SHADOW.DLL sources, close Gpf so
  236. that it releases the DLL!
  237.  
  238.  
  239.  
  240.  
  241.  
  242. Al Charov, November 1994
  243.  
  244. *****************************************************************
  245.  
  246.  
  247.  
  248. B. A more complex example
  249.  
  250.  
  251.  
  252. Examining file "Gpfuc.h" reveals a definition of
  253. GPF_CAPS_EXTENDED.  Including this bit during the registration
  254. phase enables the "Extended..." pushbutton in the User Control
  255. default dialog. Simple enough, but a bunch of extra baggage must
  256. be generated to support this capability:
  257.  
  258.     - one or more dialog templates must be made, using the toolkit
  259. dialog editor, and (resource) compiled.
  260.  
  261.     - an extra registration procedure must be written, with a
  262. specific format, for the extended dialog
  263.  
  264.     - a complete window procedure must be written for each and
  265. every new  dialog
  266.  
  267.     - a parameter/data collection and passing stategy must be
  268. implemented within the new procedures, and made known to the two
  269. original procedures (made in the simple case).
  270.  
  271.     - the .DEF and MAK files need to be expanded a bit.
  272.  
  273.     - for general ease, an .RC file is made; its only function is
  274. to pull in the .DLG file created for the extended dialogs.
  275.  
  276.  
  277.  
  278. Sounds a bit messy, but it all becomes perfectly clear after
  279. making a few dozen of these beasts. In this example we will
  280. create a User Control with the name "SHADOWX", since it is
  281. derived from the previous control but with a few bells and
  282. whistles thrown in.
  283.  
  284.  
  285.  
  286. As before, let's talk about the bare minimums:
  287.  
  288.     - you must create source code for these functions
  289.  
  290.         /* the next two are just as in the simple example */
  291.         /* except for a slight name modification */
  292.  
  293.         PUCC RegisterShadowx(VOID);
  294.  
  295.         MRESULT EXPENTRY fnwpShawdowx(HWND,ULONG,MPARAM,MPARAM)
  296.  
  297.         /* the next two are new, when using the EXTENDED feature */
  298.  
  299.         LONG APIENTRY ExtendedShadowx(LONG, PVOID);
  300.  
  301.         MRESULT EXPENTRY fnwpExtShadowx(HWND,ULONG,MPARAM,MPARAM);
  302.  
  303.     /* the new prefixes, 'Extended' and 'fnwpExt' must be exact,
  304. even their case !! */
  305.  
  306.     /* and the suffix for all (4) functions must be the name of
  307. this control: Shadowx */
  308.  
  309.     - invoke the dialog editor (not Gpf!!) and create a .DLG and .H
  310. file. In this example I created SHADEXT.DLG and SHADEXT.H.  No
  311. instructions for using this particular editor will be provided
  312. here, except an admonition to specify useful ID names.
  313.  
  314.  
  315.  
  316. As before we can immediately write the complete .DEF file, and
  317. we'll call it SHADOWX.DEF. The entire source follows:
  318.  
  319.  
  320.  
  321. LIBRARY SHADOWX INITINSTANCE TERMINSTANCE
  322.  
  323. DESCRIPTION 'any text you want'
  324.  
  325. PROTMODE
  326.  
  327. DATA        PRELOAD MULTIPLE NONSHARED
  328.  
  329. CODE    PRELOAD
  330.  
  331. EXPORTS
  332.  
  333.     RegisterShadowx @1
  334.  
  335.     ExtendedShadowx @2
  336.  
  337.     fnwpShadowx
  338.  
  339.     fnwpExtShadowx
  340.  
  341.  
  342.  
  343.  
  344.  
  345. and now the entire MAK file, which I've named SHADOWX.MAK
  346.  
  347.  
  348.  
  349. -------------------------------------------
  350.  
  351. Lib    = Os2386
  352.  
  353. Objects =    shadowx.obj shadext.obj
  354.  
  355. LinkOpt = /A:4 /BASE:0x12000000
  356.  
  357. Options = /W3 /c /Gd- /Ge- /Gm+ /Re
  358.  
  359.  
  360.  
  361. Shadowx.Dll:  $(Objects) shadext.res Shadowx.Def
  362.  
  363.     Link386 $(Objects) , Shadowx.Dll $(LinkOpt),,$(Lib) ,Shadowx.Def;
  364.  
  365.     rc shadext.res shadowx.dll
  366.  
  367.     copy shadowx.dll c:\os2\dll
  368.  
  369.  
  370. Shadowx.Obj:    shadowx.c shadowx.h shadext.h
  371.  
  372.     ICC $(Options) shadowx.C
  373.  
  374.  
  375.  
  376. Shadext.obj: shadext.c shadowx.h shadext.h
  377.  
  378.     ICC $(Options) shadext.c
  379.  
  380.  
  381.  
  382. shadext.res: shadext.rc shadext.dlg shadext.h
  383.  
  384.     rc -r shadext.rc
  385.  
  386.  
  387.  
  388. For no particular reason, except perhaps to keep all source
  389. files on the small side, I chose to split my code into two "C"
  390. files. SHADOWX.C contains the same functions as in the simple
  391. example, and SHADEXT.C contains the required new functions which
  392. support the extended dialog. Aside from ensuring the
  393. compile/link of two source files, the only real addition to the
  394. .MAK file is some extra processing for the resource compiler.
  395. SHADEXT.DLG and SHADEXT.H files were generated by the Dialog
  396. Editor, and then a simple .RC file was created which simply does
  397. an 'RCINCLUDE' of these two. With the production details out of
  398. the way it is time to discuss the source changes and additions.
  399.  
  400.  
  401.  
  402. What are the new features of this control?
  403.  
  404.     - Presentation Parameters, familiar to most Gpf users, can now
  405. be specified for this control in the same fashion. Note: only
  406. foreground/background colors have any effect. Font specifications are
  407. ignored, as the source code is hard-wired to 'Times New Roman".
  408.  
  409.     - the "Extended..." pushbutton is enabled, leading to a dialog
  410. in which we can
  411.  
  412.     a. specify a left or right tilt to our text, 0-30 degrees from
  413. vertical.
  414.  
  415.     b. specify the left shadow color
  416.  
  417.     c. specify the right shadow color
  418.  
  419.     d. override the background color
  420.  
  421.     e. override the foreground color
  422.  
  423.  
  424.  
  425. The only change to the registration procedure was the addition
  426. of two bits to 'Ucc.capability', turning ON the PresParams and
  427. Extended pushbutton in the default User Control dialog. The
  428. changes to the window procedure are a bit more involved, and
  429. depend on what happens in the extended dialog (if anything; the
  430. user should not be required to invoke it). The key to
  431. understanding this code is knowing what, and when, gets passed
  432. between our four required functions.
  433.  
  434. All Gpf User Controls make use of a structure of type UCC,
  435. contained in GPFUC.H:
  436.  
  437.  
  438.  
  439. #pragma pack(1) /* force structure alignment packing */
  440.  
  441. typedef struct
  442.  
  443.   {
  444.  
  445.     ULONG        Capability;    /* Caps Style        */
  446.     ULONG        WsStyle;    /* Initial Ws Style */
  447.     USHORT    Cx;            /* Initial Cx        */
  448.     USHORT    Cy;            /* Initial Cy        */
  449.     LONG        Reserved[60];    /* Reserved    */
  450.   } UCC;
  451.  
  452. typedef UCC FAR *PUCC;            /* Far Pointer        */
  453.  
  454.  
  455.  
  456. Previously we used the first four members of this structure and
  457. now we will use the fifth in addition. We can define any
  458. structure we wish, provided it is 'packed' and does not exceed
  459. 60 bytes in length. Gpf will be providing us with a pointer to
  460. the 'Reserved' member at appropriate times, and we can type cast
  461. it to suit our needs. So I created a structure to hold the
  462. information this control will be interested in retaining. In
  463. file SHADOWX.H:
  464.  
  465.  
  466.  
  467. #define MAX_SHADOW_TEXT 26
  468.  
  469. /* due to Gpf definitions this    structure must be <= 60 bytes */
  470.  
  471. #pragma pack(1)
  472.  
  473. typedef struct{
  474.     USHORT    cBytes;
  475.     CHAR    chText[MAX_SHADOW_TEXT];
  476.     ULONG    BgColor;
  477.     SHORT    BgIndex;
  478.     ULONG    FgColor;
  479.     SHORT    FgIndex;
  480.     ULONG    LeftColor;
  481.     SHORT    LeftIndex;
  482.     ULONG    RightColor;
  483.     SHORT    RightIndex;
  484.     SHORT    sAngle;
  485.     BOOL    fOverride;
  486.     } UCPARAMS;
  487.  
  488. typedef UCPARAMS * PUCPARAMS;
  489.  
  490. #pragma pack()
  491.  
  492.  
  493.  
  494. Member 'cBytes' will be used as a test of whether or not the
  495. structure has been initialized. After initialization the
  496. sizeof(UCPARAMS) number will be placed here.
  497.  
  498. All members suffixed with 'Color' will contain the OS/2 PM
  499. definition for a color, such as CLR_BLACK. All colors known to
  500. this control are in structure 'Colors', found at the top of file
  501. SHADEXT.C.
  502.  
  503. All members suffixed with 'Index' are used to hold the offset
  504. into the 'Colors' structure for the related 'Color' member. This
  505. is for convenience only, and comes in handy when communicating
  506. with the combo boxes which display the current and the available
  507. choices.
  508.  
  509. Member 'sAngle' will contain the tilt angle, read from a spin
  510. button having a range of +/- 30.
  511.  
  512. Member 'fOverride' will follow a check button in the extended
  513. dialog, and when true will cause the WM_PAINT procedure to
  514. ignore Presentation Parameters and use the colors specified by
  515. the user from the extended dialog.
  516.  
  517.  
  518.  
  519. It is important to understand that Gpf itself will know nothing
  520. about this structure, and will care even less. But it will
  521. maintain a pointer, set to NULL or to the enclosing area
  522. (UCC.Reserved). A NULL value is the initial value and will stay
  523. that way unless and until the extended dialog is invoked at
  524. least once. Then it will be fixed to point to the reserved
  525. block. This pointer is available at these times:
  526.  
  527.     - for Gpf's default User Control dialog functions
  528.  
  529.         In RegisterShadowx one can, if one really desires, derive
  530. &Ucc.Reserved
  531.  
  532.         In fnwpShadowx, message WM_CREATE, it is passed in as 'mp1'.
  533. It may or may not be NULL, subject to the explanation above.
  534.  
  535.     - for the extended dialog functions
  536.  
  537.         In fnwpExtShadowx, message WM_INITDLG, it is passed in as
  538. 'mp2'. Here it is never NULL and we will immediately capture it
  539. into a static variable, available then to all other messages
  540. including any further WM_INITDLG. Keep in mind that the entire
  541. UCC structure is primarily for the use of Gpf, and we will keep
  542. a local copy of the reserved area in the 'window words' of each
  543. control instance. How? Let's examine the modifications to
  544. WM_CREATE in fnwpShadowx().
  545.  
  546. As in the simple case, 'mp2' is a pointer to a CREATESTRUCT
  547. defined by OS/2 PM header files. The member of interest to us is
  548. 'pszText', into which Gpf will stuff any text entered by the Gpf
  549. user during the default dialog. The pointer to the reserved
  550. block will be in 'mp1', providing that the extended dialog has
  551. been invoked at least once. The first time that WM_CREATE is
  552. called there is no possibility of having gone through the
  553. extended dialog, hence Gpf will have set this to NULL.
  554. Meanwhile, we allocate storage for a UCPARAMS structure and
  555. initialize a pointer to this block. Next we check 'mp1', here
  556. type cast to a PUCPARAMS. If it is NULL we init two of the color
  557. fields in our local structure; if it is non-NULL, we copy the
  558. entire contents into our local instance. The text is then
  559. copied, in both cases, to the newly allocated structure. It is
  560. then tucked away for future use with the WinSetWindowPtr()
  561. function. WM_PAINT will have access to this pointer, and will
  562. modify itself according to the information. Now to the extended
  563. dialog...
  564.  
  565.  
  566.  
  567. The window procedure, fnwpExtShadowx, will always be given a
  568. true and valid pointer to the reserved, 60-byte area (via
  569. 'mp2'). As this window is constantly being created and
  570. destroyed, we must implement a mechanism to determine the
  571. validity of the structure contents. It would be nice, for
  572. example, to set all the controls to their last-invoked state.
  573. But testing for a NULL pointer will not work, as it is never
  574. NULL. Hence we do something else, we set member 'cBytes' to a
  575. certain value at the first opportunity. Now we can test the
  576. contents of 'cBytes' for this value. If it is incorrect we know
  577. this is the first time it is seen by our window procedure, and
  578. we initialize ALL relevant members to desired values. The
  579. remaining controls can then be initialized with respect to the
  580. structure contents, new or existing, and this completes our
  581. WM_INITDLG phase.
  582.  
  583.  
  584.  
  585. The only remaining job is to catch user interactions with the
  586. extended dialog controls. Since they will all generate a
  587. WM_COMMAND message, that is where we lump them. Parameter 'mp1'
  588. tells us the control ID and the nature of the message. A big
  589. switch statement is built to take care of all possibilities of
  590. interest to us. Those of no interest should not be ignored, but
  591. rather passed to WinDefDlgProc(). Bizarre things can happen if
  592. you forget to do this.
  593.  
  594.  
  595.  
  596. Using this control is similar to the simple example, except that
  597. you must change the name to 'shadowx'. Obviously nothing
  598. prevents you from using both types of control, in the same
  599. application.
  600.  
  601.  
  602. Hope this helps.
  603.  
  604. Al Charov, December 1994
  605.  
  606.