/*
 *  CELLS       An Implementation of the WireWorld cellular automata
 *              as described in Scientific American, Jan 1990.
 *
 *              Copyright 1990 by Davide P. Cervone.
 *  You may use this code, provided this copyright notice is kept intact.
 *  See the CELLS.HELP file for complete information on distribution conditions.
 */

/*
 *  File:  cDoParts.c               Handle the parts requesters.
 */


#include "cGadget.h"
#include "cRequest.h"
#include "cReqNP.h"
#include "cReqLP.h"
#include "cParts.h"

extern LIBRARY *RemoveListItem();

extern char NPNameBuf[];        /* the new part name */
extern char LPNameBuf[];        /* the part name to load */
extern int Viewing;             /* TRUE if parts requester is in VIEW mode */

#define LPGADGET    LPRequest.er_Request.ReqGadget


/*
 *  NPGadgetUP()
 *
 *  Handle the New Part requester's gadget up events:
 *
 *  OK and NAME:
 *    remove the requester and put the PART button back to normal.
 *    If the part was able to be created, cancel the selection.
 *
 *  CANCEL:
 *   remove the requester and put the PARTS button back to normal.
 */

static void NPGadgetUp(theGadget,theMessage)
struct Gadget *theGadget;
struct IntuiMessage *theMessage;
{
   switch(theGadget->GadgetID)
   {
      case NP_OK:
      case NP_NAME:
         RemoveRequest(&NPRequest);
         InvertGadget(&cGadget[ID_PART]);
         if (MakePart(NPNameBuf)) CancelSelect();
         break;

      case NP_CANCEL:
         InvertGadget(&cGadget[ID_PART]);
         RemoveRequest(&NPRequest);
         break;
   }
}


/*
 *  AddPart()
 *
 *  If the New Part requester is not already showing,
 *    clear the part name buffer
 *    Invert the PART gadget and put up the requester.
 *    If the requester could not be openned, put the button back to normal.
 */

void AddPart()
{
   if (ActiveRequest != &NPRequest)
   {
      NPNameBuf[0] = 0;
      InvertGadget(&cGadget[ID_PART]);
      AddRequest(&NPRequest,NULL,NPGadgetUp,NULL);
      if (ActiveRequest == NULL) InvertGadget(&cGadget[ID_PART]);
   }
}


/*
 *  FindPart()
 *
 *  If a library and a part name were specified,
 *    starting at the first part in the list,
 *    while there are still more parts and we have not found the right one,
 *      if the name lengths are equal, check to see if they are equal
 *      If not, go on to the next part
 *  If the part was not found, and we are displaying errors, do so.
 */

static PART *FindPart(Name,theLibrary,ShowError)
char *Name;
LIBRARY *theLibrary;
int ShowError;
{
   PART *thePart = NULL;
   int NotFound = TRUE;
   UWORD theSize;
   
   if (theLibrary && Name && Name[0])
   {
      thePart = theLibrary->FirstPart;
      theSize = strlen(Name);
      while (thePart && NotFound)
      {
         if (theSize == thePart->NameLen) NotFound = strcmp(thePart->Name,Name);
         if (NotFound) thePart = thePart->Next;
      }
   }
   if (thePart == NULL && ShowError)
      DoError("Part '%s' Not Found in Library '%s'\n",Name,theLibrary->Name);
   return(thePart);
}


/*
 *  LoadPart()
 *
 *  If a part was given,
 *    Get the part's data into the NewGen grid array
 *    Set the selct type and mouse move type to immitate a selection
 *    Invert the SELECT GADGET
 *    Start COPY mode.
 */

static void LoadPart(thePart)
PART *thePart;
{
   if (thePart)
   {
      PartToGrid(NewGen,thePart);
      SelectType = SEL_WAIT; MouseMoveType = MM_SELECT;
      InvertGadget(&cGadget[ID_SELECT]);
      DoMoveCopy(MM_COPY);
   }
}


/*
 *  RemovePart()
 *
 *  Unlink the part from the list.
 *  Remove the part from the requester's list or viewing area.
 *  Free the part's memory.
 *  Record that the circuit has been modified.
 */

static void RemovePart(thePart,theLibrary)
PART *thePart;
LIBRARY *theLibrary;
{
   if (thePart->Next) thePart->Next->Prev = thePart->Prev;
   if (thePart->Prev) thePart->Prev->Next = thePart->Next;
   if (thePart == theLibrary->FirstPart) theLibrary->FirstPart = thePart->Next;
   if (Viewing)
      RemoveViewItem(LPGADGET[LP_PNAMES].UserData,thePart);
     else
      RemoveListItem(LPGADGET[LP_PNAMES].UserData,thePart);
   FreePart(thePart);
   Changed = TRUE;
}


/*
 *  RemoveLibrary()
 *
 *  If the library is not allowed to be deleted,
 *    give a warning
 *  Otherwise,
 *    Unlink the library from the library list.
 *    Remove the library from the requester, and record the newly selected
 *      library.
 *    Reset the parts list on the requester to show the new current library.
 *    Free the library's memory.
 *    Mark the circuit as changed.
 */

static void RemoveLibrary(theLibrary)
LIBRARY *theLibrary;
{
   if (theLibrary->Flags & PL_NODELETE)
   {
      DoError("Can't Remove Library '%s'\n",theLibrary->Name);
   } else {
      if (theLibrary->Next) theLibrary->Next->Prev = theLibrary->Prev;
      if (theLibrary->Prev) theLibrary->Prev->Next = theLibrary->Next;
      if (theLibrary == FirstLibrary) FirstLibrary = theLibrary->Next;
      SelectedLibrary = RemoveListItem(LPGADGET[LP_LNAMES].UserData,theLibrary);
      if (Viewing)
         SetView(&(LPGADGET[LP_PNAMES]),SelectedLibrary->FirstPart,NULL);
        else
         SetList(&(LPGADGET[LP_PNAMES]),SelectedLibrary->FirstPart,NULL);
      FreeLibrary(theLibrary);
      Changed = TRUE;
   }
}


/*
 *  LPGadgetUP()
 *
 *  Handle the Load part requester gadget up events:
 *
 *  USE and NAME:
 *    Remove the requester and put the PART button back to normal
 *    Find the selected part in the current library
 *    If the part was found, load it, otherwise go back to drawing mode.
 *
 *  REMOVE:
 *    If the selected list is the Parts List and a name is specified,
 *      find the selected part in the library and remove it if possible.
 *    Otherwise if a name is supplied (i.e., not in the Parts List)
 *      warn about only being able to delete parts from the Parts List
 *    Otherwise (no part name supplied)
 *      remove the selected library
 *    activate the name gadget
 *
 *  CANCEL:
 *    Remove the requester and put things back to normal
 *
 *  SLIDERS:
 *    Note the fact that sliders are no longer active
 */

void LPGadgetUp(theGadget,theMessage)
struct Gadget *theGadget;
struct IntuiMessage *theMessage;
{
   PART *thePart;

   switch(theGadget->GadgetID)
   {
      case LP_USE:
      case LP_NAME:
         RemoveRequest(&LPRequest);
         InvertGadget(&cGadget[ID_PART]);
         thePart = FindPart(LPNameBuf,SelectedLibrary,TRUE);
         if (thePart)
            LoadPart(thePart);
           else
            SetPointerColor(PenInUse);
         break;

      case LP_REMOVE:
         if (SelectedLibrary == &PartsList && LPNameBuf[0])
         {
            thePart = FindPart(LPNameBuf,SelectedLibrary,TRUE);
            if (thePart) RemovePart(thePart,SelectedLibrary);
         } else if (LPNameBuf[0]) {
            DoError("Only Parts in the Parts List can be Removed");
         } else {
            RemoveLibrary(SelectedLibrary);
         }
         ActivateName(LPGADGET[LP_PNAMES].UserData);
         break;

      case LP_CANCEL:
         RemoveRequest(&LPRequest);
         InvertGadget(&cGadget[ID_PART]);
         SetPointerColor(PenInUse);
         break;

      case LP_PSLIDE:
      case LP_LSLIDE:
         EndListSlider(theGadget->UserData);
         break;
   }
}


/*
 *  LPGadgetDown()
 *
 *  Do gadget down events fot the Load Part requester:
 *
 *  VIEW:
 *    Find the selected part in the library (if possible)
 *    Set viewing mode starting at the selected part (it it could be found,
 *      otherwise start at the top of the list).
 *
 *  PART NAME:
 *    Check if a part was selected; if so,
 *      remove the requester and put things back to normal
 *      load the part
 *
 *  LIBRARY NAME:
 *    Check if a new library was selected; if so
 *      set the parts list to show the contents of the new library, and
 *      record the new library as the selected one
 *    activate the name gadget
 *
 *  ARROWS and SLIDERS:
 *    Scroll the appropriate list up or down
 *
 *  NAME:
 *    Unselect any selected parts when the name gadget is clicked
 */

void LPGadgetDown(theGadget,theMessage)
struct Gadget *theGadget;
struct IntuiMessage *theMessage;
{
   PART *thePart;
   LIBRARY *theLibrary;

   switch(theGadget->GadgetID)
   {
      case LP_VIEW:
         thePart = FindPart(LPNameBuf,SelectedLibrary,FALSE);
         SetViewMode(LPGADGET[LP_PNAMES].UserData,
            SelectedLibrary->FirstPart,thePart);
         break;

      case LP_PNAMES:
         thePart = (PART *)DoListHit(theGadget->UserData,theMessage);
         if (thePart)
         {
            RemoveRequest(&LPRequest);
            InvertGadget(&cGadget[ID_PART]);
            LoadPart(thePart);
         }
         break;

      case LP_LNAMES:
         theLibrary =
            (LIBRARY *)DoListHit(theGadget->UserData,theMessage);
         if (theLibrary)
         {
            if (Viewing)
               SetView(&(LPGADGET[LP_PNAMES]),theLibrary->FirstPart,NULL);
              else
               SetList(&(LPGADGET[LP_PNAMES]),theLibrary->FirstPart,NULL);
            SelectedLibrary = theLibrary;
         }
         ActivateName(LPGADGET[LP_PNAMES].UserData);
         break;

      case LP_PUARROW:
      case LP_LUARROW:
         DoListScrollUp(theGadget->UserData,theGadget,theMessage);
         break;

      case LP_PDARROW:
      case LP_LDARROW:
         DoListScrollDown(theGadget->UserData,theGadget,theMessage);
         break;

      case LP_PSLIDE:
      case LP_LSLIDE:
         StartListSlider(theGadget->UserData);
         break;

      case LP_NAME:
         UnsetListSelect(theGadget->UserData,TRUE);
         break;
   }
}


/*
 *  GetPart()
 *
 *  If the Load Part requester is not already active,
 *    Clear the name buffer.
 *    Set the pointer colors to normal and invert the PART button
 *    Set the library list (selecting the current library)
 *    Set the parts list to the proper viewing mode, and add the
 *      requester with the correct routines for processing the gadgets
 *    If the requester did not open, put things back to normal
 */

void GetPart()
{
   extern void DoListMouse();
   extern void ViewGadgetDown(),ViewGadgetUp(),DoViewMouse();

   if (ActiveRequest != &LPRequest)
   {
      LPNameBuf[0] = 0;
      RestorePointerColor();
      InvertGadget(&cGadget[ID_PART]);
      SetList(&(LPGADGET[LP_LNAMES]),FirstLibrary,SelectedLibrary);
      if (Viewing)
      {
         SetView(&(LPGADGET[LP_PNAMES]),SelectedLibrary->FirstPart,NULL);
         AddRequest(&LPRequest,ViewGadgetDown,ViewGadgetUp,DoViewMouse);
      } else {
         SetList(&(LPGADGET[LP_PNAMES]),SelectedLibrary->FirstPart,NULL);
         AddRequest(&LPRequest,LPGadgetDown,LPGadgetUp,DoListMouse);
      }
      if (ActiveRequest == NULL)
      {
         InvertGadget(&cGadget[ID_PART]);
         SetPointerColor(PenInUse);
      }
   }
}
