
/***     Copyright 1992 by D.W.Reisig.   ***/

unsigned char Version[] = "$VER: ENote 1.0 04-May-92 © 1992 D.W.Reisig\n";
unsigned char PrgTitle[] = "ENote 1.0: ";

#include <exec/types.h>
#include <exec/memory.h>
#include <workbench/startup.h>
#include <libraries/dosextens.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include "memory.h"

#define COMMENTSIZE	80
#define PUBCLR		MEMF_PUBLIC | MEMF_CLEAR
#define CHECK_IOERR	-1

#define btst(bitnr,var)	((var)&(1<<(bitnr)))
#define bset(bitnr,var)	((var)|=(1<<(bitnr)))
#define bclr(bitnr,var)	((var)&=~(1<<(bitnr)))
#define bchg(bitnr,var)	((var)^=(1<<(bitnr)))

extern VOID Quit(LONG ReturnValue, LONG Result2);
extern LONG Edit(UBYTE *FileName, struct FileInfoBlock *InfoBlock);
extern struct IntuitionBase *IntuitionBase;
extern struct WBStartup *WBenchMsg;

ULONG StdOut;
UBYTE *ErrMsg, *PrgName;


/*---------------------------------------------------------------------------*/

VOID main(LONG argc, UBYTE *argv[])
{
  //  static UWORD Pad;  //  Align FileInfoBlocks if necessary
  static struct FileInfoBlock WBInfoBlock, InfoBlock;
  static UBYTE NoAccess[] = "Could not access file\n";
  UBYTE *FileName;
  ULONG FileLock;
  LONG  EditResult;

  if (argc != 0){
    StdOut = Output();
    PrgName = argv[0];
  }

  IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0);
  if (!IntuitionBase){
    ErrMsg = "No Intuition\n";
    Quit(RETURN_FAIL, 0);
  }

  if (argc == 0){

    //***  From the Workbench..
  
    ULONG WBLock;

    if (WBenchMsg->sm_NumArgs < 2){
      ErrMsg = "\nSelect the ENote Icon,\nPress shift,\nAnd double-click another Icon..\n";
      Quit(RETURN_ERROR, ERROR_REQUIRED_ARG_MISSING);
    }
    WBLock = ((struct WBArg *)(WBenchMsg->sm_ArgList)+1)->wa_Lock;
    CurrentDir(WBLock);
    FileName = ((struct WBArg *)(WBenchMsg->sm_ArgList)+1)->wa_Name;
    if (*FileName == '\0'){
      
      //***  Directory selected; one up, so we can find its name..
      
      CurrentDir(ParentDir(WBLock));
      if (!Examine(WBLock, &WBInfoBlock)){
        ErrMsg = NoAccess;
        Quit(RETURN_FAIL, CHECK_IOERR);
      }
      FileName = WBInfoBlock.fib_FileName;
    }

  } else{

    //***  From the CLI..
    
    if ((argc<2) || ((argc>1)&&(*argv[1]=='?'))){  //  No argument or '?'
      static UBYTE Usage0[] = "Syntax: ";
      static UBYTE Usage1[] = " <file>\nFunction: Edit a filenote\n";
      Write(StdOut, Usage0, sizeof(Usage0)-1);
      Write(StdOut, argv[0], strlen(argv[0]));
      Write(StdOut, Usage1, sizeof(Usage1)-1);
      Quit(RETURN_ERROR, ERROR_REQUIRED_ARG_MISSING);
    }
    FileName = argv[1];
  }

  //***  Get access to file

  FileLock = Lock(FileName, ACCESS_READ);
  if (!FileLock){
    ErrMsg = NoAccess;
    Quit(RETURN_FAIL, CHECK_IOERR);
  }
  if (!Examine(FileLock, &InfoBlock)){
    UnLock(FileLock);
    ErrMsg = NoAccess;
    Quit(RETURN_FAIL, CHECK_IOERR);
  }
  UnLock(FileLock);

  //***  Edit filenote and bits
  
  EditResult = Edit(FileName, &InfoBlock);
  if (EditResult < 0)
    Quit(RETURN_FAIL, 0);
  if (EditResult > 0){
  
    //***  Store filenote and bits
    
    if (!(SetComment(FileName, InfoBlock.fib_Comment) &&
          SetProtection(FileName, InfoBlock.fib_Protection))){
      ErrMsg = "Save failed\n";
      Quit(RETURN_FAIL, CHECK_IOERR);
    }
  }

  Quit(RETURN_OK, 0);
}

/*---------------------------------------------------------------------------*/

#define MAXWTITLE 40

VOID Quit(LONG ReturnValue, LONG Result2)
{
  
  if (Result2 == CHECK_IOERR)
    Result2 = IoErr();

  //***  Print any message

  if (ErrMsg){
    LONG MsgLen = strlen(ErrMsg);

    if (StdOut){  // CLI

      Write(StdOut, PrgName, strlen(PrgName));
      Write(StdOut, ": ", 2);
      Write(StdOut, ErrMsg, MsgLen);

    } else{       // WB

      UBYTE StrBuf[MAXWTITLE+18], *StrPtr;

      strcpy(StrBuf, "CON:10/10/630/120/");
      strncpy(&StrBuf[18], WBenchMsg->sm_ArgList->wa_Name, MAXWTITLE);
      StdOut = Open(StrBuf, MODE_OLDFILE);
      if (StdOut){
        Write(StdOut, ErrMsg, MsgLen);
        if (Result2){
          strcpy(StrBuf, "Error ");
          StrPtr = &StrBuf[6];
          StrPtr += stcl_d(StrPtr, Result2);
          *StrPtr++ = '\n';
          *StrPtr = '\0';
          Write(StdOut, StrBuf, strlen(StrBuf));
        }
        Delay(100 + 4 * MsgLen);
        Close(StdOut);
      } else if (IntuitionBase){
        DisplayBeep(0);
      }
    }
  }
  
  if (IntuitionBase)
    CloseLibrary(IntuitionBase);

  ((struct Process *)FindTask(0))->pr_Result2 = Result2;
  _exit(ReturnValue);
}

/*---------------------------------------------------------------------------*/

UBYTE *NoMem(ULONG Size, ULONG Type)
{
  ErrMsg="Not enough memory\n";
  return(0);
}

/*****************************************************************************/

extern struct Gadget  *MakeStringGadget(ULONG TextWidth, ULONG TextHeight, UBYTE *TextBuf, ULONG TextLen);
extern struct Gadget *MakeBoolGadget(ULONG TextWidth, ULONG TextHeight, UBYTE *Text);
extern VOID ToggleGadget(struct Gadget *Gadget, struct Window *Window);

#define GADGET_H_DIST	10
#define GADGET_V_DIST	6
#define GADTXT_H_DIST	4
#define GADTXT_V_DIST	2
#define PEN_GREY	0
#define PEN_BLACK	1
#define PEN_WHITE	2
#define PEN_BLUE	3

#define NUMGADGETS	11
#define G_STR		10
#define G_CANCEL	9
#define G_SAVE		8
#define G_H		7
#define G_S		6
#define G_P		5
#define G_A		4
#define G_R		3
#define G_W		2
#define G_E		1
#define G_D		0

UBYTE *GtsText[] = {
  "D",
  "E",
  "W",
  "R",
  "A",
  "P",
  "S",
  "H",
  "  Save  ",
  " Cancel ",
  ""
};

struct Screen WBScreen;
struct NewWindow NewWin;

/*---------------------------------------------------------------------------*/


LONG Edit(UBYTE *FileName, struct FileInfoBlock *InfoBlock)
{
  struct Window *Window;
  struct Gadget *Gadget[NUMGADGETS];
  LONG I;
  WORD OverShoot, TextHeight;
  WORD Close = 0, FirstActivate = 1;
  

  //***  Get Screen structure for window and font sizes among others

  if (!GetScreenData(&WBScreen, sizeof(struct Screen), WBENCHSCREEN, NULL)){
    ErrMsg="No Screen\n";
    Quit(RETURN_FAIL, 0);
  }

  //***  Something like Barheight, but Barheight differs between 1.3 and 2.0

  TextHeight = WBScreen.RastPort.TxHeight + GADTXT_V_DIST;

  //***  NewWindow allocation and initialization

  NewWin.DetailPen  = -1;
  NewWin.BlockPen   = -1;
  NewWin.IDCMPFlags = IDCMP_ACTIVEWINDOW + IDCMP_GADGETUP + IDCMP_VANILLAKEY;
  NewWin.Flags      = WFLG_NOCAREREFRESH + WFLG_SMART_REFRESH + WFLG_ACTIVATE + WFLG_DRAGBAR + WFLG_DEPTHGADGET;
  NewWin.Type       = WBENCHSCREEN;
  NewWin.TopEdge    = WBScreen.MouseY;    // Nice for auto-activation..
  NewWin.Height     = TextHeight * 3 + GADGET_V_DIST * 3 + 2;
  OverShoot = NewWin.TopEdge + NewWin.Height - WBScreen.Height;
  if (OverShoot>0){
    NewWin.TopEdge -= OverShoot;
    if (NewWin.TopEdge < 0)
      NewWin.TopEdge = 0;
  }
  NewWin.Width      = WBScreen.RastPort.TxWidth * COMMENTSIZE + 2 * GADTXT_H_DIST + 2 * GADGET_H_DIST;
  if (NewWin.Width > WBScreen.Width)
    NewWin.Width    = WBScreen.Width;
  OverShoot = WBScreen.MouseX - NewWin.Width + 1;
  if (OverShoot > 0)
    NewWin.LeftEdge = OverShoot;
  NewWin.MinHeight  = NewWin.MaxHeight = NewWin.Height;   // No fiddling with our sizes!
  NewWin.MinWidth   = NewWin.MaxWidth = NewWin.Width;
  NewWin.Title      = AllocMA(sizeof(PrgTitle) + 1 + strlen(FileName), PUBCLR);
  if (!NewWin.Title)
    goto Fail;
  strcpy(NewWin.Title, PrgTitle);
  strcpy(&NewWin.Title[sizeof(PrgTitle)-1], FileName);

  //***  Create string gadget for filenote

  Gadget[G_STR]=MakeStringGadget(NewWin.Width - 2 * GADGET_H_DIST - 2 * GADTXT_H_DIST, TextHeight,
                                 (UBYTE *)&InfoBlock->fib_Comment, COMMENTSIZE);
  if (!Gadget[G_STR])
    goto Fail;
  Gadget[G_STR]->LeftEdge = GADGET_H_DIST + GADTXT_H_DIST;
  Gadget[G_STR]->TopEdge  = TextHeight + GADGET_V_DIST + 2;  // 2 for thick border

  //***  Create BOOL gadget structures

  for (I=G_CANCEL; I>=G_D; --I){
    Gadget[I]=MakeBoolGadget(strlen(GtsText[I]) * WBScreen.RastPort.TxWidth + 2 * GADTXT_H_DIST,
                             TextHeight, GtsText[I]);
    if (!Gadget[I])
      goto Fail;
    Gadget[I]->TopEdge       = Gadget[G_STR]->TopEdge + TextHeight + GADGET_V_DIST + 1;  // 1 for thin border
    Gadget[I+1]->NextGadget  = Gadget[I];
  }
  Gadget[G_SAVE]->GadgetID   = 1;  // Close gadgets
  Gadget[G_CANCEL]->GadgetID = 1;

  //***  Set BOOL gadgets horizontal locations
  
  Gadget[G_SAVE]->LeftEdge   = GADGET_H_DIST;
  Gadget[G_CANCEL]->LeftEdge = NewWin.Width - Gadget[G_CANCEL]->Width - GADGET_H_DIST;
  Gadget[G_H]->LeftEdge      = NewWin.Width/2 - 4 * (Gadget[G_H]->Width + GADGET_H_DIST);
  for (I=G_S; I>=G_D; --I)
    Gadget[I]->LeftEdge      = Gadget[I+1]->LeftEdge + Gadget[I+1]->Width + GADGET_H_DIST;

  //***  Check gaget overlap

  if ((Gadget[G_SAVE]->LeftEdge + Gadget[G_SAVE]->Width) > Gadget[G_H]->LeftEdge){
    ErrMsg = "Font too wide\n";
    goto Fail;
  }

  //***  Set Filebit gadgets activation

  for (I=G_H; I>=G_A; --I)
    if (btst(I, InfoBlock->fib_Protection))
      Gadget[I]->Flags |= GFLG_SELECTED;

  for (I=G_R; I>=G_D; --I)
    if (!btst(I, InfoBlock->fib_Protection))
      Gadget[I]->Flags |= GFLG_SELECTED;

  NewWin.FirstGadget = Gadget[G_STR];

  //***  Open Window

  Window = (struct Window *)OpenWindow(&NewWin);
  if (!Window){
    ErrMsg = "No window\n";
    goto Fail;
  }
  
  //***  Handle Window messages
  
  while(!Close){
  
    struct IntuiMessage IntuMsg, *IM;

    WaitPort(Window->UserPort);
    if (IM = (struct IntuiMsg *)GetMsg(Window->UserPort)){
      IntuMsg = *IM;
      ReplyMsg((struct Message *)IM);
      switch(IntuMsg.Class){

        case IDCMP_ACTIVEWINDOW:
          if (FirstActivate)
            ActivateGadget(Gadget[G_STR], Window, NULL);
          FirstActivate=0;
          break;
        
        case IDCMP_VANILLAKEY:
          switch(IntuMsg.Code){
            
            case 'S':
              Gadget[G_SAVE]->Flags |= SELECTED;
              Close = 1;
            break;
            
            case 'C':
              Close = 1;
              break;
            
            case 'h':
              ToggleGadget(Gadget[G_H], Window);
              break;
            
            case 's':
              ToggleGadget(Gadget[G_S], Window);
              break;
            
            case 'p':
              ToggleGadget(Gadget[G_P], Window);
              break;
            
            case 'a':
              ToggleGadget(Gadget[G_A], Window);
              break;
            
            case 'r':
              ToggleGadget(Gadget[G_R], Window);
              break;
            
            case 'w':
              ToggleGadget(Gadget[G_W], Window);
              break;
            
            case 'e':
              ToggleGadget(Gadget[G_E], Window);
              break;
            
            case 'd':
              ToggleGadget(Gadget[G_D], Window);
              break;
            
            case 'f':
              ActivateGadget(Gadget[G_STR], Window, NULL);
              break;

            default:
              DisplayBeep(0);
              break;
          }
          break;
  
        case IDCMP_GADGETUP:  /*  User perhaps asks to leave  */
          Close = ((struct Gadget *)IntuMsg.IAddress)->GadgetID;
          break;
        
        
      }
    }
  }

  //***  Set filebits from gadgets

  for (I=G_H; I>=G_A; --I)
    if (Gadget[I]->Flags & GFLG_SELECTED)
      bset(I, InfoBlock->fib_Protection);
    else
      bclr(I, InfoBlock->fib_Protection);

  for (I=G_R; I>=G_D; --I)
    if (Gadget[I]->Flags & GFLG_SELECTED)
      bclr(I, InfoBlock->fib_Protection);
    else
      bset(I, InfoBlock->fib_Protection);
  
  //***  Close Window and return status

  CloseWindow(Window);
  return((Gadget[G_SAVE]->Flags&SELECTED)? 1 : 0);

Fail:
  return(-1);
}

/*---------------------------------------------------------------------------*/


struct Gadget  *MakeStringGadget(ULONG TextWidth, ULONG TextHeight, UBYTE *TextBuf, ULONG TextLen)
{

  //***  Array of coordinates of a border around a gadget with a size of zero.

  static WORD BasicXY[] = {
    -4, -3, -4,  0, -3, -1, -3, -3,  2, -3,  1, -3,  1, -1,  0, -1,  0, -1, -1, -1,
     3,  0,  3, -3,  2, -2,  2,  0, -3,  0, -2,  0, -2, -3, -1, -2, -1, -2,  0, -2
  };
  //***  Arrays of coordinate elements to be adapted to real gadget size
  
  static WORD VAdapt[]  = {  3,  5, 13, 17, 19, 21, 27, 29, 31, 35 };
  static WORD HAdapt[]  = {  8, 10, 12, 14, 16, 20, 22, 24, 26, 38 };

  struct StringInfo *StrInfo;
  struct Gadget *Gadget;
  struct Border *BorderLT, *BorderRB;  // Border Left Top and Border Right Bottom
  WORD   *XY;
  LONG I;


  //***  Allocate and initialize StringInfo structure

  StrInfo = (struct StringInfo *)AllocMA(sizeof(struct StringInfo), PUBCLR);
  if (!StrInfo)
    goto Fail;
  StrInfo->Buffer = TextBuf;
  StrInfo->BufferPos = strlen(TextBuf);
  StrInfo->UndoBuffer = AllocMA(TextLen + 2, PUBCLR);
  if (!StrInfo->UndoBuffer)
    goto Fail;
  StrInfo->MaxChars = TextLen;
  
  //***  Allocate and coordinate array's and adapt its contents
  
  XY = (WORD *)AllocMA(sizeof(BasicXY), MEMF_PUBLIC);
  if (!XY)
    goto Fail;
  memcpy((UBYTE *)XY, (UBYTE *)BasicXY, sizeof(BasicXY));
  for (I=0; I < sizeof(VAdapt)/2; ++I)
    XY[VAdapt[I]] += TextHeight;
  for (I=0; I < sizeof(HAdapt)/2; ++I)
    XY[HAdapt[I]] += TextWidth;

  //*** Allocate and initialize Border structures

  BorderLT = (struct Border *)AllocMA(sizeof(struct Border), PUBCLR);
  if (!BorderLT)
    goto Fail;
  BorderLT->XY         = XY;
  BorderLT->Count      = (sizeof(BasicXY)/(2 * sizeof(WORD)))/2;
  BorderLT->FrontPen   = PEN_WHITE;
  BorderLT->DrawMode   = JAM2;

  BorderRB = (struct Border *)AllocMA(sizeof(struct Border), PUBCLR);
  if (!BorderRB)
    goto Fail;
  BorderRB->XY         = &XY[20];
  BorderRB->Count      = (sizeof(BasicXY)/(2 * sizeof(WORD)))/2;
  BorderRB->FrontPen   = PEN_BLACK;
  BorderRB->DrawMode   = JAM2;
  BorderRB->NextBorder = BorderLT;

  //***  Allocate and initialize string Gadget structure

  Gadget = (struct Gadget *)AllocMA(sizeof(struct Gadget), PUBCLR);
  if (!Gadget)
    goto Fail;
  Gadget->SpecialInfo  = StrInfo;
  Gadget->Flags        = GFLG_GADGHCOMP + GFLG_SELECTED;
  Gadget->Activation   = GACT_TOGGLESELECT;
  Gadget->GadgetType   = GTYP_STRGADGET;
  Gadget->Width        = TextWidth;
  Gadget->Height       = TextHeight;
  Gadget->GadgetRender = BorderRB;

  return(Gadget);
Fail:
  return(0);
}

/*---------------------------------------------------------------------------*/

struct Gadget *MakeBoolGadget(ULONG TextWidth, ULONG TextHeight, UBYTE *Text)
{
  struct Gadget *Gadget;
  struct IntuiText *IText;
  struct Border *BorderLT, *BorderRB;  // Border Left Top and Border Right Bottom
  WORD   *XY;

  //***  Allocate and initialize Border coordinates

  XY = (WORD *)AllocMA(2 * 5 * 2 * sizeof(WORD), PUBCLR);
  if (!XY)
    goto Fail;

  				XY[3]  = TextHeight;
  XY[4]  = 1;			XY[5]  = TextHeight - 1;
  XY[6]  = 1;
  XY[8]  = TextWidth - 1;

  XY[10] = TextWidth;		XY[11] = TextHeight;
  XY[12] = TextWidth;
  XY[14] = TextWidth - 1;	XY[15] = 1;
  XY[16] = TextWidth - 1;	XY[17] = TextHeight;
  XY[18] = 1;			XY[19] = TextHeight;

  //***  Allocate and initialize Border structures

  BorderLT = (struct Border *)AllocMA(sizeof(struct Border), PUBCLR);
  if (!BorderLT)
    goto Fail;
  BorderLT->XY         = XY;
  BorderLT->Count      = 5;
  BorderLT->FrontPen   = PEN_WHITE;
  BorderLT->DrawMode   = JAM2;

  BorderRB = (struct Border *)AllocMA(sizeof(struct Border), PUBCLR);
  if (!BorderRB)
    goto Fail;
  BorderRB->XY         = &XY[10];
  BorderRB->Count      = 5;
  BorderRB->FrontPen   = PEN_BLACK;
  BorderRB->DrawMode   = JAM2;
  BorderRB->NextBorder = BorderLT;
  
  //***  Allocate and initialize IntuiText structures for gadget texts
  
  IText = (struct IntuiText *)AllocMA(sizeof(struct IntuiText), PUBCLR);
  if (!IText)
    goto Fail;
  IText->FrontPen      = PEN_BLACK;
  IText->DrawMode      = JAM2;
  IText->LeftEdge      = GADTXT_H_DIST;
  IText->TopEdge       = GADTXT_V_DIST;
  IText->IText         = Text;
  
  //***  Allocate and initialize BOOL Gadget structure
  
  Gadget = (struct Gadget *)AllocMA(sizeof(struct Gadget), PUBCLR);
  if (!Gadget)
    goto Fail;
  Gadget->Width        = TextWidth + 1;
  Gadget->Height       = TextHeight + 1;
  Gadget->Flags        = GFLG_GADGHCOMP;
  Gadget->Activation   = GACT_TOGGLESELECT + GACT_RELVERIFY;
  Gadget->GadgetType   = GTYP_BOOLGADGET;
  Gadget->GadgetRender = BorderRB;
  Gadget->GadgetText   = IText;

  return(Gadget);
Fail:
  return(0);
}
  

/*---------------------------------------------------------------------------*/

/*
 *  It is not the first time I have troubles with gadgets.  I have ALWAYS
 *  troubles with gadgets.  This time gadgets must toggle by our command.
 *  We must first remove any drawing data from the gadget.  No border and
 *  no text.  Now you can toggle the drawing just by complementing.  You can
 *  let Intuition complement by refreshing a selected gadget.  Later the
 *  select flag must be set right.  Takes some time to find out.
 */
  
VOID ToggleGadget(struct Gadget *Gadget, struct Window *Window)
{
  UWORD Flags;

  Gadget->GadgetText=NULL;
  Gadget->GadgetRender=NULL;
  Flags = Gadget->Flags;
  Gadget->Flags |= SELECTED;
  RemoveGadget(Window, Gadget);
  AddGadget(Window, Gadget, -1);
  RefreshGadgets(Gadget, Window, NULL);
  Gadget->Flags = Flags ^ SELECTED;
}

/*---------------------------------------------------------------------------*/

