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

vJet V1.0 © 1992 Jan Pfeil · fileviewer for AMIGA
Last changes:   15.04.92

vJet find and goto routines

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

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <graphics/gfxbase.h>
#include <intuition/gadgetclass.h>
#include <intuition/intuitionbase.h>
#include <libraries/gadtools.h>

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

#include "extern.h"

#define GAD_STRING 1
#define GAD_OK 2
#define GAD_CANCEL 3
#define GAD_CASE 4
#define GAD_DIR 5


static struct Gadget *stringGad = NULL;
static struct VisualInfo *vI = NULL;


LONG findStr(char *pat,char *str,const LONG len,LONG pos,const LONG dir)
{
LONG patlen = strlen(pat);
char *s;

if(len >= patlen) {     //string should be able to hold pattern
    if(dir > 0) {       //find forward
        for(s=str+pos;s <= str+len-patlen; s++) {
            if(vjet->caseInSensitive) {
                //compare case insensitive
                if(!strnicmp(s,pat,patlen))         //returns ZERO for equality
                    return(pos = s-str);
                }
            else {
                //compare case sensitive
                if(!strncmp(s,pat,patlen))          //returns ZERO for equality
                    return(pos = s-str);
                }
            }
        pos = -1;
        }
    else if(dir < 0) {  //find backward
        for(s=str+pos-patlen;s >= str; s--) {
            if(vjet->caseInSensitive) {
                //compare case insensitiv
                if(!strnicmp(s,pat,patlen))
                    return(pos = s-str);
                }
            else {
                //compare case sensitive
                if(!strncmp(s,pat,patlen))
                    return(pos = s-str);
                }
            }
        pos = -1;
        }
    }
else {
    pos = -1;
    }
return(pos);
}

void mark(struct vJet *vjet,int len)
{
LONG x,y;

x = vjet->dvF->tf_XSize*(vjet->findpos[1]-vjet->topcolumn);
y = vjet->dvF->tf_YSize*(vjet->findpos[0]-vjet->topline);

SetDrMd(vjet->win->RPort,JAM2|COMPLEMENT);
Move(vjet->win->RPort, x, y);
RectFill(vjet->win->RPort,x,y,x+len*vjet->dvF->tf_XSize-1,y+vjet->dvF->tf_YSize-1);
SetDrMd(vjet->win->RPort,JAM2);
}

struct EasyStruct noFindPattern = {
    sizeof(struct EasyStruct),0,
    "Find Request ",                /*0xA0 at end of string for arq*/
    "No find pattern!",
    "OK"
    };

struct EasyStruct notFound = {
    sizeof(struct EasyStruct),0,
    "Find Request ",                /*0xA0 at end of string for arq*/
    "%s\nnot found!",
    "OK"
    };

BOOL find(struct vJet *vjet,const LONG direction)
{
BOOL found = FALSE;
LONG line,pos = -1;
BOOL ret = TRUE;

if(*vjet->find != '\0') {
    titleWin(vjet,"searching...");

    //if no marked string on current page, start at topline or bottom
    //search vertical
    if(vjet->findpos[0] < vjet->topline || vjet->findpos[0] > vjet->topline+vjet->linesPerPage) {
        //its outside this page
        if(direction > 0)
            vjet->findpos[0] = vjet->topline;
        else
            vjet->findpos[0] = vjet->topline+vjet->linesPerPage;
        vjet->findpos[1] = 0;
        }
    else {
        //unmark last
        mark(vjet,vjet->findLen);
        vjet->findpos[1] += direction;  //goto next column
        }

    //now we start from latest findpos with direction
    for(line=vjet->findpos[0];!found && line>=0 && line<=vjet->maxline;line+=direction) {
        pos = findStr(vjet->find,vjet->lineArray[line].string,vjet->lineArray[line].len,vjet->findpos[1],direction);
        if(pos>=0) {      //something found
            found = TRUE;
            titleWin(vjet,vjet->find);
            vjet->findpos[0] = line;
            vjet->findpos[1] = pos;
            vjet->findLen = strlen(vjet->find);
            //out of sight
            if(line < vjet->topline) {
                movePageVert(vjet,line-vjet->topline);
                setVSlider(vjet);
                }
            else if(line+direction > vjet->topline+vjet->linesPerPage) {
                movePageVert(vjet,line+direction-vjet->topline-vjet->linesPerPage);
                setVSlider(vjet);
                }
            if(pos < vjet->topcolumn) {
                movePageHoriz(vjet,pos-vjet->topcolumn);
                setHSlider(vjet);
                }
            else if(pos+vjet->findLen > vjet->topcolumn+vjet->columnsPerPage) {
                movePageHoriz(vjet,pos+vjet->findLen - (vjet->topcolumn+vjet->columnsPerPage));
                setHSlider(vjet);
                }
            mark(vjet,vjet->findLen);
            }
        else {
            if(direction > 0)
                vjet->findpos[1] = 0;
            else if(direction < 0)
                vjet->findpos[1] = vjet->lineArray[line+direction].len;
            }
        }
    if((line < 0 && !found) || line > vjet->maxline) {
        arqEasyRequest(vjet->win,notFound,ARQ_ID_EXCLAM,NULL,vjet->find);
        vjet->findpos[0] = -1;
        vjet->findpos[1] = -1;
        }
    }
else {
    arqEasyRequest(vjet->win,noFindPattern,ARQ_ID_EXCLAM,NULL,NULL);
    }

titleWin(vjet,vjet->path[vjet->pathNum]);
return(ret);
}
/*end of find()*/


static STRPTR directionText[]={"forward","backward",NULL};

static struct Gadget *createFindGadgets(struct vJet *vjet,struct Window *win)
{
struct NewGadget ng;
struct Gadget *gad,*glist = NULL;

gad = CreateContext(&glist);

ng.ng_TextAttr = &(vjet->dtA);
ng.ng_VisualInfo = vI;
ng.ng_UserData = NULL;
ng.ng_Height = vjet->dvF->tf_YSize +2*INTERHEIGHT;      //one pixel above and below!

//string
ng.ng_LeftEdge = vjet->dvF->tf_XSize;
ng.ng_TopEdge = win->BorderTop+INTERHEIGHT;
ng.ng_Width = win->Width - 2*vjet->dvF->tf_XSize;
ng.ng_GadgetText = "";
ng.ng_GadgetID = GAD_STRING;
ng.ng_Flags = PLACETEXT_LEFT;
stringGad = gad = CreateGadget(STRING_KIND,gad,&ng,
                   GTST_String,vjet->find,
                   GTST_MaxChars,100,
                   GA_Selected,TRUE,
                   STRINGA_ExitHelp,TRUE,
                   TAG_DONE);

//case sensitive
ng.ng_TopEdge += ng.ng_Height+INTERHEIGHT;
ng.ng_Width=20;
ng.ng_GadgetText="case insensitive";
ng.ng_GadgetID=GAD_CASE;
ng.ng_Flags=PLACETEXT_RIGHT;
gad = CreateGadget(CHECKBOX_KIND,gad,&ng,
                   GTCB_Checked,vjet->caseInSensitive,
                   GA_Disabled,FALSE,
                   TAG_DONE);

//search direction
ng.ng_Height = vjet->dvF->tf_YSize + INTERHEIGHT;
ng.ng_Width = 8*vjet->dvF->tf_XSize + 3*INTERWIDTH;
ng.ng_LeftEdge = win->Width - ng.ng_Width - vjet->dvF->tf_XSize;
ng.ng_GadgetText="";
ng.ng_GadgetID=GAD_DIR;
ng.ng_Flags=PLACETEXT_RIGHT;
gad = CreateGadget(CYCLE_KIND,gad,&ng,
                   GTCY_Labels,directionText,
                   GTCY_Active,vjet->findDir,   /*must be handled by IDCMP_GADGETUP*/
                   GA_Disabled,FALSE,
                   TAG_DONE);

//OK button
ng.ng_Height = vjet->dvF->tf_YSize +2*INTERHEIGHT;
ng.ng_LeftEdge = vjet->dvF->tf_XSize;
ng.ng_TopEdge += ng.ng_Height;   // vjet->dvF->tf_YSize + INTERHEIGHT;
ng.ng_Width = 8*vjet->dvF->tf_XSize;
ng.ng_GadgetText = "OK";
ng.ng_GadgetID = GAD_OK;
ng.ng_Flags=PLACETEXT_IN;
gad = CreateGadget(BUTTON_KIND, gad, &ng,
                            GA_Disabled,FALSE,
                            TAG_DONE);

//CANCEL button
ng.ng_LeftEdge = win->Width - ng.ng_Width - vjet->dvF->tf_XSize;
ng.ng_GadgetText = "Cancel";
ng.ng_GadgetID = GAD_CANCEL;
ng.ng_Flags=PLACETEXT_IN;
gad = CreateGadget(BUTTON_KIND, gad, &ng,
                            GA_Disabled,FALSE,
                            TAG_DONE);

return(glist);
}

static BOOL handleFindUpGad(struct vJet *vjet,struct Gadget *g,UWORD code)
{
BOOL flag = FALSE;
switch(g->GadgetID) {
    case GAD_OK:
    case GAD_STRING:
                strncpy(vjet->find,((struct StringInfo *)stringGad->SpecialInfo)->Buffer,100);
                flag = TRUE;
                break;
    case GAD_CANCEL:
                flag = -TRUE;
                break;
    case GAD_CASE:
                vjet->caseInSensitive = (g->Flags & SELECTED);
                break;
    case GAD_DIR:
                vjet->findDir = code;
                break;
    default:    break;
    }
return(flag);
}

static BOOL handleFindIDCMP(struct vJet *vjet,struct Window *win)
{
BOOL flag = FALSE;
USHORT code, qualif;
struct IntuiMessage *msg = NULL;
APTR iadr;
ULONG class;

while(msg = GT_GetIMsg(win->UserPort) ) {
    class = msg->Class;
    code = msg->Code;
    qualif = msg->Qualifier;
    iadr = msg->IAddress;
    GT_ReplyIMsg(msg);

    switch( class ) {
        case IDCMP_ACTIVEWINDOW:ActivateGadget(stringGad,win,NULL);
                                break;
        case IDCMP_GADGETUP:    flag = handleFindUpGad(vjet,(struct Gadget *)iadr,code);
                                break;
        default:            titleWin(vjet,"ERROR: unresolved msg->class");
                            break;
        } /* end switch class */

    if(flag)                        /* return immediatly */
        return(flag);
    }
return(flag);
} /*end handleIDCMP()*/



/*
    public functions
*/

BOOL findReq(struct vJet *vjet,char *string)
{
struct Window *win = NULL;
SHORT width = 2*20 + 26*vjet->dvF->tf_XSize + 3*INTERWIDTH;
SHORT height = vjet->stA->ta_YSize+3*vjet->dvF->tf_YSize + 10*INTERHEIGHT;
ULONG signalmask, signals;
struct Gadget *glist = NULL;
BOOL ret = TRUE;
BOOL done = FALSE;

if(!vjet->customScreen)
    vjet->scr = vjet->win->WScreen;

if(win = OpenWindowTags(NULL,
            WA_Left,vjet->win->LeftEdge+(vjet->win->GZZWidth-width)/2,
            WA_Top,vjet->win->TopEdge+vjet->win->BorderTop+1+(vjet->win->GZZHeight-height)/2,
            WA_Width,width,
            WA_Height,height,
            WA_DetailPen,DETAILPEN,
            WA_BlockPen,BLOCKPEN,
            WA_Flags,WFLG_DRAGBAR+WFLG_DEPTHGADGET+WFLG_SMART_REFRESH,
            WA_IDCMP,IDCMP_ACTIVEWINDOW+IDCMP_GADGETUP,
            WA_Title,"Enter find pattern ...",
            WA_ScreenTitle,"find ASCII string in Text",
            WA_CustomScreen,vjet->scr,
            WA_AutoAdjust,TRUE,
            WA_Activate,TRUE,
            TAG_END
            )) {

    vI = GetVisualInfo(win->WScreen,TAG_DONE);

    glist = createFindGadgets(vjet,win);
    AddGList(win,glist,(UWORD)-1,(UWORD)-1,NULL);
    RefreshGList(glist,win,NULL,(UWORD)-1);
    GT_RefreshWindow(win,NULL);
    ActivateGadget(stringGad,win,NULL);

    signalmask = 1L << win->UserPort->mp_SigBit;

    while(!done) {
        signals = Wait(signalmask);
        if (signals & signalmask)
            done = handleFindIDCMP(vjet,win);
        }
    }
else {
    titleWin(vjet,"ERROR: couldn't open requester window");
    ret = FALSE;
    }

if(win) {
    if(win->FirstGadget) RemoveGList(win,glist,(UWORD)-1);
    CloseWindow(win);
    FreeVisualInfo(vI);
    }
if(glist)
    FreeGadgets(glist);

if(done == TRUE) {
    if(vjet->findDir)
        find(vjet,-1);          //backward
    else
        find(vjet,1);           //forward
    }

return(ret);
}

VOID findNext(struct vJet *vjet)
{
find(vjet,1);
}

VOID findPrevious(struct vJet *vjet)
{
find(vjet,-1);
}

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

go function

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

static struct Gadget *createGoGadgets(struct vJet *vjet,struct Window *win)
{
struct NewGadget ng;
struct Gadget *gad,*glist = NULL;

gad = CreateContext(&glist);

ng.ng_TextAttr = &(vjet->dtA);
ng.ng_VisualInfo = vI;
ng.ng_UserData = NULL;
ng.ng_Height = vjet->dvF->tf_YSize +2*INTERHEIGHT;      //ein Pixel oben und unten!

//string
ng.ng_LeftEdge = vjet->dvF->tf_XSize;
ng.ng_TopEdge = win->BorderTop+INTERHEIGHT;
ng.ng_Width = win->Width - 2*vjet->dvF->tf_XSize;
ng.ng_GadgetText = "";
ng.ng_GadgetID = GAD_STRING;
ng.ng_Flags = PLACETEXT_LEFT;
stringGad = gad = CreateGadget(STRING_KIND,gad,&ng,
                   GTST_String,NULL,
                   GTIN_MaxChars,10,
                   GA_Selected,TRUE,
                   STRINGA_ExitHelp,TRUE,
                   TAG_DONE);

//OK button
ng.ng_TopEdge += ng.ng_Height+INTERHEIGHT;   // vjet->dvF->tf_YSize + INTERHEIGHT;
ng.ng_Width = 8*vjet->dvF->tf_XSize;
ng.ng_GadgetText = "OK";
ng.ng_GadgetID = GAD_OK;
ng.ng_Flags=PLACETEXT_IN;
gad = CreateGadget(BUTTON_KIND, gad, &ng,
                            GA_Disabled,FALSE,
                            TAG_DONE);

//CANCEL button
ng.ng_LeftEdge = win->Width - ng.ng_Width - vjet->dvF->tf_XSize;
ng.ng_GadgetText = "Cancel";
ng.ng_GadgetID = GAD_CANCEL;
ng.ng_Flags=PLACETEXT_IN;
gad = CreateGadget(BUTTON_KIND, gad, &ng,
                            GA_Disabled,FALSE,
                            TAG_DONE);

return(glist);
}

static BOOL handleGoUpGad(struct vJet *vjet,struct Gadget *g)
{
BOOL flag = FALSE;
switch(g->GadgetID) {
    case GAD_OK:
    case GAD_STRING:
                vjet->go = atol(((struct StringInfo *)stringGad->SpecialInfo)->Buffer);
                flag = TRUE;
                break;
    case GAD_CANCEL:
                flag = -TRUE;
                break;
    default:    break;
    }
return(flag);
}

static BOOL handleGoIDCMP(struct vJet *vjet,struct Window *win)
{
BOOL flag = FALSE;
USHORT code, qualif;
struct IntuiMessage *msg = NULL;
APTR iadr;
ULONG class;

while(msg = GT_GetIMsg(win->UserPort) ) {
    class = msg->Class;
    code = msg->Code;
    qualif = msg->Qualifier;
    iadr = msg->IAddress;
    GT_ReplyIMsg(msg);

    switch( class ) {
        case IDCMP_ACTIVEWINDOW:ActivateGadget(stringGad,win,NULL);
                                break;
        case IDCMP_GADGETUP:    flag = handleGoUpGad(vjet,(struct Gadget *)iadr);
                                break;
        default:            titleWin(vjet,"ERROR: unresolved msg->class");
                            break;
        } /* end switch class */

    if(flag)                        /* return immediatly */
        return(flag);
    }
return(flag);
} /*end handleIDCMP()*/

VOID gotoLine(struct vJet *vjet)
{
struct Window *win = NULL;
SHORT width = max(200,22*vjet->dvF->tf_XSize);
SHORT height = vjet->stA->ta_YSize+2*vjet->dvF->tf_YSize + 8*INTERHEIGHT;
ULONG signalmask, signals;
struct Gadget *glist = NULL;
BOOL done = FALSE;

if(!vjet->customScreen)
    vjet->scr = vjet->win->WScreen;

if(win = OpenWindowTags(NULL,
            WA_Left,vjet->win->LeftEdge+(vjet->win->GZZWidth-width)/2,
            WA_Top,vjet->win->TopEdge+vjet->win->BorderTop+1+(vjet->win->GZZHeight-height)/2,
            WA_Width,width,
            WA_Height,height,
            WA_DetailPen,DETAILPEN,
            WA_BlockPen,BLOCKPEN,
            WA_Flags,WFLG_DRAGBAR+WFLG_DEPTHGADGET+WFLG_SMART_REFRESH,
            WA_IDCMP,IDCMP_ACTIVEWINDOW+IDCMP_GADGETUP,
            WA_Title,"Enter line number to go to ...",
            WA_ScreenTitle,"Goto line",
            WA_CustomScreen,vjet->scr,
            WA_AutoAdjust,TRUE,
            WA_Activate,TRUE,
            TAG_END
            )) {

    vI = GetVisualInfo(win->WScreen,TAG_DONE);

    glist = createGoGadgets(vjet,win);
    AddGList(win,glist,(UWORD)-1,(UWORD)-1,NULL);
    RefreshGList(glist,win,NULL,(UWORD)-1);
    GT_RefreshWindow(win,NULL);
    ActivateGadget(stringGad,win,NULL);

    signalmask = 1L << win->UserPort->mp_SigBit;

    while(!done) {
        signals = Wait(signalmask);
        if (signals & signalmask)
            done = handleGoIDCMP(vjet,win);
        }
    }
else {
    titleWin(vjet,"ERROR: couldn't open requester window");
    }

if(win) {
    if(win->FirstGadget) RemoveGList(win,glist,(UWORD)-1);
    CloseWindow(win);
    FreeVisualInfo(vI);
    }
if(glist) FreeGadgets(glist);

if(done == TRUE && vjet->go != 0) {
    movePageVert(vjet,vjet->go-1 - vjet->topline);
    setVSlider(vjet);
    }

}

/*end of file*/