Programming AmigaOS in C

24. Gadgets

Intuition introduced gadgets but were flat and rather dull affairs. So, with AmigaOS 2, the Gadtools.library was introduced to update gadgets to use more 3D style gadgets which certainly looked better than their Intuition counterparts. Windows come with system gadgets such as the closw window, back/front, size and so on. You can define your own gadgets using Gadtools and use the NewGadget structure to define your gadget or just use Tags:

struct NewGadget
   {
   WORD ng_LeftEdge, ng_TopEdge;  /* gadget position */
   WORD ng_Width, ng_Height;      /* gadget size */
   UBYTE *ng_GadgetText;          /* gadget label */
   struct TextAttr *ng_TextAttr;  /* desired font for gadget label */
   UWORD ng_GadgetID;             /* gadget ID */
   ULONG ng_Flags;                /* see below */
   APTR ng_VisualInfo;            /* Set to retval of GetVisualInfo() */
   APTR ng_UserData;              /* gadget UserData */
};
 

You can define the position and size of the gadget, supply an optional label to go with it, a gadget ID so you know which gadget you have select, some Flags, the pointer to Visual Information which is required for Gadtools and some optional User Data. To define the type of gadget, you need to specify it with flags. For example:

GENERIC_KIND, BUTTON_KIND, CHECKBOX_KIND, INTEGER_KIND, LISTVIEW_KIND, MX_KIND
NUMBER_KIND, CYCLE_KIND, PALETTE_KIND, SCROLLER_KIND, SLIDER_KIND,
STRING_KIND, TEXT_KIND, NUM_KIND

You can also define the type(s) of IDCMP flags to look for. The most common is GADGETUP which means an event occurs when a gadget is selected and released (GADGETDOWN is only done when the gadget is selected but NOT released). You can also use the MOUSEMOVE if using scoller type gadgets.

25. Adding and removing Gadgets to a Window

To add gadgets to a window you can use CreateGadgetA or CreateGadget, the first uses a Taglist and a the second justs Tags. Both functions return a pointer to a Gadget structure.

struct Gadget *CreateGadgetA( unsigned long kind, struct Gadget *gad, struct NewGadget *ng, struct TagItem *taglist )

struct Gadget *CreateGadget( unsigned long kind, struct Gadget *gad, struct NewGadget *ng, Tag tag1, ... )

here you can specify the gadget kind value, an optional pointer to a full Gadget structure, an optional NewGadget structure and optional tags. So you could specify kind and NewGadget or kind and Gadget or kind a list of Tags etc.

Once a gadget has been finished with then they need to removed and that is done with the FreeGadgets function. This is done for the gadget list not per gadget:

void FreeGadgets( struct Gadget *gad )

26. Updating gadget attributes

Once gadgets have been created and displayed, it is possible to change various attributes of gadgets such as its flags using the GT_SetGadgetAttrsA or GT_SetGadgetAttrs functions. You can specify the Gadget, which window its located in, a requester structure and either a Tag list or list of tags with the new values.

void GT_SetGadgetAttrsA( struct Gadget *gad, struct Window *win, struct Requester *req, struct TagItem *taglist )

void GT_SetGadgetAttrs( struct Gadget *gad, struct Window *win, struct Requester *req, Tag tag1, ... )

27. An example of using Gadgets in a Window

/* Gadget example */
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <stdio.h>
APTR visual;
/* Type of gadgets to display */
   ULONG Gadgetkinds[3] = {STRING_KIND, INTEGER_KIND, BUTTON_KIND };
struct TextAttr topaz8 = {
   (STRPTR)"topaz.font", 8, 0, 1
   };
/* Data for gadget structures */
   struct NewGadget Gadgetdata[3] = {
   63, 26, 172, 13, (UBYTE *)"Name", &topaz8, 1, PLACETEXT_LEFT, NULL, NULL, 
   62, 50, 175, 15, (UBYTE *)"Age", &topaz8, 2, PLACETEXT_LEFT, NULL,    NULL,
   111, 105, 54, 31, (UBYTE *)"Calc", &topaz8, 3, PLACETEXT_IN, NULL,    NULL 
   };
/* Extra information for gadgets using Tags */
   ULONG GadgetTags[] = {
   (GTST_MaxChars), 256, (TAG_DONE),
   (GTNM_Border), TRUE, (TAG_DONE),
   (TAG_DONE)
   };
int main(void) {
      struct Screen *pubScreen;
      struct Window *myWindow;
      struct Gadget *myGadgets[3], *glist=NULL, *gad1;
      int closewin = FALSE, i;
      struct IntuiMessage *msg;
      ULONG msgClass;
   
      /* Lock screen and get visual info for gadtools */
      if (pubScreen = LockPubScreen(NULL)) {
         if (!(visual = GetVisualInfo(pubScreen,    TAG_DONE))) {
            printf("Failed to get visual info.\n");
            return(5);
         }
      }
      else {
         printf("Failed to lock screen.\n");
         return(5);
      }
    /* Create the gadget list */
      if (!(gad1 = CreateContext(&glist))) {
         printf("Failed to create gadtools context.\n");
         return(5);
      }
      /* Create gadgets specify gadget kind, a Gadget, NewGadget data and extra tag info */
      for (i=0; i<3; i++) {
         Gadgetdata[i].ng_VisualInfo = visual;
         if (myGadgets[i] = gad1 = CreateGadgetA(Gadgetkinds[i], gad1, &Gadgetdata[i], (struct TagItem *)&GadgetTags[i])) {
            printf("Gadget %d created.\n", i);
         }
         else
            printf("Failed to create gadget %d.\n",i);
      }
   
      /* Open window and specify gadget list (glist) */
      myWindow = OpenWindowTags(NULL,
         WA_Left, 10, WA_Top, 15,
         WA_Width, 280, WA_Height, 180,
         WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_GADGETUP,
         WA_Flags, WFLG_SIZEGADGET | WFLG_DRAGBAR    | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_ACTIVATE | WFLG_SMART_REFRESH,
         WA_Gadgets, glist,
         WA_Title, "My Window",
         WA_PubScreenName, "Workbench",
         TAG_DONE);
      GT_RefreshWindow(myWindow, NULL); /* Update window */
   
      while (closewin == FALSE) {
         Wait(1L << myWindow->UserPort->mp_SigBit);
         msg = GT_GetIMsg(myWindow->UserPort);
         msgClass = msg->Class;
         GT_ReplyIMsg(msg);
         if (msgClass == IDCMP_CLOSEWINDOW) {
            closewin = TRUE;
         }
      }
    if (myWindow) CloseWindow(myWindow);
    /* Free gadgets */
    if (glist) FreeGadgets(glist);
   
    if (visual) FreeVisualInfo(visual);
    if (pubScreen) UnlockPubScreen(NULL, pubScreen);
    return(0);
 }

NB: for AmigaOS 4: Prefix OpenWindowTags, CloseWindow with 'IIntuition->' Prefix Wait with 'IExec->'. Prefix GetVisualInfo, CreateContext, GT_RefreshWindow, GT_GetIMsg, GT_ReplyIMsg, FreeVisualInfo and FreeGadgets with 'IGadTools->'. Compile using the command 'gcc -o GadgetExample GadgetExample.c -lauto'.

This example opens a window a displays three gadgets: a String Gadget so you enter some text such as a name, an Integer gadget so you can enter a number such as someone's age and a Button gadget which you can press to run another process. These 'kinds' of buttons are defined in the Gadgetkinds[] array. See libraries/gadtools.h for gadget types. Next we have defined a TextAttr structure to use font topaz size 8 for the gadget text.

Next we define the actual Gadgetdata[] array using the NewGadget structure. Here we define the left, top position of the gadget in the window, the width and height of the gadget, the label for the gadget (this can be placed to left, right, inside, top or bottom of the gadget), the gadget number, the placement of the gadget label, a pointer to the visual data which is set later and finally a pointer to some user data which we are not using at present.

Finally we define some extra GadgetTags[] array to define additional data for the string and integer gadgets which will be used when creating the gadgets.

In the main program, we need to set up the screen by Locking the default public screen, in most cases this is Workbench using LockPubScreen. Then we can set the visual pointer with the GetVisualInfo function which is required by the Gadgets in our window.

Next we need to set up a gadget list using the CreateContext function, the glist pointer should be set to NULL before passing it to the function and then when we create new gadgets they are automatically added to this list. It will return a pointer to a Gadget structure called gad1. Gad1 is set when we create our Gadgets. Then we create our 3 gadgets in a for loop using the CreateGadgetsA function. This function requires the Gadget Kind, a pointer to a Gadget structure ie gad1, the address of the NewGadget data ie Gadgetdata, and optionally a pointer to an array of TagItems for additional gadget information. Note that before creating each gadget, we set each gadgets ng_VisualInfo to the value of the visual, set previously by GetVisualInfo. This will bind the gadget to this screen. The CreateGadgetsA function returns a pointer to a Gadget structure which we store in gad1 and an array called myGadgets[].

Then we can open the window myWindow and include an additional tag called WA_Gadgets and pass our gadget list glist to it, this will display the gadgets created earlier in our window. We have also added a new IDCMP Flag called IDCMP_GADGETUP which we can check if gadgets are selected. In this program we are not doing anything with it at present.

In the while loop, we can test for Intution messages if the gadgets are selected or the Window is closed. If the window is closed, the the while loop is exited and the close down procedure is performed. The window is closed first and any gadgets freed from memory (if gadgets are freed before the Window, then it may cause Recoverable Alerts). Then the visual informantion is freed from memory and finally the screen unlocked before the program ends.

28. Determining which gadget is pressed.

Next we need to know which gadget is pressed after determining that a GADGETUP IDCMP event has occured. In this case we can use the IAddress section of a message to retreive the data as follows:

struct Gadget *gadAddr;
UWORD gadgetid;
if (msgClass == GADGETUP) {
   gadAddr = (struct Gadget *)msg->IAddress;
   gadgetid = gadAddr->GadgetID;
   if (gadgetid == 3) {           /* Test if Button pressed */
/* do something here */ } }