/***************************************************************************
 * demo.c   - GadTools demo program                                        *
 *            Written by Paul Miller for The AmigaWorld Tech Journal       *
 *            04-03-91                                                     *
 *-------------------------------------------------------------------------*
 * Special Requirements: WorkBench 2.0, version 36 or above                *
 ***************************************************************************/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/lists.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>

#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/exec_protos.h>

/* some constant definitions */
#define MIN_VERSION     36L      /* minimum version number for our libs */

/* Gadget ID's */
#define BUTTON_ID    0
#define CYCLE_ID     1
#define STRING_ID    2
#define LIST_ID      3
#define MX_ID        4
#define SLIDER_ID    5
#define CHECK_ID     6
#define INTEGER_ID   7
#define INCREMENT_ID 8
#define PALETTE_ID   9

#define SLIDER_MIN   1
#define SLIDER_MAX   31

/* libraries we'll need to open */
struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct Library *GadToolsBase = NULL;

struct Screen *screen = NULL;
struct Window *window = NULL;
struct Gadget *glist = NULL;     /* Gadget list pointer */
struct List list;                /* we need this for the ListView Gadget */
struct Remember *rmem = NULL;    /* Intuition memory allocation for List */
void *vi = NULL;                 /* VisualInfo pointer */

struct Gadget *intgad;           /* a pointer to our integer gadget later */
long value = 42;

/* It's possible to use the default font with GadTools, but extra
   computations must be made to get the size and positions of the Gadgets
   right. We'll want to use Topaz-80 for simplicity. */

struct TextAttr topaz80 = {
   "topaz.font", 8, 0, 0
};

/* here we declare the information for our new window - notice that GadTools
   provides some constants which describe which IDCMP events the Window will
   need to handle the GadTools Gadgets properly. Just OR them into your
   normal IDCMPFlags field.
*/

struct NewWindow new_window = {
   50, 20, 510, 150,
   -1, -1,              /* DetailPen, BlockPen */
   LISTVIEWIDCMP | SLIDERIDCMP | CHECKBOXIDCMP | MXIDCMP | CLOSEWINDOW |
      REFRESHWINDOW,
   ACTIVATE | WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE | SIMPLE_REFRESH,
   NULL,                /* FirstGadget */
   NULL,                /* CheckMark */
   "GadTools Demo",
   NULL,                /* Screen */
   NULL,                /* BitMap */
   0, 0, 0, 0,          /* min/max sizes */
   WBENCHSCREEN
};

/* here is data for some of our Gadgets */
char *day_labels[] = {
   "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
   "Saturday", NULL
};
char *month_labels[] = {
   "January", "February", "March", "April", "May", "June", "July",
   "August", "September", "October", "November", "December", NULL
};

char textbuffer[20];    /* for displaying Gadget event information */

/* function prototypes */
void main(void);
void abort(char *);
void setup(void);
void closedown(void);
void create_gadgets(void);
void handle_input(void);
void handle_gadgetup(struct Gadget *, UWORD);
void handle_gadgetdown(struct Gadget *, UWORD);

void main()
{
   setup();
   create_gadgets();

   handle_input();
}

void abort(txt)
char *txt;
{
   puts(txt);
   closedown();
   exit(0);
}

void setup(void)
{
   /* open ze libraries */
   GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", MIN_VERSION);
   if (!GfxBase)
      abort("Can't open graphics.library. WB Version 36 or above required.");
   IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",
                                                       MIN_VERSION);
   if (!IntuitionBase)
      abort("Can't open intuition.library. WB Version 36 or above required.");
   GadToolsBase = (struct Library *)OpenLibrary("gadtools.library",
                                                MIN_VERSION);
   if (!GadToolsBase)
      abort("Can't open gadtools.library. WB Version 36 or above required.");

   /* get access to the WorkBench screen */
   screen = LockPubScreen(NULL);
   if (!screen)
      abort("Couldn't lock the default public screen.");

   /* get the screen's visual information data */
   vi = GetVisualInfo(screen, TAG_DONE);
   if (!vi)
      abort("Couldn't get default public screen's VisualInfo.");

   /* now open up a Window on the WorkBench */
   window = OpenWindowTags(&new_window,
                           WA_PubScreen, screen,
                           TAG_DONE);
   if (!window)
      abort("Couldn't open the new Window.");
}

void closedown(void)
{
   if (window) CloseWindow(window);

   /* these two functions can take a NULL, but let's stay consistent */
   if (glist) FreeGadgets(glist);
   if (vi) FreeVisualInfo(vi);

   if (screen) UnlockPubScreen(NULL, screen);
   if (rmem) FreeRemember(&rmem, TRUE);

   if (GadToolsBase) CloseLibrary(GadToolsBase);
   if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
   if (GfxBase) CloseLibrary((struct Library *)GfxBase);
}

void create_gadgets()
{
   UWORD top;              /* offset into Window under titlebar */
   struct NewGadget ng;    /* for Gadget positioning */
   struct Gadget *gad;     /* our running Gadget pointer */
   struct Node *node;      /* for our LISTVIEW list allocation */
   int index;              /* ditto */

   /* let's determine the top Window border height (taking into account
      whatever system font has been used to render the titlebar) so we can
      place the Gadgets properly within the Window. Overwriting of the
      titlebar is possible if you're not careful. */
   top = window->BorderTop + 1;

   /* this initial call is required when using the GadTool toolkit. It
      gives the toolkit a place to keep track of Gadget context information,
      and also forms a starting point to begin the Gadget creation with.
      Each Gadget creation call requires a pointer to the previous Gadget
      as one of its arguments, and this pointer is used for the first one.
      The Gadgets are automatically linked with this facility. Also, there
      is no need to check the returned Gadget pointer until it is actually
      used, such as if the Gadget data need be referenced, and of course
      before the Gadget list is added to a Window. */

   /* we pass the ADDRESS of our Gadget list pointer, so the toolkit can
      allocate some memory there */
   gad = CreateContext(&glist);

   /* now we can fill out the NewGadget structure to describe where we want
      the Gadget to be placed */

   /* create a centered read-only Gadget */
   ng.ng_LeftEdge = window->Width / 2;
   ng.ng_TopEdge = top + 4;
   ng.ng_Width = 0;           /* this is computed automatically */
   ng.ng_Height = 8;
   ng.ng_GadgetText = "GadTools Toolkit Demo";
   ng.ng_TextAttr = &topaz80;                      /* this is required!! */
   ng.ng_GadgetID = 0;
   ng.ng_Flags = PLACETEXT_IN | NG_HIGHLABEL;
   ng.ng_VisualInfo = vi;                          /* this is required!! */

   /* note the specified flags - see gadtools.h for additional flags and
      their descriptions */
   gad = CreateGadget(TEXT_KIND, gad, &ng, TAG_DONE);

   /* the NewGadget stucture is not modified by any Gadget creation call,
      so you'll only need to change information that actually needs to be
      changed */
   ng.ng_LeftEdge = 10;
   ng.ng_TopEdge = 5+top;
   ng.ng_Width = 100;
   ng.ng_Height = 12;
   ng.ng_GadgetText = "QUIT DEMO";
   ng.ng_GadgetID = BUTTON_ID;
   ng.ng_Flags = NULL;
   gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);

   /* prepare a List for the LISTVIEW Gadget below */
   NewList(&list);
   index = 0;
   while (month_labels[index])
   {
      node = (struct Node *)AllocRemember(&rmem, sizeof(struct Node),
                                          MEMF_CLEAR);
      if (!node)
         abort("Couldn't allocate LISTVIEW List.");
      node->ln_Name = month_labels[index++];
      AddTail(&list, node);
   }

   /* we'll create a string Gadget to be attached to the LISTVIEW below */
   ng.ng_Width = 150;
   ng.ng_Height = 14;
   ng.ng_GadgetText = NULL;
   ng.ng_GadgetID = STRING_ID;
   gad = CreateGadget(STRING_KIND, gad, &ng,
                        GTST_MaxChars, 50,
                        TAG_DONE);

   /* a LISTVIEW gadget - note that it works using an EXEC List */
   ng.ng_LeftEdge = 10;
   ng.ng_TopEdge = 40+top;
   ng.ng_Width = 150;
   ng.ng_Height = 57;
   ng.ng_GadgetText = "Months:";
   ng.ng_GadgetID = LIST_ID;
   ng.ng_Flags = NG_HIGHLABEL | PLACETEXT_ABOVE;
   gad = CreateGadget(LISTVIEW_KIND, gad, &ng,
                        GTLV_Labels, &list,
                        GTLV_Top, 1,
                        GTLV_ShowSelected, gad,    /* String Gadget */
                        GTLV_Selected, 3,
                        GTLV_ScrollWidth, 18,
                        TAG_DONE);

   /* a cycle gadget */
   ng.ng_LeftEdge = 50;
   ng.ng_TopEdge = 110+top;
   ng.ng_Width = 100;
   ng.ng_Height = 12;
   ng.ng_GadgetText = "Day:";
   ng.ng_GadgetID = CYCLE_ID;
   ng.ng_Flags = NG_HIGHLABEL | PLACETEXT_LEFT;
   gad = CreateGadget(CYCLE_KIND, gad, &ng,
                        GTCY_Labels, day_labels,
                        GTCY_Active, 1,
                        TAG_DONE);

   /* a set of mutually-exclusive Gadgets which performs the same function
      as the CYCLE Gadget above -- if you have 5 or less items to select
      from, the smaller CYCLE Gadget would be a better choice -- the user
      doesn't want to have to click through too many choices -- if you
      have room go with a MX Gadget -- it's more visual as to the choices */
   ng.ng_LeftEdge = 260;
   ng.ng_TopEdge = 25+top;
   ng.ng_GadgetID = MX_ID;
   ng.ng_Flags = PLACETEXT_LEFT;
   gad = CreateGadget(MX_KIND, gad, &ng,
                        GTMX_Labels, day_labels,
                        GTMX_Active, 5,
                        GTMX_Spacing, 3,
                        TAG_DONE);

   /* here we'll create a SLIDER with an automatic value display */
   ng.ng_LeftEdge = 300;
   ng.ng_TopEdge = 120+top;
   ng.ng_Width = 180;
   ng.ng_Height = 12;
   ng.ng_GadgetText = "DAY:  ";
   ng.ng_GadgetID = SLIDER_ID;
   ng.ng_Flags = PLACETEXT_LEFT;
   gad = CreateGadget(SLIDER_KIND, gad, &ng,
                        GTSL_Min, SLIDER_MIN,
                        GTSL_Max, SLIDER_MAX,
                        GTSL_Level, 1,
                        GTSL_LevelFormat, "%2ld",  /* don't forget the 'l'*/
                        GTSL_MaxLevelLen, 2,
                        GA_RELVERIFY, TRUE,
                        TAG_DONE);

   /* a CHECKBOX */
   ng.ng_LeftEdge = window->Width - 40;
   ng.ng_TopEdge = 10+top;
   ng.ng_GadgetID = CHECK_ID;
   ng.ng_GadgetText = "Check Me:";
   ng.ng_Flags = PLACETEXT_LEFT;
   gad = CreateGadget(CHECKBOX_KIND, gad, &ng, TAG_DONE);

   /* an integer Gadget for cosmic significance */
   ng.ng_TopEdge = 30+top;
   ng.ng_LeftEdge = window->Width - 50;
   ng.ng_Width = 40;
   ng.ng_Height = 14;
   ng.ng_GadgetText = "Cosmic Significance:";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL;
   ng.ng_GadgetID = INTEGER_ID;
   intgad = gad = CreateGadget(INTEGER_KIND, gad, &ng,
                        GTIN_Number, value,
                        GTIN_MaxChars, 4,
                        TAG_DONE);

   /* and make a button to play with the integer Gadget with */
   ng.ng_LeftEdge = window->Width - 110;
   ng.ng_TopEdge = 50+top;
   ng.ng_Width = 100;
   ng.ng_Height = 12;
   ng.ng_GadgetText = "INCREMENT";
   ng.ng_GadgetID = INCREMENT_ID;
   ng.ng_Flags = NULL;
   gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);

   /* neat-o! make a ready-to-go palette selector Gadget */
   ng.ng_LeftEdge = window->Width - 160;
   ng.ng_TopEdge = 80+top;
   ng.ng_Width = 150;
   ng.ng_Height = 30;
   ng.ng_GadgetText = "Our Colors";
   ng.ng_GadgetID = PALETTE_ID;
   ng.ng_Flags = PLACETEXT_ABOVE | NG_HIGHLABEL;
   gad = CreateGadget(PALETTE_KIND, gad, &ng,
                        GTPA_Depth, screen->BitMap.Depth,
                        GTPA_Color, 1,
                        GTPA_IndicatorWidth, 20,
                        TAG_DONE);


   /* now we're ready to add the Gadget list. Quit if the final Gadget
      pointer is NULL - this means that one of the Gadget allocations failed.
      If it exists, add it to the window and refresh, then add the new
      GT_RefreshWindow() call. */

   if (!gad)
      abort("Couldn't allocate the Gadget list.");

   AddGList(window, glist, (UWORD)-1, (UWORD)-1, NULL);
   RefreshGList(glist, window, NULL, (UWORD)-1);
   GT_RefreshWindow(window, NULL);
}

void handle_input(void)
{
   struct IntuiMessage *message;
   struct Gadget *gadget;
   ULONG class;
   UWORD code;

   while (1)
   {
      WaitPort(window->UserPort);

      /* use the new GadTools GT_GetIMsg() function to get input events */
      while (message = GT_GetIMsg(window->UserPort))
      {
         class = message->Class;
         code = message->Code;

         /* we'll assume it's a Gadget, but no harm is done if it isn't */
         gadget = (struct Gadget *)message->IAddress;

         /* use the GT_ReplyIMsg() function to reply to the message */
         GT_ReplyIMsg(message);

         switch (class)
         {
            case CLOSEWINDOW:
               abort("Done.");
            case GADGETUP:
               handle_gadgetup(gadget, code);
               break;
            case GADGETDOWN:
               handle_gadgetdown(gadget, code);
               break;
            case REFRESHWINDOW:
               /* when using a window of type SIMPLE_REFRESH, use this
                  input event and GadTools-compatible refreshing code. */
               GT_BeginRefresh(window);
               GT_EndRefresh(window, TRUE);
               break;
         }
      }
   }
}

void handle_gadgetup(gadget, code)
struct Gadget *gadget;
UWORD code;
{
   switch (gadget->GadgetID)
   {
      case BUTTON_ID:
         abort("QUIT.");
         break;
      case CYCLE_ID:
         printf("CYCLE GADGET: item=%s\n", day_labels[code]);
         break;
      case STRING_ID:
         printf("LISTVIEW STRING GADGET: string=%s\n",
                ((struct StringInfo *)gadget->SpecialInfo)->Buffer);
         break;
      case LIST_ID:
         printf("LISTVIEW GADGET: item=%s\n", month_labels[code]);
         break;
      case SLIDER_ID:
         printf("SLIDER GADGET: %d\n", code);
         break;
      case CHECK_ID:
         printf("CHECKBOX GADGET: state=");
         if (gadget->Flags & SELECTED)
            printf("ON\n");
         else
            printf("OFF\n");
         break;
      case INTEGER_ID:
         printf("INTEGER GADGET: value=%ld\n",
               ((struct StringInfo *)gadget->SpecialInfo)->LongInt);
         break;
      case INCREMENT_ID:
         /* here we use a Tag-based Gadget attributes modifying function --
            just pass the appropriate Tags you want modified */
         GT_SetGadgetAttrs(intgad, window, NULL,
                           GTIN_Number, ++value,
                           TAG_DONE);

         printf("INCREMENT INTEGER: value=%ld\n",
               ((struct StringInfo *)intgad->SpecialInfo)->LongInt);
         break;
      case PALETTE_ID:
         printf("PALETTE GADGET: color=%d\n", code);
         break;
   }
}

void handle_gadgetdown(gadget, code)
struct Gadget *gadget;
UWORD code;
{
   switch (gadget->GadgetID)
   {
      case MX_ID:
         /* seems this requires a GADGETDOWN event */
         printf("MX (MUTUAL EXCLUDE) GADGET: item=%s\n", day_labels[code]);
         break;
   }
}

