home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 14 Text / 14-Text.zip / cnrbastx.zip / CNRBAS.NEW
Text File  |  1993-06-07  |  24KB  |  508 lines

  1. Programming the OS/2 Container Control: The Basics
  2. ==================================================
  3.  
  4. by Peter Haggar and Peter Brightbill
  5. OS/2 Developer Winter 1993, Vol. 5 No. 1, Pages 96-101
  6.  
  7. The container control is shipped in OS/2 2.0 as a 32-bit control and in
  8. the IBM CUA controls library (CCL/2) as a 16-bit control.  The container
  9. is functionally equivalent in the two products, and the information in
  10. this article can be used by application developers working with either
  11. product.  This article assumes the reader has limited knowledge of the
  12. container control, and at least has read the information in the OS/2 2.0
  13. Programming Guide Volume II, Chapter 18.  More information on the
  14. container control will follow in the next issue.
  15.  
  16. The container control is large and complex.  While this article is not
  17. intended to cover every aspect of the container, it provides information
  18. that can help developers write an application using it.
  19.  
  20.  
  21. Structure of a Container Application
  22. ====================================
  23.  
  24. The container control was designed to be extremely flexible to satisfy
  25. the needs of most applications.  There are several basic concerns when
  26. designing an application.  First, applications need not expose all the
  27. container's functions, such as drag and drop or the direct edit feature.
  28. To prohibit the editing of text in the container, set the CSS_READONLY
  29. style at creation.  The container provides many views to display its
  30. data; the application can choose to support some or all of those views.
  31.  
  32. Developers must also decide how to allocate and insert container
  33. records.  For the purpose of optimization, records should be allocated
  34. and inserted in groups rather than individually.  Grouping records
  35. reduces the time needed to populate the container.  You can allocate and
  36. insert n records with two WinSendMsg calls, as opposed to 2n WinSendMsg
  37. calls if you were to allocate and insert each separately.  A good rule
  38. is to avoid any excessive WinSendMsg calls.  This is especially
  39. important with 16-bit PMWIN in OS/2.  In this setup, the 32-bit
  40. application calls 16-bit code in PMWIN, which in turn calls the 32-bit
  41. container control.  On the return, the 32-bit container code returns to
  42. the 16-bit PMWIN code and then back to the 32-bit application code.
  43. Converting between 16- and 32-bit memory models, or "thunking", can be
  44. costly in terms of clock cycles.
  45.  
  46.  
  47. Structures
  48. ==========
  49.  
  50. RECORDCORE and MINIRECORDCORE
  51. =============================
  52.  
  53. The container uses one of two structures to store record data,
  54. RECORDCORE and MINIRECORDCORE.  The structure used depends on the style
  55. set by the application when a container window is created.  (The
  56. CCS_MINIRECORDCORE style indicates that the container is to use
  57. MINIRECORDCORE) MINIRECORDCORE, which minimizes application memory use,
  58. using 28 bytes, or half the size of the RECORDCORE structure's 56 bytes.
  59. But the ability to use less memory is rarely free; this situation is no
  60. exception.  Certain functions are inoperable, or at least more
  61. complicated, with the MINIRECORDCORE structure.
  62.  
  63. With MINIRECORDCORE, the tree name view cannot be used properly.  The
  64. bitmaps and icons displayed in this view are stored in the TREEITEMDESC
  65. structure, which is pointed to by RECORDCORE.  If you use the tree name
  66. view with MINIRECORDCORE, the same icon designates the expanded and
  67. collapsed icon.  This can be confusing to the user.  In addition, the
  68. MINIRECORDCORE is restricted to one text string pointer that displays
  69. text for each record in text, name, icon, and tree view.  RECORDCORE,
  70. however, provides a separate text string pointer for each view.  In
  71. MINIRECORDCORE, you cannot use bitmaps, as only one icon handle is
  72. provided (compared to two icon and two bitmap handles in RECORDCORE).
  73.  
  74. Unlike RECORDCORE, MINIRECORDCORE does not provide a mini icon handle.
  75. To use mini icons with MINRECORDCORE, provide a mini icon handle for the
  76. hptrIcon field of the MINIRECORDCORE structure.  Set the slBitmapOrIcon
  77. field of the CNRINFO structure to specify icon size.  This is necessary
  78. because the container is expecting a system default icon size.  The
  79. container uses the system value SV_CXICON and SV_CYICON to determine the
  80. size of a default icon.  If the given icon is a different size than that
  81. specified in the slBitmapOrIcon field, it will be stretched or
  82. compressed as needed.  This technique is shown in Figure 1.
  83.  
  84. ========================================================================
  85.  
  86.  /* Using the MINIRECORDCORE to display mini icons in the container. */
  87.  PMINIRECORDCORE  pRecord;
  88.  CNRINFO          CnrInfo;
  89.  
  90.  /* Specify a size for the mini icon.  The system value for the size of
  91.   * a menu is commonly used. */
  92.  CnrInfo.slBitmapOrIcon.cx = WinQuerySysValue (HWND_DESKTOP, SV_CYMENU);
  93.  CnrInfo.slBitmapOrIcon.cy = CnrInfo.slBitmapOrIcon.cx;
  94.  
  95.  /* Tell the container to display all icons this size. */
  96.  WinSendMsg (hwndCnr, CM_SETCNRINFO,
  97.              MPFROMP(&CnrInfo), MPFROMLONG(CMA_SLBITMAPORICON));
  98.  
  99.  /* Assign the mini icon to the MINIRECORDCORE.  Then insert the
  100.   * record into the container. */
  101.  pRecord->hptrIcon = WinLoadPointer (HWND_DESKTOP, NULL, ID_MINIICON);
  102.  
  103.  WinSendMsg (hwndCnr, CM_INSERTRECORD,
  104.              MPFROMP(pRecord), MPFROMP(&RecordInsert));
  105.  
  106. ========================================================================
  107. Figure 1. Mini Icons with MINIRECORDCORE
  108.  
  109. After determining which of the two container record storage structures
  110. to use, you may want to store more information per record than is
  111. provided by either the RECORDCORE or MINIRECORDCORE.  Determine what
  112. information you wish to store for each record, then allocate the extra
  113. memory on the CM_ALLOCRECORD message.  Applications that use the details
  114. view usually add more memory to each record to store information to be
  115. displayed in the columns.  If an object-oriented environment in which
  116. the record represents an object, the instance data for the object can be
  117. added to the record.  This is the technique used by the OS/2 2.0
  118. Workplace Shell, which uses the container control for its folder object.
  119.  
  120. If any additional data is to be added to each record, the application
  121. needs to typedef a structure as shown in Figure 2.
  122.  
  123. ========================================================================
  124.  
  125.  typedef struct _RECORDOBJECT
  126.  {
  127.    MINIRECORDCORE  MiniRec;  <------- Be sure the MINIRECORDCORE or
  128.    ULONG           ulCount;  <-------             RECORDCORE is first.
  129.    BOOL            bInit;           |
  130.    SHORT           sID;             |-- Additional data.
  131.    PSZ             pszDept;         |
  132.    CDATE           Date;     <-------
  133.  } RECORDOBJECT;
  134.  typedef RECORDOBJECT *PRECORDOBJECT;
  135.  
  136. ========================================================================
  137. Figure 2. Application-defined container item
  138.  
  139. MINIRECORDCORE or RECORDCORE, must be the first field of the structure.
  140. This is important, as the container always returns pointers to
  141. RECORDCORE or MINIRECORDCORE.  You can then typecast the pointer to, in
  142. this case, PRECORDOBJECT, and be able to address the container record
  143. and all additional data.  In addition, when an application has to pass a
  144. pointer to a record for a particular message, it must point to either
  145. MINIRECORDCORE or RECORDCORE, as shown in Figure 3.
  146.  
  147. =======================================================================
  148.  
  149.  PRECORDOBJECT  pRecObj;
  150.  PRECORDOBJECT  pRecParent;
  151.  RECORDINSERT   RecordInsert;
  152.  
  153.  /* Allocate a MINIRECORDCORE plus the additional bytes needed from the
  154.   * RECORDOBJECT structure. */
  155.  pRecObj = (PRECORDOBJECT)WinSendMsg(hwndCnr, CM_ALLOCRECORD,
  156.                                      MPFROMLONG(sizeof(RECORDOBJECT) -
  157.                                                 sizeof(MINIRECORDCORE)),
  158.                                      MPFROMLONG(nRecords));
  159.  
  160.  /* Assign the necessary data to the record. */
  161.  pRecObj->MiniRec.cb = sizeof(MINIRECORDCORE);
  162.  pRecObj->MiniRec.hptrIcon = hptrCarIcon;
  163.  pRecObj->ulCount = 1;
  164.  pRecObj->bInit = TRUE;
  165.  pRecObj->sID   = 100;
  166.  strcpy (pRecObj->pszDept, pszDeptTitle);
  167.  .
  168.  .
  169.  RecordInsert.pRecordParent = (PRECORDCORE)pRecParent;
  170.  .
  171.  .
  172.  /* Insert the record */
  173.  WinSendMsg (hwndCnr, CM_INSERTRECORD,
  174.              MPFROMP(&pRecObj), MPFROMP(&RecordInsert));
  175.  
  176. =======================================================================
  177. Figure 3. Record allocation and insertion
  178.  
  179.  
  180. CNRINFO and CM_SETCNRINFO
  181. =========================
  182.  
  183. An internal CNRINFO structure describes most of the relevant information
  184. used by the container.  To change the information in this structure, use
  185. the CM_SETCNRINFO message.  The application should allocate its copy of
  186. the CNRINFO structure and set all fields that are to be changed.  The
  187. application should send the CM_SETCNRINFO message, setting the
  188. appropriate flags to indicate which fields are to be updated.  The
  189. container will then copy the information to the internal CNRINFO
  190. structure it manages.  CM_QUERYCNRINFO can be used to retrieve a CNRINFO
  191. structure in use by the container.  This message is necessary if your
  192. application needed information about the container stored in the
  193. structure.  For example, to determine the container's current view, use
  194. CM_QUERYCNRINFO and check the attributes in the flWindowAttr field.
  195.  
  196.  
  197. The Details on Details View
  198. ===========================
  199.  
  200. Details view is one of the most popular views, and is the only view that
  201. uses the FIELDINFO structure.  It can be one of the more complicated
  202. views to program to.  The details view is a table of rows and columns.
  203. Each row is represented by a RECORDCORE or MINIRECORDCORE structure, and
  204. each column by a FIELDINFO structure. Many container attributes apply to
  205. the entire details view, while others can be set specifically for each
  206. column.
  207.  
  208. Fields from the CNRINFO structure that apply to the entire details view
  209. are xVertSplitbar, pFieldInfoLast, and pFieldInfoObject.  Respectively,
  210. they set the x position of the splitbar, the last column in the left
  211. split window, and the column to receive IN-USE (CRA_INUSE) emphasis.
  212. Each is optional, depending on the setup of the details view.  If your
  213. application does not display a splitbar, the xVertSplitbar and
  214. pFieldInfoLast fields are not used. The pFieldInfoObject field, if not
  215. set specifically, will default to the first column in the left window,
  216. or the first column in an unsplit details view.  All these fields can be
  217. specified individually or as a group with the CM_SETCNRINFO message.
  218. Figure 4 shows how to set these fields with a single CM_SETCNRINFO
  219. message.
  220.  
  221. =======================================================================
  222.  
  223.  /* Fill in the fields of the CNRINFO structure that we want
  224.   * to update */
  225.  CnrInfo.xVertSplitbar    = 100;
  226.  CnrInfo.pFieldInfoLast   = pFieldInfoLastLeft;
  227.  CnrInfo.pFieldInfoObject = pFieldInfo;
  228.  
  229.  /* Using the proper flags, tell the container to use this
  230.   * new information */
  231.  WinSendMsg (hwndCnr, CM_SETCNRINFO, MPFROMP(&CnrInfo),
  232.              MPFROMLONG(CMA_XVERTSPLITBAR | CMA_PFIELDINFOLAST |
  233.                         CMA_PFIELDINFOOBJECT));
  234.  
  235. =======================================================================
  236. Figure 4. Updating CNRINFO
  237.  
  238. Other attributes, the CFA_* attributes, apply only to a specific column
  239. in the details view.  Some apply only to the titles and others to the
  240. data area of the column.  Memory for the FIELDINFO structure is
  241. allocated with the CM_ALLOCDETAILFIELDINFO message.  The flTitle and
  242. flData fields of FIELDINFO are used to set specific attributes for each
  243. column before they are inserted into the container with
  244. CM_INSERTDETAILFIELDINFO.  Any attribute can be changed after the
  245. FIELDINFOs are inserted by sending the CM_INVALIDATEDETAILFIELDINFO message.
  246.  
  247. To display column titles in details view, set the CA_DETAILSVIEWTITLES
  248. container attribute in the flWindowAttr field of the CNRINFO structure,
  249. then send the CM_SETCNRINFO message specifying CMA_FLWINDOWATTR.
  250. For example:
  251.  
  252.     CnrInfo.flWindowAttr = CV_DETAIL |
  253.                              CA_DETAILSVIEWTITLES |
  254.                              CA_DRAWICON;
  255.     WinSendMsg (hwndCnr, CM_SETCNRINFO,
  256.                    MPFROMP(&CnrInfo),
  257.                    MPFROMLONG(CMA_FLWINDOWATTR));
  258.  
  259. When column titles are to be used in details view, specify the type of
  260. data to appear in each column title.  This is done through the flTitle
  261. field of the FIELDINFO structure.  Only two types of data, first, icons
  262. or bitmaps and second, text strings, are allowed in the details view
  263. column titles.  When CFA_BITMAPORICON attribute is specified, the
  264. container assumes that the pTitleData field points to an icon or bitmap
  265. handle, depending on whether the CA_DRAWICON or CA_DRAWBITMAP attribute
  266. is specified.  If the CFA_BITMAPORICON attribute is not specified, the
  267. container will assume that the pTitleData field points to a text string.
  268.  
  269. Column data can be icons, bitmaps, text strings, dates, times, or
  270. numbers.  These are specified by setting the flData field of the
  271. FIELDINFO structure to CFA_BITMAPORICON, CFA_STRING, CFA_DATE, CFA_TIME,
  272. or CFA_ULONG, respectively.  When one of these attributes is specified,
  273. the container assumes the same type of data is contained at the location
  274. specified by the FIELDINFO's offstruct field, as in Figure 5.
  275.  
  276. =======================================================================
  277.  
  278.  PFIELDINFO  pFieldInfo;
  279.  
  280.   if (pFieldInfo = WinSendMsg (hwndCnr, CM_ALLOCDETAILFIELDINFO,
  281.                                MPFROMSHORT(numColumns), NULL))
  282.   {
  283.     /* First column title will be a string, and the data will be
  284.      * an icon */
  285.     strcpy (pFieldInfo->pTitleData, pszFirstColTitle);
  286.     pFieldInfo->flData = CFA_BITMAPORICON;
  287.     pFieldInfo->offstruct = FIELDOFFSET(MINIRECORDCORE, hptrIcon);
  288.     .
  289.     .
  290.     pFieldInfo = pFieldInfo->pNextFieldInfo;
  291.  
  292.     /* Second column title will be a string, and the data will be
  293.      * the name */
  294.     strcpy (pFieldInfo->pTitleData, pszSecondColTitle);
  295.     pFieldInfo->flData = CFA_STRING;
  296.     pFieldInfo->offstruct = FIELDOFFSET(MINIRECORDCORE, pszIcon);
  297.     .
  298.     .
  299.     pFieldInfo = pFieldInfo->pNextFieldInfo;
  300.  
  301.     /* Third column title will be an icon of a calendar, and the data
  302.      * will be the date */
  303.     pFieldInfo->flTitle = CFA_BITMAPORICON;
  304.     pFieldInfo->pTitleData = hptrCalendar;
  305.     pFieldInfo->flData = CFA_DATE;
  306.     pFieldInfo->offstruct = FIELDOFFSET(RECORDOBJECT, Date);
  307.     .
  308.     .
  309.   }
  310.  
  311. =======================================================================
  312. Figure 5. Setting up FIELDINFO
  313.  
  314. Other options specific to the flData field are:
  315.  
  316. CFA_HORZSEPARATOR - Draws a horizontal line between the column title
  317.                     and the column data.
  318. CFA_SEPARATOR     - Draws a vertical line after a column; the line
  319.                     extends through the title area.
  320. CFA_OWNER         - Allows the application to ownerdraw a column.
  321. CFA_INVISIBLE     - Makes a column invisible.
  322. CFA_FIREADONLY    - Makes a column's data, but not its title, read-only.
  323.                     To make a column title read-only, specify
  324.                     CFA_FITITLEREADONLY in the flTitle field.
  325.  
  326. Data in a column can be aligned in any of nine different ways using the
  327. CFA_TOP, CFA_BOTTOM, and CFA_VCENTER for vertical positioning and
  328. CFA_LEFT, CFA_RIGHT, and CFA_CENTER for horizontal positioning.  These
  329. can be set differently for title and data in one column.
  330.  
  331. The most important field in the FIELDINFO structure is offstruct, which
  332. tells the column at what offset from the beginning of RECORDCORE or
  333. MINIRECORDCORE to find its data.  All data displayed in details view is
  334. contained in or after RECORDCORE or MINIRECORDCORE. If any data is to be
  335. contained after, allocate record memory shown in Figure 3.  Figure 5
  336. shows how FIELDINFO structures could be set up to display different
  337. columns of data with the RECORDOBJECT structure defined earlier.  Be
  338. sure that if any column contains string data, the corresponding
  339. FIELDINFO's offstruct parameter must refer to a PSZ (pointer to a
  340. character string), not a character string itself.
  341.  
  342.  
  343. Tree View
  344. =========
  345.  
  346. The tree view is used when the objects of a container are best
  347. represented in a hierarchy.  The drives folder of OS/2 2.0, where a
  348. drives directory structure is displayed, is an example of where a tree
  349. view is useful.  In all, the container offers three different types of
  350. tree views: tree-text, tree-icon, and tree-name.
  351.  
  352. The layout of the tree view is flexible and can be tailored to an
  353. application.  The lines that connect child records to their parents can
  354. be specified as to their thickness and indentation depth.  These values
  355. are set with the cxTreeLine and cxTreeIndent fields of the CNRINFO
  356. structure, respectively.  This example shows how to display longer and
  357. thicker lines than those provided by default:
  358.  
  359.     CnrInfo.cxTreeLine =   5;
  360.     CnrInfo.cxTreeIndent = 50;
  361.     WinSendMsg (hwndCnr, CM_SETCNRINFO, &CnrInfo,
  362.                 CMA_CXTREELINE | CMA_CXTREEINDENT);
  363.  
  364. The tree-icon and tree-text views use an icon to display the state
  365. (expanded or collapsed) of a parent record.  The container provides
  366. default expanded and collapsed icons, which can be replaced with the
  367. hptrExpanded and hptrCollapsed fields of the CNRINFO structure.  As with
  368. all icons used by the container, these can be sized.  The following code
  369. sample makes the expanded and collapsed icons larger than the default size.
  370.  
  371.     CnrInfo.slTreeBitmapOrIcon.cx = 100;
  372.     CnrInfo.slTreeBitmapOrIcon.cy = 100;
  373.     WinSendMsg (hwndCnr, CM_SETCNRINFO, &CnrInfo,
  374.                 CMA_SLTREEBITMAPORICON);
  375.  
  376. The tree-name view can be used to preserve screen real estate.  In this
  377. view, the record's expanded and collapsed icon are two separate icons.
  378. The container displays the appropriate icon based on the state of a
  379. parent record.  In this case, the application should utilize the
  380. TREEITEMDESC structure.  Because the MINIRECORDCORE structure does not
  381. contain a PTREEITEMDESC field, the TREEITEMDESC structure and tree-name
  382. view should be used only with the RECORDCORE structure.  For all
  383. records, the appropriate icons should be loaded in the TREEITEMDESC
  384. structures hptrExpanded and hptrCollapsed fields.  This code sample
  385. loads icons to be displayed in tree-name view for a parent record.
  386.  
  387.     pRecord->pTreeItemDesc = malloc(
  388.                sizeof(TREEITEMDESC) );
  389.     pRecord->pTreeItemDesc->hptrExpanded  =
  390.                WinLoadPointer(...);
  391.     pRecord->pTreeItemDesc->hptrCollapsed =
  392.                WinLoadPointer(...);
  393.  
  394. To insert records in tree-view, the RECORDINSERT structure needs to be
  395. set up properly.  To insert records at the root level (items with no
  396. parents), the pRecordParent field should always be set to NULL.  There
  397. are two options when inserting child records.  If inserting the first or
  398. last (CMA_FIRST or CMA_END) child, the pRecordParent field must point to
  399. the appropriate parent record.  When inserting child records that are
  400. not first or last, the pRecordOrder field must point to the previous
  401. child record and the pRecordOrder should be set to NULL.
  402.  
  403. Although the container's tree view is flexible, there are some
  404. limitations.  Regardless of the selection model (extended, multiple, or
  405. single) chosen, the tree view is always single selection.  Only the
  406. root-level items may be shown in the other container views.  To show
  407. child records in other views, you could use record sharing to share
  408. these records with another container.  While we have mentioned icons in
  409. this section, the container offers the same flexibility for bitmaps when
  410. the CA_DRAWBITMAP attribute is set.
  411.  
  412.  
  413. Query Messages and Coordinate Systems
  414. =====================================
  415.  
  416. There are a number of query messages provided by the container that
  417. allow you to query useful information.  CM_QUERYVIEWPORTRECT,
  418. CM_QUERYRECORDRECT, and CM_QUERYRECORDFROMRECT are query messages that
  419. deal with coordinate systems.
  420.  
  421. CM_QUERYVIEWPORTRECT deals with the workspace coordinates and container
  422. window coordinates while the others work only in window coordinates.
  423. The workspace is defined by the extreme positions of the visible records
  424. and the workspace coordinate system is stationary.  The container window
  425. viewport (the viewable section of the container's workspace) contains a
  426. subset of the entire workspace that is moved as the container is
  427. scrolled.  The viewport does not include scroll bars and titles that may
  428. be present in the container window.  CM_QUERYVIEWPORTRECT can retrieve
  429. the viewport's size and position relative to the workspace
  430. (CMA_WORKSPACE) or relative to the container window (CMA_WINDOW).  If
  431. only the size of the viewport is desired, either coordinate system can
  432. be used.
  433.  
  434. CM_QUERYRECORDRECT is used to query a record's position relative to the
  435. container window's origin.  This value is dependent on the viewport's
  436. current location and changes when the container is scrolled.  The
  437. position of the record in workspace coordinates can be queried with
  438. CM_QUERYRECORDINFO.  The record's workspace position does not change
  439. when the container is scrolled.
  440.  
  441. It may be useful to determine which records intersect a given rectangle
  442. whose coordinates are relative to the container window origin, for
  443. example, if you wanted to know which record was under the mouse.  With
  444. the mouse coordinates expressed as a rectangle, use
  445. CM_QUERYRECORDFROMRECT to search for the record under the mouse, if one
  446. exists.  This code sample can be used in any view to retrieve a record
  447. under the mouse:
  448.  
  449.     QueryRecordFromRect.cb =
  450.          sizeof(QUERYRECFROMRECT);
  451.     QueryRecordFromRect.rect.xLeft   = MousePt.x;
  452.     QueryRecordFromRect.rect.xRight  = MousePt.x;
  453.     QueryRecordFromRect.rect.yTop    = MousePt.y;
  454.     QueryRecordFromRect.rect.yBottom = MousePt.y;
  455.     QueryRecordFromRect.rect.fsSearch = CMA_PARTIAL |
  456.          CMA_ITEMORDER;
  457.     pRecord = WinSendMsg (hwndCnr,
  458.          CM_QUERYRECORDFROMRECT,
  459.          MPFROMP(CMA_FIRST),
  460.          &QueryRecordFromRect);
  461.  
  462. You may want to use the CMA_ZORDER flag in the fsSearch field if the
  463. container is in a nonautopositioned icon view (one in which the
  464. CCS_AUTOPOSITION is not set upon container creation).  In this case,
  465. records may be positioned on top of one another.
  466.  
  467.  
  468. Closing a Container Application
  469. ===============================
  470.  
  471. When your application is closed, it is responsible for freeing all
  472. memory, such as memory allocated for text strings used in records and
  473. columns.  All icons and bitmaps loaded should be released.
  474.  
  475.  
  476. References
  477. ==========
  478.  
  479. Haggar, Peter, Tai Woo Nam, and Ruth Anne Taylor.  "Container Control:
  480. Implementing the Workplace Model," IBM Personal Systems Developer
  481. (Winter 1992): 48-54.
  482.  
  483. OS/2 2.0 Programmers Guide Volume II, (IBM Doc. S10G-6494)
  484.  
  485. OS/2 2.0 Presentation Manager Programming Reference Volume III, (IBM Doc. S10G-6272)
  486.  
  487. SAA CUA Guide to User Interface Design, (IBM Doc. SC34-4289)
  488.  
  489. SAA CUA Advanced Interface Design Reference, (IBM Doc. SC34-4290)
  490.  
  491.  
  492. Peter Brightbill, IBM Programming Systems Laboratory, 11000 Regency
  493. Parkway, Cary, NC 27512.  Brightbill is a senior associate programmer in
  494. OS/2 PM extensions development.  He joined IBM in 1989 and has been
  495. working in OS/2 PM development since that time.  Brightbill was a member
  496. of the OS/2 container control development team and is currently working
  497. on additional OS/2 controls.  He received a B.S. in mathematics and
  498. computer science from Millersville University, Pa.
  499.  
  500. Peter Haggar, IBM Programming Systems Laboratory, 11000 Regency Parkway,
  501. Cary, NC 27512.  Haggar is a staff programmer in OS/2 PM extensions
  502. development.  He joined IBM in 1987 and has been working in OS/2 PM
  503. development since 1989.  Haggar was a member of the OS/2 container
  504. control development team and recently completed a five-month assignment
  505. with the OS/2 2.0 Workplace Shell development team.  He received a B.S.
  506. in computer science from Clarkson University, N.Y.
  507.  
  508.