/*
 * MandelVroom 2.0
 *
 * (c) Copyright 1987,1989  Kevin L. Clague, San Jose, CA
 *
 * All rights reserved.
 *
 * Permission is hereby granted to distribute this program's source
 * executable, and documentation for non-comercial purposes, so long as the
 * copyright notices are not removed from the sources, executable or
 * documentation.  This program may not be distributed for a profit without
 * the express written consent of the author Kevin L. Clague.
 *
 * This program is not in the public domain.
 *
 * Fred Fish is expressly granted permission to distribute this program's
 * source and executable as part of the "Fred Fish freely redistributable
 * Amiga software library."
 *
 * Permission is expressly granted for this program and it's source to be
 * distributed as part of the Amicus Amiga software disks, and the
 * First Amiga User Group's Hot Mix disks.
 *
 * contents:  this file contains the code to open and close the color
 * cycling tool.  It also contains the functions that implement the color
 * cycling commands.
 */

#include "mandp.h"
#include <ctype.h>

#define PENLEFT 8
#define PENTOP  15

UBYTE CycleOpen;

/*
 * Holder for Allocated color potentiometer gadgets.
 */
struct Gadget *SpeedPot;
struct Gadget *DirGadget;
struct Gadget *CycleOnGadget;
struct Gadget *CycleOffGadget;

struct Window          *CycWind;

struct NewWindow NewCyc = {
   128,70,                   /* start position           */
   164,80,                   /* width, height            */
   (UBYTE) 0, (UBYTE) -1,    /* detail pen, block pen    */
   NULL,                     /* IDCMP flags */
                             /* MandWind flags */
   WINDOWCLOSE   | WINDOWDRAG | WINDOWDEPTH | NOCAREREFRESH | SMART_REFRESH,
   (struct Gadget *) NULL,   /* first gadget             */
   (struct Image *) NULL,    /* user checkmark           */
   (UBYTE *) "Cycling",      /* window title             */
   (struct Screen *) NULL,   /* pointer to screen        */
   (struct BitMap *) NULL,   /* pointer to superbitmap   */
   80,80,80,80,              /* sizing                   */
   CUSTOMSCREEN              /* type of screen           */
   };

int EditRange;
int RangeFirstPen;

SlideSpeedCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Gadget *gadget;

  gadget = (struct Gadget *) Msg->IAddress;

  switch( Msg->Class ) {

    case GADGETDOWN:                          /* Start Speed slide    */
         if (CurPict->CycleOn == 0)
           CreateCycle();
         ModifyIDCMP(CycWind,(long) CycWind->IDCMPFlags | MOUSEMOVE);
         State = SLIDESPEEDSTATE;
         break;

    case MOUSEMOVE:                           /* change the speed */
         SetSpeed();
         break;

    case GADGETUP:                            /* Stop Speed slide */
         if (CurPict->CycleOn == 0) {
           KillCycle();
           LoadRGB4(vp, CurPict->RGBs, Num_vp_Colors);
         }
         SetSpeed();
         ModifyIDCMP(CycWind,(long) CycWind->IDCMPFlags & ~MOUSEMOVE);
         State = IDLESTATE;
         break;
  }
}

CycleRangeCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Gadget  *gadget;
  int id;

  /* Need contour selection to complete */

  gadget = (struct Gadget *) Msg->IAddress;
  id = gadget->GadgetID;

  if (Msg->Class == GADGETDOWN) {
    if (id == CYCRANGE) {

      StopCycle();

      /* Color Palette command picks up the finish of the range cmd*/

      if (CurPen < Num_vp_Colors) {
        SetToPointer();
        RangeFirstPen = CurPen;
        State = CYCLERANGESTATE;
      } else {
        DispErrMsg("Can't set color cycle with hafbrite pens",0);
      }
    } else
    if (WIND_TYPE(id) == PALTYPE && GADG_TYPE(id) == PALPENS) {

      FinishRangeCmd(GADG_NUM(id));
      State = IDLESTATE;
    }
  }
}

ToggleDirCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Gadget *gadget = (struct Gadget *) Msg->IAddress;

  StopCycle();

  /* Set Direction into current cycle info structure */

  if (gadget->Flags & SELECTED) {
    CurPict->Crngs[ EditRange ].active &= ~REVERSE;
  } else {
    CurPict->Crngs[ EditRange ].active |= REVERSE;
  }
  State = IDLESTATE;
}

CycleOnOffCmd(Msg)
  struct IntuiMessage *Msg;
{
  if (CurPict == NULL)
    return;

  if (CurPict->CycleOn) {

    StopCycle();
  } else {

    CreateCycle();
    CurPict->CycleOn = 1;
    SetOnString(1);
  }
  State = IDLESTATE;
}

StopCycle()
{
  if (CurPict->CycleOn == 1) {
    KillCycle();
    LoadRGB4(vp, CurPict->RGBs, Num_vp_Colors);
    CurPict->CycleOn = 0;
    SetOnString( 0 );
  }
}

SelRangeCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Gadget *gadget = (struct Gadget *) Msg->IAddress;
  int    id = gadget->GadgetID;
  int    newpen;
  static int end;

  if (WIND_TYPE(id) == CYCTYPE && GADG_TYPE(id) == CYCRNUMS) {

    EditRange = GADG_NUM(id);

    StopCycle();

    /* move current pen to this range's first pen */

    BoxPen( CurPen, NORMALPEN );

    /* toggle endpoint to display */

    if (end ^= 1) {
      CurPen = CurPict->Crngs[ EditRange ].low;
    } else {
      CurPen = CurPict->Crngs[ EditRange ].high;
    }

    BoxPen( CurPen, MEDIUMPEN );

    ModifySpeedPot();

    SetDirGadget( CurPict );

    ExcludeGadgets( gadget );
  }
}

SetDirGadget( Pict )
  struct Picture *Pict;
{
  int place;

  if (Pict) {

    if (CycWind) {

      place = RemoveGadget(CycWind,DirGadget);

      if (Pict->Crngs[EditRange].active & REVERSE) {
        DirGadget->Flags &= ~SELECTED;
      } else {
        DirGadget->Flags |=  SELECTED;
      }
      AddGadget( CycWind, DirGadget, place);
      RefreshGList( DirGadget, CycWind, NULL, 1);
    }
  }
}

ModifySpeedPot()
{
  int place;

  if (CycWind) {
    /* ModifyProp to match EditRange's CycleSpeed */

    place = RemoveGadget(CycWind,SpeedPot);

    ((struct PropInfo *) SpeedPot->SpecialInfo)->HorizPot =
      CurPict->Crngs[ EditRange ].rate << 1;

    AddGadget( CycWind, SpeedPot, place);
    RefreshGList( SpeedPot, CycWind, NULL, 1);
  }
}

ExcludeGadgets( gadget )
  register struct Gadget *gadget;
{
  register struct Gadget *NextGadget;
  register place;

  NextGadget = gadget;

  do {

    place = RemoveGadget( CycWind, NextGadget );

    if (NextGadget == gadget) {
      NextGadget->Flags |=  SELECTED;
    } else {
      NextGadget->Flags &= ~SELECTED;
    }

    AddGadget( CycWind, NextGadget, place );
    RefreshGList( NextGadget, CycWind, NULL, 1);

    NextGadget = (struct Gadget *) NextGadget->UserData;

  } while ( NextGadget && NextGadget != gadget );
}

static struct PropInfo *PropInfo;

SetSpeed()
{
  extern LONG cyClocks[];

  CurPict->Crngs[EditRange].rate = PropInfo->HorizPot >> 1;
  cyClocks[EditRange] = 0;
}

FinishRangeCmd(pen)
  int pen;
{
  int t;

  if (CurPict == NULL)
    return;

  t = Num_vp_Colors - 1;

  if (RangeFirstPen > t || pen > t)
    return;

  if (RangeFirstPen > pen) {

    t = pen;
    pen = RangeFirstPen;
    RangeFirstPen = t;

    CurPict->Crngs[EditRange].active |= REVERSE;

  } else {

    CurPict->Crngs[EditRange].active &= ~REVERSE;
  }

  SetDirGadget( CurPict );

  CurPict->Crngs[EditRange].low  = RangeFirstPen;
  CurPict->Crngs[EditRange].high = pen;

  if (pen == RangeFirstPen) {
    CurPict->Crngs[EditRange].active &= ~1;
  } else {
    CurPict->Crngs[EditRange].active |=  1;
  }
}

SetOnString( OnOff )
{
  int place;
  register struct Gadget *SrcGadget,*DstGadget;

  if (CycWind == NULL)
    return;

  if ( OnOff ) {
    SrcGadget = CycleOffGadget; /* Make it turn on */
    DstGadget = CycleOnGadget;
  } else {
    SrcGadget = CycleOnGadget;
    DstGadget = CycleOffGadget;
  }

  if (SrcGadget->NextGadget != NULL) {
    place = RemoveGadget( CycWind, SrcGadget );
    SrcGadget->NextGadget = NULL;

    AddGadget( CycWind, DstGadget, place );

    SetAPen( CycWind->RPort, NORMALPEN );
    RectFill( CycWind->RPort, SrcGadget->LeftEdge,  SrcGadget->TopEdge,
                              SrcGadget->LeftEdge + SrcGadget->Width,
                              SrcGadget->TopEdge +  SrcGadget->Height);
    RefreshGList( DstGadget, CycWind, NULL, 1);
  }
}

static struct Border *PotBorder;

/*
 * Allocate all the gadgets for the color palette window
 */
struct Gadget *MakeCycle()
{
           struct Gadget *FirstGadget;
  register struct Gadget *NextGadget;
  register LONG i,Left,x,y,c = 0;

  struct Gadget *ExcludeGadget, *PrevGadget;

  struct Image *Image0;
  struct Image *Image1;
  struct IntuiText *Text, *NextText;

  extern struct Image Arrow_Image[];

  LONG fourx = 4 << XScale;
  LONG foury = 4 << YScale;

  char *str;

  Left = PENLEFT;

  FirstGadget = NextGadget = MakePot( 84, 35, 64, 4, CYCSPEED, y);

  if ( NextGadget == NULL ) goto error;

  SpeedPot = NextGadget;

  NextGadget->Activation = GADGIMMEDIATE | FOLLOWMOUSE | RELVERIFY;
  NextGadget->GadgetText = Text = ShadowIntui("Speed",-46, -3);

  if ( Text == NULL ) goto error;

  Image0 = (struct Image *) NextGadget->GadgetRender;
  Image0->Width  = 4;
  Image0->Height = 4;
  Image0->PlaneOnOff = NORMALPEN;

  PropInfo = (struct PropInfo *) NextGadget->SpecialInfo;
  PropInfo->Flags = PROPBORDERLESS | FREEHORIZ;
  PropInfo->HorizBody = 0xff;

  PotBorder = ShadowBorder( BEVELEDDOWN, 83, 34, 65, 5);

  NextGadget->NextGadget =
                     MakeBool( LEFTMARG+1, 11, 48, 13, 1, CYCRANGE, NULL);

  NextGadget = NextGadget->NextGadget;

  if ( NextGadget == NULL) goto error;

  NextGadget->GadgetText = ShadowIntui("Range", 4,3);

  NextGadget->NextGadget = MakeBool( 151, 32,16, 8, 4, CYCDIR, GADGIMAGE);

  NextGadget = NextGadget->NextGadget;

  if ( NextGadget == NULL) goto error;

  DirGadget = NextGadget;

  NextGadget->Flags = GADGHIMAGE | GADGIMAGE | SELECTED;
  NextGadget->Activation |= TOGGLESELECT;

  Image0 = (struct Image *) NextGadget->GadgetRender;
  Image1 = (struct Image *) Image0->NextImage;
  Image0->NextImage  = NULL;

  NextGadget->SelectRender = (APTR) Image1;

  SetupArrow(Image0, &Arrow_Image[0]);
  SetupArrow(Image1, &Arrow_Image[1]);

  CycleOffGadget = MakeBool( 3, 28, 30, 13, 1, CYCON, NULL);

  if ( CycleOffGadget == NULL) goto error;

  CycleOffGadget->GadgetText = ShadowIntui("Off", 4,4);

  CycleOnGadget = MakeBool( 3, 28, 30, 13, 1, CYCON, NULL);

  if ( CycleOnGadget == NULL) goto error;

  CycleOnGadget->GadgetText = ShadowIntui("On", 8,4);

  if (CurPict && CurPict->CycleOn) {

    NextGadget->NextGadget = CycleOnGadget;
  } else {
    NextGadget->NextGadget = CycleOffGadget;
  }

  NextGadget = NextGadget->NextGadget;

  for (c = 0, x = 0; c < 4; c++, x += 28) {

    NextGadget->NextGadget =
      MakeBool( 55 + x, 11, 24, 13, 1, CYCRNUM+c, NULL);

    NextGadget = NextGadget->NextGadget;

    if ( NextGadget == NULL ) goto error;

    NextGadget->UserData = (APTR) PrevGadget;

    PrevGadget = NextGadget;

    NextGadget->Flags = GADGHIMAGE;
    NextGadget->Activation |= TOGGLESELECT;
    NextGadget->SelectRender =
        (APTR) ShadowBorder( BEVELEDDOWN, 1, 1, 23, 12);

    if (c == 0) {
      ExcludeGadget = NextGadget;
      NextGadget->Flags |= SELECTED;
      EditRange = 0;
    }

    switch (c) {
      case 0:  NextGadget->GadgetText = ShadowIntui("c1", 4,3); break;
      case 1:  NextGadget->GadgetText = ShadowIntui("c2", 4,3); break;
      case 2:  NextGadget->GadgetText = ShadowIntui("c3", 4,3); break;
      case 3:  NextGadget->GadgetText = ShadowIntui("c4", 4,3);
    }
  }
  ExcludeGadget->UserData = (APTR) NextGadget;

  return(FirstGadget);

error:
  FreeGadgets( FirstGadget );
  return( NULL );
} /* MakeCycle */

SetupArrow( ImageDst, ImageSrc,  )
  register struct Image *ImageDst;
  register struct Image *ImageSrc;
{
  ImageDst->LeftEdge   = ImageDst->TopEdge = 0;
  ImageDst->Width      = 16;
  ImageDst->Height     = 9;
  ImageDst->ImageData  = ImageSrc->ImageData;
  ImageDst->Depth      = 1;
  ImageDst->PlanePick  = NORMALPEN;
  ImageDst->PlaneOnOff = 0;
}

static struct Gadget *CycGadgets;

/*
 * Open the Cycle window
 */
OpenCycWind()
{
  struct Window *OpenMyWind();

  register struct Gadget *gadget;
  register struct RastPort *Rp;

  if (CurPict == NULL)
    return;

  if ( CycWind == NULL ) {

    gadget = MakeCycle();

    if ( gadget == NULL ) {
      DispErrMsg("Couldn't get palette gadgets", 0 );
      return;
    }

    CycWind = OpenMyWind( &NewCyc, screen, NULL, 168, 46);

    if ( CycWind != NULL ) {

      Rp = CycWind->RPort;

      SetAPen( Rp, NORMALPEN );
      RectFill( Rp, LEFTMARG, TOPMARG, CycWind->Width, CycWind->Height);

      BorderWindow( CycWind );

      CycGadgets = gadget;

      AddGList( CycWind, gadget, -1, -1);

      RefreshGadgets( gadget, CycWind, NULL );
      DrawBorder( Rp, PotBorder, 0L, 0L);
      FreeBorder(PotBorder);
    }
  } else {
    WindowToFront( CycWind );
  }
  CycleOpen = 1;
} /* OpenCycWind */

/*
 * Close the Cycle window
 */
CloseCycWind()
{
  register struct Gadget *FreeGadget;
  if (CycWind != NULL) {

    NewCyc.LeftEdge = CycWind->LeftEdge;
    NewCyc.TopEdge  = CycWind->TopEdge;

    if (CycleOffGadget->NextGadget == NULL) {
      FreeGadgets( CycleOffGadget );
    } else {
      FreeGadgets( CycleOnGadget );
    }

    CloseMyWind(CycWind,CycGadgets);
    CycGadgets = NULL;
  }
  CycWind = NULL;
} /* CloseCycWind */

