Copyright (c) Hyperion Entertainment and contributors.

Tags

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

Introduction

Tags make it possible to add new parameters to system functions without interfering with the original parameters. They also make specifying parameter lists much clearer and easier.

Tag Structures

A tag is made up of an attribute/value pair as defined below (from <utility/tagitem.h>):

struct TagItem
{
    uint32 ti_Tag;    /* identifies the type of this item */
    uint32 ti_Data;   /* type-specific data, can be a pointer */
};

The ti_Tag field specifies an attribute to set. The possible values of ti_Tag are implementation specific. System tags are defined in the include files. The value the attribute is set to is specified in ti_Data. An example of the attribute/value pair that will specify a window's name is:

ti_Tag  = WA_Title;
ti_Data = "My Window's Name";

The ti_Data field often contains 32-bit data as well as pointers.

These are brief descriptions of the utility functions you can use to manipulate and access tags. For complete descriptions, see the "Simple Tag Usage" and "Advanced Tag Usage" sections.

Simple Tag Usage

One way tags are passed to system functions is in the form of tag lists. A tag list is an array or chain of arrays of TagItem structures. Within this array, different data items are identified by the value of ti_Tag. Items specific to a subsystem (Intuition, Graphics,...) have a ti_Tag value which has the TAG_USER bit set. Global system tags have a ti_Tag value with TAG_USER bit clear. The global system tags include:

Global System Tags
Tag Value Meaning
TAG_IGNORE A no-op. The data item is ignored.
TAG_MORE The ti_Data points to another tag list, to support chaining of TagItem arrays.
TAG_END Terminates the TagItem array (or chain). TAG_DONE is a synonym.
TAG_SKIP Ignore the current tag item, and skip the next n array elements, where n is kept in ti_Data.

Note that user tags need only be unique within the particular context of their use. For example, the attribute tags defined for OpenWindow() have the same numeric value as some tags used by OpenScreen(), but the same numeric value has different meaning in the different contexts.

System functions receive TagItems in several ways. One way is illustrated in the Intuition function OpenWindow(). This function supports an extented NewWindow structure called ExtNewWindow. When the NW_EXTENDED flag is set in the ExtNewWindow.Flags field, OpenWindow() assumes that the ExtNewWindow.Extension field contains a pointer to a tag list.

Another method of passing a tag list is to directly pass a pointer to a tag list, as OpenWindowTagList() does in the following code fragment.

struct TagItem tagitem[3];
struct Screen *screen;
struct Window *window;
 
tagitem[0].ti_Tag  = WA_CustomScreen;
tagitem[0].ti_Data = screen;    /* Open on my own screen */
tagitem[1].ti_Tag  = WA_Title;
tagitem[1].ti_Data = "Amiga Test Window";
tagitem[2].ti_Tag  = TAG_END;   /* Marks the end of the tag array. */
 
/* Use defaults for everything else. Will open as big as the screen. */
/* Because all window parameters are specified using tags, we don't  */
/* need a NewWindow structure                                        */
 
if (window = IIntuition->OpenWindowTagList(NULL, tagitem))
{
    /* rest of code */
    IIntuition->CloseWindow(window);
}

Notice that window parameters need not be explicitly specified. Functions that utilize tags have reasonable defaults to fall back on in case no valid attribute/value pair was supplied for a particular parameter. This fall back capability is a useful feature. An application only has to specify the attributes that differ from the default, rather than unnecessarily listing all the possible attributes.

Many functions also support another way to pass TagItems. Rather than passing a tag list, the function OpenWindowTags() receives the attribute/value pairs in the argument list, much like the standard C function printf() receives its arguments. Any number of attribute/value pairs can be specified. This type of argument passing is called VarArgs. The following code fragment illustrates the usage of OpenWindowTags().

struct Window *window;
 
/* Just pass NULL to show we aren't using a NewWindow */
window = IIntuition->OpenWindowTags( NULL,
                         WA_CustomScreen, screen,
                         WA_Title, "Amiga Test Window",
                         TAG_END );

Tags are not exclusively for use with the operating system; the programmer can implement them as well. The run-time utility library contains several functions to make using tags easier.

Simple Tag Usage Example

The following example shows simple usage of tags. It shows how to allocate a tag array and use it, it also shows how to build a tag array on the stack.

/*
** tag1.c
*/
 
#include <exec/types.h>
#include <exec/libraries.h>
#include <utility/tagitem.h>
#include <intuition/intuition.h>
 
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/utility.h>
 
struct IntuitionIFace *IIntuition = NULL;
 
int main (int argc, char **argv)
{
    struct TagItem *tags;
    struct Window  *win;
 
    struct Library *IntuitionBase = IExec->OpenLibrary ("intuition.library", 50);
    IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
 
    if (IIntuition != NULL)
    {
        /****************************************************************/
        /* This section allocates a tag array, fills it in with values, */
        /* and then uses it.                                            */
        /****************************************************************/
 
        /* Allocate a tag array */
        if (tags = IUtility->AllocateTagItems (7))
        {
            /* Fill in our tag array */
            tags[0].ti_Tag = WA_Width;
            tags[0].ti_Data = 320;
            tags[1].ti_Tag = WA_Height;
            tags[1].ti_Data = 50;
            tags[2].ti_Tag = WA_Title;
            tags[2].ti_Data = (uint32) "Amiga Tag Example 1";
            tags[3].ti_Tag = WA_IDCMP;
            tags[3].ti_Data = IDCMP_CLOSEWINDOW;
            tags[4].ti_Tag = WA_CloseGadget;
            tags[4].ti_Data = TRUE;
            tags[5].ti_Tag = WA_DragBar;
            tags[5].ti_Data = TRUE;
            tags[6].ti_Tag = TAG_END;
 
            /* Open the window, using the tag attributes as the
             * only description. */
            if (win = IIntuition->OpenWindowTagList (NULL, tags))
            {
                /* Wait for an event to occur */
                IExec->WaitPort (win->UserPort);
 
                /* Close the window now that we're done with it */
                IIntuition->CloseWindow (win);
            }
 
            /* Free the tag list now that we're done with it */
            IUtility->FreeTagItems(tags);
        }
 
        /****************************************************************/
        /* This section builds the tag array on the stack, and passes   */
        /* the array to a function.                                     */
        /****************************************************************/
 
        /* Now use the VarArgs (or stack based) version. */
        if (win = IIntuition->OpenWindowTags ( NULL,
                                   WA_Width, 320,
                                   WA_Height, 50,
                                   WA_Title, "Amiga Tag Example 1",
                                   WA_IDCMP, IDCMP_CLOSEWINDOW,
                                   WA_CloseGadget, TRUE,
                                   WA_DragBar, TRUE,
                                   TAG_END))
        {
            /* Wait for an event to occur */
            IExec->WaitPort (win->UserPort);
 
            /* Close the window now that we're done with it */
            IIntuition->CloseWindow (win);
        }
    }
 
    IExec->DropInterface((struct Interface*)IIntuition);
    IExec->CloseLibrary (IntuitionBase);
 
    return 0;
}

Advanced Tag Usage

The previous section provided the background material necessary to start using tags. This section will show how to use the more advanced features of tags using functions within utility library.

Creating a New Tag List

The AllocSysObjectTags() function with an object type of ASOT_TAGLIST can be used to create a new tag array ready for use. The tag array should be passed to FreeSysObject() when the application is done with it.

/* Indicate how many tags we need */
uint32 tags_needed = 10;
 
/* Allocate a tag array */
struct TagItem *tags = IExec->AllocSysObjectTags(ASOT_TAGLIST,
  ASOTAGS_NumEntries, tags_needed,
  TAG_END);
 
if (tags != NULL)
{
    /* ...do something with the array... */
 
    /* Free the array when your done with it */
    IExec->FreeSysObject (ASOT_TAGLSIT, tags);
}

Copying an Existing Tag List

The CloneTagItems() function is used to copy an existing tag array into a new tag array.

struct TagItem *otags;      /* Original tag array */
struct TagItem *ntags;      /* New tag array */
 
/* Make sure there is a TagItem array */
if (otags)
{
    /* Copy the original tags into a new tag array */
    if (ntags = IUtility->CloneTagItems(otags))
    {
        /* ...do something with the array... */
 
        /* Free the array when your done with it */
        IUtility->FreeTagItems (ntags);
    }
}

This function can also be used to implement a function that will insert tag items into an array.

struct TagItem *otags;      /* Original tag array */
struct TagItem *tags;       /* New tag array */
 
/* Insert a couple of tags into an existing tag array */
if (tags = IUtility->MakeNewTagList (GA_LeftEdge, 10,
                                     GA_TopEdge, 20,
                                     TAG_MORE, otags))
{
    /* ...do something with the array... */
 
    /* Free the array when your done with it */
    IUtility->FreeTagItems (tags);
}
 
/* This function will create a tag array from tag pairs placed on
 * the stack */
struct TagItem *MakeNewTagList (uint32 data,...)
{
    struct TagItem *tags = (struct TagItem *) &data;
 
    return IUtility->CloneTagItems (tags);
}

Filtering an Existing Tag List

Sometimes it is necessary to only allow certain attributes to be visible in a tag list. In order to achieve this, the tag array would need to be filtered.

A number of functions are provided for filtering items in a tag array. They are FilterTagChanges(), FilterTagItems() and RefreshTagItemClone().

/* We want the text entry gadget to receive the following tags */
Tag string_attrs[] =
{
    STRINGA_MaxChars,
    STRINGA_Buffer,
    STRINGA_TextVal,
    TAG_END,
};
 
/* These are attributes that the model understands */
Tag model_attrs[] =
{
    CGTA_Total,
    CGTA_Visible,
    CGTA_Top,
    ICA_TARGET,
    ICA_MAP,
    TAG_END,
};
 
struct TagItem *otags;      /* Original tag list */
struct TagItem *ntags;      /* New, work, tag list */
 
/* Make a copy of the original for us to work with */
ntags = IUtility->CloneTagItems (otags);
 
/* Create a tag list that only contains attributes that are
 * listed in the model_attrs list. */
if (IUtility->FilterTagItems (ntags, model_attrs, TAGFILTER_AND))
{
    /* Work with filtered tag list (ntags) */
 
    /* Restore the tag list */
    IUtility->RefreshTagItemClones (ntags, otags);
 
    /* Create a tag list that only contains attributes that
     * aren't in the model_attrs list. */
    if (IUtility->FilterTagItems (ntags, model_attrs, TAGFILTER_NOT))
    {
        /* Work with filtered tag list (ntags) */
    }
 
    /* Restore the tag list */
    IUtility->RefreshTagItemClones (ntags, otags);
 
    /* Create a tag list that only contains attributes that
     * are in the string_attrs list. */
    if (IUtility->FilterTagItems (ntags, string_attrs, TAGFILTER_AND))
    {
        /* Work with filtered tag list (ntags) */
    }
}
 
/* Free work tag list. */
IUtility->FreeTagItems (ntags);

Locating an Attribute

To see if an attribute is in a tag array, the TagInArray() function is used.

/* See if the listview labels attribute is located in a tag array */
if (IUtility->TagItemArray(GTLV_Labels, tags))
{
    /* Yes, the attribute is in the list */
}
else
{
    /* No, the attribute isn't in the list */
}

The FindTagItem() function will return a pointer to the actual tag that has the desired attribute. This allows you to manipulate the tag or to determine if the attribute exists but just has a NULL value.

struct TagItem *tag;
 
/* See if they are trying to set a sound */
if (tag = IUtility->FindTagItem(MGA_Sound, attrs))
{
    /* Set the sound attribute to point to the specified sound data */
    tag->ti_Data = sound;
}

Sequential Access of Tag Lists

In order to sequentially access the members of a tag array, the NextTagItem() function is used.

struct TagItem *tags = msg->ops_AttrList;
struct TagItem *tstate;
struct TagItem *tag;
 
/* Start at the beginning */
tstate = tags;
 
/* Step through the tag list while there are still items in the
 * list */
while (tag = IUtility->NextTagItem (&tstate))
{
    /* Cache the data for the current element */
    uint32 tidata = tag->ti_Data;
 
    /* Handle each attribute that we understand */
    switch (tag->ti_Tag)
    {
        /* Put a case statement here for each attribute that your
         * function understands */
        case PGA_Freedom:
            lod->lod_Flags |= tidata;
            break;
 
        case GTLV_Labels:
            lod->lod_List = (struct List *) tidata;
            break;
 
        /* We don't understand this attribute */
        default:
            break;
    }
}

Random Access of Tag Lists

The GetTagData() function will return the data for the specified attribute. If there isn't a tag that matches, then the default value is returned.

APTR sound;
 
/* Get the sound data that our function will use. */
sound = (APTR) GetTagData (MGA_Sound, (uint32) DefaultSound, attrs);

Obtaining Boolean Values

Often times data is best represented as simple boolean (TRUE or FALSE) values. The PackBoolTags() function provides an easy method for converting a tag list to bit fields.

/* These are the attributes that we understand, with the
 * corresponding flag value. */
struct TagItem activation_bools[] =
{
    /* Attribute            Flags */
    {GA_ENDGADGET,          ENDGADGET},
    {GA_IMMEDIATE,          GADGIMMEDIATE},
    {GA_RELVERIFY,          RELVERIFY},
    {GA_FOLLOWMOUSE,        FOLLOWMOUSE},
    {GA_RIGHTBORDER,        RIGHTBORDER},
    {GA_LEFTBORDER,         LEFTBORDER},
    {GA_TOPBORDER,          TOPBORDER},
    {GA_BOTTOMBORDER,       BOTTOMBORDER},
    {GA_TOGGLESELECT,       TOGGLESELECT},
 
    /* Terminate the array */
    {TAG_END}
};
 
/* Set the activation field, based on the attributes passed */
g->Activation = IUtility->PackBoolTags(g->Activation, tags, activation_bools);

Mapping Tag Attributes

To translate all occurrences of an attribute to another attribute, the MapTags() function is used.

struct TagItem map_list[] =
{
    /* Original     New */
    {MGA_LeftEdge,  GA_LeftEdge},
    {MGA_TopEdge,   GA_TopEdge},
    {MGA_Width,     GA_Width},
    {MGA_Height,    GA_Height},
 
    /* Terminate the array */
    {TAG_END}
}
 
/* Map the tags to the new attributes, keeping all attributes that
 * aren't included in the mapping array */
IUtility->MapTags(tags, map_list, MAP_KEEP_NOT_FOUND);

Function Reference

The following are brief descriptions of the utility library functions which pertain to tags and tag lists. See the SDK for details on each function call.

Function Description
AllocSysObjectTags(ASOT_TAGLIST) Allocate a TagItem array (or chain).
FreeSysObject(ASOT_TAGLIST) Frees allocated TagItem lists.
CloneTagItems() Copies a TagItem list.
RefreshTagItemClone() Rejuvenates a clone from the original.
FindTagItem() Scans TagItem list for a tag.
GetTagData() Obtain data corresponding to tag.
NextTagItem() Iterate TagItem lists.
TagInArray() Check if a tag value appears in a Tag array.
FilterTagChanges() Eliminate TagItems which specify no change.
FilterTagItems() Remove selected items from a TagItem list.
MapTags() Convert ti_Tag values in a list via map pairing.
PackBoolTags() Builds a "Flag" word from a TagItem list.