
#include <exec/types.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>
#include <proto/utility.h>
#include <proto/exec.h>                 /* CopyMem() */
#include <string.h>             /* strcpy */
#include <stdlib.h>     /* abs() macro */

#include "Design.h"
#include "dbGUI.h"
#include "dbparser.h"
#include "Toolbox.h"
#include "DesignGUI.h"
#include "Version.h"

#include "Pointers.h"

#include <stdio.h> // Diagnostics

/*
 * Externs to make the new GadToolsBox interface work
 * Remove "static" from these in DesignGUI.c
 */
extern struct Gadget         *VisGadgets[];
extern struct Window         *VisWnd;
extern struct NewMenu                   DesignNewMenu[];
extern struct Menu           *DesignMenus;

#define CURSORWIDTH 3
#define DRAGTRESHOLD 6
#define TOGGLE_LED *(UBYTE *)0xbfe001 ^= 2      /* Debug */

/* Handy macros */
#define FLOOR(x,f) if ((x)<(f)) (x) = (f) 

/* Return codes from GadToolBox handlers */
#define RET_OK 1
#define RET_CANCEL 2



extern WORD LastLeftEdge;
extern WORD LastTopEdge;                /* For nice layoutswitching */

extern void UpdateWindow(struct Pro *Pr);
extern void UpdateDragBar(struct Pro *Pr);

typedef int Bool;       /* A char would be smaller, but will produce non-cast warnings */

typedef struct {
        struct VisFldInfo *vf;
        Bool after;     /* Has cursor passed field? (to the left or right of field) */
} Cursor;


struct EasyStruct ES_ViewDesignHelp =
{
        sizeof(struct EasyStruct),
        0,
        (STRPTR)"View design",
        (STRPTR)"%s",
        (STRPTR)"Ok"
};


/**********************************************************************/
/*                              Globals                               */
/**********************************************************************/

Cursor Crsr;
struct VisFldInfo *Selected;    /* The selected field that has a rubberband */
int DragCnt = 0;        /* Non-zero if dragging Selected. */
Bool Sizing = FALSE;

struct Window *ToolBox = NULL;

/* GadToolBox handlers uses global variables */
extern struct Pro *CurrentPro;
struct VisFldInfo *Vf;
extern char *RFFTagNames[];

/* The clipboard */
struct VisFldInfo *Clip = NULL;

/**********************************************************************/
/*                         Support functions                          */
/**********************************************************************/

struct VisFldInfo *PrevVisFldInfo(struct Layout *Lay, struct VisFldInfo *vf)
{
        struct VisFldInfo *t = Lay->FirstVisFldInfo;
        if (t == vf) return NULL;
        for (; t; t = t->Next) if (t->Next == vf) return t;
        return NULL; /* Safety */
}

static void InitCursor(struct Layout *Lay, Cursor *crsr)
{
        crsr->vf = Lay->FirstVisFldInfo;
        crsr->after = FALSE;
}

static void GetCursorPosition(Cursor *crsr, WORD *x, WORD *y)
{
        /* Return position of cursor in (x,y) coords */
        if (crsr->vf) {
                *y = crsr->vf->Pos.YOffset;
                *x = crsr->vf->Pos.XOffset;
                *x += (crsr->after) ? crsr->vf->Pos.Width + CURSORWIDTH : -2*CURSORWIDTH;
        }
        else {
                *x = OffX + FontX;                      /* As in CalcAllPos() */
                *y = OffY + (FontY >> 1);       /* As in CalcAllPos() */
        }
}

static int PreviousPosition(struct Layout *Lay, Cursor *crsr)
{
        /* Position cursor one step to the left (maybe up) */
        /* Return FALSE if this is not possible */
        struct VisFldInfo *vf;

        if (!crsr->vf) return FALSE;    /* No gadgets at all */

        if (crsr->after) {
                crsr->after = FALSE;
                return TRUE;
        }
        else if (vf = PrevVisFldInfo(Lay, crsr->vf)) {
                crsr->vf = vf;
                crsr->after = TRUE;
                return TRUE;
        }
        return FALSE;
}

static int NextPosition(struct Layout *Lay, Cursor *crsr)
{
        /* Position cursor one step to the right (maybe down) */
        /* Return FALSE if this is not possible */

        if (!crsr->vf) return FALSE;    /* No gadgets at all */

        if (!crsr->after) {
                crsr->after = TRUE;
                return TRUE;
        }
        else if (crsr->vf->Next) {
                crsr->vf = crsr->vf->Next;
                crsr->after = FALSE;
                return TRUE;

        }
        return FALSE;
}


static void DisplayCursor(struct RastPort *rp, WORD x, WORD y)
{
        /* Draws a cursor in complement colour at given position */
        /* Deletes cursor at old position automatically */
        /* Specify a negative coordinate in either x or y to remove cursor */
        /* DisplayCursor assumes a locked cursorheight */

        static WORD oldx = -1, oldy = -1;

        SetDrMd(rp, COMPLEMENT);

        if (oldx>=0 && oldy>=0)
                RectFill(rp, oldx, oldy, oldx+CURSORWIDTH-1, oldy+FontY+STRGADFRAMESHEIGHT-1);

        if (x>=0 && y>=0)
                RectFill(rp, x, y, x+CURSORWIDTH-1, y+FontY+STRGADFRAMESHEIGHT-1);
        oldx = x;
        oldy = y;
}

static void Rubberband(struct RastPort *rp, WORD left, WORD top, WORD width, WORD height, char handle)
{
        /* Draws a border rectangle in complement colour. Adds a handle square if "handle" is TRUE */
        /* Deletes old rubberband automatically */
        /* Specify a negative coordinate in either left or top to remove rubberband */
        static WORD oldleft = -1, oldtop = -1, oldwidth=-1, oldheight=-1, oldhandle = 0;


        SetDrMd(rp, COMPLEMENT);
        left--; top--; width++; height++;

        if (oldleft>=0 && oldtop>=0) {
                Move(rp, oldleft, oldtop);
                Draw(rp, oldleft+oldwidth, oldtop);
                Draw(rp, oldleft+oldwidth, oldtop+oldheight);
                Draw(rp, oldleft, oldtop+oldheight);
                Draw(rp, oldleft, oldtop);
                if (oldhandle) { /* Draw a 3x4 square in the middle of the right border */
                        RectFill(rp, oldleft+oldwidth+1, oldtop+(oldheight>>1), oldleft+oldwidth+4, oldtop+(oldheight>>1)+2);
                }
        }

        if (left>=0 && top>=0) {
                Move(rp, left, top);
                Draw(rp, left+width, top);
                Draw(rp, left+width, top+height);
                Draw(rp, left, top+height);
                Draw(rp, left, top);
                if (handle) { /* Draw a 3x4 square in the middle of the right border */
                        RectFill(rp, left+width+1, top+(height>>1), left+width+4, top+(height>>1)+2);
                }
        }
        oldleft = left; oldtop = top; oldwidth = width; oldheight = height; oldhandle = handle;

}


void FollowMouse(struct Layout *Lay, WORD mx, WORD my)
{
        /* Moves the cursor to the position that is nearest to (x,y) */
        Cursor nearest;
        WORD x,y;
        WORD nx, ny;

        if (!Crsr.vf) return;
        while (PreviousPosition(Lay, &Crsr));   /* Go to the beginning */
        nearest = Crsr;
        GetCursorPosition(&nearest, &nx, &ny);

        while (NextPosition(Lay, &Crsr)) {
                GetCursorPosition(&Crsr, &x, &y);
                if (abs(my-y) < abs(my-ny)) {
                        nearest = Crsr;
                        GetCursorPosition(&nearest, &nx, &ny);
                }
                if (abs(my-y) == abs(my-ny))
                        if (abs(mx-x) < abs(mx-nx)) {
                                nearest = Crsr;
                                GetCursorPosition(&nearest, &nx, &ny);
                        }
        }
        Crsr = nearest;
        DisplayCursor(Lay->Window->RPort, nx, ny);
}

static struct VisFldInfo *VisFldHit(struct Layout *Lay, WORD x, WORD y)
{
        /* Returns the address of the VisFldInfo that is hit by (x,y) */
        struct VisFldInfo *vf = NULL;
        
        for (vf = Lay->FirstVisFldInfo; vf; vf = vf->Next) {
                if (x >= vf->Pos.XOffset && x < vf->Pos.XOffset+vf->Pos.Width &&
                         y >= vf->Pos.YOffset && y < vf->Pos.YOffset+vf->Pos.Height)
                         break;
        }
        return vf;
}

static Bool SizeHit(struct VisFldInfo *hit, WORD mouseX)
{
        /* Return true if mouse is close to the right border of the "hit" field */
        return (hit && (hit->Pos.XOffset + hit->Pos.Width - mouseX) < 5);
}

void SelectVisFld(struct Layout *Lay, struct VisFldInfo *vf)
{
        if (Selected = vf) {
                struct Space *sp = &Selected->Pos;
                Rubberband(Lay->Window->RPort, sp->XOffset, sp->YOffset, sp->Width, sp->Height, 0);
        }
        else {
                Rubberband(Lay->Window->RPort, -1, -1, -1, -1, 0);
        }
}


static struct VisFldInfo *RemoveVisFldInfo(struct Layout *Lay, struct VisFldInfo *oldvf)
{
        /* Remove oldvf from the layout and handle all side effects this may have */

        struct VisFldInfo *prev;

        if (!oldvf) return NULL;
        prev = PrevVisFldInfo(Lay, oldvf);

        if (prev) {
                prev->Next = oldvf->Next;
                if (oldvf->VisSep == '\n' || oldvf->VisSep == '\f') {   /* end of line */
                        prev->VisSep = oldvf->VisSep;
                        WriteVisTags(prev);     /* Update RFF tags in vf */
                }
        }
        else Lay->FirstVisFldInfo = oldvf->Next;

        /* Handle cursor and rubberband */
        if (Crsr.vf == oldvf) {
                if (Crsr.vf->Next) Crsr.vf = Crsr.vf->Next;
                else Crsr.vf = prev;    /* This might be NULL, and that's ok */
        }
        SelectVisFld(Lay, NULL);

        return oldvf;
}


static void InsertVisFldInfo(struct Layout *Lay, struct VisFldInfo *vf)
{
        /* Insert vf in the layout and handle all side effects this may have */
        /* vf is placed at the cursor position */

        struct VisFldInfo **link_here;

        if (!Lay->FirstVisFldInfo) link_here = &Lay->FirstVisFldInfo;
        else if (Crsr.after) {
                link_here = &Crsr.vf->Next;
                vf->VisSep = Crsr.vf->VisSep;
                Crsr.vf->VisSep = ' ';
                WriteVisTags(vf);
                WriteVisTags(Crsr.vf);
        }
        else {
                struct VisFldInfo *prevvf = PrevVisFldInfo(Lay, Crsr.vf);
                if (!prevvf) link_here = &Lay->FirstVisFldInfo;
                else link_here = &prevvf->Next;
                vf->VisSep = ' ';
        }
        /* Link */
        vf->Next = *link_here;
        *link_here = vf;

//      SelectVisFld(Lay, vf);          /* Now, it's easy to spot the new field */
}

int DeleteUndo(struct Layout *Lay)
{
        struct VisFldInfo *vf,*nextvf;
        for (vf = Lay->FirstUndoVisFldInfo; vf; vf=nextvf) {
                nextvf = vf->Next;
                DeleteVisFldInfo(vf);
        }
        Lay->FirstUndoVisFldInfo = NULL;
        OffMenu(CurrentPro->CurrentLayout->Window, FULLMENUNUM(1,0,NOSUB)); /* Edit->Undo */
        return 0;
}

Bool PerformUndo(struct Layout *Lay)
{
        struct VisFldInfo *tmp;
        if (!Lay->FirstUndoVisFldInfo) return FALSE;
        tmp = Lay->FirstVisFldInfo;
        Lay->FirstVisFldInfo = Lay->FirstUndoVisFldInfo;
        Lay->FirstUndoVisFldInfo = tmp;
        
        Crsr.vf = Lay->FirstVisFldInfo;
        Selected = NULL;
        return TRUE;
}

Bool SaveUndo(struct Layout *Lay)
{
        struct VisFldInfo *vf, **link_here;
        DeleteUndo(Lay);        /* Remove old undo */
        link_here = &Lay->FirstUndoVisFldInfo;
        for (vf = Lay->FirstVisFldInfo; vf; vf=vf->Next, link_here = &((*link_here)->Next)) {
                if (!(*link_here = CopyVisFldInfo(vf))) return DeleteUndo(Lay);
        }
        OnMenu(CurrentPro->CurrentLayout->Window, FULLMENUNUM(1,0,NOSUB)); /* Edit->Undo */
        CurrentPro->Modified |= PROMODIFIED;
        return TRUE;
}

static int Redraw(struct Pro *Pr, struct Layout *Lay)
{
        /* Recalculates all gadgets and redraws the updated window */
        WORD ww, wh;
        WORD x,y;
        int ret;
        Bool ok = TRUE;

        /* Tear down */
        DisplayCursor(Lay->Window->RPort, -1, -1);
        Rubberband(Lay->Window->RPort, -1, -1, -1, -1, 0);

        /* Rebuild with undo fallback */
        do {
                if ((ret = CalcAllPos(Pr, Lay, &ww, &wh)) >= 0) {
                        if ((ret = CreateAllGadgets(Pr, Lay, ww, wh, DESIGN_MODE)) >= 0) {
                                if ((ret = AttachAllGadgets(Lay, ww, wh, DESIGN_MODE)) >= 0) {

                                        DeleteAllGadgets(Lay, DESIGN_MODE);
                                        GetCursorPosition(&Crsr, &x, &y);
                                        DisplayCursor(Lay->Window->RPort, x, y);
                                        SelectVisFld(Lay, Selected);    /* Update rubberband */

                                        /* Update pointer */
                                        if (SizeHit(VisFldHit(Lay, DB_Msg.MouseX, DB_Msg.MouseY), DB_Msg.MouseX))
                                                ChangePointer(Lay->Window, LRPointer);
                                        else RestorePointer(Lay->Window);
                                        return ret;
                                }
                        }
                        DeleteAllGadgets(Lay, DESIGN_MODE);
                }
                DisplayBeep(Scr);
                ok = PerformUndo(Lay);
                DeleteUndo(Lay);
        } while (ok);
        return ret;             /* This is serious trouble */
}


int DoEdit(struct Pro *Pr, struct VisFldInfo *vf)
{
        /* Let the user perform the actual editing of a vf */
        /* returns 0 if no changes has been made to vf */

        struct FldInfo *f;
        BOOL quit = FALSE;
        ULONG field_type = _STRING, new_field_type = _STRING;

        if (OpenVisWindow()) return 0;

        /* Prepare gadgets */
        GT_SetGadgetAttrs(VisGadgets[GD_VIS_NAME], VisWnd, NULL,
                GTST_String, vf->Name, TAG_END);

        GT_SetGadgetAttrs(VisGadgets[GD_VIS_SIZE], VisWnd, NULL,
                GTIN_Number, vf->VisLen, TAG_END);


        {
        struct RFFTag *tag = FindTag(&vf->VisTags, FTYP);

        if(tag)
           {
           if(!stricmp(tag->Data, STR_TEXT))      field_type = _TEXT;
           if(!stricmp(tag->Data, STR_CYCLE))     field_type = _CYCLE;
           if(!stricmp(tag->Data, STR_CHECKBOX))  field_type = _CHECKBOX;
           if(!stricmp(tag->Data, STR_TEXTFIELD)) field_type = _TEXTFIELD;
           }
        }
        GT_SetGadgetAttrs(VisGadgets[GD_VIS_TYPE], VisWnd, NULL,
                GTCY_Active, field_type, TAG_END);


        if (f = GetFldInfo(Pr, vf->Offset))
                GT_SetGadgetAttrs(VisGadgets[GD_VIS_FIELD], VisWnd, NULL,
                        GTTX_Text, f->Name,
                        TAG_END);
        else GT_SetGadgetAttrs(VisGadgets[GD_VIS_FIELD], VisWnd, NULL,
                        GTTX_Text, GetAppStr(MSG_EDVIEW_SELECT),
                        TAG_END);

        Vf = vf;                /* GadTooBox handlers use global variables */

        /* Handle input */
        while (!quit)
           {
                WaitPort(VisWnd->UserPort);
                quit = HandleVisIDCMP();
           }

        GT_GetGadgetAttrs(VisGadgets[GD_VIS_TYPE], VisWnd, NULL,
              GTCY_Active, &new_field_type, TAG_END);

        CloseVisWindow();       /* GadToolBox automatically NULLs pointers */

        if (quit == RET_CANCEL) return 0;

        /* update vf */
        strcpy(vf->Name, GetStringGadget(VisGadgets[GD_VIS_NAME]));
        vf->VisLen = GetNumber(VisGadgets[GD_VIS_SIZE]);

        if(new_field_type != field_type)
           {
           struct RFFTag *tag;

           while(tag = FindTag(&vf->VisTags, CENT))
               {
               Remove((struct Node *)tag);
               DeleteTag(tag);
               }

           switch(new_field_type)
               {
               case _STRING:
                   {
                   struct RFFTag *tag;
                   tag = FindTag(&vf->VisTags, FTYP);

                   if(tag)
                      {
                      Remove((struct Node *)tag);
                      DeleteTag(tag);
                      }
                   }
                   break;
              case _TEXT:
                   UpdateTag(&vf->VisTags, FTYP, STR_TEXT);
                   break;
              case _CHECKBOX:
                   UpdateTag(&vf->VisTags, FTYP, STR_CHECKBOX);
                   break;
              case _TEXTFIELD:
                   UpdateTag(&vf->VisTags, FTYP, STR_TEXTFIELD);
                   break;
              case _CYCLE:
                   UpdateTag(&vf->VisTags, FTYP, STR_CYCLE);
                   CreateAndAddTag(&vf->VisTags, CENT, GetAppStr(MSG_EDVIEW_CYCLE_1));
                   CreateAndAddTag(&vf->VisTags, CENT, GetAppStr(MSG_EDVIEW_CYCLE_2));
                   CreateAndAddTag(&vf->VisTags, CENT, GetAppStr(MSG_EDVIEW_CYCLE_3));
                   break;
              }
          }

        WriteVisTags(vf);       /* Update RFF tags in vf */

        /* As user might have connected the vf to an internal field of cycle type,
    * we must then remake the cycle list by reading the VisTags
    */
        if (ReadVisTags(Pr, vf) < 0) return 0;
        /* vf->Offset and the tags has been updated inside GadToolBox */

        return 1;       /* Use */
}

void ReplaceVisFldInfo(struct Layout *Lay, struct VisFldInfo *old, struct VisFldInfo *new)
{
        struct VisFldInfo *prev = PrevVisFldInfo(Lay, old);
        new->Next = old->Next;
        if (prev) prev->Next = new;
        else Lay->FirstVisFldInfo = new;
        
        /* Don't forget to make sure that the cursor and rubberband points correctly */
        if (Crsr.vf == old) Crsr.vf = new;
        if (Selected == old) Selected = new;
}


static void EditVisFldInfo(struct Pro *Pr, struct Layout *Lay, struct VisFldInfo *vf)
{
        struct VisFldInfo *copy = CopyVisFldInfo(vf);
        if (!copy) return;

        for (;;) {
                if (!DoEdit(Pr, copy)) {                /* Cancel */
                        DeleteVisFldInfo(copy);
                        Redraw(Pr, Lay);
                        return;
                }
                else {
                        ReplaceVisFldInfo(Lay, vf, copy);
                        if (Redraw(Pr, Lay) < 0) {              /* Oops, this didn't fit on screen */
                                ReplaceVisFldInfo(Lay, copy, vf);
                        }
                        else break;
                }
        }
        DeleteVisFldInfo(vf);
}

/**********************************************************************/
/*                           Event handlers                           */
/**********************************************************************/

static void CloseDesignWin(struct Layout *Lay)
{
        if (Lay->Window) {

                DisplayCursor(Lay->Window->RPort, -1, -1);                              /* Remove cursor */
                Rubberband(Lay->Window->RPort, -1, -1, -1, -1, 0); /* Remove rubberband */

                /* Remember old window pos */
                LastLeftEdge = Lay->Window->LeftEdge;
                LastTopEdge = Lay->Window->TopEdge;
                ClearMenuStrip(Lay->Window);
                FreeMenus( DesignMenus );
                DesignMenus = NULL;
                CloseWindow(Lay->Window);
                Lay->Window = NULL;
        }
}

static int RawKey(struct Pro *Pr, struct Layout *Lay)
{
        WORD x,y;
        WORD oldx,oldy;
        struct VisFldInfo *oldpos = Crsr.vf;

        switch (DB_Msg.Code) {
/*              case RAW_F10:
                        Redraw(Pr, Lay);
                        return 0;
                        break;  // Looks nice :-)

                case RAW_F10-3:
                        DeleteUndo(Lay);
                        printf("DeleteUndo\n");
                        break;

                case RAW_F10-2:
                        SaveUndo(Lay);
                        printf("Save\n");
                        break;

                case RAW_F10-1:
                        PerformUndo(Lay);
                        Redraw(Pr, Lay);
                        break;
*/


                case RAW_F1:
                        ToolBoxClicked(Pr, Lay, StringID);
                        break;
                case RAW_F2:
                        ToolBoxClicked(Pr, Lay, TextID);
                        break;
                case RAW_F3:
                        ToolBoxClicked(Pr, Lay, TextFieldID);
                        break;
                case RAW_F4:
                        ToolBoxClicked(Pr, Lay, CycleID);
                        break;
                case RAW_F5:
                        ToolBoxClicked(Pr, Lay, CheckBoxID);
                        break;



                case RAW_HELP:
                        EasyRequest(Lay->Window, &ES_ViewDesignHelp, NULL, GetAppStr(MSG_EDVIEW_HELP_BODY));

                        break;
                case RAW_LEFT:
                        PreviousPosition(Lay, &Crsr);
                        break;

                case RAW_RIGHT:
                        NextPosition(Lay, &Crsr);
                        break;

                case RAW_UP:
                        GetCursorPosition(&Crsr, &oldx, &oldy);

                        while (PreviousPosition(Lay, &Crsr)) {
                                GetCursorPosition(&Crsr, &x, &y);
                                if (y < oldy && x <= oldx) break;
                        }
                        break;

                case RAW_DOWN:
                        GetCursorPosition(&Crsr, &oldx, &oldy);

                        while (NextPosition(Lay, &Crsr)) {
                                GetCursorPosition(&Crsr, &x, &y);
                                if (y > oldy) {
                                        oldy = y;
                                        break;
                                }
                        }
                        while (NextPosition(Lay, &Crsr)) {
                                GetCursorPosition(&Crsr, &x, &y);
                                if (y > oldy || x > oldx) {
                                        PreviousPosition(Lay, &Crsr);
                                        break;
                                }
                        }
                        break;
        }
        GetCursorPosition(&Crsr, &x, &y);
        DisplayCursor(Lay->Window->RPort, x, y);
        if (Crsr.vf != oldpos) SelectVisFld(Lay, NULL); /* Cursor has moved, so clear rubberband */
        
        return 0;
}


static int VanillaKey(struct Pro *Pr, struct Layout *Lay)
{
        struct VisFldInfo *vf = NULL;

        char oldVisSep;

        SaveUndo(Lay);
        switch (DB_Msg.Code) {
                case '\r':
                        if (Crsr.after) vf = Crsr.vf;
                        else vf = PrevVisFldInfo(Lay, Crsr.vf);
                        if (!vf) break;
                        oldVisSep = vf->VisSep;
                        switch (vf->VisSep) {
                                case ' ':
                                case '\t':
                                        vf->VisSep = '\n';
                                        break;
                                case '\n':
                                        vf->VisSep = '\f';
                                        break;
                        }
                        break;

                case '\t':
                        if (Crsr.after) vf = Crsr.vf;
                        else vf = PrevVisFldInfo(Lay, Crsr.vf);
                        if (!vf) break;
                        oldVisSep = vf->VisSep;
                        if (vf->VisSep == ' ') {
                                vf->VisSep = '\t';
                        }
                        break;
                        
                case '\b':
                        if (Crsr.after) break;
                        else vf = PrevVisFldInfo(Lay, Crsr.vf);
                        if (!vf) break;
                        oldVisSep = vf->VisSep;
                        switch (vf->VisSep) {
                                case '\t':
                                case '\n':
                                        vf->VisSep = ' ';
                                        break;
                                case '\f':
                                        vf->VisSep = '\n';
                                        break;
                        }
                        break;

                case VANILLA_DEL:
                        if (Selected) { /* Delete the selected field */
                                SaveUndo(Lay);
                                DeleteVisFldInfo(RemoveVisFldInfo(Lay, Selected));
                                Redraw(Pr, Lay);
                                return 0;
                        }
                        else {
                                if (!Crsr.after) break;
                                else vf = Crsr.vf;
                                if (!vf) break;
                                oldVisSep = vf->VisSep;
                                switch (vf->VisSep) {
                                        case '\t':
                                        case '\n':
                                                vf->VisSep = ' ';
                                                break;
                                        case '\f':
                                                vf->VisSep = '\n';
                                                break;
                                }
                        }
                        break;

                case VANILLA_ESC:
                        return 1;       /* Exit design mode */
                        break;
        }

        if (vf && vf->VisSep != oldVisSep) {
                if (Redraw(Pr, Lay) < 0) {              /* Oops, this didn't fit on screen */
                        DisplayBeep(Scr);
                        vf->VisSep = oldVisSep;
                        if (Redraw(Pr, Lay) < 0) ByeBye();              /* This should never happen */
                }
                WriteVisTags(vf);       /* Update RFF tags in vf */
        }

        return 0;
}


int MouseButtons(struct Pro *Pr, struct Layout *Lay)
{
        static ULONG StartSeconds=0, StartMicros=0;
        struct VisFldInfo *oldselected = Selected;

        if (Selected = VisFldHit(Lay, DB_Msg.MouseX, DB_Msg.MouseY)) {
                struct Space *sp = &Selected->Pos;
                Rubberband(Lay->Window->RPort, sp->XOffset, sp->YOffset, sp->Width, sp->Height, 0);

                if (DoubleClick(StartSeconds, StartMicros, DB_Msg.Seconds, DB_Msg.Micros) &&
                         Selected == oldselected) {
                        if (DragCnt) {  /* Cancel dragging */
                                DragCnt = 0;
                                RestorePointer(Lay->Window);
                        }
                        EditVisFldInfo(Pr, Lay, Selected);
                        StartSeconds = StartMicros = 0; /* Prevent "triple clicks" */
                }
                else {
                        StartSeconds = DB_Msg.Seconds; StartMicros = DB_Msg.Micros;
                        if (Selected) {
                                if (SizeHit(Selected, DB_Msg.MouseX)) {
                                        Sizing = TRUE;
                                        Forbid();
                                        Lay->Window->Flags |= WFLG_RMBTRAP;
                                        Permit();
                                }
                                else DragCnt = 1; /* Initiate dragging */
                        }
                }
        }
        else {
                Rubberband(Lay->Window->RPort, -1, -1, -1, -1, 0);
                FollowMouse(Lay, DB_Msg.MouseX, DB_Msg.MouseY);
        }
        return 0;
}


static void ToolBoxClicked(struct Pro *Pr, struct Layout *Lay, int gadgetID)
{
        struct VisFldInfo *vf;

        /* Make a VisFldInfo */
        if (!(vf = NewVisFldInfo())) return;
        
        /* Fill in type (string, cycle, checkbox, textfield or text) */
        switch (gadgetID) {
                case CheckBoxID:
                        CreateAndAddTag(&vf->VisTags, FTYP, STR_CHECKBOX);
                        vf->VisLen = 3; /* smaller is better */
                        break;
                case TextID:
                        CreateAndAddTag(&vf->VisTags, FTYP, STR_TEXT);
                        vf->VisLen = 10;
                        break;
                case TextFieldID:
                        if(TextFieldClass)
                            {
                            CreateAndAddTag(&vf->VisTags, FTYP, STR_TEXTFIELD);
                            CreateAndAddTag(&vf->VisTags, ROWS, "4");
                            vf->VisLen = 15;
                            }
                        break;
                case CycleID:
                        CreateAndAddTag(&vf->VisTags, FTYP, STR_CYCLE);
                        CreateAndAddTag(&vf->VisTags, CENT, GetAppStr(MSG_EDVIEW_CYCLE_1));
                        CreateAndAddTag(&vf->VisTags, CENT, GetAppStr(MSG_EDVIEW_CYCLE_2));
                        CreateAndAddTag(&vf->VisTags, CENT, GetAppStr(MSG_EDVIEW_CYCLE_3));
                        vf->VisLen = 16;        /* smaller is better */
                        break;
        }
        ReadVisTags(Pr, vf);    /* This way we get the cycle-list in vf */
        vf->Name[0] = '\0'; 

        /* Release the user */
        if (!DoEdit(Pr, vf)) {  /* Cancel */
                DeleteVisFldInfo(vf);
                return;
        }

        InsertVisFldInfo(Lay, vf);              /* link it */

        if (Redraw(Pr, Lay) < 0) {              /* Oops, this didn't fit on screen */
                DisplayBeep(Scr);
                DeleteVisFldInfo(RemoveVisFldInfo(Lay, vf));
                Redraw(Pr, Lay);
        }
        if (!Crsr.vf) Crsr.vf = Lay->FirstVisFldInfo;   /* Move cursor on-track */
}

/**********************************************************************/
/*                           IDCMP handler                            */
/**********************************************************************/

static int DesignWindowIDCMP(struct Pro *Pr, struct Layout *Lay)
{
        struct IntuiMessage     *m;
        struct MenuItem         *n;
        int                     (*func)(void);
        struct Window *Win = Lay->Window;
        ULONG signals, winsig, toolboxsig = 0;
        BOOL running = TRUE;

        winsig = 1L << Win->UserPort->mp_SigBit;
        if (ToolBox) toolboxsig = 1L << ToolBox->UserPort->mp_SigBit;

        for (;;) {
//              WaitPort(Win->UserPort);
                signals = Wait(winsig | toolboxsig);

                if (signals & winsig) {
                        while( m = GT_GetIMsg( Win->UserPort )) {

                                CopyMem(( char * )m, ( char * )&DB_Msg, (long)sizeof( struct IntuiMessage ));

                                GT_ReplyIMsg( m );
                                
                                switch ( DB_Msg.Class ) {
                                
                                        case    IDCMP_REFRESHWINDOW:
                                                GT_BeginRefresh(Win);
                                                GT_EndRefresh(Win, TRUE );
                                                break;

                                        case    IDCMP_CLOSEWINDOW:
                                                return 0;
                                                break;

                                        case    IDCMP_NEWSIZE:
//      //                                      printf("IDCMP_NEWSIZE\n");
                                                if (!(Win->Flags & WFLG_ZOOMED)) Redraw(Pr, Lay);
                                                break;

                                        case    IDCMP_VANILLAKEY:
//      //                                      printf("IDCMP_VANILLAKEY\n");
                                                running = VanillaKey(Pr, Lay);
                                                if (running) return running;
                                                break;

                                        case    IDCMP_RAWKEY:
//      //                                      printf("IDCMP_RAWKEY\n");
                                                running = RawKey(Pr, Lay);
                                                break;

                                        case    IDCMP_MOUSEMOVE:
                                                {
                                                        if (Sizing) {
                                                                Selected->Pos.Width = DB_Msg.MouseX - Selected->Pos.XOffset;
                                                                FLOOR(Selected->Pos.Width, FontX+STRGADFRAMESWIDTH);
                                                                SelectVisFld(Lay, Selected);    /* Update rubberband */
                                                                break;
                                                        }
                                                        else if (DragCnt) {
                                                                if (DragCnt++ == DRAGTRESHOLD) {
                                                                        ChangePointer(Win, MovePointer);
                                                                        Forbid();
                                                                        Win->Flags |= WFLG_RMBTRAP;
                                                                        Permit();
                                                                }
                                                                FollowMouse(Lay, DB_Msg.MouseX, DB_Msg.MouseY);
                                                        }
                                                        else if (SizeHit(VisFldHit(Lay, DB_Msg.MouseX, DB_Msg.MouseY), DB_Msg.MouseX))
                                                                ChangePointer(Win, LRPointer);
                                                        else RestorePointer(Win);
                                                }
//                                              printf("IDCMP_MOUSEMOVE\n");
                                                break;

                                        case IDCMP_MOUSEBUTTONS:
                                                if (DB_Msg.Code == SELECTDOWN) {
                                                        running = MouseButtons(Pr, Lay);
                                                }
                                                else if (DB_Msg.Code == SELECTUP) {
                                                        if (Win->Flags & WFLG_RMBTRAP) {        /* Enable menu button again */
                                                                Forbid();
                                                                Win->Flags &= ~WFLG_RMBTRAP;
                                                                Permit();
                                                        }
                                                        if (DragCnt >= DRAGTRESHOLD) {
                                                                RestorePointer(Win);
                                                                if (Crsr.vf != Selected) {              /* Move it */
                                                                        SaveUndo(Lay);
                                                                        InsertVisFldInfo(Lay, RemoveVisFldInfo(Lay, Selected));
                                                                        Redraw(Pr, Lay);
                                                                }
                                                        }
                                                        else if (Sizing) {
                                                                Sizing = FALSE;
                                                                SaveUndo(Lay);
                                                                Selected->VisLen = (Selected->Pos.Width+(FontX+1)/2-STRGADFRAMESWIDTH)/FontX;
                                                                WriteVisTags(Selected); /* Update RFF tags in vf */
                                                                Redraw(Pr, Lay);
                                                        }
                                                        DragCnt = 0;
                                                }
                                                else if (DB_Msg.Code == MENUUP) { /* Cancel */
                                                        if (Win->Flags & WFLG_RMBTRAP) {        /* Enable menu button again */
                                                                Forbid();
                                                                Win->Flags &= ~WFLG_RMBTRAP;
                                                                Permit();
                                                        }

                                                        if (DragCnt) {  /* Cancel dragging */
                                                                DragCnt = 0;
                                                                RestorePointer(Win);
                                                        }
                                                        else if (Sizing) {      /* Cancel sizechange */
                                                                Sizing = FALSE;
                                                                Redraw(Pr, Lay);
                                                        }
                                                }
                                                break;
                                                
                                        case IDCMP_MENUPICK:
                                                while( DB_Msg.Code != MENUNULL ) {
                                                        n = ItemAddress( Win->MenuStrip, DB_Msg.Code );
                                                        func = (int (*)(void))(GTMENUITEM_USERDATA( n ));
                                                        running = func();
                                                        if (running) return running;    /* The window has been closed! */
                                                        DB_Msg.Code = n->NextSelect;
                                                }
                                                break;
                                }
                        }
                }       /* winsig signals */

                if (signals & toolboxsig) {
                        while( m = GT_GetIMsg( ToolBox->UserPort )) {

                                CopyMem(( char * )m, ( char * )&DB_Msg, (long)sizeof( struct IntuiMessage ));

                                GT_ReplyIMsg( m );
                                
                                switch ( DB_Msg.Class ) {
                                        case IDCMP_GADGETUP:
//                                              printf("Gadget up\n");
                                                ActivateWindow(Win);
                                                ToolBoxClicked(Pr, Lay, ((struct Gadget *)DB_Msg.IAddress)->GadgetID);
                                                break;
                                }
                        }
                } /* toolbox signals */
        }


        return( running );
}


char TitleBuffer[80];

static int DoOpenDesignWindow(struct Pro *Pr, struct Layout *Lay, WORD ww, WORD wh)
{

        /* Does the actual opening (in DESIGN_MODE) */
        WORD leftedge, topedge;

        /* Position window nicely */
        if (LastLeftEdge == -1) {       /* No previous window to use */
                leftedge = (Scr->Width - ww) >> 1;      /* Middle */
                topedge = (Scr->Height-wh) >> 1;                /* Middle */
        }
        else {
                leftedge = LastLeftEdge;
                topedge = LastTopEdge;
        }

        /* We create this menu every time we enter view design */
        if ( ! ( DesignMenus = CreateMenus( DesignNewMenu, TAG_DONE )))
                return( 3L );

        LayoutMenus( DesignMenus, VisualInfo, GTMN_NewLookMenus, TRUE, TAG_DONE );


        /* Localization. Not optimal, but works ;-) */
        ES_ViewDesignHelp.es_Title = GetAppStr(MSG_EDVIEW_HELP_TITLE);
        ES_ViewDesignHelp.es_GadgetFormat = GetAppStr(MSG_REQ_OK);
        sprintf(TitleBuffer, GetAppStr(MSG_EDVIEW_SCR_TITLE), VERSION_NUMBER);

        if ( ! ( Lay->Window = OpenWindowTags( NULL,
                                WA_Left,           leftedge,
                                WA_Top,         topedge,
                                WA_Width,       ww,
                                WA_Height,      wh,
                                WA_IDCMP,       IDCMP_MOUSEBUTTONS|IDCMP_MOUSEMOVE|IDCMP_NEWSIZE|IDCMP_MENUPICK|IDCMP_CLOSEWINDOW|IDCMP_RAWKEY|IDCMP_VANILLAKEY|IDCMP_REFRESHWINDOW,
                                WA_Flags,       WFLG_REPORTMOUSE|WFLG_NEWLOOKMENUS|WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|WFLG_SMART_REFRESH|WFLG_ACTIVATE,
                                WA_Gadgets,     Lay->GList,
                                WA_Title,       GetAppStr(MSG_EDVIEW_WIN_TITLE),
                                WA_ScreenTitle, TitleBuffer,
                                WA_PubScreen,   Scr,
                                WA_Zoom,        DB_Zoom,
//                              WA_MenuHelp, TRUE,
                                TAG_DONE )))
        return WIN_ERR;

        SetMenuStrip( Lay->Window, DesignMenus );
        GT_RefreshWindow( Lay->Window, NULL);

        /* Now detach all gadgets from the window as this is the design mode */
        RemoveGList(Lay->Window, Lay->GList, -1);

        /* We delete the gadgets and just leave the imagery */
        DeleteAllGadgets(Lay, DESIGN_MODE);

        /* For IDCMP handling used in this file */
        WinSig = 1L << Lay->Window->UserPort->mp_SigBit;

        return 0;
}

void FillClip(struct VisFldInfo *vf)
{
        if (Clip) {
                DeleteVisFldInfo(Clip);
        }
        Clip = vf;
}

/**********************************************************************/
/*                        Interface functions                         */
/**********************************************************************/

void Design(struct Pro *Pr, struct Layout *Lay)
{
        /* This is the main function in the design system */
        WORD ww, wh;
        WORD x,y;

/*      ww = Lay->Window->Width;
        wh = Lay->Window->Height;
*/
        CloseLayWin(Pr, Lay);
        CalcAllPos(Pr, Lay, &ww, &wh); // Shouldn't be nessesary as we reopen the window
        CreateAllGadgets(Pr, Lay, ww, wh, DESIGN_MODE);
        DoOpenDesignWindow(Pr, Lay, ww, wh);

        InitCursor(Lay, &Crsr);
        Selected = NULL;
        GetCursorPosition(&Crsr, &x, &y);
        DisplayCursor(Lay->Window->RPort, x, y);

        ToolBox = OpenToolBox(Lay->Window);     /* "Parent" window */

        DesignWindowIDCMP(Pr, Lay);

        DeleteUndo(Lay);
        FillClip(NULL);

        CloseDesignWin(Lay);
        if (ToolBox) {
                CloseToolBox(ToolBox);
                ToolBox = NULL;
        }
        OpenLayWin(Pr, Lay);
        UpdateWindow(Pr);
        UpdateDragBar(Pr);

        /* Back to db run-mode */
}



/**********************************************************************/
/*                        GadToolBox handlers                         */
/**********************************************************************/


int LISTClicked( void )
{
        /* routine when gadget "Object list" is clicked. */
        return 0;
}

int TOPClicked( void )
{
        /* routine when gadget "Top" is clicked. */
        return 0;
}

int UPClicked( void )
{
        /* routine when gadget "Up" is clicked. */
        return 0;
}

int DOWNClicked( void )
{
        /* routine when gadget "Down" is clicked. */
        return 0;
}

int BOTTOMClicked( void )
{
        /* routine when gadget "Bottom" is clicked. */
        return 0;
}

int NEWClicked( void )
{
        /* routine when gadget "_New..." is clicked. */
        return 0;
}

int EDITClicked( void )
{
        /* routine when gadget "_Edit..." is clicked. */
        return 0;
}

int COPYClicked( void )
{
        /* routine when gadget "Co_py" is clicked. */
        return 0;
}

int REMOVEClicked( void )
{
        /* routine when gadget "Remove" is clicked. */
        return 0;
}

int USEClicked( void )
{
        /* routine when gadget "_Use" is clicked. */
        return 0;
}

int CANCELClicked( void )
{
        /* routine when gadget "_Cancel" is clicked. */
        return 0;
}

int TAG_VALUEClicked( void )
{
        /* routine when gadget "_Value" is clicked. */
        return 0;
}

int TAG_IDClicked( void )
{
        /* routine when gadget "_ID" is clicked. */
        return 0;
}

int TAG_OKClicked( void )
{
        /* routine when gadget "_Ok" is clicked. */
        return 0;
}

int TAG_CANCELClicked( void )
{
        /* routine when gadget "_Cancel" is clicked. */
        return 0;
}

int NAME_NAMEClicked( void )
{
        /* routine when gadget "_Name" is clicked. */
        return 0;
}

int NAME_OKClicked( void )
{
        /* routine when gadget "_Ok" is clicked. */
        return 0;
}

int NAME_CANCELClicked( void )
{
        /* routine when gadget "_Cancel" is clicked. */
        return 0;
}

int FIELD_NAMEClicked( void )
{
        /* routine when gadget "_Name" is clicked. */
        return 0;
}

int FIELD_MAXLENClicked( void )
{
        /* routine when gadget "Max _length" is clicked. */
        return 0;
}

int FIELD_OKClicked( void )
{
        /* routine when gadget "_Ok" is clicked. */
        return 0;
}

int FIELD_CANCELClicked( void )
{
        /* routine when gadget "_Cancel" is clicked. */
        return 0;
}

int FIELD_MOREClicked( void )
{
        /* routine when gadget "_More..." is clicked. */
        return 0;
}

int VIS_NAMEClicked( void )
{
        /* routine when gadget "_Name" is clicked. */
        return 0;
}

int VIS_SIZEClicked( void )
{
        /* routine when gadget "Si_ze" is clicked. */
        return 0;
}

int VIS_OKClicked( void )
{
        /* routine when gadget "_Ok" is clicked. */
        return RET_OK;
}

int VIS_CANCELClicked( void )
{
        /* routine when gadget "_Cancel" is clicked. */
        return RET_CANCEL;
}

int VIS_MOREClicked( void )
{
        /* routine when gadget "_More..." is clicked. */
        return 0;
}

int VIS_SELECTClicked( void )
{
        /* routine when gadget "..." is clicked. */
        Select *s;
        struct FldInfo *f;
        BOOL again = TRUE;


        if (!(s = NewSelect())) {
                DisplayBeep(Scr);
                return 0;
        }
        for (f = CurrentPro->FirstFldInfo; f; f = f->Next)
                AddStringLast(s, f->Name);

        s->SelIndex = Vf->Offset;
        OpenSelect(s, VisWnd, GetAppStr(MSG_EDVIEW_WIN_SELECT_TITLE), NULL);
        while (again) {
                WaitPort(s->SelWnd->UserPort);
                switch(HandleSelectIDCMP(s)) {
                        case SEL_SELECT :
                                break;
                        case SEL_OK :
                                Vf->Offset = s->SelIndex;
                                GT_SetGadgetAttrs(VisGadgets[GD_VIS_FIELD], VisWnd, NULL,
                                        GTTX_Text, SelectedString(s),
                                        TAG_END);
                                again = FALSE;
                                break;
                        case SEL_CANCEL :
                                again = FALSE;
                                break;
                        case SEL_HELP :
                                break;
                }
        }

/*
        CloseSelect(s);
        RemoveAllStrings(s);
*/
        DeleteSelect(s);

        return 0;
}

int MainCloseWindow( void )
{
        /* routine for "IDCMP_CLOSEWINDOW". */
        return 1;       /* i.e. Leave this mode */
}

int MainVanillaKey( void )
{
        /* routine for "IDCMP_VANILLAKEY". */
        return 0;
}

int MainRawKey( void )
{
        /* routine for "IDCMP_RAWKEY". */
        return 0;
}

int TagCloseWindow( void )
{
        /* routine for "IDCMP_CLOSEWINDOW". */
        return 0;
}

int TagVanillaKey( void )
{
        /* routine for "IDCMP_VANILLAKEY". */
        return 0;
}

int NameCloseWindow( void )
{
        /* routine for "IDCMP_CLOSEWINDOW". */
        return 0;
}

int NameVanillaKey( void )
{
        /* routine for "IDCMP_VANILLAKEY". */
        return 0;
}

int FieldCloseWindow( void )
{
        /* routine for "IDCMP_CLOSEWINDOW". */
        return 0;
}

int FieldVanillaKey( void )
{
        /* routine for "IDCMP_VANILLAKEY". */
        return 0;
}

int VisCloseWindow( void )
{
        /* routine for "IDCMP_CLOSEWINDOW". */
        return RET_CANCEL;
}

int VisVanillaKey( void )
{
        /* routine for "IDCMP_VANILLAKEY". */

        switch (ToUpper(VisMsg.Code)) {
        case 'N':
                ActivateGadget(VisGadgets[GD_VIS_NAME], VisWnd, NULL);
                break;

        case 'Z':
                ActivateGadget(VisGadgets[GD_VIS_SIZE], VisWnd, NULL);
                break;

        case 'T':
                {
                ULONG active;

                if(GT_GetGadgetAttrs(VisGadgets[GD_VIS_TYPE], VisWnd, NULL,
                                   GTCY_Active, &active, TAG_END) == 1)
                    {
                    active = (active + 1) % 5;

                    GT_SetGadgetAttrs(VisGadgets[GD_VIS_TYPE], VisWnd, NULL,
                                   GTCY_Active, active, TAG_END);
                    }
                }
                break;

        case 'O':
        case '\r':
                return VIS_OKClicked();
                break;
                
        case 'C':
        case VANILLA_ESC:
                return VIS_CANCELClicked();
                break;
        }
        return 0;
}

int DesignPROJECT_ABOUT( void )
{
        /* routine when (sub)item "About view design..." is selected. */
        EasyRequest(CurrentPro->CurrentLayout->Window, &ES_ViewDesignHelp, NULL, GetAppStr(MSG_EDVIEW_HELP_BODY));

        return 0;
}

int DesignPROJECT_EXIT( void )
{
        /* routine when (sub)item "Exit view design" is selected. */
        return 1;       /* Leave View design */
}

int DesignEDIT_UNDO( void )
{
        /* routine when (sub)item "Undo" is selected. */

        if (PerformUndo(CurrentPro->CurrentLayout))
                Redraw(CurrentPro, CurrentPro->CurrentLayout);
        else DisplayBeep(Scr);
        return 0;
}

int DesignEDIT_CUT( void )
{
        /* routine when (sub)item "Cut" is selected. */

        if (Selected) {
                SaveUndo(CurrentPro->CurrentLayout);
                FillClip(RemoveVisFldInfo(CurrentPro->CurrentLayout, Selected));
                OnMenu(CurrentPro->CurrentLayout->Window, FULLMENUNUM(1,4,NOSUB)); /* Edit->Paste */
                Redraw(CurrentPro, CurrentPro->CurrentLayout);
        }
        return 0;
}

int DesignEDIT_COPY( void )
{
        /* routine when (sub)item "Copy" is selected. */

        if (Selected) {
                FillClip(CopyVisFldInfo(Selected));
                OnMenu(CurrentPro->CurrentLayout->Window, FULLMENUNUM(1,4,NOSUB)); /* Edit->Paste */
        }
        return 0;
}

int DesignEDIT_PASTE( void )
{
        /* routine when (sub)item "Paste" is selected. */

        SaveUndo(CurrentPro->CurrentLayout);
        InsertVisFldInfo(CurrentPro->CurrentLayout, CopyVisFldInfo(Clip));
        Redraw(CurrentPro, CurrentPro->CurrentLayout);
        return 0;
}
