Creating and Using Containers

The IContainer class supports both the CUA '91 container control and the Windows container. The Windows CUA container is a ported version of the OS/2 Presentation Manager container control. It has the same look-and-feel as the OS/2 Presentation Manager container control. The Windows container is composed of the list view and tree view control which are native to the Windows environment.
On all platforms, you can get CUA 91 style controls using the IContainerControl::pmCompatible option. On NT, you can also get the Win 32 list view control. This style of control is not available on OS/2 or AIX. The Windows list view control is the default selection in the Windows environment.

The following figure shows an example of a container:

Use the IContainerControl class to create an instance of a container object. With this class, you can control, for example, the view of the objects inside the container. The following example shows one way to create a container:

IContainerControl cnrCtl(CNR_RESID, this, this);

Several styles are available for containers that you can use to manage such activities as multiple-selection and automatic positioning.

You can define the styles in the constructor, or you can use member functions to set the style required after you create an instance of the container object. An example of a style statement is highlighted in the following code:

cnrCtl = new IContainerControl (CNR_RESID, this, this);
cnrCtl->setExtendedSelection();
Refer to the Open Class Library Reference to learn about other styles and related member functions.

Creating Container Objects

Because a container has no meaning without its objects, use the IContainerObject class to create objects to put into it. At a minimum, an IContainerObject has an icon and a name.

The following is an example of an IContainerObject constructor:

IContainerObject  ( const IString&        string,
                         const IPointerHandle& iconHandle = 0);

To design your own objects for your applications, create a class that is derived from the IContainerObject class. If you use multiple inheritance, you must list the IContainerObject class first. To create a container object with department names, addresses, and zip codes for your company, define this class as follows:

class Department : public IContainerObject
{
   public:
        Department(const IString& Name,
                   const IPointerHandle& Icon,
                   const IString& Code,
                   const IString& Address);
 
        IString Code()
          const {  return strCode; }
 
        IString Address()
          const {  return strAddress; }
 
        void setCode (IString code)
          {strCode = code;}
 
        void setAddress (IString address)
          {strAddress = address;}
 
        virtual void handleOpen
          (IContainerControl* container);
 
   private:
 
      IString strAddress;
      IString strCode;
};
The statements for a constructor definition are as follows:
Department :: Department(const IString& Name,
                            const IPointerHandle& Icon,
                            const IString& Code,
                            const IString& Address):
        IContainerObject(Name, Icon),
        strCode (Code),
        strAddress (Address),
        {}
After you define the class, create an instance of an object using either of the following statement:
dept1 = new Department (
              "OS2 Development",
              IApplication::current().userResourceLibrary().loadIcon(IBMLOGO),
              "TWPD",
              "Building 71");
 
dept2=new Department(reslib.loadString(STR_ITEM_21),
              reslib.loadIcon(CLOGO),
              reslib.loadString(STR_ITEM_22),
              reslib.loadString(STR_ITEM_23));

Adding and Removing Container Objects

After you create the objects and the container, add the objects to the container.

The following statements add objects to the container, cnrCtl. The first line adds an object, dept1. The next three lines add dept2, dept3, and dept4 in a hierarchy under dept1. The last two lines add dept5 and dept6.

cnrCtl->addObject(dept1);       // Add Department 1 to container
cnrCtl->addObject(dept2,dept1); // Add Department 2 under Department 1
cnrCtl->addObject(dept3,dept1); // Add Department 3 under Department 1
cnrCtl->addObject(dept4,dept1); // Add Department 4 under Department 1
cnrCtl->addObject(dept5);       // Add Department 5 to container
cnrCtl->addObject(dept6);       // Add Department 6 to container

When you place the container in the client window and show the window and the container, you see a window like the one below.


Example of a Container Showing Objects in an Expanded Tree View

The window shows a tree view of the container's objects. This view is discussed later.

Note that in these figures we show both the PM compatible version and the Windows native container.

You can also use the ICnrAllocator class to allocate a list of container items to be inserted into an IContainerControl. When you construct instances of this class, you can allocate memory from the container control for a specified number of objects with one call.

The IContainerControl::addObjects member function inserts all the initialized items of the allocator.

The following example shows how to use the ICnrAllocator class and the IContainerControl::addObjects member function:

/**********************************************/
/* Define your derived IContainerObject class */
/**********************************************/
class MyObject : public IContainerObject
{
public:
  MyObject(const IString& name) : IContainerObject(name) {}
  ~MyObject() {}
};
 
/**********************************/
/* Create a frame and a container */
/**********************************/
IFrameWindow frame(0x1300);
IContainerControl cnr(0x1400, &frame, &frame);
cnrCtl.showTextView();
 
/**************************************************/
/* Create an allocator and allocate 10000 objects */
/**************************************************/
ICnrAllocator allocator(10000, sizeof(MyObject));
 
/************************/
/* Initialize all 10000 */
/************************/
for(int i=0; i<10000; i++)
{
  new(allocator) MyObject("Peter");
}
 
/****************************************/
/* Add all the objects to the container */
/****************************************/
cnrCtl.addObjects(allocator);

By default, the container only removes objects when the container is deleted. It does not delete them. However, you can delete all objects in the container when the container is deleted by using the following code statement:

cnrCtl->setDeleteObjectsOnClose();
You can call IContainerControl::deleteAllObjects to delete all objects in a container. Specify the style IContainerControl::noSharedObjects when you create a container that does not share any objects with other containers. This increases the performance of the IContainerControl::deleteAllObjects member function.

The following example shows how to create a container with the noSharedObjects style:

/*****************************************************/
/* Create a container with the noSharedObjects style */
/*****************************************************/
IContainerControl* cnrCtl = new IContainerControl(0x1400, &frame,
                                &frame, IRectangle(0,0,0,0),
                                IContainerControl::defaultStyle() |
                                IContainerControl::noSharedObjects);

Sharing Objects Among Containers

You can also create objects and place them in multiple containers. The same object is then shared by two or more different containers.

In our example, dept2, dept3, and dept4 are in a hierarchy under dept1. We now want to create another container with only the main departments. This new container will then share dept1, dept5, and dept6 with the other container.

The following statements add three objects to a container, cnrCtl2, that already exists in another container, cnrCtl:

/*************************************************/
/*        Container with all departments         */
/*************************************************/
cnrCtl->addObject(dept1);       // Add Department 1 to container      
cnrCtl->addObject(dept2,dept1); // Add Department 2 under Department 1
cnrCtl->addObject(dept3,dept1); // Add Department 3 under Department 1
cnrCtl->addObject(dept4,dept1); // Add Department 4 under Department 1
cnrCtl->addObject(dept5);       // Add Department 5 to container      
cnrCtl->addObject(dept6);       // Add Department 6 to container      
/*************************************************/
/*      Container with main departments only     */
/*************************************************/
cnrCtl2->addObject(dept1);       // Add Department 1 to second container
cnrCtl2->addObject(dept5);       // Add Department 5 to second container
cnrCtl2->addObject(dept6);       // Add Department 6 to second container

Since the same object can exist in more than one container, the attributes of an object also reflect the state of that object. For example, an object can be visible in one container but hidden in another. You should consider the state of an object's attribute in each container and the state of the attribute in each place the object resides.

The following container attributes can be modified:

Use the base container handler, ICnrHandler, to capture the event notifications provided by the container class. When the values of object attributes in the container change, these series of notifications are captured by the handler and routed to virtual functions within the handler.

For example, for both containers to reflect the same selection emphasis, you must attach an ICnrHandler to keep the objects in the same state in each container. Once both icons are selected, the same action is performed on both containers.

If you are performing multiple actions that cause the container to refresh, you can manipulate the refresh state so that the container will not repaint, as follows:

cnrCntl.setRefreshOff();
:
:
cnrCntl.setRefreshOn();
cnrCntl.refresh();

Filtering Container Objects

You can filter objects in a container when using the CUA container control. The container uses the FilterFn nested class to show a subset of the existing objects by filtering some of the objects.

The native Windows list view and tree view controls do not support filtering container objects.

To create a filter, do the following:

  1. Define a class derived from the FilterFn class and override the member function isMemberOf to code the conditions of a valid object.
  2. class SelectedObjectsFilter : public IContainerControl::FilterFn
    {
    virtual bool
      isMemberOf( IContainerObject* object,
                  IContainerControl* container) const
      {
        return container->isSelected(object);
      }
    };
    

    If true is returned by the FilterFn derived class, the container object remains displayed in the container; if false, the object is hidden.

    The isSelected member function returns true if the object has selection emphasis.

  3. Call IContainerControl::filter. Use the following statements:
  4. SelectedObjectsFilter selObjects;
    cnrCtl->filter(selObjects);

Before Filtering the Container Objects:

After Filtering the Container Objects:



Sorting Objects in a Container

To sort objects in the container using their icon text, call IContainerControl::sortByIconText. You can also use a comparison function provided by your application. With an application comparison function, you can sort the objects in the container a variety of different ways. When you call the function sortByIconText, the container provides its own comparison function to do the sorting.

To provide your own sort behavior, do the following:

  1. Define a class derived from IContainerConrol::CompareFn and implement the function isEqual. The function isEqual should return an integer value that is less than zero if the first object is less than the second object, zero if the first object is equal to the second object, or greater than zero if the first object is greater than the second object.
  2. Create an object of the newly defined comparison class. Call the function IContainerControl::sort, and pass the comparison function object.
  3. The following example sorts the objects in our container by two different criteria:

        
        case ID_SORT1:
        {
    /*-----------------------------------------------------------------------------
    | Sort the container based on the Icon Text.                                  |
    | We call a container function to do this.                                    |
     ----------------------------------------------------------------------------*/
          pcnr->sortByIconText ( true );
          break;
        }
     
        case ID_SORT2:
        {
    /*-----------------------------------------------------------------------------
    | Sort the container based on the Code Text.                                  |
    | We must create an instance of the SortByCode class and pass this in.        |
     ----------------------------------------------------------------------------*/
          SortByCode   sortByCode;
          pcnr->sort( sortByCode );
          break;
        }
    To view the entire sample, see the acnr.cpp file in the samples directory.

Accessing Container Objects Using an Object Cursor

Use an object cursor to find all objects or find only those objects that meet a specific criteria.

The following example creates an ObjectCursor and uses it to select all container objects:

IContainerControl::ObjectCursor CO1 (*cnrCtl);
 
for (CO1.setToFirst(); CO1.isValid(); CO1.setToNext())
  {
  cnrCtl->setSelected(cnrCtl->objectAt(CO1));
  }

The following figure shows the before and after result of setting the selection emphasis using an object cursor.



Changing Views in a Container

You can specify the view using a style on the constructor, or you can set it with a member function. For example, the following statement uses the member function that causes a container to display the icon view:
cnrCtl->showIconView();
This statement provides the container view shown below:

The following statement provides the tree icon view:

cnrCtl->showTreeIconView();
The following figure shows a container with the tree icon view:



Defining the Details View Using Container Columns

The IContainerColumn class defines the information that is displayed for a given object when the container is in the details view. Only the items that you added with no parent display in the details view. You can use this class to set text in the heading of the columns, add horizontal and vertical separators by column, and align the column contents.

One way to create an instance of an IContainerColumn is for you to provide the offset of the object data to be displayed in the column and, optionally, the styles to be used for the heading and data.

IContainerColumn     ( unsigned long       dataOffset,
                       const HeadingStyle& title = defaultHeadingStyle(),
                       const DataStyle&    data = defaultDataStyle());

ANSI C++ allows use of the offsetof macro only for structures and not classes. Use the following macro if your compiler raises an error for offsetof:

ICONTAINERCOLUMN_OFFSETOF

To create an instance of a container column, use the following statements:

colIcon = new IContainerColumn (IContainerColumn::isIcon);
colName = new IContainerColumn (IContainerColumn::isIconViewText);
colCode = new IContainerColumn (ICONTAINERCOLUMN_OFFSETOF(Department, strCode));
colAddress = new IContainerColumn (ICONTAINERCOLUMN_OFFSETOF(Department, strAddress));

In the previous example, colIcon, colName, colCode, and colAddress are defined as members of an IFrameWindow. The statements look like this:

private:                                 //Define private information
  IContainerControl * cnrCtl;
  Department *dept1, *dept2, *dept3, *dept4, *dept5, *dept6 ;
  IContainerColumn *colIcon, *colName, *colCode, *colAddress;
  IMenuBar       * menuBar;

After creating the container columns, you can add heading text to them using the following statements:

colIcon->setHeadingText("Icon");
colName->setHeadingText("Department Name");
colCode->setHeadingText("Code");
colAddress->setHeadingText("Address");

When using the CUA container you can use showSeparators to add a vertical separator next to a column or a horizontal separator under the heading text. The default adds both. To create only one of the separators, specify it in the member function statement. The following statements show examples of how to create separators:

  
  //Only Horizontal Separator
colIcon->showSeparators(IContainerColumn::horizontalSeparator);
  //Only Vertical Separator
colName->showSeparators(IContainerColumn::verticalSeparator);
colCode->showSeparators(); //both separator by default
colAddress->showSeparators(); //both separator by default

After you create the container columns, add them into the container using the following statements:

cnrCtl->addColumn(colIcon);
cnrCtl->addColumn(colName);
cnrCtl->addColumn(colCode);
cnrCtl->addColumn(colAddress);

The following shows an example of a details view of a container:

When using the CUA container control, you can use the following code statement to put a split bar in the details view by specifying the last column to be viewed in the left window and the location of the split bar in pixels.

cnrCtl->setDetailsViewSplit(colName, 350);

Separators and split bars are ignored in the native Windows list view and tree view containers. These options are supported by the CUA control on both the Windows and OS/2 operating systems. The native control, however, provides dynamic sizing of all columns.


Creating a Pop-Up Menu in a Container

To create a pop-up menu in a container, create a class derived from ICnrMenuHandler and override the makePopUpMenu. The following statements in acnr.hpp create the class:
class ACnrMenuHandler: public ICnrMenuHandler
{
  public:
/*------------------------ Constructors/Destructor ----------------------------
| Construct the object in only one way:                                       |
| 1) Parameter for IContainerControl                                          |
-----------------------------------------------------------------------------*/
    ACnrMenuHandler( IContainerControl* cnr ) { pcnr = cnr; };
 
  protected:
    virtual bool
      makePopUpMenu( IMenuEvent& event );
 
  private:
    IContainerControl
     *pcnr;
};

After overriding the makePopUpMenu member function, you can add your own statements. The following statements in acnr.cpp create a pop-up menu displayed next to a container object with source emphasis:

bool ACnrMenuHandler::makePopUpMenu( IMenuEvent& event )
{
/*-----------------------------------------------------------------------------
| If a valid container object, continue                                       |
 ----------------------------------------------------------------------------*/
  if ( popupMenuObject() )
  {
/*-----------------------------------------------------------------------------
| Create a popup menu                                                         |
| If not in details view, disable editing of Name, Code, and Address columsn. |
 ----------------------------------------------------------------------------*/
    IPopUpMenu* popUp = new IPopUpMenu( ID_POPMENU,
                event.window() );
    if ( !pcnr->isDetailsView() )
    {
      popUp->disableItem( MI_EDNAME );
      popUp->disableItem( MI_EDCODE );
      popUp->disableItem( MI_EDADDRESS );
    }
    else
    {
/*-----------------------------------------------------------------------------
| Disable editing of the object                                               |
 ----------------------------------------------------------------------------*/
      popUp->disableItem( MI_EDRECORD );
    }
 
/*-----------------------------------------------------------------------------
| To avoid memory leaks, auto delete the C++ popup menu when the GUI popup    |
|   window closes                                                             |
| Show the popup menu at the current mouse position                           |
| Visually indicate the container object is the source for this action        |
| Visually indicate the container object is the current cursor                |
 ----------------------------------------------------------------------------*/
    popUp->setAutoDeleteObject();
    pcnr->showSourceEmphasis( popupMenuObject() );
    pcnr->setCursor( popupMenuObject() );
    popUp->show( event.mousePosition() );
    return true;
  }
  return false;
};

The following figure shows the pop-up menu in a container object:



Using the Windows Native Container Control

This section lists the differences between the native Windows container control and the PM compatible CUA '91 control. For specific platform information on each member function, refer to the Open Class Library Reference

The default IContainerControl wrappers the Windows native ListView and TreeView controls. The underlying controls are created when needed. If you create an IContainerControl in the default icon view, only a ListView control is created. A TreeView control is not created until the view is switched to a tree view or a child object is added. To optimize a tree view container, consider the following:

Constructors

For the native Windows control, you cannot use the following constructors:

  
  IContainerControl ( unsigned long id,
                      IWindow*      parentDialog);
 
  IContainerControl ( const IWindowHandle& handle);

Member Functions

Source emphasis is not supported. This is true of both the Windows CUA control and the native Windows control.

There is no windows native multiple selection. IContainerControl::multipleSelection is the same as extendedSelection and IContainerControl::verifyPointers is ignored.

The following IContainerControl attributes are ignored:

The following are not supported:
showTextView
Always flowed using the native container
showNameView
Non tree views are all flowed using the native container
setTreeViewIndent
Not supported using the native control
setTreeItemIcons
Not supported using the native control
setTreeExpandIconSize
Not supported using the native control
The Open Class Library does not support the following when using the native Windows container control:

Note: The native ListView control requires the first column in a details view to be the icon and text pair that is displayed in the other views. This column is sized to nonzero if either a IContainerColumn with

objectDataType=isIcon
or
objectDataType=isIconViewText
is added. Regardless of where they are added, these columns are displayed as the first column. If neither is added, the column size is set to zero but it is possible for the user to size this column using the mouse.

This is the only icon that may be displayed in the details view. If you add any other columns to display icons, the icons are not be visible.

The native control allows selection only in this first column. If this column is sized to zero, objects in the container cannot be selected.



Containers


Using Default Direct Manipulation
Task and Samples Cross-Reference Table


ICnrHandler
IContainerColumn
IContainerControl
IContainerObject