Programming AmigaOS in C

Reaction Gadgets

Reaction Windows will need various gadgets that the user can interact with in the application, these can be menus, buttons,text boxes, check boxes, radio buttons and so on.

a) Reaction gadgets and images

Reaction supports the following gadget types (these are defined in the header classes/reaction_macros.h)

ButtonObject = a pressable button
ToggleObject = a on/off or true/false type gadget
CheckBoxObject = a tick box
ChooserObject = cycle selection type gadget
ClickTabObject = allows to split window in to tabs
ClickTabsObject
PopUpObject
DropDownObject
FuelGaugeObject = for progress bars or level indicators
FuelObject
GetFileObject = for browsing of files on a disk
GetFontObject = for selection of typeface and style for fonts
GetScreenModeObject = for selection screen size and depth
IntegerObject = an input box for numbers
PaletteObject = for selection of colours
PenMapObject
ListBrowserObject= for lists of items
RadioButtonObject = an item selection
ScrollerObject = Add a scroll bar to window or gadget
SpeedBarObject
SliderObject = value selection using a slider
StatusObject
StringObject = for simple text entry
SpaceObject = for spacing between objects
TextFieldObject = for long text entry

Also, Reaction supports the following image objects:

BevelObject
BitMapObject
DrawListObject
GlyphObject
LabelObject = For adding labels and text for gadgets

And if you want to integrate Arexx scripts with your application, you can add:

ARexxObject

b) Setting up for gadgets

Before you you can use gadgets, you need some setting up first. You will need to include header information, set up constants and load some more libraries. The gadgets and images are stored in SYS:Classes/Gadgets, and SYS:Classes/Images and be opened using OpenLibrary.

In our example, we will be using a label, an Integer input gadget and a button gadget, so we need these headers adding:

#include <proto/integer.h>
#include <proto/button.h>
#include <proto/label>
#include <gadgets/integer.h>
#include <gadgets/button.h>
#include <images/label.h>

Also,in the main program, we need to open the gadgets and image using OpenLibrary() routine.

struct Library *IntegerBase, *LabelBase, *ButtonBase;

IntegerBase = (struct Library *)OpenLibrary("gadgets/integer.gadget", 44);
LabelBase = (struct Library *)OpenLibrary("images/label.image", 44);
ButtonBase = (struct Library *)OpenLibrary("gadgets/button.gadget", 44);

when you are done with the libraries at the end of the program, do not forget to close these Libraties.

if (IntegerBase) CloseLibrary(IntegerBase);
if (LabelBase) CloseLibrary(LabelBase);
if (ButtonBase) CloseLibrary(ButtonBase);

In AmigaOS 4, gadgets and images are now called Classes. They are an extended versions of Libraries with an extra Class field
e.g.
struct ClassLibrary *LayoutBase;
Class *LayoutClass;
LayoutBase = IIntuition->OpenClass("layout.gadget", 52, &LayoutClass);

IIntuition->CloseClass(LayoutBase);

If you have added the '-lauto' option to makefile for gcc, the compiler will take care of opening/closing libraries and classes for you.

c) Adding objects to a Reaction Window

Reaction windows consists of a window object (the parent object) , a parent group, a layout object and child objects which are the gadgets themselves. A defined object can be defined in to a variable to be accessed later if needed:
e.g.
LAYOUT_AddChild, integerobj = IntegerObject,

For Storm C or VBCC, the above code gives a 'unterminated macro call' error, so you will have to expand the object e.g. IntegeObject, to the full NewObject function call and replace the EndInteger to TAG_END) equivalent.

Once an object is defined, you will need to provider its attributes such as its how it looks, and range of values it accepts as so on using items called Tags, and each object must be finished with its own End tag e.g. IntegerEnd.

For example, the following adds a label called "Integer _1" and then an Integer input box to the window, a pointer to the gadget is stored in the gadgets[] array. The gadget is given an id, this case GID_INTEGER (1). The integer box will accept values upto 6 digits long and values between 0 and 100,000. Initial value is 100.

Objects *gadgets[2]; /* To save references to gadgets */

CHILD_Label, LabelObject, LABEL_Text, "Integer _1", LabelEnd,

LAYOUT_AddChild, gadgets[GID_INTEGER1] = IntegerObject,
   GA_ID, GID_INTEGER1,
   GA_RelVerify, TRUE,
   GA_TabCycle, TRUE,
   INTEGER_Arrows, FALSE,
   INTEGER_MaxChars, 6,
   INTEGER_Minimum, 0,
   INTEGER_Maximum, 100000,
   INTEGER_Number, 100,
IntegerEnd,

Here is an example, of a button gadget with the word Quit in it (the underscore will allow the user to press Q for Quit instead of using the mouse).

Object *button1;

LAYOUT_AddChild, button1 = ButtonObject,
   GA_ID, GID_QUIT,
   GA_RelVerify, TRUE,
   GA_Text, "_Quit",
ButtonEnd,

Note, that GID_INTEGER1 and GID_QUIT will be defined with an integer value using the enum pre-processor comand. The button1 variable can be used to Get or Set attributes later.

In AmigaOS 4, gadgets are handled with Intuition using OM_NEW (NewObject()) and OM_DISPOSE (DisposeObject()) functions. Child Objects are defined using the LAYOUT_AddChild Tag for the parent object.
e.g.
Object *LayoutGadget, *PageGadget;

LayoutGadget = IIntuition->NewObject(LayoutClass, NULL, LAYOUT_Orientation, LAYOUT_ORIENT_VERT, TAG_END);
PageGadget = IIntuition->NewObject(NULL, "page.gadget", TAG_END);

If you include the reaction/reaction_macros.h header file, then you can save time and coding by using the macros to add gadgets to your window.
There are macros for Label, Button, PushButton, TextLine, LabelTextLine, String, LabelString and PopString gadgets.

e.g.
LAYOUT_AddChild, String( "", ID_Name,20 ), /* Add a string gadget, max 20 chars, GA_ID set to ID_Name */
Label("Enter your name"),

LAYOUT_AddChild, Button("Submit", ID_Submit),

Here are definitions for Slider and Cycle (Chooser) macros:

#define Slider(min,max,level) \
   SliderObject, \
   SLIDER_Min , min, \
   SLIDER_Max , max, \
   SLIDER_Level, level, \
   SLIDER_Orientation, SLIDER_HORIZONTAL, \
   SLIDER_LevelFormat, "%1ld", \
   SLIDER_LevelPlace, PLACETEXT_IN, \
End

#define Cycle(labellist, idname) \
  ChooserObject, \
  GA_ID, idname, \
  CHOOSER_LabelArray, labellist, \
  CHOOSER_Selected, 0, \
End

You can easily define your own macros in your own headers files, for your customised gadgets.

d) Laying out gadgets in a Reaction Window

You may have noticed that unlike Intuition you do not specify the x,y co-ordinates of the gadget or the size of the gadget. This is taken care for you, using Layout objects and groups.

There are two types of layout: VGroupObject for vertical groupings and HGroupOject for Horizontal groups (default layout). This means that gadgets can be placed horizontally or across the width of the window, or vertically down down the length of the window. Layout groups can be nested to create quite complex layouts. Layout groups are completed with the EndGroup tag.

For example, the following vertical layout group is created within the window object:

objects[OID_MAIN] = WindowObject,
   /* window attributes defined here */
   WINDOW_ParentGroup, gadgets[GID_MAIN] = VGroupObject,
      LAYOUT_SpaceOuter, TRUE,
      LAYOUT_DeferLayout, TRUE,
         /* gadgets defined here */
     EndGroup,
EndWindow;

There are a large number of attributes that can be defined for layouts. They are defined in the header gadgets/layout.h, they include spacing between gadgets, bevels, labels, images, layout type, colours, fill patterns, and help facilities.

In AmigaOS 4, gadgets are added to the Window by using the Intuition function AddGList() and displayed using RefreshGList().

e) Changing gadget attributes

You can change the attributes after the initial configuration, if you need to using the Intuition SetAttrs command. The format of the command is:

SetAttrsA(APTR object, struct TagItem *tagList, ...)
or
SetAttr(APTR object, long tag1, ...)

e.g.
SetAttrs(Pal_Object1, ICA_TARGET, But_Object, TAG_DONE); /* For palette object set the ICA_Target to the Button object */

Once the window, is open some gadgets e.g. strings, do not update properly using SetAttr, so use the SetGadgetAttr() function instead.

SetGadgetAttrs((void *)object, (struct Window *)window, NULL, attribute, value, TAG_DONE)

f) Getting values from gadgets

Once a user a has entered data or selected a value in a given gadget, then you need to retreive the data. This can be done using the GetAttr command. The format of the command is:

GetAttr(ULONG AttrID, APTR Object, ULONG *Storageptr)

where AttrID is the name of the gadget attribute that contains the data. Object is the address of the gadget object and Storageptr is the address of a variable to store the data in which can be a integer, string etc.

Here is a list of Objects and AttrIDs you can use, see Autodocs for other gadgets:

Object AttrID
Integer INTEGER_Number
String STRINGA_Buffer
Chooser CHOOSER_Selected
CheckBox CHECKBOX_Checked
GetFile GETFILE_FullFile
ListBrowser LISTBROWSER_Selected

Radio

RADIOBUTTON_Selected

Here are are a couple of examples:

GetAttr(CHOOSER_Selected, OBJ_CHOOSER1), &choosenum1); /* Get chooser selection and put number in choosenum1 */
printf("Choice 1 = %s\n", chooser1[choosenum1]); /* Print string value from a string array using value from choosenum1 */

GetAttr(INTEGER_Number, OBJ_INTEGER1, &int_value1); /* Get value from integer gadget and put number in int_value1 */
printf("Value1 = %ld\n", int_value1); /* Print integer value from int_value1 */

You can use these Get and Set macros from these definitions and add them to your own header files.

#undef get
#define get(obj, attr, store) IIntuition->GetAttr((attr), (obj), (ULONG *)(void *)(store))
#define getstring(obj, str) IIntuition->GetAttr(STRINGA_TextVal, obj, str) /* MUIA_String_Contents */
#define getcycle(obj, n) IIntuition->GetAttr(CHOOSER_Selected, obj, n) /* MUIA_Cycle_Active */
#define getcheckmark(obj, n) IIntuition->GetAttr(CHECKBOX_Checked, obj, n) /* MUIA_Selected */
#define getslider(obj, n) IIntuition->GetAttr(SLIDER_Level, obj, n) /* MUIA_Numeric_Value */

#undef set
#define set(obj, attr, value) IIntuition->SetAttrs((obj), (attr), value, TAG_DONE)
#define setcycle(obj, n) set(obj, CHOOSER_Selected, (n))
#define setstring(obj, s) set(obj, STRINGA_TextVal, (s))
#define setcheckmark(obj, b) set(obj, CHECKBOX_Checked, (b))
#define setslider(obj, l) set(obj, SLIDER_Level, (l))
/* Use these to update string and objects */
#define setgadstring(obj,win,s) IIntuition->SetGadgetAttrs((obj), (win), NULL, STRINGA_TextVal, (s), TAG_DONE)
#define setgadobjobj,win,attr,s) IIntuition->SetGadgetAttrs((obj), (win), NULL, (attr), (s), TAG_DONE)

g) Handling gadgets in the Window

To handle gadget events such as a user clicking on the Button or clicking a Check box etc, you need to tell your program how to pick this up and process it.
Use the GetAttr on the Window's sigmask to retreive the signal mask and then use Wait() to wait until a signal is received. The RA_HandleInput() function
handles any input from the window and returns a result which, if matches the WMHI_GADGETUP mask means that a gadget event has been recieved.
Use the result & WMHI_GADGETMASK to return the gadget ID. For example,

IIntuition->GetAttr(WINDOW_SigMask, win, &sigmask); // Get signal mask
while (running) //
{
siggot = IExec->Wait(sigmask | SIGBREAKF_CTRL_C); // Wait for next signal
if (siggot & SIGBREAKF_CTRL_C) running = FALSE;
while ((result = RA_HandleInput(win, &code))) // handle Reaction inputs
{
   switch (result & WMHI_CLASSMASK) // Handle all events
{

case WMHI_GADGETUP: // Is it a gadget?
    switch (result & WMHI_GADGETMASK) // Check which gadget used?
    {
    case ID_GAD1:
    { code }
    case ID_GAD2:
    { code }
    case ID_GAD3:
    { code }
  

 

Other example code: reaction_dn, allgadgets.

Example Reaction code on Aminet

Reaction menus