Motif Programming Manual (Volume 6A)

Previous Next Contents Document Index Motif Docs motifdeveloper.com Imperial Software Technology X-Designer

X-Designer - The Leading X/Motif GUI Builder - Click to download a FREE evaluation





In this chapter:



Chapter: 9

Containers and IconGadgets



The Container was possibly the most complex widget introduced into Motif 2. The Container is a Constraint Manager widget which is designed to work alongside the IconGadget. The idea is that IconGadgets are meant to pictorially represent application objects in some abstract way, and the Container lays them out in a variety of formats appropriate to the underlying data model. The Container can lay out its children in Tree, Multi-column Table, Grid, or Free-Floating formats; it is possible to dynamically switch layout in order to provide alternative presentations of the objects which it contains, and thus the widget is an approximation to a Model-View-Controller (MVC) component for the Motif widget set.

IconGadgets are derived from the Gadget class, as the name suggests. Although not derived from the LabelGadget class, they are similar to LabelGadgets in that they can display a Label string and an image Pixmap, the difference being that the IconGadget can display these simultaneously. It is also possible to associate with an IconGadget an array of compound strings which represents additional information about the object. This additional information is known as Detail, and the Container widget knows how to lay the Detail of its various IconGadget children out so that it is all arranged in columns. Each column may be assigned a heading label, much like a Table. Figure 9-1 shows a Container widget with IconGadget children laid out in the Detail style. The column headers are simply specified through Container resources.

Figure  9-1 A Container in Detail layout, with IconGadget children

Since the Container and IconGadget widget classes fully support Render Table resources, and since the Detail information is held in the form of compound strings, each of the columns and column headers could appear in distinct colors and fonts, although Figure 9-1 does not make use of this feature. Render Tables are discussed in more detail in Chapter 24.

IconGadgets support not one but two image Pixmap resources: the programmer can specify a large and a small image, and then switch between the two views of the IconGadget dynamically. This could be used for displaying both a representative thumbnail and a detailed picture of the application object which the IconGadget represents. A typical usage of the small image display is when the IconGadget is placed in a Container which is configured to lay out its children in a Tree (or more properly, Outline) format. Figure 9-2 has such an example, which is a simplified file system browser.

Figure  9-2 A Container in the Outline layout style

In this exampleonly small images are required in order to represent the general type of the objects concerned. The Tree layout is effected through simple constraints placed on the IconGadget children: a given IconGadget A is placed as a child of IconGadget B if the XmNentryParent constraint of A has the value B. The order of children can be controlled using the XmNpositionIndex constraint, although the Container will lay them out in child order by default.

The Detail column format is not restricted to a particular view of the application object relationships. That is, it is possible to combine a Tree layout with multi-column Detail information. Since the relationships between the application objects is expressed using Container constraints on each of the IconGadget children, this is independent of the current Container layout style. For example, Figure 9-3 shows a Container where the IconGadgets are laid out in a Tree format because their XmNentryParent constraints are set, with the extra IconGadget information still visible because the Container layout style is set to Detail.

Figure  9-3 A Container in Detail layout, with IconGadgets in a Tree format

The remaining Container layout to consider is known as the Spatial style. In this style, the Container does not attempt to attach Detail to the IconGadget children. The Spatial style is really two styles, which is configured by further resources. Either the IconGadget children are simply placed at the coordinates specified by their XmNx, XmNy resources, very much like a BulletinBoard, or the IconGadget children are laid out in a Grid formation. In the Grid formation, the layout is considered to consist of cells in a rectangular arrangement, and the IconGadget children are aligned within the cells, although it is possible for an IconGadget to span multiple cells. Only one IconGadget may occupy any given cell, however, at any one time. The Grid arrangement behaves very much like a RowColumn in that if the Container is resized, the IconGadgets are automatically laid out to maintain a suitable rectangular arrangement in the space available.

The difference to a BulletinBoard or RowColumn is that the Container in the Spatial layout style supports dynamic selection and drag-and-drop of the IconGadget children: the user can move the IconGadget children around the Container using the mouse. A simple application using the Spatial style would be an Icon Box, or a Tool launch site, where each IconGadget represents pictorially a separate tool.

Figure 9-4 shows such an application in a free-format arrangement - the only positioning is by the x, y coordinate resources of each IconGadget as the Container is initialized, and any movement which the user makes of each IconGadget thereafter. This time, the IconGadgets are configured to display the large images.

Figure  9-4 A Container in Spatial layout using coordinate placement

Figure 9-5 is also in the Spatial style, except that the same data has been configured into a Grid format. The x, y coordinates of the IconGadget children is ignored, and the user may only move IconGadgets into empty cells of the grid.

Figure  9-5 A Container in Spatial layout using Grid placement

The Container in Spatial layout supports single, multiple, browse, and extended selection of the IconGadget children. Selection can be effected either by directly clicking on an IconGadget, or by drawing a rectangle around the items to be selected. The rubberband rectangle which the user describes using the mouse is formally known as a marquee. An alternative selection method is to simply swipe the mouse over the IconGadget to be selected. The style of selection preferred is controlled through the XmNselectionTechnique resource. Note that the user may only move Container items if the layout style is Spatial.

Notification of selection is through callbacks of the Container: the IconGadget does not support any callback resources of its own. The Container supports both the XmNconvertCallback and XmNdestinationCallback resources, and so fully participates in the Uniform Transfer Model, which is described in Chapter 23.

Creating a Container

Applications that wish to use the Container need to include the file <Xm/Container.h>. This file defines the types and functions associated with the widget, as well as defining the widget class name xmContainerWidgetClass. A Container can be created in either of the ways shown in the following code fragment:

Widget container = XmCreateContainer ( parent, name, resource-value-array,
                                       resource-value-count)
Widget container = XtCreateWidget ( "name", xmContainerWidgetClass, parent,
                                    resource-value-list, NULL);
The Container can potentially contain a very large number of IconGadget children, and some of the layout algorithms which it applies are quite complex, and so it is probably best not to create the widget in a managed state (XtCreateManagedWidget()) otherwise performance may suffer. See Chapter 8, Manager Widgets, for a discussion of when widgets should be created in the managed or unmanaged state.

The parent of the Container can be any Shell or Manager widget. Once the Container has been instantiated, the next step is to add IconGadget children.

Creating IconGadgets

The data types and functions associated with the IconGadget object are defined in the header file <Xm/IconG.h>, which should be included in any Application which uses the gadget. The header file also defines the gadget class xmIconGadgetClass. The following code fragment illustrates how to create an IconGadget:

Widget icon = XmCreateIconGadget ( parent, name, resource-value-array,
                                   resource-value-count)
Widget icon = XtCreateWidget ( "name", xmIconGadgetClass, parent,
                               resource-value-list, NULL);
The parent of an IconGadget can be any Manager widget, although only the Container widget has sufficient knowledge of the IconGadget to display any Detail information associated with the object, or to provide IconGadget selection. The IconGadget can successfully be used outside the Container context if all that you require is the ability to display a label and a Pixmap simultaneously.

Container Resources

The resources of the Container can be logically divided into four groups; those which control the general layout of the IconGadget children, those related to visual aspects of the Container, those associated with any Detail to be displayed, and those which control the selection mechanisms.


Layout Resources

The general layout policy of the Container is controlled through the XmNlayoutType resource, which has the following possible values:

XmOUTLINE         XmSPATIAL         XmDETAIL
The default value is XmSPATIAL, which results in the BulletinBoard or Grid-style layout. The Tree layout is specified through the value XmOUTLINE, and the IconGadget Detail is made visible using the value XmDETAIL. The XmDETAIL layout may also logically appear as a Tree if the relationship between the IconGadget children has been specified using the XmNentryParent constraint. This is described later in this chapter.


Spatial Layout

In a Spatial layout, the specific Container layout algorithm depends upon a further resource, XmNspatialStyle. If the XmNspatialStyle is XmNONE, layout depends only upon the XmNx, XmNy values of the IconGadget children. If XmNspatialStyle is XmGRID, the Container is laid out in a grid of same-sized cells, and an IconGadget may occupy only once cell. If the spatial style is XmCELLS, the Container is also laid out in same-sized cells, except that this time an IconGadget may span multiple cells.

How the IconGadget is placed within a cell depends upon the XmNspatialSnapModel resource.The value XmCENTER centers the IconGadget within the cell. The resource value XmSNAP_TO_GRID positions the IconGadget at the upper left or right of the cell, depending upon any specified XmNlayoutDirection. The value XmNONE places the IconGadget within the cell using the XmNx, XmNy resources of the object, provided that these coordinates fall within the bounds of the cell - otherwise the IconGadget is laid out in the cell as though the value is XmSNAP_TO_GRID.

The size of a logical cell is specified using the XmNsmallCellHeight, XmNsmallCellWidth, XmNlargeCellHeight, and XmNlargeCellWidth resources. Which of these is operative depends upon whether the Container is configured to display IconGadgets using the large or small Pixmap of the object. This is described in the following Visual Resources Section The Container in the spatial style is like a RowColumn in that it creates a logically rectangular arrangement of cells. The difference is that for a RowColumn you have to specify the number of columns and not the size of the cells.

The manner in which the IconGadget children fill the cells depends upon the XmNspatialIncludeModel. If the value is XmFIRST_FIT, the cells are filled in the order that they are free, although this may be in a right to left sense depending upon the XmNlayoutDirection. The value XmAPPEND places an IconGadget into the first free cell after the last filled cell. XmFIRST_FIT and XmAPPEND may well have the same effect when the Container is first displayed, but will have different behavior if a new IconGadget is added after the user has moved the existing objects around. The value XmCLOSEST places an IconGadget into the nearest free cell to the x, y coordinates specified for the IconGadget.

Lastly on the subject of Spatial layout, the XmNspatialResizeModel controls the way in which the Container attempts to grow when there is insufficient space to contain a new IconGadget child. The value XmGROW_BALANCED causes the Container to request both new width and height from its parent as required. The other possible values, XmGROW_MAJOR and XmGROW_MINOR, depend for their interpretation on the value of the XmNlayoutDirection resource. The major dimension is width if the layout policy is horizontally oriented, and height if the policy is vertically oriented. The minor dimension is the reverse: height for a horizontal layout, width for the vertical. Assuming that the layout policy is horizontally aligned, then XmGROW_MAJOR will cause the Container only to ask for more width from its parent. The default is XmGROW_MINOR.


Outline Layout

Whether or not to create a PushButtonGadget used for folding/unfolding portions of the outline Tree is controlled through the XmNoutlineButtonPolicy resource. The default value, XmOUTLINE_BUTTON_PRESENT, creates the button for every container item which has logical children, specified by the constraint resource XmNentryParent. Folding buttons can be disabled by specifying the value XmOUTLINE_BUTTON_ABSENT.

Connecting lines and indentation between portions of the Tree can be configured in a number of ways. The resource XmNoutlineLineStyle can be used to completely disable line drawing - so that indentation is the only clue of the logical parent/child relationships - by specifying the value XmNO_LINE. The default value, XmSINGLE, draws a single line between related Container items. The indentation level, and thus indirectly the length of the lines, depends upon the XmNoutlineIndentation resource. The line length is also partially controlled by the XmNoutlineColumnWidth resource, which specifies preferred width of the first column. The default value, zero, causes the Container to deduce a default value based upon the width of the widest item and the XmNoutlineIndentation specification.


Visual Resources

The Container can specify whether the IconGadget children are forced to display large or small Pixmaps through the XmNentryViewType resource. The default value, XmANY_ICON, leaves the decision to each individual Container item. However, the values XmLARGE_ICON and XmSMALL_ICON can be used to override the settings on each IconGadget in order to give a consistent logical size to all the Container items.

The Container has two Pixmap resources associated with it which control the appearance of the fold/unfold buttons when displaying an Outline layout. The XmNexpandedStatePixmap resource specifies the unfolded state image, and the resource XmNcollapsedStatePixmap specifies the folded image. The default image for the folded state is a sideways pointing arrow, the direction of which depends upon the XmNlayoutDirection resource. The default for the collapsed (folded) state is a downwards pointing arrow.

In common with all other widgets in the Motif set, the Container supports the XmNrenderTable resource, which controls the rendering of compound strings in the widget. Render Tables are described fully in Chapter 24, Compound Strings in Chapter 25.


Detail Resources

The heading labels which appear at the top of each column of the Container are specified through the XmNdetailColumnHeading and XmNdetailColumnHeadingCount resources. These resources specify an array of compound strings. Each separate compound string in the array forms a distinct column header. The first column header is placed over the IconGadget children themselves.

By default, the order of compound strings in the XmNdetailColumnHeading resource corresponds to the order in which the logical columns appear in the Detail. That is, the first compound string in the XmNdetailColumnHeading array appears above the first column, and so forth. But this need not be the case. The resources XmNdetailOrder and XmNdetailOrderCount specify an array of Cardinal values which represents the order of the columns. If this is not NULL, then column ordering depends on the index values specified. For example, suppose we have three heading labels, A, B, and C. In the absence of any XmNdetailOrder, the headings will appear A, B, C from left to right at the top of the Container. If however we construct a list of Cardinal values, 2, 3, 1, and apply this as the XmNdetailOrder value, then the column headings will appear in the order B, C, A.

By default, the Container works out for itself where each column of the Detail is to begin. It is possible to override the internal algorithm by constructing a Tab List, and then specifying this as the XmNdetailTabList resource. Tab Lists are described in Chapter 24, Render Tables, although in this unique instance the required Tab List is used outside the context of a Render Table. Possibly confusing is the fact that the Container and IconGadget also support the XmNrenderTable resource, however both the IconGadget and the Container use the XmNdetailTabList value when calculating the column header layout, although in the case of the IconGadget the value is fetched from the Container parent.


Selection Resources

The XmNselectionPolicy resource specifies the way in which IconGadget children may be selected in the Container. The possible values are:

XmSINGLE_SELECT               XmBROWSE_SELECT
XmMULTIPLE_SELECT             XmEXTENDED_SELECT
The default is XmEXTENDED_SELECT, which allows for a discontiguous range of IconGadget children to be selected. Discontiguous in this context means that the children are not necessarily contained within adjacent cells in the Spatial layout. The value XmMULTIPLE_SELECT forces selection from adjacent cells.

The way in which multiple selection is performed using a marquee is configured through the XmNselectionTechnique resource. If an IconGadget must be wholly contained within the marquee rectangle for selection to take place, the value XmMARQUEE should be specified. If the value is XmMARQUEE_EXTEND_START, it is sufficient for the starting coordinates of the marquee to fall within the IconGadget bounds for selection to take effect. The value XmMARQUEE_EXTEND_BOTH includes in the selected set any IconGadget which falls partially within either the start or the end coordinates of the marquee. If selection should take place only if the mouse directly passes over an IconGadget when describing the marquee, the value XmTOUCH_OVER should be specified. Lastly, XmTOUCH_ONLY only selects items which fall between the marquee start and end coordinates. The default is XmTOUCH_OVER.

The set of selected objects for the Container is described by the XmNselectedObjects and XmNselectedObjectCount resources. These specify an array of Widget IDs corresponding to the selected IconGadgets.

The way in which selection callbacks are invoked depends upon the XmNautomaticSelection resource. If the value is XmAUTO_SELECT, callbacks are invoked every time and immediately that an item is selected. The value XmNO_AUTO_SELECT delays callback invocation until the user has finished her current action.

Whether the Container takes control of the Primary Selection when the user selects IconGadget children depends upon the XmNprimaryOwnership resource. The possible values are:

XmOWN_NEVER                  XmOWN_ALWAYS
XmOWN_MULTIPLE               XmOWN_POSSIBLE_MULTIPLE
The default value is XmOWN_POSSIBLE_MULTIPLE, which indicates that Primary Selection is owned if multiple selection is possible (the XmNselectionPolicy is XmMULTIPLE_SELECT or XmEXTENDED_SELECT). XmOWN_MULTIPLE is similar, except that ownership takes place only if multiple selection has occurred. The difference is that XmOWN_POSSIBLE_MULTIPLE would allow ownership of the selection even if a single IconGadget is selected (although the selection policy allows for more). The other values have a natural interpretation.

Lastly, the color of a selected IconGadget can be specified using the XmNselectColor resource, which takes a Pixel as its value. Since this is specified on a Container-wide basis, it is not possible to control the selection color of IconGadget children separately.

IconGadget Resources

The IconGadget has two logical sets of resources: those which control the visual aspects of the gadget, and those which specify the Detail information. The Detail resources are only operative in the context of a Container - no other Motif widget knows how to lay out an IconGadget Detail.


Visual Resources

The label of the IconGadget is specified using the XmNlabelString resource: this is a compound string. If the value is NULL, the IconGadget label is derived from the name of the widget as passed to the widgets create routine.1

The image which the IconGadget displays is specified in two halves: a Pixmap representing the foreground image, and a Pixmap controlling the background Mask. Since the IconGadget in fact supports two logical images - small and a large - there are therefore four resources to consider. These are the resources XmNsmallIconPixmap, XmNsmallIconMask, XmNlargeIconPixmap, and XmNlargeIconMask. The Mask resources should only have a depth of 1 - they should be bitmaps. Since the IconGadget only displays one image at a time, the choice of large or small icon is controlled through the XmNviewType resource, which has the possible values XmLARGE_ICON (the default) or XmSMALL_ICON. This may be overridden by a Container parent if the Container resource XmNentryViewType is not set to XmANY_ICON.

The XmNalignment resource specifies the relative alignment of the image and label of the IconGadget. Possible values are:

XmALIGNMENT_BEGINNING      XmALIGNMENT_CENTER     XmALIGNMENT_END
The vertical distance between the image and the label is controlled through the resource XmNspacing. Figure 9-6 shows the effect of setting the alignment and spacing resources.

Figure  9-6 The effect of IconGadget alignment and spacing resources

In common with other widgets in the Motif set, the IconGadget supports the XmNrenderTable resource which controls the appearance of compound strings in the object. Render Tables are covered in detail in Chapter 24.


Detail Resources

The resources XmNdetail and XmNdetailCount specify an array of compound strings which represents the Detail of the IconGadget. Each entry in the compound string array represents a single column entry in the Container parent. Only the Container knows how to lay IconGadget detail out - the resource has no effect in any other parent context.

Container Constraints

There are three constraint resources defined by the Container: XmNentryParent, XmNoutlineState, and XmNpositionIndex. The constraints have no effect in a Spatial layout - they are used to specify the logical Tree arrangement for Outline and Detail layouts.

The XmNentryParent resource is used to specify the parent-child relationships between IconGadget children in the Container. An IconGadget A is considered to be a logical child of IconGadget B if the XmNentryParent constraint resource of A has the value B. If the value is NULL, the IconGadget is considered to be a root object, and is not indented in the layout.

The XmNoutlineState resource controls whether a given IconGadget is visible in an Outline or Detail layout - the value XmCOLLAPSED hides the object, XmEXPANDED displays it.

The order in which IconGadgets are laid out is controlled through the XmNpositionIndex resource. This is not the same thing as specifying the level of indentation, as specified through the XmNentryParent. The XmNpositionIndex resource sorts children at the same level of indentation. For example, suppose objects A and B both have the same XmNentryParent value. A will appear first in the Container if the XmNpositionIndex constraint is less than that of B.

Example 9-1 is a simple application which constructs a partial view of the file system from the current working directory. It does this by creating a Container in the Outline layout style, and adds file system entries in the form of IconGadget children. The file system structure is specified through XmNentryParent resources. The program is not meant to be a full working application, but a demonstration of how the Tree structure is specified for an Outline layout. Output from the program is identical in effect to that of Figure 9-2.

Example  9-1 The outline.c program

/* outline.c -- demonstrate the container and icon gadget
** in an outline layout
*/

#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Container.h>
#include <Xm/IconG.h>
#include <Xm/ScrolledW.h>
#include <Xm/XmosP.h>
#include <sys/stat.h>
#include <dirent.h>

static Pixmap small_folder_icon = XmUNSPECIFIED_PIXMAP;
static Pixmap small_folder_mask = XmUNSPECIFIED_PIXMAP;
static Pixmap small_file_icon = XmUNSPECIFIED_PIXMAP;
static Pixmap small_file_mask = XmUNSPECIFIED_PIXMAP;

/*
** Adds one level of directory entries to the Container, using
** parent_entry as the XmNentryParent value
*/
static void add_directory( char       *path,
                           Widget     parent_entry,
                           Widget     container)
{
    DIR              *dir;
    struct dirent    *entry;
    Arg              args[8];
    int              n;

    /* Loop through the directory entries */
    dir = opendir (path);

    while ((entry = readdir (dir)) != NULL) {
        char   name[256];
        struct stat sb;

        /* Get the file details. Note: no real error checking */
        (void) sprintf (name, "%s/%s", path, entry->d_name);

        if (stat (name, &sb) == 0) {
            XmString s;
            Widget item;
            int isDirectory;

            if ((strcmp (entry->d_name, ".") == 0) ||
                    (strcmp (entry->d_name, "..") == 0)) {
                continue;
            }

            s = XmStringCreateLocalized (entry->d_name);

            isDirectory = ((sb.st_mode & S_IFDIR) != 0);

            /* Create the IconGadget */
            n = 0;
            XtSetArg (args[n], XmNlabelString, s); n++;

            if (isDirectory) {
                XtSetArg (args[n], XmNsmallIconPixmap, small_folder_icon);
                n++;
                XtSetArg (args[n], XmNsmallIconMask, small_folder_mask);
                n++;
            }
            else {
                XtSetArg (args[n], XmNsmallIconPixmap, small_file_icon);
                n++;
                XtSetArg (args[n], XmNsmallIconMask, small_file_mask);
                n++;
            }

            /* This gives the Tree its structure */
            XtSetArg (args[n], XmNentryParent, parent_entry); n++;

            item = XmCreateIconGadget (container, "icon", args, n);
            XtManageChild (item);

            /* Recurse to subdirectories */
            /* This item becomes the new XmNentryParent */
            if (entry->d_name[0] != `.') {
                if (isDirectory) {
                    add_directory (name, item, container);
                }
            }
            }
        }

        (void) closedir (dir);
    }

    /* Utility: set up a path to find bitmaps and pixmaps */
    static char *GetBitmapPath (Display *display, char *file)
    {
        char               *bmPath = (char *) 0;
        char               *name = (char *) 0;
        Boolean            user_path = False;
        SubstitutionRec    subs;

        subs.substitution = file;

        bmPath = (char *) _XmOSInitPath (file, "XBMLANGPATH", &user_path);

        if (user_path)   subs.match = `B';
        else             subs.match = `P';

        name = XtResolvePathname (display, "bitmaps", file, NULL, bmPath, 
                                &subs, 1, NULL);

        if (bmPath) {
            /* Some XtResolvePathname() return non-heap copy of parameter */
            /* This causes serious memory corruption if freed inadvertently */

        if (name != bmPath) {
            XtFree (bmPath);
        }
    }

    return name;
}

/* Utility: load a Pixmap file for the IconGadget image */
static Pixmap GetPixmap (Widget widget, char *file)
{
    Screen *screen = XtScreen (widget);
    char *path = GetBitmapPath (XtDisplay (widget), file);

    return XmGetPixmap  (screen,
                         path,
                         BlackPixelOfScreen (screen),
                         WhitePixelOfScreen (screen));
}

/* Utility: load a Bitmap file for the IconGadget image mask */
static Pixmap GetBitmap (Widget widget, char *file)
{
    Pixmap           pixmap = XmUNSPECIFIED_PIXMAP;
    unsigned int     width;
    unsigned int     height;
    int              hotx;
    int              hoty;
    char             *path = GetBitmapPath (XtDisplay (widget), file);

    XReadBitmapFile (XtDisplay (widget), XtWindow (widget), path, &width, 
                    &height, &pixmap, &hotx, &hoty);

    return pixmap;
}

main (int argc, char *argv[])
{
    Widget           toplevel, form, scrolled_win, container;
    XtAppContext     app;
    int              n;
    Arg              args[8];

    XtSetLanguageProc (NULL, NULL, NULL);

    /* Create the top-level shell and two RowColumns */
    toplevel = XtVaOpenApplication (&app, "Demos", NULL, 0, &argc, argv, NULL, 
                                   sessionShellWidgetClass, NULL);

    XtVaSetValues (toplevel, XmNtitle, "XmContainer - Outline Layout", NULL);

    form = XmCreateForm (toplevel, "form", NULL, 0);

    n = 0;
    XtSetArg (args[n], XmNscrollingPolicy, XmAUTOMATIC);    n++;
    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM);    n++;
    XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
    XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM);   n++;
    XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM);  n++;
    scrolled_win = XmCreateScrolledWindow (form, "scrolled_win", args, n);

    /* Create the Container */

    n = 0;
    XtSetArg (args[n], XmNwidth, 600);                  n++;
    XtSetArg (args[n], XmNheight, 500);                 n++;
    XtSetArg (args[n], XmNlayoutType, XmOUTLINE);       n++;
    XtSetArg (args[n], XmNentryViewType, XmSMALL_ICON); n++;
    container = XmCreateContainer (scrolled_win, "container", args, n);

    XtManageChild (container);
    XtManageChild (scrolled_win);
    XtManageChild (form);
    XtRealizeWidget (toplevel);

    /* Read in the icons and their masks */
    /* The bitmaps require a window, therefore must be done post-realize */
    small_folder_icon = GetPixmap (container, "folder_small.xbm");
    small_file_icon = GetPixmap (container, "file_small.xbm");

    small_folder_mask = GetBitmap (container, "folder_small_mask.xbm");
    small_file_mask = GetBitmap (container, "file_small_mask.xbm");

    /* Load the Icon Gadgets */
    add_directory(".", NULL, container);

    XtAppMainLoop (app);
}

Container Callbacks

The Container supports a number of callback resources: not all callback types will be invoked in all contexts since some callbacks depend upon the layout policy of the widget.

Firstly, the Container, supporting as it does the dragging and dropping of IconGadget children, defines the XmNconvertCallback and XmNdestinationCallback resources, which are fully described in Chapter 23, The Uniform Transfer Model. They will not otherwise be discussed here.


Outline Callbacks

In Outline layout style, the Container calls the XmNoutlineChangedCallback whenever an item in the Tree changes from a collapsed or expanded state. Each callback of this type is passed a pointer to an XmContainerOutlineCallbackStruct data structure, which is defined as follows:

typedef struct
{
    int              reason;
    XEvent           *event;
    Widget           item;
    unsigned char    new_outline_state;
} XmContainerOutlineCallbackStruct;
The reason element will be either XmCR_COLLAPSED or XmCR_EXPANDED, depending upon the new state of the IconGadget item. The new_outline_state element will be either XmCOLLAPSED or XmEXPANDED, logically matching the reason element. However, the new_outline_state element can be changed by the programmer to force a particular state for the changed item.


Selection Callbacks

There are two selection callbacks supported by the Container. The XmNdefaultActionCallback is invoked whenever an IconGadget item is double clicked on using the mouse, or if the user presses the ACTIVATE key over the item. The XmNselectionCallback is invoked if the user selects an item otherwise. Both types of callback are passed a pointer to an XmContainerSelectCallbackStruct structure, which is specified as follows:

typedef struct
{
    int              reason;
    XEvent           *event;
    WidgetList       selected_items;
    int              selected_item_count;
    unsigned char    auto_selection_type;
} XmContainerSelectCallbackStruct;
The reason element will reflect the current selection policy: it will be either the value XmCR_SINGLE_SELECT, XmCR_BROWSE_SELECT, XmCR_MULTIPLE_SELECT, or XmCR_EXTENDED_SELECT.

The set of selected Container IconGadget children is specified by the selected_items and selected_item_count elements.

If automatic selection is operative, the auto_selection_type element specifies the current user action state: it will be either XmAUTO_BEGIN (the user has started a new selection), XmAUTO_CANCEL (the user cancels the current selection action), XmAUTO_CHANGE (a new item has been added to the selected set), XmAUTO_MOTION (a new item has been added by a button drag action), or XmAUTO_NO_CHANGE (the user action has not modified the current selected set). If automatic selection is inoperative, the value is XmAUTO_UNSET.

Example 9-2 extends the code of Example 9-1 to include both Detail information and a selection callback which prints out the file system path for the currently selected IconGadget.

Example  9-2 The detail.c program

/* detail.c -- demonstrate the container and icon gadget
** in Detail layout, with a selection callback printing
** out the selected IconGadget data
*/

#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Container.h>
#include <Xm/IconG.h>
#include <Xm/ScrolledW.h>
#include <Xm/XmosP.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>

static char *column_headings[] = { "Name", "Size", "Modified" };

static Pixmap large_folder_icon = XmUNSPECIFIED_PIXMAP;
static Pixmap small_folder_icon = XmUNSPECIFIED_PIXMAP;
static Pixmap large_folder_mask = XmUNSPECIFIED_PIXMAP;
static Pixmap small_folder_mask = XmUNSPECIFIED_PIXMAP;
static Pixmap large_file_icon = XmUNSPECIFIED_PIXMAP;
static Pixmap small_file_icon = XmUNSPECIFIED_PIXMAP;
static Pixmap large_file_mask = XmUNSPECIFIED_PIXMAP;
static Pixmap small_file_mask = XmUNSPECIFIED_PIXMAP;

/* Create the Icongadget children at a given level in the
** file system. This time, Detail is added about the files.
*/
static void add_directory( char       *path,
                           Widget     parent_entry,
                           Widget     container)
{
    DIR              *dir;
    struct dirent    *entry;
    Arg              args[12];
    int              n;

    /* Loop through the directory entries */
    dir = opendir (path);

    while ((entry = readdir (dir)) != NULL) {
        char name[256];
        struct stat sb;

        /* Get the file details. Note: no real error checking */
        (void) sprintf (name, "%s/%s", path, entry->d_name);

        if (stat (name, &sb) == 0) {
            XmString         s;
            XmStringTable    details;
            char             buf[20];
            Widget           item;
            int              isDirectory;

            if ((strcmp (entry->d_name, ".") == 0) ||
                    (strcmp (entry->d_name, "..") == 0)) {
            continue;
        }

        s = XmStringCreateLocalized (entry->d_name);

        isDirectory = ((sb.st_mode & S_IFDIR) != 0);

        /* Create the details array */
        details = (XmStringTable) XtMalloc(2 * sizeof (XmString *));

        (void) sprintf (buf, "%d", sb.st_size);
        details[0] = XmStringCreateLocalized (buf);
        details[1] = XmStringCreateLocalized (
                                (char *) ctime(&sb.st_mtim.tv_sec));

        /* Create the IconGadget */
        n = 0;
        XtSetArg (args[n], XmNlabelString, s); n++;

        if (isDirectory) {
            XtSetArg (args[n], XmNlargeIconPixmap, large_folder_icon);
            n++;
        }
        else {
            XtSetArg (args[n], XmNlargeIconPixmap, large_file_icon);
            n++;
        }

        if (isDirectory) {
            XtSetArg (args[n], XmNlargeIconMask, large_folder_mask);
            n++;
        }
        else {
            XtSetArg (args[n], XmNlargeIconMask, large_file_mask);
            n++;
        }

        if (isDirectory) {
            XtSetArg (args[n], XmNsmallIconPixmap, small_folder_icon);
            n++;
        }
        else {
            XtSetArg (args[n], XmNsmallIconPixmap, small_file_icon);
            n++;
        }

        if (isDirectory) {
            XtSetArg (args[n], XmNsmallIconMask, small_folder_mask);
            n++;
        }
        else {
            XtSetArg (args[n], XmNsmallIconMask, small_file_mask);
            n++;
        }

        XtSetArg (args[n], XmNentryParent, parent_entry); n++;
        XtSetArg (args[n], XmNdetail, details);           n++;
        XtSetArg (args[n], XmNdetailCount, 2);            n++;

        item = XmCreateIconGadget (container, "icon", args, n);
        XtManageChild (item);

        XmStringFree(details[0]);
        XmStringFree(details[1]);
        XtFree ((char *) details);

        /* Recurse to subdirectories */
        if (entry->d_name[0] != `.') {
            if (isDirectory) {
                add_directory (name, item, container);
            }
        }
    }
    }

    (void) closedir (dir);
}

/* For brevity, these are copied from Example 9-1 */
extern char *GetBitmapPath (Display *display, char *file);
extern Pixmap GetPixmap (Widget widget, char *file);
extern Pixmap GetBitmap (Widget widget, char *file);

/* Construct a file system path from the selected IconGadget */
/* This is crude, but it works. Optimization is a reader exercise */
static char *GetPath (Widget w, char *sofar)
{
    static char      buffer[512];
    char             temp[512], *leaf;
    Widget           parent = NULL;
    XmString         xms;

    if (sofar == NULL) {
        (void) strcpy (buffer, "");
    }

    if (w == NULL) {
        return buffer;
    }

    (void) strcpy (temp, buffer);

    XtVaGetValues (w, XmNentryParent, &parent, XmNlabelString, &xms, NULL);

    leaf = (char *) XmStringUnparse (xms, NULL,
                                    XmCHARSET_TEXT,
                                    XmCHARSET_TEXT,
                                    NULL, 0,
                                    XmOUTPUT_ALL);

    (void) sprintf (buffer, "%s%s%s", leaf, (sofar ? "/" : ""), temp);

    XtFree (leaf);

    return GetPath (parent, buffer);
}

/* The selection callback: simply prints out 
** the selected IconGadget file system path
*/
static void select_callback (   Widget       w,
                              XtPointer    client_data,
                              XtPointer    call_data)
    {
    XmContainerSelectCallbackStruct *cptr;
    int i;
    char *path;

    cptr = (XmContainerSelectCallbackStruct *) call_data;

    for (i = 0; i < cptr->selected_item_count; i++) {
        printf ("Selected: %s\n",
        GetPath (cptr->selected_items[i], NULL));
    }
}

main (int argc, char *argv[])
{
    Widget           toplevel, form, scrolled_win, container;
    XtAppContext     app;
    int              i, n, count;
    Arg              args[12];
    XmStringTable    column_headings_table;

    XtSetLanguageProc (NULL, NULL, NULL);
    /* Create the top-level shell and two RowColumns */
    toplevel = XtVaOpenApplication (&app, "Demos", NULL, 0, &argc, argv, NULL, 
                                   sessionShellWidgetClass, NULL);

    XtVaSetValues (toplevel,
    XmNtitle, "XmContainer - Detail Layout", NULL);

    form = XmCreateForm (toplevel, "form", NULL, 0);

    n = 0;
    XtSetArg (args[n], XmNscrollingPolicy, XmAUTOMATIC);    n++;
    XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM);    n++;
    XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
    XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM);   n++;
    XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM);  n++;
    scrolled_win = XmCreateScrolledWindow (form, "scrolled_win", args, n);

    /* Create the Container */
    count = XtNumber (column_headings);
    column_headings_table = (XmStringTable) XtMalloc(
    count * sizeof (XmString *));
    for (i = 0; i < count; i++)
        column_headings_table[i] = 
                        XmStringCreateLocalized (column_headings[i]);

    n = 0;
    XtSetArg (args[n], XmNwidth, 600);                                  n++;
    XtSetArg (args[n], XmNheight, 500);                                 n++;
    XtSetArg (args[n], XmNdetailColumnHeading, column_headings_table);  n++;
    XtSetArg (args[n], XmNdetailColumnHeadingCount, count);             n++;
    XtSetArg (args[n], XmNlayoutType, XmDETAIL);                        n++;
    XtSetArg (args[n], XmNentryViewType, XmSMALL_ICON);                 n++;
    XtSetArg (args[n], XmNselectionPolicy, XmSINGLE_SELECT);            n++;
    container = XmCreateContainer (scrolled_win, "container", args, n);

    /* Register a Selection Callback for the Container */
    XtAddCallback (container,
    XmNselectionCallback, select_callback, NULL);

    /* Reclaim Memory which the widgets have copied */
    for (i = 0; i < count; i++)
        XmStringFree (column_headings_table[i]);
    XtFree ((char *) column_headings_table);

    XtManageChild (container);
    XtManageChild (scrolled_win);
    XtManageChild (form);
    XtRealizeWidget (toplevel);

    /* Read in the icons and their masks */
    /* The bitmaps require a window, therefore must be done post-realize */
    large_folder_icon = GetPixmap (container, "folder_large.xbm");
    small_folder_icon = GetPixmap (container, "folder_small.xbm");
    large_file_icon = GetPixmap (container, "file_large.xbm");
    small_file_icon = GetPixmap (container, "file_small.xbm");

    large_folder_mask = GetBitmap (container, "folder_large_mask.xbm");
    small_folder_mask = GetBitmap (container, "folder_small_mask.xbm");
    large_file_mask = GetBitmap (container, "file_large_mask.xbm");
    small_file_mask = GetBitmap (container, "file_small_mask.xbm");

    add_directory(".", NULL, container);

    XtAppMainLoop (app);
}
The example output is logically the same as Figure 9-3, although the contents of the Tree will naturally depend upon your file system.

Container Functions

The Container support utilities fall into two groups: those concerned with transferring Container selected items to and from the Clipboard, and general purpose utilities. It is probably best to start in the reverse order and describe the utility functions first, as these are generally the more useful for typical application programming.


Forcing Container Layout

The convenience function XmContainerRelayout() forces the widget to recalculate the layout of all its IconGadget children. This only takes effect if the layout style is Spatial, and if the Container is managed. The routine could be used if some kind of sorting of the IconGadget children has taken place, either by respecifying the XmNx, XmNy coordinates of each or through manipulation of the XmNpositionIndex resource. The routine has the following functional signature:

void XmContainerRelayout (Widget container)
XmContainerRelayout() does not cause geometry management side effects: the routine will not result in the Container requesting size changes from its parent in turn.


Sorting Container Items

After a set of Container items has their XmNpositionIndex resource modified, the routine XmContainerReorder() can be used to force the Container to internally update its knowledge of the layout. If the layout style is Detail or Outline, the Container will also refresh the display. Note that XmContainerRelayout() is required for an update to the Spatial style - XmContainerReorder() has no effect on this layout style. XmContainerReorder() has the following specification:

void XmContainerReorder ( Widget         container,
                          WidgetList     items,
                          int            item_count)
The function simply reorders the items in the layout of the given container according to the XmNpositionIndex of each, using a quicksort algorithm.


Fetching Container Items

Given an arbitrary Container item in a Detail or Outline layout, presumably as a result of a selection operation, we can deduce the logical parent of that item simply by inspecting the XmNentryParent constraint. If however we wanted to know the logical children of the item, there is no resource which will provide the required information. This is where the routine XmContainerGetItemChildren() is useful. The routine returns the list of IconGadgets whose XmNentryParent resource points at a given item, and it is defined as follows:

int XmContainerGetItemChildren ( Widget          container,
                                 Widget         icon_gadget,
                                 WidgetList     *item_children)
The set of logical children of the icon_gadget is returned at the address specified by icon_children. The routine allocates memory for this, and it is the responsibility of the programmer to reclaim the memory using XtFree() at an appropriate point. The number of items in the array is returned by the function.


Container Clipboard Routines

There are five routines defined for copying and pasting container items into the clipboard. They are XmContainerCut(), XmContainerCopy(), XmContainerCopyLink(), XmContainerPaste(), and XmContainerPasteLink(). They are defined as follows:

Boolean XmContainerCut (Widget container, Time timestamp);
Boolean XmContainerCopy (Widget container, Time timestamp)
Boolean XmContainerCopyLink (Widget container, Time timestamp)
Boolean XmContainerPaste (Widget container)
Boolean XmContainerPasteLink (Widget container)
In each case, the timestamp parameter should simply be specified using the routine XtLastTimestampProcessed().

The routines are fully integrated with the Uniform Transfer Model - they do not actually cut or copy data to and from the clipboard, but internally invoke convert and destination callbacks to perform these tasks. The Uniform Transfer Model is covered in detail in Chapter 23.

A typical usage of these routines is where an Edit menu has been defined for the Container: the cut/copy/paste actions of the menu entries will be a simple interface onto the above functions, and is appropriate if the programmer is presenting an interface where the user can edit a Tree or reorder a Detail layout by moving items around through the clipboard.

Summary

The Container is the most flexible layout Manager in Motif. It offers multiple views of the same data by allowing the programmer to switch layout dynamically. The selection mechanisms whereby the Container reports actions in the widget are simple to understand and easy to program. By separating the functionality whereby the Container performs the layout and selection and the IconGadget represents the application object, the widget can be considered as an approximation to a Model-View-Controller architecture. Whether the application data needs to be viewed in Tabular, Tree, Grid, or free-floating format, the Container provides the necessary layout.

Exercises

The following exercises expand upon concepts presented in this chapter.

  1. Modify the program of Example 9-2 to display file ownership, permissions, and other status information amongst the Detail. Remember to expand the column headings for the Container. Add options to the program to allow the user to display or hide particular columns of detail, and to change the order of the columns.
  2. Add new images to the program to represent different types of file: binary, source files, objects. Add a default action callback, and make the callback spawn an appropriate action for the selected object: binaries could be executed, source files edited.


1 If, however, the value is an empty compound string (no text components), the widget displays only the image, not both image and label. This can be achieved by creating a simple compound string consisting of a single direction component.






X-Designer - The Leading X/Motif GUI Builder - Click to download a FREE evaluation

Previous Next Contents Document Index Motif Docs motifdeveloper.com Imperial Software Technology X-Designer




Copyright © 1991, 1994, 2000, 2001, 2002 O'Reilly & Associates, Inc., Antony Fountain and Jeremy Huxtable. All Rights Reserved.