Jump to content

PMGuide - Container Controls

From EDM2

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

Presentation Manager Programming Guide and Reference
  1. How to Use this Book
  2. Device Functions
  3. Direct Manipulation Functions
  4. Dynamic Data Formatting Functions
  5. Hooks and Procedures
  6. Profile Functions
  7. Spooler Functions
  8. Window Functions
  9. Message Processing
  10. Data Types
  11. Errors
  12. Atom Tables
  13. Button Controls
  14. Clipboards
  15. Combination Box
  16. Container Controls
  17. Control Windows
  18. Cursors
  19. Dialog Windows
  20. Direct Manipulation
  21. Drawing in Windows
  22. Dynamic Data Exchange
  23. Entry-Field Controls
  24. File Dialog Controls
  25. Font Dialog Controls
  26. Frame Windows
  27. Hooks
  28. Initialization Files
  29. Keyboard Accelerators
  30. List-Box Controls
  31. Menus
  32. Messages and Message Queues
  33. Multiple-Line Entry Field Controls
  34. Mouse and Keyboard Input
  35. Mouse Pointers and Icons
  36. Notebook Controls
  37. Painting and Drawing
  38. Presentation Parameters
  39. Resource Files
  40. Scroll-Bar Controls
  41. Slider Controls
  42. Spin Button Controls
  43. Static Controls
  44. Title-Bar Controls
  45. Value Set Controls
  46. Windows
  47. Window Classes
  48. Window Procedures
  49. Window Timers
  50. Appendices
  51. Notices
  52. Glossary

A container control (WC_CONTAINER window class) is a visual component that holds objects. It provides a powerful and flexible component for easily developing products that conform to the Common User Access (CUA) user interface guidelines. This chapter describes the container control component and how to use it in PM applications.

About Container Controls

A container can display objects in various formats and views. Generally speaking, each view displays different information about each object. If a container's data is too large for the window's work area, scrolling mechanisms are enabled. The CUA direct manipulation protocol is fully supported, thereby enabling a user to visually drag an object in a container window and drop it on another object or container window. Containers are an integral component of the CUA user interface.


Container Control Functionality

The container control provides multiple views of a container's contents, such as Icon, Name, Text, Tree, and Details views. The container control lets you change container views quickly and easily, display each view with a different font, or vertically split the Details view into two parts so that a user can widen one part to see more information.

Graphical user interface (GUI) support is part of the container control. GUI support allows:

  • Direct manipulation
  • Multiple selection types: single, extended, and multiple selections
  • Multiple selection techniques: marquee, swipe, and first-letter selection
  • Multiple selection mechanisms: mouse button 1, mouse button 2, and keyboard augmentation
  • Multiple forms of emphasis: selected-state, unavailable-state, in-use, and target emphasis
  • Scrolling when a container's work area is not large enough for all the container items to be visible
  • Dynamic scrolling to provide visible feedback to show the movement of the container items relative to the position of the scroll box

The container control supports various data types, such as icons or bit maps for the Icon, Name, Tree, and Details views. In the Details view, this includes the ability to use icons or bit maps in column headings as well as in the columns themselves. The container control also supports text in the following situations:

  • For container titles in all views
    • Beneath icons or bit maps in the Icon view
    • To the right of icons or bit maps in the Name and Tree views
    • For any column or column heading in the Details view
    • For container items in the Text view
    • For container items in the Details view, text in date, time, and number format

The container control provides a variety of options for enhancing the performance of the container:

  • Direct editing of container control text
    • Blank text fields in all views
    • Ownerdraw, which enables an application, rather than the container control, to draw the container items
    • Automatic reposition mode which is used in the Icon view. The container control provides an automatic reposition mode that repositions the items as a result of inserting, removing, sorting, filtering items, or changing window or font size.
    • Arrange message mode that arranges overlapping icons or bit maps so they no longer overlap
    • Data caching to efficiently remove items from and insert items into a container as they scroll in and out of view
    • Methods for sharing records among multiple containers
    • Memory usage optimization

Container Items

Container items can be anything that your application or a user might store in a container. Examples are executable programs, word processing files, graphic images, and database records.

Container item data is stored in the RECORDCORE and MINIRECORDCORE data structures. Both the application and the container have access to the data stored in these records.

The application must allocate memory for each record by using the CM_ALLOCRECORD message.

The maximum number of records is limited only by the amount of memory in the user's computer. The container control does not limit the number of records that a container can have.

Container Views

When a user opens a container, the contents of that container are displayed in a window. A container window can present various views of its contents, and each view can provide different information about its container items. The following table describes the views the container control provides:

Container Views
View Type Contents Displayed
Icon view Displays either icons or bit maps, with text beneath the icons or bit maps, to represent container items. These are called icon/text or bit-map/text pairs. Each icon/text or bit-map/text pair represents one container item. This is the default view.
Name view Displays either icons or bit maps, with text to the right of the icons or bit maps, to represent container items. These are called icon/text or bit-map/text pairs. Each icon/text or bit-map/text pair represents one container item.
Text view Displays a simple text list to represent container items.
Tree view Displays a hierarchical view of the container items. Three types of Tree views are available: Tree text, Tree icon, and Tree name.
Details view Displays detailed information about each container item. The same type of data is displayed for each container item, arranged in columns. The data in each column can consist of an icon or bit map, text, numbers, dates, or times.

If a text string is not specified for a view in a place where a text string could be used, a blank space is used as a placeholder. For example, if a text string is not placed beneath an icon in the Icon view, a blank space is inserted just as though the text string were there. If this blank space is not a read-only field, the user can put text into the space by editing it directly.

Icon View

The Icon view (CV_ICON attribute) displays icon/text pairs or bit-map/text pairs to represent container items; this is the default view. CV_ICON is an attribute of the CNRINFO data structure's flWindowAttr field.

In the Icon view, icon/text pairs and bit-map/text pairs are icons and bit maps, respectively, with one or more lines of text displayed below each icon or bit map. Each line can contain one or more text characters, which are centered below the icon or bit map. The container control does not limit the number of lines or the number of characters in each line.

Generally, the icon or bit map contains an image that depicts the type of container item that it represents. For example, an icon or bit map that represents a bar chart might contain an image of a bar chart.

Because the container control does not support both icons and bit maps in the same view, an application must specify which are used by setting either the CA_DRAWICON attribute or the CA_DRAWBITMAP attribute in the flWindowAttr field of the CNRINFO data structure. The default is the CA_DRAWICON attribute. The size of the icon or bit map can be specified in the slBitmapOrIcon field of the CNRINFO data structure.

In the Icon view, container items are positioned according to x- and y-coordinate positions. These are called workspace coordinates. You can supply these coordinates for each container item by using the ptlIcon field of the RECORDCORE data structure.

If you do not specify x- and y-coordinate positions, the container control places the icons or bit maps at (0,0). However, your application can arrange the icons or bit maps either by sending the CM_ARRANGE message or by setting the CCS_AUTOPOSITION style bit when creating a container. With both of these methods, the container items are arranged in rows, and any coordinates specified in the ptlIcon field are ignored. As they are arranged each ptlIcon is updated with its new location.

The container items fill the topmost row until the width of the work area is reached. The items then wrap to form another row immediately below the filled row. This process is repeated until all the container items are positioned in rows. Default spacing is implemented according to the guidelines for the CUA user interface.

If the CCS_AUTOPOSITION style bit is set and the container is displaying the Icon view, container items are arranged automatically, without the CM_ARRANGE message being sent, when:

  • The window size changes
  • Container items are inserted, removed, sorted, invalidated, or filtered
  • The font or font size changes

In all of these cases, container items are arranged the same as when the CM_ARRANGE message is sent. The CCS_AUTOPOSITION style bit is valid only when it is used with the Icon view.

If the CM_ARRANGE message is issued and the container control is not currently displaying the Icon view, the container items are still arranged logically. Nothing changes in the current view; the arrangement of the container items is not visible until the user switches to the Icon view.

Name View

The Name view (CV_NAME attribute) displays icon/text or bit-map/text pairs to represent container items. CV_NAME is an attribute of the CNRINFO data structure's flWindowAttr field.

In the Name view, icon/text pairs and bit-map/text pairs are icons and bit maps, respectively, with one or more lines of text displayed to the right of each icon or bit map. Each line can contain one or more text characters, which are left-justified. The container control does not limit the number of lines or the number of characters in each line.

The container control offers the option of flowing or not flowing the container items in the Name view. To flow container items means to dynamically arrange them in columns.

Non-Flowed Name View

If the container items are not flowed, the icon/text or bit-map/text pairs are placed in a single column in the leftmost portion of the work area.

Flowed Name View

If the container items are flowed (CV_NAME | CV_FLOW), the container appears. In this case, the container items fill the leftmost column until the depth of the work area is reached. The items then wrap to form another column immediately to the right of the filled column. This process is repeated until all of the container items are positioned in columns.

The width of each column is determined by the widest text string within the column. The height of the work area is determined by the size of the window.

Text View

The Text view (CV_TEXT attribute) displays one or more lines of text to represent container items. CV_TEXT is an attribute of the CNRINFO data structure's flWindowAttr field.

Each line can contain one or more text characters, which are left-justified. The container control does not limit the number of lines or the number of characters in each line.

The container control offers the option of flowing or not flowing the container items in the Text view.

Non-Flowed Text View

If the text strings are not flowed, the text for each container item is placed in a single column in the leftmost portion of the work area.

Flowed Text View

If the text strings are flowed (CV_TEXT | CV_FLOW), the container appears. In this case, the text strings fill the leftmost column until the depth of the work area is reached. The text strings then wrap to form another column immediately to the right of the filled column. This process is repeated until all the text strings are positioned in columns.

The width of each column is determined by the widest text string within the column. The height of the work area is determined by the size of the window.

Tree View

The Tree view (CV_TREE attribute) displays container items arranged hierarchically. CV_TREE is an attribute of the CNRINFO data structure's flWindowAttr field.

The leftmost items displayed in the Tree view are at the root level and are the same items displayed in all the other container views. Items that contain other items are called parent items. The items that a parent item contains are called child items and can be displayed only in the Tree view. Child items that contain other items serve a dual role: they are the children of their parent item, but they are parent items as well, with children of their own. For example, a parent item might be a book that contains individual child items for its chapters or a folder that contains several reports. The chapters or reports, in turn, could be parent items that contain their own children, such as the major sections of a chapter or report.

If the child items of a parent item are not displayed, the parent item can be Expanded to display them as a new branch in the Tree view. Once a parent item has been expanded, it can be Collapsed to remove its child items from the display.

You can use the cxTreeIndent and cxTreeLine fields of the CNRINFO data structure to specify the number of pels that a new branch is to be indented horizontally, and the width of the lines that are used to connect branches of the tree. These lines are displayed only if the CA_TREELINE attribute is specified in the flWindowAttr field.

The Tree view has three different types: Tree icon view, Tree text view, and Tree name view. If CV_TREE is specified, the Tree icon view is the default view. If neither CV_ICON, CV_TEXT, or CV_NAME are specified, CV_ICON is assumed.

Tree Icon View and Tree Text View

The Tree icon and Tree text views are identical in every aspect except their appearance on the screen. Container items in the Tree icon view (CV_TREE | CV_ICON) are displayed as either icon/text pairs or bit-map/text pairs. The items are drawn as icons or bit maps with one or more lines of text displayed to the right of each icon or bit map.

Container items in the Tree text view (CV_TREE | CV_TEXT) are displayed as text strings. In both views, the container control does not limit the number of lines of text or the number of characters in each line.

In the Tree icon and Tree text views, a parent item is expanded by selecting the Collapsed icon/bit map, which is displayed to the left of the parent item.

The Collapsed icon/bit map should contain some visible indication that the item can be expanded. The default Collapsed bit map that is provided by the container control uses a plus sign (+) to indicate that more items, the children of this parent, can be added to the view.

When the child items of a parent item are displayed, the Collapsed icon/bit map to the left of that parent item changes to an Expanded icon/bit map. Just as the Collapsed icon/bit map provides a visible indication that an item can be expanded, so should the Expanded icon/bit map indicate that an item can be collapsed. The default Expanded bit map provided by the container control uses a minus sign (-) to indicate that the child items of this parent can be subtracted from the view. If any of the child items have children of their own, a Collapsed or Expanded icon/bit map is displayed to their immediate left as well.

To display your own Collapsed and Expanded icons or bit maps, specify their handles by using the hptrCollapsed and hptrExpanded fields of the CNRINFO data structure for icons, and the hbmCollapsed and hbmExpanded fields for bit maps. Also, you can use the slTreeBitmapOrIcon field to specify the size, in pels, of these Collapsed and Expanded icons and bit maps.

Tree Name View

Container items in the Tree name view (CV_TREE | CV_NAME) are displayed as either icon/text pairs or bit-map/text pairs. Similar to the Tree icon view, the items are drawn as icons or bit maps with one or more lines of text displayed to the right of each icon or bit map. The container control does not limit the number of lines or the number of characters in each line of text.

Unlike the Tree icon view, however, separate Collapsed and Expanded icons/bit maps are not used. Instead, if an item is a parent, the icon or bit map that represents that item contains the same type of visible indication that is placed in a separate icon/bit map in the Tree icon view to show that an item can be collapsed or expanded. In this way, the icon or bit map that represents the parent item can serve a dual purpose and thus preserve space on the screen, an important consideration if the text strings used to describe items become too long.

The container control does not provide default icons or bit maps for the Tree name view. To display your own Collapsed and Expanded icons or bit maps, specify their handles using the hptrCollapsed and hptrExpanded fields of the TREEITEMDESC data structure for icons, and the hbmCollapsed and hbmExpanded fields for bit maps. Also, you can use the slBitmapOrIcon field of the CNRINFO data structure to specify the size, in pels, of these Collapsed and Expanded icons and bit maps.

Details View

The Details view (CV_DETAIL attribute) of the container control can display the following data types to represent container item: icons or bit maps, text, numbers, dates, and times. CV_DETAIL is an attribute of the CNRINFO data structure's flWindowAttr field.

The data is arranged in columns, which can have headings. Each column can contain data that belongs to only one of the valid data types. Column headings can contain text, icons, or bit maps.

The width of each column can be explicitly specified in the cxWidth field of the FIELDINFO data structure. If a column width is not specified, it is determined by the widest entry in the column.

Columns can be inserted or removed dynamically. All of the columns in a given row belong to a single container item; selecting the data portion of a row selects the entire row, not just the individual column.

Details view column headings and data can be top- or bottom-justified or vertically centered, as well as left- or right-justified or horizontally centered. In addition, horizontal separator lines can be specified between the column headings and the data; vertical separator lines can be placed between columns.

Determining the Width of a Column in a Details View

There are instances when you might want to determine the width of a column in the Details view. A function has been added to the container control to allow you to determine the width of the data in a column. You can then compute the width of the entire column by adding the width of the data to the left and right margins of the column. To determine the width of a column:

1.- Define an attribute with a value of 0x0200 and give it a name such as CMA_DATAWIDTH.

2.- Issue the CM_QUERYDETAILFIELDINFO message with the following values:

a. Provide a pointer to the FIELDINFO data structure in param1.

b. Specify your attribute (see step 1) in param2.

c. Request a return value with a type of LONG, not PFIELDINFO, to retrieve the width of the column in the FIELDINFO data structure to which you are pointing. The value returned is the width of the data (text, icon, or bit map) in this column.

3.- Use GpiQueryFontMetrics to query the average character width of the font used by the container. This value will be used to calculate the total column width.

4.- Multiply 3 by the average character width and add this to the data width returned from step 2 for all columns except the following:

    • The first and last columns in each split window. In these cases, multiply 2.5 by the average character width and add the column data width returned from step 2.
    • The only other special case is where there is only 1 column in either the left or right split windows. In this case, you would multiply 2 by the average character width and add the column data width returned from step 2.

5.- The value returned is the total width of the column.

Split Bar Support for the Details View

A split bar enables the application to split the container window vertically between two column boundaries. This function is available only in the Details view.

The two portions of the work area on either side of the split bar appear side-by-side. They scroll in unison vertically, but they scroll independently horizontally.

The application is responsible for specifying the position of the split bar, which is defined with the xVertSplitbar field. Also, the rightmost column of the left split window is specified with the pFieldInfoLast field. xVertSplitbar and pFieldInfoLast are fields of the CNRINFO data structure.

The left split window cannot be empty if there is data in the right window. The right split window is not required to have data. However, because data cannot be scrolled from the right split window into the left split window, or from left to right, the split bar loses much of its usefulness if the right split window is empty.

The user can drag the vertical split bar within the limits of the window. As the user drags the split bar to the left, the right split window becomes wider; as the user drags the split bar to the right, the left split window becomes wider.

Each container control can have one vertical split bar. Horizontal split bars are not supported.

Using Container Controls

This section provides information about the following topics:

  • Creating a container
  • Allocating memory for container records
  • Allocating memory for container columns
  • Inserting container records
  • Removing container records
  • Setting the container control focus
  • Using container views
  • Changing a container view
  • Arranging icons in a grid

Note: Most of the sample code in this section is part of a complete program that creates a container for a small address book. The program is illustrated in "Sample Code for Container Controls".

Creating a Container

You create a container by using the WC_CONTAINER window class name in the ClassName parameter of WinCreateWindow. Before you create the container, you can create a frame window as a parent. If you create the frame window, it sizes the container to fill its work area. The following sample code shows the code to create both the frame and the container:

HAB     hab;
HWND    hPopupMenu;
HWND    hFrameWnd, hCnrWnd;   /* Frame and Container window handles */
PFNWP   SysWndProc;

INT main (VOID)
{
    HMQ         hmq;
    FRAMECDATA  fcd;
    QMSG        qmsg;

    if (!(hab = WinInitialize(0)))
      return FALSE;

    if (!(hmq = WinCreateMsgQueue(hab, 0)))
      return FALSE;

/***********************************************************************/
/*  Set up the frame control data for the frame window.                */
/***********************************************************************/
    fcd.cb = sizeof(FRAMECDATA);
    fcd.flCreateFlags = FCF_TITLEBAR      |
                        FCF_SYSMENU       |
                        FCF_SIZEBORDER    |
                        FCF_SHELLPOSITION |
                        FCF_MINMAX        |
                        FCF_TASKLIST;
    fcd.hmodResources = NULLHANDLE;
    fcd.idResources = 0;

/***********************************************************************/
/*  Create the frame to hold the container control.                    */
/***********************************************************************/
    hFrameWnd = WinCreateWindow(HWND_DESKTOP,
                                WC_FRAME,
                                "Phone Book",
                                0, 0, 0, 0, 0,
                                NULLHANDLE,
                                HWND_TOP,
                                0,
                                &fcd,
                                NULL);

/***********************************************************************/
/*  Verify that the frame was created; otherwise, stop.                */
/***********************************************************************/
    if (!hFrameWnd)
      return FALSE;

/***********************************************************************/
/*  Set an icon for the frame window.                                  */
/***********************************************************************/
    WinSendMsg(hFrameWnd,
               WM_SETICON,
               (MPARAM)WinQuerySysPointer(HWND_DESKTOP,
                                          SPTR_FOLDER,
                                          FALSE),
               NULL);

/***********************************************************************/
/*  Create the container.                                              */
/***********************************************************************/
    hCnrWnd = WinCreateWindow(hFrameWnd,
                              WC_CONTAINER,
                              NULL,
                              CCS_AUTOPOSITION |
                              CCS_READONLY     |
                              CCS_SINGLESEL,
                              0, 0, 0, 0,
                              hFrameWnd,
                              HWND_BOTTOM,
                              FID_CLIENT,
                              NULL,
                              NULL);

The container is created with a default set of control data, which can be changed using the CM_SETCNRINFO message.

Allocating Memory for Container Records

Your application must allocate memory for a container record by using the CM_ALLOCRECORD message, which also enables you to allocate memory for additional application data.

The maximum number of records is limited by the amount of memory in the user's computer. The container control does not limit the number of records that a container can have.

The following sample code shows how to allocate memory for records that populate the container. A pointer to the record is returned.

    HWND            hIcon;
    PRECORDCORE     Address, FirstRec;
    RECORDINSERT    recsIn;
    ULONG           x;

/**********************************************************************/
/*  Allocate MAXFRIENDS records all at once -                         */
/*  CM_ALLOCRECORD returns them in a linked list.                     */
/**********************************************************************/
    Address = (PRECORDCORE)WinSendMsg(hWnd,
                                      CM_ALLOCRECORD,
                                      0,
                                      MPFROMLONG(MAXFRIENDS));

Your application can use the CM_ALLOCRECORD message to allocate memory for one or more container records. The application can request n container records with an nRecords parameter. If n is one, a pointer to that record is returned. If n is greater than one, a pointer to the first record in a linked list of n records is returned.

Allocating Memory for Container Columns

In addition to allocating memory for records, an application also must allocate memory for columns of data if the details view is used. In the Details view, a container's data is displayed in columns, each of which is described in a FIELDINFO data structure.

Memory is allocated for FIELDINFO data structures using the CM_ALLOCDETAILFIELDINFO message. Unlike the CM_ALLOCRECORD message, the CM_ALLOCDETAILFIELDINFO message does not allow the application to allocate memory for additional application data. However, the pUserData field of the FIELDINFO data structure can be used to store a pointer to the application-allocated data.

Multiple FIELDINFO data structures can be allocated with the nFieldInfo parameter of the CM_ALLOCDETAILFIELDINFO message.

Inserting Container Records

After the memory is allocated, you can insert one or more container records by using the CM_INSERTRECORD message.

The following sample code inserts records into a container for which memory was allocated in the previous code fragment:

/**********************************************************************/
/*  We will need the first record's address to                        */
/*  insert them into the container.                                   */
/**********************************************************************/
    FirstRec = Address;

/**********************************************************************/
/*  Loop through the address book, loading as we go.                  */
/*  Because the CM_ALLOCRECORD returns a linked                       */
/*  list, the address of the next record is retrieved                 */
/*  from each record as we go (preccNextRecord).                      */
/**********************************************************************/
    for (x = 0; x < MAXFRIENDS; x++)
    {
       Address->cb = sizeof(RECORDCORE);    /* Standard records       */
       Address->hptrIcon = hIcon;           /* File icon              */
       Address->pszIcon = Friends[x].NickName;
       Address->pszName = Friends[x].FullName;
       Address->pszText = Friends[x].FullName;
       Address = Address->preccNextRecord;  /* Next record in list    */
    }

/**********************************************************************/
/*  Set up the insert record structure to place the                   */
/*  records in the container.                                         */
/**********************************************************************/
    recsIn.cb = sizeof(RECORDINSERT);

    /* Put the records in after any others */
    recsIn.pRecordOrder = (PRECORDCORE)CMA_END;

    /* All the records are top level (not children of other records) */
    recsIn.pRecordParent = NULL;

    /* The icons are top level */
    recsIn.zOrder = (USHORT)CMA_TOP;

    /* Redraw the container */
    recsIn.fInvalidateRecord = TRUE;

    /* Set the number of records to insert */
    recsIn.cRecordsInsert = MAXFRIENDS;

/**********************************************************************/
/*  Insert the records into the container.                            */
/**********************************************************************/
    WinSendMsg(hWnd,
               CM_INSERTRECORD,
               (PRECORDCORE)FirstRec,
               &recsIn);
}

The CM_INSERTRECORD message requires you to provide two pointers. The first pointer points to the record that is to be inserted, which is specified in the FirstRec parameter. When you are inserting multiple records, use this parameter to specify a pointer to a linked list of records.

The second pointer points to a RECORDINSERT data structure (&recsIn), which specifies information the container needs for inserting records.

One of the elements of information that this data structure contains is the order in which the records are to be inserted, which is specified in the pRecordOrder field. In this field you have two options. The first option is to specify a pointer to a container record. The records being inserted are placed immediately after that record. In this case, the pRecordParent field is ignored.

The second option is to specify whether the records being inserted are to be placed at the beginning or end of a list of records. This is done by specifying either the CMA_FIRST or CMA_END attributes. If you choose this option, the list of records used depends on the value of the pRecordParent field.

If CMA_FIRST or CMA_END is specified and the value of the pRecordParent field is NULL, the inserted records are placed at the beginning or end of the root-level records. However, if CMA_FIRST or CMA_END is specified and pRecordParent contains a pointer to a parent item record, the records are inserted at the beginning or end of the list of child item records that this parent record contains.

The RECORDINSERT data structure also lets you specify the z-order position of the records being inserted. The CMA_TOP and CMA_BOTTOM attributes of the zOrder field place the record at the top or bottom, relative to the other records in the z-order list. This field applies to the Icon view only.

To specify the number of records that are being inserted, use the cRecordsInsert field. The value of this field must be greater than 0.

The last field in the RECORDINSERT data structure is flInvalidateRecord, which enables you to control whether the records are displayed automatically when they are inserted. If you specify TRUE in this field, the display is updated automatically. However, if you specify FALSE, the application must send the CM_INVALIDATERECORD message after the records are inserted to update the display.

Where items are positioned in a container depends on the view the user has specified. If the Icon view is specified and the CCS_AUTOPOSITION style bit is not set, the x- and y-coordinates for each record, which are stored in the ptlIcon field of the RECORDCORE and MINIRECORDCORE data structures, determine its position. Records displayed in the Name view, Text view, Tree view, and Details view are positioned as previously described in this section.

Note: Records inserted into a list of child record items can be displayed in the Tree view only. These records are visible only if the parent record item to which these child record items belong is expanded.

Removing Container Records

The CM_REMOVERECORD message can be used to remove one or more container records from the container control. The following sample code removes all records from a container and frees the memory associated with those records. It is the application's responsibility to free all application-allocated memory that is associated with the removed container records. The container is invalidated and repainted.

USHORT cNumRecord;             /* Number of records to be removed     */
USHORT fRemoveRecord;          /* Container message attributes        */

/**********************************************************************/
/*  Zero means remove all records.                                    */
/**********************************************************************/
cNumRecord = 0;

/**********************************************************************/
/*  Specify attributes to invalidate the container                    */
/*  and free the memory.                                              */
/**********************************************************************/
fRemoveRecord =
  CMA_INVALIDATERECORD | CMA_FREE;

/**********************************************************************/
/*  Remove the records.                                               */
/**********************************************************************/
WinSendMsg(hwndCnr,            /* Container window handle             */
  CM_REMOVERECORD,             /* Container message for removing      */
                               /* records                             */
  NULL,                        /* NULL PRECORDARRAY                   */
  MPFROM2SHORT(
    cNumRecord,                /* Number of records                   */
    fRemoveRecord));           /* Memory invalidation flags           */

The application must set the pointers to each record to be removed in an array. If the fRemoveRecord parameter of this message includes the CMA_FREE attribute, the records are removed and the memory is freed. If this attribute is not set, the records are removed from the list of items in the container, and the application must use the CM_FREERECORD message to free the memory. The default is not to free the memory.

If the fRemoveRecord parameter includes the CMA_INVALIDATERECORD attribute, the container is invalidated after the records are removed. The default is not to invalidate the container. The CMA_INVALIDATERECORD attribute can be used with the CMA_FREE attribute, separated by a logical OR operator (|), to free the record's memory and invalidate the container.

Setting the Container Control Focus

The application must set the focus to the container control using WinSetFocus so that all mouse and keyboard activity goes to the container window. The following code fragment shows how to use WinSetFocus:

 WinSetFocus(HWND_DESKTOP,       /* Desktop window handle             */
             hListWnd)           /* Handle of window to receive focus */

Using Container Views

Container views are specified by using attributes on the flWindowAttr field of the CNRINFO data structure.

Because the container control does not support both icons and bit maps in the same view, an application must specify which are used by setting either the CA_DRAWICON attribute or the CA_DRAWBITMAP attribute in the flWindowAttr field of the CNRINFO data structure. The default is the CA_DRAWICON attribute. The size of the icon or bit map can be specified in the slBitmapOrIcon field of the CNRINFO data structure.

Changing a Container View

The following sample code shows how to use the CM_SETCNRINFO message to change from the current view of a container (Name, Details, or Text) to the Icon view:

CNRINFO cnrInfo;

/**********************************************************************/
/*  Set the attribute field to the Icon view.                         */
/**********************************************************************/
cnrInfo.flWindowAttr = CV_ICON;

/**********************************************************************/
/*  Change the view from the current view to the Icon view.           */
/**********************************************************************/
WinSendMsg(
  hwndCnr,                     /* Container window handle             */
  CM_SETCNRINFO,               /* Container message for setting       */
  MPFROMP(&cnrInfo),           /* Container control data              */
  MPFROMLONG(
    CMA_FLWINDOWATTR));        /* Message attribute that sets         */
                               /* container window attributes         */

Creating a Grid for Icons

This section will describe how to create a grid.

Graphical User Interface Support for Container Controls

This section describes the container control support for graphical user interfaces (GUIs). Except where noted, this support conforms to CUA interface design guidelines. The GUI support provided by the container control consists of the following:

  • Scrolling
  • Dynamic scrolling
  • Selecting container items
  • Providing emphasis
  • Using direct manipulation

Scrolling

The container control automatically provides horizontal or vertical scroll bars, or both, whenever the container window's work area is not large enough to display all of the container items.

If all container items are visible in the work area, the scroll bars are either removed or disabled, depending on the view and how the items are positioned, as follows:

  • If container items are displayed in the icon or tree view, and one or more items are not visible in the work area, a horizontal scroll bar, vertical scroll bar, or both, are provided, depending on the position of the items outside of the work area. If container items are positioned to the right or left of the work area, a horizontal scroll bar is provided; if container items are positioned below or above the work area, a vertical scroll bar is provided.
Scroll bars are not provided if all the container items are visible in the work area. Scroll bars are removed from the container window if either of the following occurs:
  • Container items positioned outside the work area are moved into the work area.
  • The size of the container window is increased so that container items formerly not visible become visible.
  • If container items are displayed in non-flowed text and non-flowed Name views, a vertical scroll bar is provided; this scroll bar is disabled if all the container items are visible in the work area. A horizontal scroll bar is used in these views only when the work area is too narrow to allow the widest container item to be seen in its entirety. If the user changes the window size to allow the entire widest container item to be seen, the horizontal scroll bar is removed.
  • If container items are displayed in flowed text and flowed name views, a horizontal scroll bar is provided; this scroll bar is disabled if all the container items are visible in the work area. A vertical scroll bar is used in these views only when the work area is too short to allow the tallest container item to be seen in its entirety. If the user changes the window size to allow the entire tallest container item to be seen, the vertical scroll bar is removed.
  • If container items are displayed in the Details view, both horizontal and vertical scroll bars are provided. These scroll bars are disabled if all the container items are visible in the work area.
Note: A Details view that is split has two horizontal scroll bars, one for each portion of the split window.

Dynamic Scrolling

The container control supports dynamic scrolling, which enables the user to drag the scroll box in the scroll bar and get immediate visible feedback on where the scrolling stops when the scroll box is dropped. If the scrolling range is greater than 32K pels, dynamic scrolling is disabled.

Selecting Container Items

Except during direct manipulation and direct editing of text in a container, a user must select a container item before performing an action on it. The container control provides several selection types, along with selection techniques to implement those types. The container control also supports two selection mechanisms: pointing device, such as a mouse, and the keyboard.

Selection Types

The container control supports the following selection types:

Single selection
Single selection enables a user to select only one container item at a time. This is the default selection type for all views and is the only selection type supported for the Tree view.
Extended selection
Extended selection enables a user to select one or more container items, in any combination. The CUA-defined keyboard augmentation keys are implemented for extended selection. When used with a pointing device, these keys enable a user to select discontiguous sets of container items. Extended selection is valid for all views except the Tree view.
Multiple selection
Multiple selection enables a user to select none, some, or all of the container items. Multiple selection is valid for all views except the Tree view.

Only one of these selection types can be used for each container. The selection type for a container is defined when the container is created.

Selection Techniques

Depending on the type of view and the type of selection, a user can select container items using the following selection techniques:

Marquee selection
Marquee selection is supported only in the Icon view and is only valid with the extended and multiple selection types. This selection technique enables a user to begin selection from an anchor point that is established by moving the pointer to white space in the container and pressing, but not releasing, the select button on the pointing device. As the user presses the select button and drags the pointer, a tracking rectangle is drawn between the anchor point and the current pointer position. All items whose icons or bit maps are entirely within the tracking rectangle are dynamically selected.
Swipe selection
Swipe selection is valid only with the extended and multiple selection types. The container control implements two techniques for swipe selection: touch swipe and range swipe.
Touch swipe
Touch-swipe selection is implemented in the Icon view. With this selection technique, the pointer must pass over some portion of a container item while the user is pressing the select button for that item to be selected.
Range swipe
In views other than the Icon and Tree views, range-swipe selection is available. With this method, the user presses the select button while moving the pointer. However, the pointer does not have to pass directly over a container item for that item to be selected. Aside from pressing the select button and moving the pointer, the only other requirement for selection is that the container item must be within a range of items that is being selected. The range begins at the pointer's position when the user presses the select button; it ends at the pointer's position when the user releases the select button.
First-letter selection
For the Icon, Name, Text, and Tree views, first letter selection occurs when a character key is pressed, and the first container item whose text begins with that character is displayed with selected-state emphasis. The same is true for the Details view, except that all the columns for a record are searched for a matching character before the next record is searched. The effect of first letter selection on other selected container items depends on the chosen selection type (single, multiple, or extended).
Note
If more than one container window is open, selecting a container item in one window has no effect on the selections in any other window.

Selection Mechanisms

Mouse button 1 (the select button) is used for selecting container items, and mouse button 2 (the drag button) is used for dragging and dropping container items during direct manipulation. These definitions also apply to the same buttons on any other pointing device.

In addition, a user can press a keyboard key while pressing a mouse button; this is called keyboard augmentation. The only instance of keyboard augmentation defined specifically for the container control is pressing the Alt key with the select button, which starts direct editing of text in a container.

In addition, the container control supports two keyboard cursors that can be moved by using keyboard navigation keys:

  • The selection cursor, a dotted black box drawn around a container item, which represents the current position for the purpose of keyboard navigation.
  • The text cursor, a vertical line that shows the user where text can be inserted or deleted when container text is being edited directly.

Keyboard navigation consists of the use of the Up and Down Arrow, Left and Right Arrow, Home, End, PgUp, and PgDn keys. If container items are not visible within the work area, navigation with these keys causes the items to scroll into view if the user is not editing container text directly.

Providing Emphasis

The container control supports various types of emphasis. The following list describes forms of emphasis that have a distinct visible representation in the container control:

Selected-state and unavailable-state emphasis
When a container item is selected, the container item receives selected-state emphasis, which means that the emphasis is applied to icon/text or bit-map/text pairs in the Icon, Name, Tree icon, and Tree name views; text strings in the Text and Tree text views; and an entire row that represents a container item in the Details view.
The color for selected-state emphasis can be changed by using the control panel or WinSetPresParam, which results in a WM_PRESPARAMCHANGED message being sent to the container.
In-use emphasis
Cross-hatching behind an icon or bit map indicates in-use emphasis. In-use emphasis is not applied to container items in the Text view, Tree text view, or Details view when it contains text only. However, the Details view often includes icons or bit maps in one column of each record, usually the leftmost column. In this situation, specify the column that contains the icons or bit maps so that in-use emphasis can be applied to them. This column can be set by using the pFieldInfoObject field of the CNRINFO data structure.
Target emphasis
Target emphasis is used during direct manipulation. When a user drags one container item over another, the item beneath the dragged item displays target emphasis. Two forms of target emphasis (visible feedback) are available: a black line and a black border. These forms of emphasis indicate the target, where the container item is dropped if the user releases the drag button.

Using Direct Manipulation

Direct manipulation is a protocol that enables the user to drag a container item within its current window or from one window to another. The user can drop the container item either on white space in a window or on another item.

Direct manipulation can be performed with all views of the container control. A function is provided so that the application is notified if an item is dropped on another item in the container and if an item is dragged from the container.

The user can drag any container item, whether or not it is selected. If the user presses the drag button when the pointer is over a selected container item, the application drags all selected items.

If the user presses the drag button when the pointer is over a container item that is not selected, the application drags only the item that the pointer is over.

The container control fully supports direct manipulation.

Enhancing Container Controls Performance and Effectiveness

The following topics offer information about fine-tuning a container to enhance its performance and effectiveness:

  • Positioning container items
  • Specifying space between container items
  • Providing target emphasis
  • Specifying deltas for large amounts of data
  • Direct editing of text in a container
  • Specifying container titles
  • Specifying fonts and colors
  • Drawing container items and painting backgrounds
  • Filtering container items
  • Optimizing container memory usage
  • Sharing records among multiple containers

Positioning Container Items

Container items are positioned in the Icon view according to workspace coordinates.

The workspace is a two-dimensional Cartesian-coordinate system. The user can see a portion of the workspace in the work area, which is the scrollable viewing area of the container that is defined by the size of the container window. The work area is logically scrollable within the workspace.

Scrollable Workspace Areas

The workspace is indicated by the solid black line that runs even with:

  • The top and bottom edges of the topmost and bottommost container items
  • The left and right edges of the leftmost and rightmost container items

The workspace is defined by the minimum and maximum x- and y-coordinates of the items in the container. The work area of the container window can be scrolled only within the workspace and only as far as is necessary to see the topmost, bottommost, leftmost, and rightmost container items.

Workspace and Work Area Origins

When the container is created, the work area and workspace share the same origin, (0,0). If the application requires that the work area and the workspace have different origins, the application can use the ptlOrigin field of the CNRINFO data structure and the CM_SETCNRINFO message to set the origin of the work area. The application can use the CM_QUERYCNRINFO and CM_SETCNRINFO messages to obtain the origin when the user ends the application and to reset the origins when the user restarts the application.

Container items are located in reference to the workspace origin. There is a visual shift as the work area is scrolled; however, because the work area moves over a fixed workspace, the coordinates of the container items do not change.

Specifying Space between Container Items

You can specify the amount of vertical space, in pels, to allow between container items by using the cyLineSpacing field of the CNRINFO data structure. If you do not specify how much vertical space can be used, the container control sets the space between the items using a default value. For the Tree view, you can specify the horizontal distance between the levels by using the cxTreeIndent field of the CNRINFO data structure. If this value is less than 0, a default is used.

Providing Source Emphasis

Source emphasis is the type of emphasis provided when a context menu is displayed. It appears as a dotted box with rounded corners that surrounds the item for which the context menu is requested, or the item that is being dragged.

To provide source emphasis for container items issue the CM_SETRECORDEMPHASIS message with the following values:

1.- Provide a pointer to the RECORDCORE or MINIRECORDCORE data structure in param1.

You can provide source emphasis for the entire container by setting param1 to NULL.

2.- Set the usChangeEmphasis parameter to TRUE in param2.

3.- Set the fEmphasisAttribute parameter in param2 to CRA_SOURCE (0x00004000L).

To remove source emphasis follow the same procedure outlined above, but set the usChangeEmphasis parameter in param2 to FALSE instead of TRUE.

Providing Target Emphasis

The CA_ORDEREDTARGETEMPH and CA_MIXEDTARGETEMPH attributes of the CNRINFO data structure's flWindowAttr field determine the form of emphasis applied for the Text, Name, and Details views, as follows:

  • If the CA_ORDEREDTARGETEMPH attribute is set:
  • The CN_DRAGAFTER notification code is sent when a container item is being dragged.
  • A black line is drawn between container items to show the current target position.
  • If the CA_MIXEDTARGETEMPH attribute is set:
  • The CN_DRAGAFTER and CN_DRAGOVER notification codes are sent when a container item is being dragged. The notification code sent depends on the position of the pointer relative to the item it is positioned over.
  • A black line is drawn if the pointer is positioned such that the item being dragged is inserted between two target items.
  • A black border is drawn around either the entire target item for the Text and Details views or the icon or bit map for the Name view if the pointer is positioned such that the item being dragged is dropped on the target item.
  • If the CA_ORDEREDTARGETEMPH and CA_MIXEDTARGETEMPH attributes are not set:
  • The CN_DRAGOVER notification code is sent when a container item is being dragged.
  • A black border is drawn around the entire target item for the Text and Details views, and around the icon or bit map only for the Name view.

For the Icon and Tree view, the CA_ORDEREDTARGETEMPH and CA_MIXEDTARGETEMPH attributes are ignored, so target emphasis is applied as follows:

  • The CN_DRAGOVER notification code is sent when a container item is dragged.
  • A black border is drawn around the target, as follows:
  • For the Icon view, if the target is another container item, a black border is drawn around the icon or bit map that represents the container item, but not around the text string beneath it. If the target is white space, a black border is drawn around the outer edge of the entire work area.
  • For the Tree icon and Tree name views, a black border is drawn around the icon or bit map that represents the container item, but not around the text string to the right of it.
  • For the Tree text view, a black border is drawn around the entire target item.

Specifying Deltas for Large Amounts of Data

The container control can accommodate large amounts of data with an application-defined delta. The delta is an application-defined threshold, or number of container items, from either end of the list. The application is responsible for specifying the delta value in the CNRINFO data structure's cDelta field. It also is responsible for setting the delta value with the CMA_DELTA attribute of the CM_SETCNRINFO message's ulCnrInfoFl parameter.

The container control monitors its place in the list of container items when the user is scrolling through it. When the user scrolls to the delta from either end of the list, the container control sends a CN_QUERYDELTA notification code to the application as a request for more container items in the list.

The application is responsible for managing the records in the container. When the application receives the CN_QUERYDELTA notification code, the application is responsible for removing and inserting container records by using the CM_REMOVERECORD message and the CM_INSERTRECORD message, respectively.

Note:

The delta concept is intended for applications with large amounts of data, or several thousand records. Applications with smaller amounts of data are not required to use the delta function. The default delta value is 0.

The delta function is not available in the Icon view because it is intended for data displayed in a linear format.

Direct Editing of Text in a Container

Direct editing of text is supported for any text field in a container, including the container title, column headings, and container items. If a text field, such as the text field beneath an icon in the Icon view, has no text and is not read-only, a user can place text in that field by editing the field directly. The font specified for the container by the application is used for the edited text.

Direct editing is supported only for text data. Therefore, if the data type in the Details view is other than CFA_STRING, a user cannot edit it. CFA_STRING is an attribute of the FIELDINFO data structure's flData field.

You can prevent a user from editing any of the text in a container window by setting the CCS_READONLY style bit when a container is created. If you do not set this style bit, the user can edit any of the text in a container window unless you set the following read-only attributes:

  • CA_TITLEREADONLY of the CNRINFO data structure's flWindowAttr field
  • CRA_RECORDREADONLY of the RECORDCORE data structure's flRecordAttr field
  • CFA_FIREADONLY of the FIELDINFO data structure's flData field
  • CFA_FITITLEREADONLY of the FIELDINFO data structure's flTitle field

If one of these read-only attributes is set, a user's attempts to edit container text directly are ignored.

A user can edit container text directly by doing either of the following:

  • Moving the pointer to an editable text field, holding down the Alt key, and clicking the select button
  • Sending a CM_OPENEDIT message to the container control. The application can assign a key or menu choice to this message so that the keyboard can be used to edit container text directly.

The container control responds by using the WM_CONTROL message to send the CN_BEGINEDIT notification code to the application. A window that contains a multiple-line entry (MLE) field opens to show that container text can be edited directly.

The editing actions supported by MLEs, such as Cut, Copy, and Paste, are also supported by the container control. These actions can be performed using system-defined shortcut keys. The actions and shortcut keys are defined by CUA interface design guidelines.

If the user enters a text string that is longer than the text field, the text string scrolls. If multiple lines of text are needed or wanted, a user can press the Enter key to insert a new line.

A user can end the direct editing of container text and save the changes by doing either of the following:

  • Moving the pointer outside the MLE and pressing the select button
  • Sending a CM_CLOSEEDIT message to the container control. The application can assign a key or menu choice to this message so that the keyboard can be used to end the direct editing of container text.

The container responds by sending the WM_CONTROL message to the application again, but this time with the CN_REALLOCPSZ notification code. The application can allocate more memory on receipt of the CN_REALLOCPSZ notification code, if necessary. If the application returns TRUE, the container control copies the new text to the application's text string. If the application returns FALSE, the text change in the MLE is disregarded. The container then sends the WM_CONTROL message to the application again, this time with the CN_ENDEDIT notification code. The MLE field is removed from the screen, leaving only the text string.

A user can end the direct editing of container text without saving any changes to the text in numerous ways, including the following:

  • Pressing the Esc key
  • Dragging the container item that is being edited
  • Pressing the Alt key and the select button before the direct editing of container text has ended
  • Scrolling the container window

The CN_ENDEDIT notification code is sent to the application in each of these cases.

Searching for Exact Text String Matches

There might be times when you need to search the container for a text string that is an exact match of your search string argument. To find an exact match:

  • In the SEARCHSTRING data structure, specify values for the fields as you normally would, with the following exception:
Along with an attribute for the type of view being displayed in the container, in the usView field specify the CV_EXACTLENGTH (0x10000000L) flag. For example:
CV_EXACTLENGTH | CV_ICON
Note: The usView field if used for specifying the exact match attribute, and the type of view. Despite the "us" prefix this field is a ULONG. The "us" prefix is used in the header files to maintain backward compatibility.

Specifying Container Titles

The container control can have a non-scrollable title that consists of one or more lines of text. The container control does not limit the number of lines or the number of characters in each line. If specified, this title is the first line or lines of the container control. The text of the title is determined by the application and can be used to identify the container or to contain status information.

The CA_CONTAINERTITLE attribute must be set to include a title in a container window. The default is no container title.

If you do not want the user to be able to edit the container title directly, you can set the CA_TITLEREADONLY attribute. The default is that the container title can be edited.

Below the title a horizontal line separates the container title from the container items. The CA_TITLESEPARATOR attribute must be set in order to include a separator line in a container window. The default is no separator line.

The container titles in both figures are centered. This is the default. However, the CA_TITLECENTER, CA_TITLELEFT, or CA_TITLERIGHT attribute can be used to specify whether a container title is to be centered, left-justified, or right-justified.

All the container attributes described here are attributes of the CNRINFO data structure's flWindowAttr field.

Specifying Fonts and Colors

A different font can be specified for each view. The same font is used for the text within each view. Text color can be configured from the system control panel. The application can override the system-defined font and colors by using WinSetPresParam.

The font and color can be changed for the text in all views. However, font and color cannot be changed for text in individual columns in the Details view. Therefore, all text in the details view, including the container title, columns, and column headings, has the same font and color.

Drawing Container Items and Painting Backgrounds

The container control enables your application to paint the container's background, draw the container items, or both. If the CA_OWNERPAINTBACKGROUND attribute is set, the container control sends the CM_PAINTBACKGROUND message to itself. Your application can control background painting by subclassing the container control and intercepting the CM_PAINTBACKGROUND message. CA_OWNERPAINTBACKGROUND is an attribute of the CNRINFO data structure's flWindowAttr field.

To support ownerdraw, the drawing of container items by the application, the container control provides the CA_OWNERDRAW attribute of the CNRINFO data structure's flWindowAttr field. If this attribute is set and the application processes the WM_DRAWITEM window message, the application is responsible for drawing each container item, including the types of emphasis.

In addition, the container control supports ownerdraw for each column in the Details view. This support is indicated by the CFA_OWNER attribute, which is specified in the FIELDINFO data structure's flData field.

If the CA_OWNERDRAW attribute or CFA_OWNER attribute is set, the container control sends the application a WM_DRAWITEM message with a pointer to an OWNERITEM data structure as the owneritem parameter.

Filtering Container Items

If the CRA_FILTERED attribute is set for a container item, that item is not displayed. Therefore, filtering can be used to hide container items. CRA_FILTERED is an attribute of the RECORDCORE data structure's flRecordAttr field.

Optimizing Container Memory Usage

The container control provides an option to enable you to develop applications that minimize the amount of memory used for each container record. This is done by specifying the CCS_MINIRECORDCORE style bit when the container is created, which causes a smaller version of the RECORDCORE data structure, MINIRECORDCORE, to be used. The following table shows the differences between these two data structures:

RECORDCORE vs. MINIRECORDCORE
RECORDCORE MINIRECORDCORE
Up to eight image handles can be specified for each record. Only one image handle can be specified for each record.

Note: This image must be an icon.

Up to four text strings can be specified for each record. Only one text string can be specified for each record.

Allocating Memory for when Using MINIRECORDCORE

The following sample code shows how to allocate memory for one container record when the MINIRECORDCORE data structure is used. A pointer to the MINIRECORDCORE data structure is returned.

HWND            hwndCnr;        /* Container window handle             */
PMINIRECORDCORE pRecord;        /* Pointer to MINIRECORDCORE structure */
ULONG           nRecords = 1;   /* 1 record to be allocated            */

pRecord =
  (PMINIRECORDCORE)WinSendMsg(
    hwndCnr,                    /* Container window handle             */
    CM_ALLOCRECORD,             /* Message for allocating the record   */
    NULL,                       /* No additional memory                */
    (MPARAM)nRecords);          /* Number of records to be allocated   */

Sharing Records among Multiple Containers

The container control enables the application to share records that are allocated among multiple containers in the same process. That is, records can be allocated once and then inserted into many containers in the same process. Only one copy of each record is in memory, but the container provides the flexibility for the records to appear as though they are independent of one another.

When a record is inserted into the container, the flRecordAttr and ptlIcon fields of the record structure are saved internally. The values in these fields cause the record attributes for all views and the icon position for the Icon view to be associated with the specific container into which the record is inserted. If the same record is inserted into multiple containers, the attributes and icon location of each record are maintained separately. The application uses the CM_QUERYRECORDINFO message to retrieve the current values of these two fields for a particular record in a specific container.

Invalidating Records Shared by Multiple Containers

When a record is invalidated by an application, the flRecordAttr and ptlIcon fields are saved internally, just as when a record is inserted. The CM_QUERYRECORDINFO message is used to acquire the current data for each record that is being invalidated. After querying the current data, the data can be changed before invalidating its record.

Freeing Records Shared by Multiple Containers

When an application attempts to free a record in an open container, the record is freed only if it is not being used in any other open container. The methods of freeing records in an open container are to use the CM_FREERECORD message, or use the CM_REMOVERECORD message and specify the CMA_FREE attribute.

Sample Code for Container Controls

This section illustrates a complete container control sample program. Several parts of this program are explained in "Using Container Controls".

Container Application Sample Code

The container application includes the following files:

   Contain.C
   Contain.RC
   Contain.H
   Contain.LNK
   Phones.H 

The following sample illustrates the container application code:

CONTAIN.C

#define  INCL_WIN
#define  INCL_GPI

#include <os2.h>
#include <stdio.h>
#include <string.h>
#include "contain.h"
#include "phones.h"

/***********************************************************************/
/*  Program Overview:                                                  */
/*                                                                     */
/*  This program creates a frame window as a parent, then creates      */
/*  a container window as a child.  The frame window sizes the         */
/*  container to fill its client area.                                 */
/*                                                                     */
/*  After the windows are created successfully, the container          */
/*  window is populated.  First, the container is sent a message to    */
/*  allocate memory for each of the records which will be inserted.    */
/*  After the memory is allocated, we set the values for each record.  */
/*  (This sample program reads data from a static array - you could    */
/*  also load values from a file.) Then, the container is sent a       */
/*  message to insert the records (which makes them visible).          */
/*                                                                     */
/*  This container is read-only, which means the end user cannot       */
/*  change the title text.  It supports single selection.              */
/*                                                                     */
/*  In the message loop, we must check for WM_CONTROL messages,        */
/*  which are generated from the container control.  This sample       */
/*  processes CN_ENTER messages, when an item in the container is      */
/*  selected (either with the mouse or the keyboard), and              */
/*  CN_CONTEXTMENU messages, when a context menu is requested.  The    */
/*  context menu allows the user to change the display mode of the     */
/*  container.  Our container supports Icon, Text, and Name views.     */
/*                                                                     */
/*  When a CN_ENTER message is received, we loop through the array     */
/*  of names until we find a match.  On a match, we pop up a message   */
/*  box which contains the nickname, name, and number of the person    */
/*  selected.                                                          */
/*                                                                     */
/***********************************************************************/

#pragma linkage (main,optlink)
INT main(VOID);
VOID LoadDatabase(HWND);

/***********************************************************************/
/*  Main() - program entry point.                                      */
/***********************************************************************/
MRESULT EXPENTRY LocalWndProc(HWND, ULONG, MPARAM, MPARAM);

HAB     hab;
HWND    hPopupMenu;
HWND    hFrameWnd, hCnrWnd;
PFNWP   SysWndProc;

INT main (VOID)
{
   HMQ         hmq;
   FRAMECDATA  fcd;
   QMSG        qmsg;

   if (!(hab = WinInitialize(0)))
     return FALSE;

   if (!(hmq = WinCreateMsgQueue(hab, 0)))
     return FALSE;

/***********************************************************************/
/*  Set up the frame control data for the frame window.                */
/***********************************************************************/
   fcd.cb = sizeof(FRAMECDATA);
   fcd.flCreateFlags = FCF_TITLEBAR |
                       FCF_SYSMENU |
                       FCF_SIZEBORDER |
                       FCF_SHELLPOSITION |
                       FCF_MINMAX |
                       FCF_TASKLIST;
   fcd.hmodResources = NULLHANDLE;
   fcd.idResources = 0;

/***********************************************************************/
/*  Create the frame to hold the container control.                    */
/***********************************************************************/
   hFrameWnd = WinCreateWindow(HWND_DESKTOP,
                               WC_FRAME,
                               "Phone Book",
                               0, 0, 0, 0, 0,
                               NULLHANDLE,
                               HWND_TOP,
                               0,
                               &fcd,
                               NULL);

/***********************************************************************/
/*  Verify that the frame was created; otherwise, stop.                */
/***********************************************************************/
   if (!hFrameWnd)
     return FALSE;

/***********************************************************************/
/*  Set an icon for the frame window.                                  */
/***********************************************************************/
   WinSendMsg(hFrameWnd,
              WM_SETICON,
              (MPARAM)WinQuerySysPointer(HWND_DESKTOP,
                                         SPTR_FOLDER,
                                         FALSE),
               NULL);

/***********************************************************************/
/*  Create the container.                                              */
/***********************************************************************/
   hCnrWnd = WinCreateWindow(hFrameWnd,
                             WC_CONTAINER,
                             NULL,
                             CCS_AUTOPOSITION |
                             CCS_READONLY |
                             CCS_SINGLESEL,
                             0, 0, 0, 0,
                             hFrameWnd,
                             HWND_BOTTOM,
                             FID_CLIENT,
                             NULL,
                             NULL);

/***********************************************************************/
/*  If we got it, fill it up.                                          */
/***********************************************************************/
   if (hCnrWnd)
     LoadDatabase(hCnrWnd);

/***********************************************************************/
/*  We must intercept the frame window's messages                      */
/*  (to capture any input from the container control).                 */
/*  We save the return value (the current WndProc),                    */
/*  so we can pass it all the other                                    */
/*  messages the frame gets.                                           */
/***********************************************************************/
   SysWndProc = WinSubclassWindow(hFrameWnd, (PFNWP)LocalWndProc);

/***********************************************************************/
/*  Load the popup menu from the resources                             */
/*  and show the frame window.                                         */
/***********************************************************************/
   hPopupMenu = WinLoadMenu(HWND_OBJECT, NULLHANDLE, IDM_DISPLAY);

   WinShowWindow(hFrameWnd, TRUE);

/***********************************************************************/
/*  Standard PM message loop - get it, dispatch it.                    */
/***********************************************************************/
   while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
   {
      WinDispatchMsg(hab, &qmsg);
   }

/***********************************************************************/
/*  Clean up on the way out.                                           */
/***********************************************************************/
   WinDestroyMsgQueue(hmq);
   WinTerminate(hab);

   return TRUE;
}

/***********************************************************************/
/*  LocalWndProc() - window procedure for the frame window.            */
/*  Called by PM whenever a message is sent to the frame.              */
/***********************************************************************/
MRESULT EXPENTRY LocalWndProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2)
{
   char                szBuffer[80];
   CNRINFO             cnrInfo;
   PNOTIFYRECORDENTER  Selected;
   POINTL              pt;
   int                 x;

   switch(msg)
   {
      case WM_CONTROL:
      switch (SHORT2FROMMP(mp1))
      {

/***********************************************************************/
/*  Context menu - usually right mouse button clicked                  */
/*  on window. Popup a menu to allow the user to                       */
/*  select a new view of the container.                                */
/***********************************************************************/
         case CN_CONTEXTMENU:
            WinQueryPointerPos(HWND_DESKTOP, &pt);
            WinPopupMenu(HWND_DESKTOP,
                         hwnd,
                         hPopupMenu,
                         (SHORT)pt.x,
                         (SHORT)pt.y,
                         IDM_ICON,
                         PU_NONE |
                         PU_MOUSEBUTTON1 |
                         PU_KEYBOARD |
                         PU_SELECTITEM);
            break;

         case CN_ENTER:

/***********************************************************************/
/*  User selected an item - we take the icon text                      */
/*  and spin through the array of Friends, looking for                 */
/*  a match - on match, print out the phone number                     */
/***********************************************************************/
         Selected = (PNOTIFYRECORDENTER)mp2;
         for (x = 0; x < MAXFRIENDS; x++)
         {
            if (!strcmpi(Friends[x].NickName,
              Selected->pRecord->pszIcon))
            {

               sprintf(szBuffer,
                       "'%s' (%s) %s",
                       Friends[x].NickName,
                       Friends[x].FullName,
                       Friends[x].Phone);
                       WinMessageBox(HWND_DESKTOP,
                                     HWND_DESKTOP,
                                     szBuffer,
                                     "Phone",
                                     0,
                                     MB_OK);
            }
         }
         break;
      }
      break;

      case WM_COMMAND:
         switch (SHORT1FROMMP(mp1))
         {
            case IDM_ICON:
               cnrInfo.flWindowAttr = CV_ICON;
               break;
            case IDM_NAME:
               cnrInfo.flWindowAttr = CV_NAME;
               break;
            case IDM_TEXT:
               cnrInfo.flWindowAttr = CV_TEXT;
               break;
            default:
               return (*SysWndProc)(hwnd, msg, mp1, mp2);
               break;
         }

         WinSendMsg(hCnrWnd,
                    CM_SETCNRINFO,
                    &cnrInfo,
                    MPFROMLONG(CMA_FLWINDOWATTR));
         break;

/***********************************************************************/
/*  Send the message to the usual WC_FRAME WndProc.                    */
/***********************************************************************/
         default:
            return (*SysWndProc)(hwnd, msg, mp1, mp2);
            break;
   }
   return (*SysWndProc)(hwnd, msg, mp1, mp2);
}

/***********************************************************************/
/*  LoadDatabase() - utility function                                  */
/*  called after the WC_CONTAINER window is created successfully,      */
/*  allocates and populates container records, and then inserts        */
/*  the records into the container window.                             */
/***********************************************************************/
VOID LoadDatabase (HWND hWnd)
{
   HWND            hIcon;
   PRECORDCORE     Address, FirstRec;
   RECORDINSERT    recsIn;
   ULONG           x;

/***********************************************************************/
/*  The Icon view for each of the records in the                       */
/*  container will use the standard File icon,                         */
/*  so we grab the handle now for reference later.                     */
/***********************************************************************/
   hIcon = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE);

/***********************************************************************/
/*  Allocate MAXFRIENDS records all at once -                          */
/*  CM_ALLOCRECORD returns them in a linked list.                      */
/***********************************************************************/
   Address = (PRECORDCORE)WinSendMsg(hWnd,
                                     CM_ALLOCRECORD,
                                     0,
                                     MPFROMLONG(MAXFRIENDS));

/**********************************************************************/
/*  We will need the first record's address to                        */
/*  insert them into the container.                                   */
/**********************************************************************/
   FirstRec = Address;

/**********************************************************************/
/*  Loop through the address book, loading as we go.                  */
/*  Because the CM_ALLOCRECORD returns a linked list,                 */
/*  the address of the next record is retrieved                       */
/*  from each record as we go (preccNextRecord).                      */
/**********************************************************************/
   for (x = 0; x < MAXFRIENDS; x++)
   {
      Address->cb       = sizeof(RECORDCORE); /* Standard records     */
      Address->hptrIcon = hIcon;              /* File icon            */
      Address->pszIcon  = Friends[x].NickName;
      Address->pszName  = Friends[x].FullName;
      Address->pszText  = Friends[x].FullName;
      Address = Address->preccNextRecord;     /* Next record in list  */
   }

/**********************************************************************/
/*  Set up the insert record structure to place                       */
/*  the records in the container.                                     */
/**********************************************************************/
   recsIn.cb = sizeof(RECORDINSERT);

   /* Put the records in after any others */
   recsIn.pRecordOrder = (PRECORDCORE)CMA_END;

   /* All the records are top level (not children of other records) */
   recsIn.pRecordParent = NULL;

   /* The icons are top level */
   recsIn.zOrder = (USHORT)CMA_TOP;

   /* Redraw the container */
   recsIn.fInvalidateRecord = TRUE;

   /* Set the number of records to insert */
   recsIn.cRecordsInsert = MAXFRIENDS;

/**********************************************************************/
/*  Insert the records into the container.                            */
/**********************************************************************/
   WinSendMsg(hWnd,
              CM_INSERTRECORD,
              (PRECORDCORE)FirstRec,
              &recsIn);
}

CONTAIN.RC

#include <os2.h>
#include "contain.h"

MENU            IDM_DISPLAY
BEGIN
    MENUITEM    "Icon",     IDM_ICON
    MENUITEM    "Text",     IDM_TEXT
    MENUITEM    "Name",     IDM_NAME
END

CONTAIN.H

#define DLG_ADDRBOOK     100
#define CNR_ADDRBOOK     101
#define PB_ADD           102
#define PB_DIAL          103
#define PHONEBOOK        256
#define IDM_DISPLAY      400
#define IDM_ICON         401
#define IDM_NAME         402
#define IDM_TEXT         403

CONTAIN.LNK

contain.obj
contain.exe
contain.map
contain.def

PHONES.H

#define MAXFRIENDS  9

/***********************************************************************/
/*  This is a simple phone book database.                              */
/***********************************************************************/
typedef struct _Phones
{
   PSZ NickName;
   PSZ FullName;
   PSZ Phone;
}PhoneBook;

/***********************************************************************/
/*  Normal programs would read this data from a file.                  */
/***********************************************************************/
PhoneBook Friends[MAXFRIENDS] =
{
   "Giles",      "Kevin Giles",        "214-555-1212",
   "Bubba",      "Hank Smith",         "713-555-1212",
   "Fred",       "Fred Bicycle",       "817-555-1212",
   "Jack",       "Jack Anjill",        "919-555-1212",
   "John",       "John Richards",      "214-555-1212",
   "Toni",       "Toni Henderson",     "919-555-1212",
   "Babe",       "George Herman Ruth", "212-555-1212",
   "Kevin",      "Kevin Kortrel",      "817-555-1212",
   "Honest Abe", "Abraham Lincoln",    "none"
};