/*  File:         gui.c
 *  Created:      20-10-95
 *  Updated:      30-12-95
 *  Version:      1.0
 *  Project:      Clicker
 *  Owner:        Jeroen Vermeulen
 *  Requirements: KickStart V39+
 *  Legal:        PD
 *  Status:       Release
 */

#include <proto/exec.h>
#include <exec/memory.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <intuition/gadgetclass.h>

#include "gui.h"
#include "sound.h"
#include "prefs.h"


static STRPTR
        AllocFailWindow = "Couldn't allocate memory for prefs window!\n",
        OpenFailWindow  = "Couldn't open prefs window!\n",
        LockFailPubScr  = "Couldn't lock public screen\n",
        GetFailVisInfo  = "Couldn't get VisualInfo for screen\n",
        AllocFailGadget = "Unable to create gadgets for prefs window!\n";


/* Topaz-sensitive TextAttr structure.
 * The catchphrase "Topaze-sensitive" is a trademark of Hans Guijt.
 */
static struct TextAttr Topaz80 = { "topaz.font", 8, 0, 0 };


/* Constant GUI tags
 */
static const struct TagItem StaticFrequencyTags[] =
{
  {GTSL_Min,         -5*12},
  {GTSL_Max,         4*12},
  {GTSL_LevelFormat, (ULONG)"%4ld"},
  {GTSL_LevelPlace,  PLACETEXT_RIGHT},
  {GTSL_DispFunc,    (ULONG)SliderToHertz},
  {GTSL_MaxLevelLen, 4},
  {GA_Immediate,     TRUE},
  {TAG_END,          NULL}
},
StaticVolumeTags[] =
{
  {GTSL_Max,         64},
  {GTSL_LevelFormat, (ULONG)"%2ld"},
  {GTSL_LevelPlace,  PLACETEXT_RIGHT},
  {GTSL_MaxLevelLen, 4},
  {GA_Immediate,     TRUE},
  {TAG_END,          NULL}
},
StaticCycleTags[] =
{
  {GTSL_Min,         1},
  {GTSL_Max,         20},
  {GTSL_LevelFormat, (ULONG)"%2ld"},
  {GTSL_LevelPlace,  PLACETEXT_RIGHT},
  {GTSL_MaxLevelLen, 4},
  {GA_Immediate,     TRUE},
  {TAG_END,          NULL}
},
StaticPrefsWindowTags[] =
{
  {WA_Width,       250},
  {WA_Height,      100},
  {WA_Title,       (ULONG)"Clicker Prefs"},
  {WA_Flags,       WFLG_DRAGBAR | WFLG_DEPTHGADGET |
                   WFLG_CLOSEGADGET | WFLG_SMART_REFRESH |
                   WFLG_ACTIVATE | WFLG_NEWLOOKMENUS},
  {WA_IDCMP,       SLIDERIDCMP | CHECKBOXIDCMP | BUTTONIDCMP |
                   IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW},
  {TAG_END,        NULL}
};



/* MakeWindow():
 * Sets up the prefs window, but doesn't show it unless the Show argument is
 * TRUE.  If successful, MakeWindow() returns a pointer to a WindowContext
 * structure.  If not, NULL is returned and the STRPTR pointed to by errptr will
 * point to an error string.
 * The window must later be deallocated with DestroyWindow().
 */
struct WindowContext *MakeWindow(STRPTR *const errptr, const BOOL Show)
{
  struct WindowContext *WinStruct;
  /* --- */
  if ((WinStruct = AllocMem(sizeof(struct WindowContext),MEMF_ANY|MEMF_CLEAR)))
  {
    if (Show) ShowWindow(errptr,WinStruct);
  }
  else *errptr = AllocFailWindow;

  return WinStruct;
}

/* ShowWindow():
 * Reveal prefs window, setting it up first if necessary.  A non-NULL pointer to
 * its WindowContext structure is passed in as an argument.
 * If the window fails to open, errptr will point to an error string.
 */
void ShowWindow(STRPTR *const errptr, struct WindowContext *const WinStruct)
{
  if (!WinStruct->Shown)
  {
    struct Screen *pubscr;
    if (!(WinStruct->pubscr = pubscr = LockPubScreen(NULL)))
    {
      *errptr = LockFailPubScr;
      return;
    }
    if ((WinStruct->visinfo = GetVisualInfoA(pubscr,NULL)))
    {
      struct Gadget *gad;
      /* --- */
      if ((gad = CreateContext(&WinStruct->glist)))
      {
        struct TagItem ClickMouseTags[] =
        {
          {GTCB_Checked,   FALSE},  /* Set to ClickPrefs.ClickMouse later! */
#ifdef    NOCLICKMOUSE
          {GA_Disabled,    TRUE},   /* Mouse click permanently disabled */
#endif /* NOCLICKMOUSE */
          {TAG_END,        NULL}
        };

        struct NewGadget gadget =
        {
          10, 0, 26, 11,
          "Click Mouse button",
          &Topaz80,
          mygadget_clickmouse,
          PLACETEXT_RIGHT,
          NULL,
          NULL
        };

        /* --- */

        gadget.ng_TopEdge = 12 + pubscr->WBorTop + pubscr->Font->ta_YSize+1;
        gadget.ng_VisualInfo = WinStruct->visinfo;

#ifndef   NOCLICKMOUSE
        ClickMouseTags[0].ti_Data = ClickPrefs.ClickMouse;
#endif /* NOCLICKMOUSE */

        gad = CreateGadgetA(CHECKBOX_KIND, gad, &gadget,ClickMouseTags);

        gadget.ng_Width = 100;
        gadget.ng_LeftEdge = 90;
        gadget.ng_Flags = 0;

        gadget.ng_TopEdge += 12;
        gadget.ng_GadgetText = "Pitch";
        gadget.ng_GadgetID = mygadget_period;

        /* The gadget is supposed to show frequency in Hz, but the actual slider
         * positions are logarithmic.  Hitting the A at 440 Hz is the hard one.
         * A 12-tone scale over 9 octaves is used; frequency can be determined
         * from the slider position by the formula
         *
         *                      Freq = 440 * 2^(n/12)
         *
         * It thus ranges from the A at 13.75 Hz to the one at 7040 Hz.  This
         * conversion is performed by SliderToHertz().
         */
        gad = CreateGadget(SLIDER_KIND, gad, &gadget,
                           GTSL_Level,     PeriodToSlider(ClickPrefs.period),
                           TAG_MORE,       (ULONG)StaticFrequencyTags);

        gadget.ng_TopEdge += 12;
        gadget.ng_GadgetText = "Volume";
        gadget.ng_GadgetID = mygadget_volume;

        gad = CreateGadget(SLIDER_KIND, gad, &gadget,
                           GTSL_Level,     ClickPrefs.volume,
                           TAG_MORE,       (ULONG)StaticVolumeTags);

        gadget.ng_TopEdge += 12;
        gadget.ng_GadgetText = "Length";
        gadget.ng_GadgetID = mygadget_cycles;

        gad = CreateGadget(SLIDER_KIND, gad, &gadget,
                           GTSL_Level,     ClickPrefs.cycles,
                           TAG_MORE,       (ULONG)StaticCycleTags);

        if (gad)
        {
          WinStruct->Win = OpenWindowTags(NULL,
                                 WA_Gadgets,    (ULONG)WinStruct->glist,
                                 TAG_MORE,      (ULONG)StaticPrefsWindowTags);
          if (WinStruct->Win)
          {
            GT_RefreshWindow(WinStruct->Win,NULL);
            WinStruct->Shown = TRUE;
            WinStruct->SigMask = 1L<<WinStruct->Win->UserPort->mp_SigBit;
          }
          else
          {
            FreeGadgets(gad);
            gad = WinStruct->glist = NULL;
            *errptr = OpenFailWindow;
          }
        }
        else *errptr = AllocFailGadget;
      }
      if (!gad)
      {
        FreeVisualInfo(WinStruct->visinfo);
        WinStruct->visinfo = NULL;
      }
    }
    else *errptr = GetFailVisInfo;
    if (!WinStruct->visinfo)
    {
      UnlockPubScreen(NULL,pubscr);
      WinStruct->pubscr = NULL;
    }
  }
}


/* HideWindow():
 * Removes the prefs window from the screen, but may keep its structures around
 * for speed and convenience.
 */
void HideWindow(struct WindowContext *const WinStruct)
{
  if (WinStruct && WinStruct->Shown)
  {
    CloseWindow(WinStruct->Win);
    FreeGadgets(WinStruct->glist);
    FreeVisualInfo(WinStruct->visinfo);
    UnlockPubScreen(NULL,WinStruct->pubscr);
    WinStruct->Shown = FALSE;
    WinStruct->SigMask = 0;
    WinStruct->visinfo = NULL;
    WinStruct->glist = NULL;
    WinStruct->pubscr = NULL;
  }
}


/* DestroyWindow():
 * Call this to deallocate the prefs window.  It will be closed first if
 * necessary.
 */
void DestroyWindow(struct WindowContext *const WinStruct)
{
  HideWindow(WinStruct);
}

