#include <p_std.h>
#include <wlib.h>

#include "lined.h"


GLREF_D UINT wMainWid;


LOCAL_C INT CreateGC(LINED *ed)
    {
    G_GC gc;

    if (ed->gc)
        return(FALSE);
    gc.font=ed->i.font;
    gc.style=ed->i.style;
    gCreateTempGC(ed->i.winid,G_GC_MASK_FONT|G_GC_MASK_STYLE,&gc);
    ed->gc=TRUE;
    return(TRUE);
    }

LOCAL_C VOID FreeGC(LINED *ed,INT gc)
    {
    if (!gc)
        return;
    ed->gc=FALSE;
    gFreeTempGC();
    }

LOCAL_C INT WidthTo(LINED *ed,INT blen)
    {
    return(gTextWidth(ed->i.font,ed->i.style,ed->pb,blen));
    }

LOCAL_C VOID GetBox(LINED *ed,P_RECT *pbox)
    {
    pbox->tl.x=ed->i.xoff;
    pbox->tl.y=ed->i.yoff;
    pbox->br.x=pbox->tl.x+ed->i.width;
    pbox->br.y=pbox->tl.y+ed->i.height;
    }

LOCAL_C VOID DrawCursor(LINED *ed)
    {
    W_CURSOR flash;

    flash.pos.x=ed->i.xoff+ed->marg+WidthTo(ed,ed->cur);
    flash.pos.y=ed->i.yoff+ed->i.asc;
    flash.height=ed->i.height;
    flash.ascent=ed->i.asc;
    flash.width=ed->cwidth;
    flash.flags=0;
    wTextCursor(ed->i.winid,&flash);
    }

LOCAL_C VOID ToggleHighlight(LINED *ed)
    {
    P_RECT box;
    INT gc;

    if (ed->select)
        {
        gc=CreateGC(ed);
        GetBox(ed,&box);
        box.br.x=box.tl.x+ed->marg+WidthTo(ed,ed->blen);
        gClrRect(&box,G_TRMODE_INV);
        FreeGC(ed,gc);
        }
    }

LOCAL_C VOID DrawAll(LINED *ed)
    {
    P_RECT box;
    INT gc;

    GetBox(ed,&box);
    gc=CreateGC(ed);
    gPrintBoxText(&box,ed->i.asc,G_TEXT_ALIGN_LEFT,ed->marg,ed->pb,ed->blen);
    if (ed->emph)
        {
        DrawCursor(ed);
        ToggleHighlight(ed);
        }
    FreeGC(ed,gc);
    }

GLDEF_C VOID le_destroy(LINED *ed)
    {
    p_free(ed->pb);
    p_free(ed);
    }

GLDEF_C LINED *le_init(IN_LINED *pin)
    {
    LINED *ed;

    ed=p_alloc(sizeof(LINED));
    if (!ed)
        return(NULL);
    p_bfil(ed,sizeof(LINED),0);
    ed->cwidth=2;
    ed->pb=p_alloc(pin->maxchars+1);
    if (!ed->pb)
        {
        p_free(ed);
        return(NULL);
        }
    ed->i=(*pin);
    ed->lmarg=ed->i.width/4;
    return(ed);
    }

LOCAL_C VOID ScrollForCursor(LINED *ed,INT draw)
    {
    INT oldmarg;
    INT curx;
    INT exx;
    INT exl;
    INT exr;

    oldmarg=ed->marg;
    curx=WidthTo(ed,ed->cur);
    exx=curx+ed->marg;
    exl=exx-ed->lmarg;
    exr=exx+ed->cwidth-ed->i.width;
    if (exl<0 && ed->marg)
        {
        ed->marg-=exl-ed->scrollx;
        if (ed->marg>0)
            ed->marg=0;
        }
    else if (exr>0)
        ed->marg-=exr+ed->scrollx;
    if (ed->marg!=oldmarg && draw)
        DrawAll(ed);
    }

GLDEF_C VOID le_set_text(LINED *ed,INT blen,TEXT *pb)
    {
    *(p_bcpy(ed->pb,pb,blen))=0;
    ed->blen=blen;
    ed->marg=0;
    ed->select=FALSE;
    ed->cur=0;
    if (ed->i.autoselect && blen)
        {
        ed->cur=blen;
        ed->select=TRUE;
        ScrollForCursor(ed,FALSE);
        }
    if (ed->visible)
        DrawAll(ed);
    }

LOCAL_C VOID DelSelRange(LINED *ed)
    {
    ed->select=FALSE;
    ed->cur=0;
    ed->marg=0;
    ed->blen=0;
    *ed->pb=0;
    }

LOCAL_C VOID DoDelete(LINED *ed,INT modifier)
    {
    INT start;
    INT finish;

    if (ed->select)
        DelSelRange(ed);
    else
        {
        if (modifier&W_SHIFT_MODIFIER)
            {
            if (ed->cur==ed->blen)
                return;
            }
        else if (!ed->cur)
            return;
        start=finish=ed->cur;
        switch (modifier)
            {
        case W_SHIFT_MODIFIER:
            finish++;
            break;
        case W_PSION_MODIFIER:
            finish=ed->blen;
            break;
        default:
            start--;
            }
        p_bcpy(ed->pb+start,ed->pb+finish,ed->blen+1-finish);
        ed->blen-=finish-start;
        ed->cur=start;
        ScrollForCursor(ed,FALSE);
        }
    DrawAll(ed);
    }

LOCAL_C VOID LoseHighlight(LINED *ed)
    {
    ToggleHighlight(ed);
    ed->select=FALSE;
    }

LOCAL_C VOID DisplayCursor(LINED *ed)
    {
    ScrollForCursor(ed,TRUE);
    DrawCursor(ed);
    }

GLDEF_C INT le_key(LINED *ed,INT keycode,INT modifier)
    {
    TEXT *pb;

    if (keycode<0x100 && p_isprint(keycode))
        {
        if (ed->select)
            DelSelRange(ed);
        if (ed->blen==ed->i.maxchars)
            {
            p_sound(-5,320);
            return(-1);
            }
        pb=ed->pb+ed->cur;
        p_bcpy(pb+1,pb,ed->blen+1-ed->cur);
        *pb=keycode;
        (ed->blen)++;
        (ed->cur)++;
        ScrollForCursor(ed,FALSE);
        DrawAll(ed);
        return(0);
        }
    if (keycode==W_KEY_DELETE_LEFT || keycode==W_KEY_DELETE_RIGHT)
        DoDelete(ed,modifier);
    else
        {
        switch (keycode)
            {
        case W_KEY_LEFT:
            if (ed->select)
                {
                LoseHighlight(ed);
                ed->cur=0;
                }
            else if (!ed->cur)
                return(0);
            else
                {
                if (modifier&W_PSION_MODIFIER)
                    ed->cur=0;
                else
                    (ed->cur)--;
                }
            DisplayCursor(ed);
            break;
        case W_KEY_RIGHT:
            if (ed->select)
                {
                LoseHighlight(ed);
                ed->cur=ed->blen;
                }
            else if (ed->cur==ed->blen)
                return(0);
            else
                {
                if (modifier&W_PSION_MODIFIER)
                    ed->cur=ed->blen;
                else
                    (ed->cur)++;
                }
            DisplayCursor(ed);
            }
        }
    return(0);
    }

GLDEF_C VOID le_emphasise(LINED *ed,INT emph)
    {
    if (emph)
        emph=TRUE;
    if (ed->emph==emph)
        return;
    ed->emph=emph;
    if (!ed->visible)
        return;
    DrawAll(ed);
    if (!emph)
        wEraseTextCursor();
    }

GLDEF_C VOID le_set_cwidth(LINED *ed,INT cwidth)
    {
    ed->cwidth=cwidth;
    if (ed->visible && ed->emph)
        {
        if (cwidth)
            DrawCursor(ed);
        else
            wEraseTextCursor();
        }
    }

GLDEF_C VOID le_visible(LINED *ed,INT visible)
    {
    P_RECT box;
    INT gc;

    if (visible)
        visible=TRUE;
    if (ed->visible==visible)
        return;
    ed->visible=visible;
    if (visible)
        DrawAll(ed);
    else
        {
        GetBox(ed,&box);
        gc=CreateGC(ed);
        gClrRect(&box,G_TRMODE_CLR);
        FreeGC(ed,gc);
        }
    }


/************************** TEST CODE FOLLOWS ***********************/


LOCAL_C LINED *CreateLined(INT yoff,TEXT *msg,INT emph)
    {
    IN_LINED init;
    LINED *ed;

    init.maxchars=20;
    init.winid=wMainWid;
    init.xoff=10;
    init.yoff=yoff;
    init.width=80;
    init.height=10;
    init.asc=8;
    init.font=WS_FONT_BASE+4;
    init.style=0;
    init.autoselect=TRUE;
    ed=le_init(&init);
    le_set_text(ed,p_slen(msg),msg);
    le_emphasise(ed,emph);
    le_visible(ed,TRUE);
    return(ed);
    }

GLDEF_C VOID main(VOID)
    {
    LINED *ed[3];
    INT which;
    WS_EV event;
    INT keycode;

    wStartup();
    gBorder(W_BORD_CORNER_4);
    ed[0]=CreateLined(10,"One",TRUE);
    ed[1]=CreateLined(30,"Two",FALSE);
    ed[2]=CreateLined(50,"Three",FALSE);
    which=0;
    FOREVER
        {
        do
            {
            wGetEventWait(&event);
            } while (event.type!=WM_KEY);
        keycode=event.p.key.keycode&(~W_SPECIAL_KEY);
        switch (keycode)
            {
        case W_KEY_ESCAPE:
            if (event.p.key.modifiers==W_PSION_MODIFIER)
                p_exit(0);
        case W_KEY_UP:
            if (which)
                {
                le_emphasise(ed[which--],FALSE);
                le_emphasise(ed[which],TRUE);
                }
            break;
        case W_KEY_DOWN:
            if (which<2)
                {
                le_emphasise(ed[which++],FALSE);
                le_emphasise(ed[which],TRUE);
                }
            break;
        default:
            le_key(ed[which],keycode,event.p.key.modifiers);
            }
        }
    }

