The value to the ATTR_LIST attribute is an Attr_avlist, the same type returned by
attr_create_list(). The following code fragment shows how this can be used:
...
Canvas canvas1, canvas2, canvas3;
Attr_avlist attr_list;
attr_list = attr_create_list(
WIN_CMS, cms,
WIN_EVENT_PROC, my_event_proc,
XV_WIDTH, 100,
NULL);
canvas1 = xv_create(frame, CANVAS,
ATTR_LIST, attr_list,
WIN_BACKGROUND_COLOR, 1,
NULL);
canvas2 = xv_create(frame, CANVAS,
ATTR_LIST, attr_list,
WIN_BACKGROUND_COLOR, 2,
NULL);
canvas3 = xv_create(frame, CANVAS,
ATTR_LIST, attr_list,
WIN_BACKGROUND_COLOR, 3,
NULL);
free(attr_list);
...
The only restriction on the use of ATTR_LIST is that it must be the first attribute specified in
the call to xv_create(), xv_set() or any other xv_* routine that accepts attribute-val-
ue lists. Be sure that ATTR_LIST is the first attribute specified when you use it. There are
cases when ATTR_LIST may appear to be the first attribute specified, but it is actually not the
first attribute. This occurs when you use a macro. For panels, the following are macros:
PANEL_CHOICE_STACK, PANEL_CHECK_BOX, and PANEL_TOGGLE. These macros expand to
PANEL_CHOICE objects with several associated attributes that will not be visible. These attri-
butes displace ATTR_LIST as the first attribute. These and similar cases should be avoided if
you use ATTR_LIST.
Lastly, since attr_create_list() allocates memory, the list should be freed when it is
no longer needed.
25.2.3 Interpreting Attributes
When a routine is passed an Attr_avlist, it needs to scan the list looking for attributes of
interest. The function may not be interested in all the attributes, so when scanning the list, it
needs to skip ahead to successive attributes for evaluation. To facilitate this task, XView
provides the macro attr_next() to make scanning Attr_avlist easier. This macro
looks at a particular attribute and, by the nature of the attribute itself, knows how many items
to scan ahead for the next one. The macro returns a pointer to that next attribute.
584 XView Programming Manual
The first thing to do when scanning the Attr_avlist is to set a pointer to the beginning of
the list and then advance forward until you reach the NULL attribute indicating the end of the
attribute-value pairs.
function(param1, param2, avlist)
Xv_opaque param1;
Xv_opaque param2;
Attr_avlist avlist;
{
Attr_avlist attrs;
for (attrs = avlist; attrs[0]; attrs = attr_next(attrs)) {
switch ((int) attrs[0]) {
...
}
}
...
}
The for() loop initializes the attrs variable to the beginning of the list and tests for the
NULL attribute. Upon each iteration of the loop, the variable is set to the next attribute in the
list. For this to work, attrs should never be moved in either direction in the list. To look at
the value of a particular index into the attribute list, you should index that position relative to
the current value of attrs. This is precisely what is done in the switch() statement
within the loop.
attr_next() looks at the “type” of the attribute to determine how many, if any, value
parameters are associated with the attribute. For attributes that take lists such as PAN-
EL_CHOICE_STRINGS
, it knows to look ahead for the next NULL-valued index in the array.
This is why lists may not contain the value NULL or 0 as elements in the list. Once a NULL or
0 is found, attr_next() returns the element following the terminating NULL.*
The switch looks at index 0 of the attrs pointer for the attribute to evaluate. Each case
in the switch statement handles the value that pertains specifically to the package in ques-
tion. For example, the set routine for the CANVAS package has the following code fragment:
Attr_avlist attr;
for (attr = avlist; attr[0]; attr = attr_next(avlist)) {
switch ((int) attr[0]) {
case CANVAS_WIDTH:
if (canvas->width != (int) attr[1]) {
width = (int) attr[1];
new_paint_size = TRUE;
}
break;
case CANVAS_HEIGHT:
if (canvas->height != (int) attr[1]) {
height = (int) attr[1];
new_paint_size = TRUE;
}
break;
*For portability reasons, NULL should always be used rather than 0 to terminate a list.
XView Internals
XView Internals 585
/* .... */
default:
*status = xv_check_bad_attr(&xv_canvas_pkg, attr);
}
}
For each attribute, the case statement knows what to interpret as the value parameter in the
attribute list. The CANVAS_WIDTH case sets the width variable to the value set in attr[1]
and assumes it is an int.
25.2.4 Checking for Bad Attributes
When the attribute being evaluated in the switch statement falls to the default case, there
may or may not be something wrong with the attribute. Since the switch statement should
have had a case for all the known attributes to the package, it is assumed that the attribute
that had fallen through probably belongs to another package.
To check for this, the function xv_check_bad_attr() is used. The form of the function
is:
int
xv_check_bad_attr(pkg, attr)
Xv_pkg *pkg;
Attr_attribute attr;
The function checks to see if the attribute in the second parameter belongs to the package
specified in the first parameter. If the attribute does belong to the package, then an error mes-
sage is printed and the function returns XV_OK. Otherwise, the function does nothing and re-
turns XV_ERROR. Yes, this is counter-intuitive, but this value is utilized more appropriately
by the get method. Details are discussed in Section 25.8.3, “The Bitmap Get Method.”
An unknown attribute does not indicate that an error has been made. Remember that pack-
ages can be subclassed from other packages, so an attribute may apply to another level of the
class hierarchy and will be dealt with at another time by another function.
25.2.4.1 Searching for specific attributes
Rather than scanning the entire Attr_avlist looking for one particular attribute, XView
provides the convenience function, attr_find(). This function takes an Attr_avlist
and an Attr_attribute as parameters and returns the location within the list where the
attribute was found.
586 XView Programming Manual
Here is its implementation:
Attr_avlist
attr_find(attrs, attr)
register Attr_avlist attrs;
register Attr_attribute attr;
{
for (; *attrs; attrs = attr_next(attrs)) {
if (*attrs == (Xv_opaque) attr)
break;
}
return (attrs);
}
25.2.5 Consuming Attributes
Once an attribute has been evaluated, it should be consumed so that no other functions may
see it. Consuming attributes should not be done if multiple packages (or functions) care to
examine the same attribute. The attr_skip() macro knows to skip over attributes (and
their associated values) that have been consumed.
Attribute consumption is done with the ATTR_CONSUME() macro.*
25.3 Customizable Attributes
New attributes that are introduced when you create extensions to XView can be made cus-
tomizable, via the X resource database, with the function xv_add_custom_attrs().
The format of xv_add_custom_attrs() is:
void
xv_add_custom_attrs(pkg, va_alist)
Xv_pkg *pkg;
va_dcli va_alist; /* var args list */
The argument pkg is the XView package to which the customizable attributes belong.
va_alist is a NULL-terminated list of pairs using the following format:
<customizable attribute, attribute resource name>
*While the pre-built XView packages should consume attributes, few of them actually do. This will change in later
releases of XView.
XView Internals
XView Internals 587
The type of “customizable attribute” is Attr_attribute. The type of attribute resource
name is char*. For example,
xv_add_custom_attrs(pkg,
<attribute1, attribute1 resource name>,
<attribute2, attribute2 resource name>,
<attribute3, attribute3 resource name>,
<attribute4, attribute4 resource name>,
...
NULL);
The attribute resource name is used to construct the key for database lookup when the attri-
bute is used with XV_USE_DB.
xv_add_custom_attrs() must be called before any of the customizable attributes are
used. A good place to call xv_add_custom_attrs() would be immediately following
xv_init().
For example, you can use xv_add_custom_attrs() to make attributes customizable for
the package extension called
LOGO (see Section 25.5, “The Logo Package,” in this chapter.)
Make the new attributes
LOGO_WIDTH and LOGO_HEIGHT customizable with the following
call:
xv_add_custom_attrs(LOGO,
LOGO_WIDTH, "logo_width",
LOGO_HEIGHT, "logo_height",
NULL);
The attributes LOGO_WIDTH and LOGO_HEIGHT can then be customized as in the following
example:
logo = xv_create(owner, LOGO,
XV_USE_DB
LOGO_HEIGHT, 300,
LOGO_WIDTH, 250,
NULL,
...
NULL);
The resource names constructed for database lookup for
LOGO_HEIGHT and LOGO_WIDTH will
be:
CONCAT_INSTANCE_NAME.logo_height
CONCAT_INSTANCE_NAME.logo_width
where
CONCAT_INSTANCE_NAME is the concatenation of instance names of all objects in the
current object’s lineage.
If such entries did exist in the X resource database, then their values will be used for
LOGO_HEIGHT and LOGO_WIDTH. Otherwise, LOGO_HEIGHT will default to 300, and
LOGO_WIDTH to 250.
Currently, support for customizable attributes is provided only for attributes of type long,
int, boolean, char, and string (char *). See Section 22.3, “Object Layout and Cus-
tomization,” in Chapter 22, Internationalization, for more details on customizable attributes.
588 XView Programming Manual

Get Volume 7A: XView Programming Manual now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.