/*
    ICONED.C

Written by DavidW
Started on 12/9/91

Based in part on work by Robert, May 91
*/

#include <p_std.h>
#include <p_gen.h>
#include <p_file.h>
#include <wlib.h>
#include <hwif.h>

GLREF_D UWORD DatLocked;
GLREF_D TEXT *DatUsedPathNamePtr;


LOCAL_D TEXT *cmds[]=
    {
    "nNew icon",
    "oOpen icon",
    "aSave as",
    "sSave",
    "rRevert",
    "xExit",
    "iInsert",
    "cCopy",
    "uUse",
    "pPlaster",
    "mMove",
    "gGoto",
    "zSet",
    "qClear",
    "tToggle",
    "fFrame off",
    "kCursor off",
    "hHighlight select",
    "wWrite text",
    "bBorder",
    "yInterrogation",
    NULL
    };

#define FILE_ITEMS      6
#define EDIT_ITEMS      6
#define BRUSH_ITEMS     3
#define SPECIAL_ITEMS   6

#define ITEMS_BEFORE_FILE       0
#define ITEMS_BEFORE_EDIT       FILE_ITEMS
#define ITEMS_BEFORE_BRUSH      (ITEMS_BEFORE_EDIT+EDIT_ITEMS)
#define ITEMS_BEFORE_SPECIAL    (ITEMS_BEFORE_BRUSH+BRUSH_ITEMS)

#define FRAME_ITEM          ITEMS_BEFORE_SPECIAL
#define CURSOR_ITEM         ITEMS_BEFORE_SPECIAL+1

LOCAL_D H_MENU_DATA mdata[]=
    {
    "File",FILE_ITEMS,
    "Edit",EDIT_ITEMS,
    "Brush",BRUSH_ITEMS,
    "Special",SPECIAL_ITEMS,
    NULL
    };

GLDEF_D TEXT ** _cmds=(&cmds[0]);
GLDEF_D H_MENU_DATA * _mdata=(&mdata[0]);


LOCAL_D UINT MainWid;

LOCAL_D WMSG_KEY key;

LOCAL_D WORD bid;
LOCAL_D VOID *fcb;
LOCAL_D TEXT filename[P_FNAMESIZE];

LOCAL_D WORD clipbid[6]={0,0,0,0,0,0};
LOCAL_D P_POINT clipsize[6];

#define MOVE_BACKUP 0
#define MOVE_RAFT 5

LOCAL_D UWORD MoveUpdatePending;
LOCAL_D P_RECT moveorigin;
LOCAL_D P_RECT movedto;
LOCAL_D UWORD MoveSingle;

LOCAL_D UWORD using=1;
LOCAL_D UWORD brush=2;
LOCAL_D UWORD changed=FALSE;
LOCAL_D UWORD show_cursor=TRUE;

LOCAL_D UWORD show_frame=FALSE;
LOCAL_D UWORD show_statwin=FALSE;
LOCAL_D UWORD require_query=TRUE;

LOCAL_D P_POINT anch;
LOCAL_D P_POINT curs;
LOCAL_D UWORD issel;

LOCAL_D TEXT ToWrite[10]={0};
LOCAL_D UWORD font=1;
LOCAL_D UWORD italic=1;

LOCAL_D UWORD BordCorner=2;
LOCAL_D UWORD BordShadow=1;
LOCAL_D UWORD BordCushion=1;

#define ICON_SIZE           24
#define LARGE_ICON_SCALE    3

#define SMALL_ICON_X        41
#define SMALL_ICON_WIDTH    ICON_SIZE
#define SMALL_ICON_Y        8
#define SMALL_ICON_HEIGHT   ICON_SIZE

#define LEFT_CURSOR_X       106
#define TOP_CURSOR_Y        0

#define LARGE_ICON_X        110
#define LARGE_ICON_Y        4

#define RIGHT_CURSOR_X      185
#define BOTTOM_CURSOR_Y     79

#define TEXT_TL_X       4
#define BRUSH_TEXT_Y    41
#define USING_TEXT_Y    50
#define CURSOR_TEXT_Y   59
#define ANCHOR_TEXT_Y   68
#define TEXT_BR_X       104
#define TEXT_HEIGHT     9
#define TEXT_ASCENT     8


LOCAL_C VOID CDECL WriteAt(INT y,TEXT *str,...)
    {
    P_RECT box;
    TEXT buf[40];
    INT len;

    len=p_atob(&buf[0],str,&str+1);
    box.tl.x=TEXT_TL_X;
    box.tl.y=y;
    box.br.x=TEXT_BR_X;
    box.br.y=y+TEXT_HEIGHT;
    gPrintBoxText(&box,TEXT_ASCENT,G_TEXT_ALIGN_LEFT,0,&buf[0],len);
    }

LOCAL_C VOID WriteBrushText(VOID)
    {
    WriteAt(BRUSH_TEXT_Y,"Brush is `%s'",cmds[brush+ITEMS_BEFORE_BRUSH]+1);
    }

LOCAL_C VOID WriteCursorText(VOID)
    {
    WriteAt(CURSOR_TEXT_Y,"Cursor: X %d, Y %d",curs.x,curs.y);
    WriteAt(ANCHOR_TEXT_Y,(issel? "Anchor: X %d, Y %d": ""),anch.x,anch.y);
    }

LOCAL_C VOID WriteClipboardText(VOID)
    {
    WriteAt(USING_TEXT_Y,"Using clipboard %d",using);
    }

LOCAL_C VOID ScaleUp(P_RECT *pbox)
    {
    pbox->tl.x=LARGE_ICON_X+pbox->tl.x*LARGE_ICON_SCALE;
    pbox->tl.y=LARGE_ICON_Y+pbox->tl.y*LARGE_ICON_SCALE;
    pbox->br.x=LARGE_ICON_X+pbox->br.x*LARGE_ICON_SCALE;
    pbox->br.y=LARGE_ICON_Y+pbox->br.y*LARGE_ICON_SCALE;
    }

LOCAL_C VOID GetSmallSelectRegion(P_RECT *pbox)
    {
    if (anch.x>curs.x)
        {
        pbox->tl.x=curs.x;
        pbox->br.x=anch.x+1;
        }
    else
        {
        pbox->tl.x=anch.x;
        pbox->br.x=curs.x+1;
        }
    if (anch.y>curs.y)
        {
        pbox->tl.y=curs.y;
        pbox->br.y=anch.y+1;
        }
    else
        {
        pbox->tl.y=anch.y;
        pbox->br.y=curs.y+1;
        }
    }

LOCAL_C VOID GetSelectRegion(P_RECT *pbox)
    {
    GetSmallSelectRegion(pbox);
    ScaleUp(pbox);
    }

LOCAL_C VOID GetCursorBox(P_RECT *pbox)
    {
    pbox->tl.x=LARGE_ICON_X+curs.x*LARGE_ICON_SCALE;
    pbox->tl.y=LARGE_ICON_Y+curs.y*LARGE_ICON_SCALE;
    pbox->br.x=pbox->tl.x+LARGE_ICON_SCALE;
    pbox->br.y=pbox->tl.y+LARGE_ICON_SCALE;
    }

LOCAL_C VOID MakeCursorFlash(P_RECT *pbox)
    {
    W_CURSOR wc;

    wc.pos.x=pbox->tl.x+1;
    wc.pos.y=pbox->tl.y+1;
    wc.height=1;
    wc.width=1;
    wc.ascent=0;
    wDrawTextCursor(MainWid,&wc);
    }

LOCAL_C VOID ReassertCursor(VOID)
    {
    P_RECT box;

    if (show_cursor)
        {
        GetCursorBox(&box);
        MakeCursorFlash(&box);
        }
    }

LOCAL_C INT RunDialog(VOID)
    {
    INT ret;

    ret=uRunDialog();
    ReassertCursor();
    return(ret);
    }

LOCAL_C VOID ToggleHorizCurs(P_RECT *pbox,INT tly,INT dely)
    {
    P_RECT box;

    box=(*pbox);
    box.tl.y=tly;
    box.br.y=tly+1;
    gClrRect(&box,G_TRMODE_INV);
    p_offrec(&box,0,dely);
    p_insrec(&box,1,0);
    gClrRect(&box,G_TRMODE_INV);
    }

LOCAL_C VOID ToggleVertCurs(P_RECT *pbox,INT tlx,INT delx)
    {
    P_RECT box;

    box=(*pbox);
    box.tl.x=tlx;
    box.br.x=tlx+1;
    gClrRect(&box,G_TRMODE_INV);
    p_offrec(&box,delx,0);
    p_insrec(&box,0,1);
    gClrRect(&box,G_TRMODE_INV);
    }

LOCAL_C VOID ToggleSelectBox(VOID)
    {
    P_RECT box;

    GetSelectRegion(&box);
    gDrawBox(&box);
    }

LOCAL_C VOID ToggleCursor(VOID)
    {
    P_RECT box;

    if (show_cursor)
        {
        GetCursorBox(&box);
        ToggleHorizCurs(&box,TOP_CURSOR_Y,1);
        ToggleHorizCurs(&box,BOTTOM_CURSOR_Y,-1);
        ToggleVertCurs(&box,LEFT_CURSOR_X,1);
        ToggleVertCurs(&box,RIGHT_CURSOR_X,-1);
        MakeCursorFlash(&box);
        if (issel)
            ToggleSelectBox();
        }
    }

LOCAL_C VOID DrawSmallIcon(VOID)
    {
    P_POINT offset;
    P_RECT size;

    offset.x=SMALL_ICON_X;
    offset.y=SMALL_ICON_Y;
    size.tl.x=size.tl.y=0;
    size.br.x=SMALL_ICON_WIDTH;
    size.br.y=SMALL_ICON_HEIGHT;
    gCopyBit(&offset,bid,&size,G_TRMODE_REPL);
    }

LOCAL_C VOID DrawLargeIcon(VOID)
    {
    ULONG bits[2];
    P_POINT log;
    P_RECT box;
    INT j;

    log.x=0;
    box.tl.y=LARGE_ICON_Y;
    for (log.y=0; log.y<ICON_SIZE; log.y++)
        {
        gPeekBit(bid,&log,ICON_SIZE,&bits[0]);
        box.br.y=box.tl.y+LARGE_ICON_SCALE;
        box.tl.x=LARGE_ICON_X;
        for (j=0; j<ICON_SIZE; j++)
            {
            box.br.x=box.tl.x+LARGE_ICON_SCALE;
            gClrRect(&box,(bits[0]&(1L<<j))? G_TRMODE_SET: G_TRMODE_CLR);
            box.tl.x=box.br.x;
            }
        box.tl.y=box.br.y;
        }
    }

LOCAL_C VOID DrawIcons(VOID)
    {
    DrawSmallIcon();
    DrawLargeIcon();
    }

LOCAL_C VOID SetupFilename(TEXT *pb)
    {
    p_bcpy(pb+1,DatUsedPathNamePtr,*pb=p_slen(DatUsedPathNamePtr));
    }

LOCAL_C INT TryOpenIconFile(TEXT *newname,VOID **pfcb)
    {
    INT newbid;
    P_POINT size;
    INT ret;

    newbid=gOpenBit(newname,0,WS_BIT_WRITE,&size);
    if (newbid<0)
        uErrorValue(newbid);
    else if (size.x!=ICON_SIZE || size.y!=ICON_SIZE)
        {
        uErrorString("File has wrong size of icon");
        wFree(newbid);
        newbid=0;
        }
    else if ((ret=p_open(pfcb,newname,P_FOPEN))<0)
        {
        uErrorValue(ret);
        wFree(newbid);
        newbid=0;
        }
    return(newbid);
    }

LOCAL_C VOID RestartDisplay(VOID)
    {
    changed=FALSE;
    DrawIcons();
    curs.x=curs.y=0;
    issel=FALSE;
    ToggleCursor();
    WriteCursorText();
    }

LOCAL_C VOID RetryOrExit(TEXT *message)
    {
    uForceToFront();
    DatLocked=TRUE;
    if (!wsAlertW(WS_ALERT_CLIENT,0,message,"Terminate","Retry",0))
        p_exit(0);
    DatLocked=FALSE;
    }

LOCAL_C VOID ChangeName(TEXT *newname)
    {
    p_scpy(&filename[0],newname);
    hSetUpStatusNames(&filename[0]);
    wsUpdate(WS_UPDATE_NAME);
    }

LOCAL_C VOID DoSave(TEXT *savename,INT usenew)
    {
    INT ret;

    p_close(fcb);
    while ((ret=gSaveBit(savename,bid))!=0)
        {
        uErrorValue(ret);
        RetryOrExit("Failed to save icon");
        }
    if (usenew)
        {
        ChangeName(savename);
        changed=FALSE;
        }
    while (p_open(&fcb,DatUsedPathNamePtr,P_FOPEN))
        RetryOrExit("No memory to continue");
    }

LOCAL_C VOID SaveIfChanged(VOID)
    {
    if (changed)
        {
        DoSave(DatUsedPathNamePtr,FALSE);
        changed=FALSE;
        }
    }

LOCAL_C VOID ChangeOverTo(INT newbid,VOID *newfcb,TEXT *newname)
    {
    wFree(bid);
    bid=newbid;
    p_close(fcb);
    fcb=newfcb;
    ChangeName(newname);
    ToggleCursor();
    RestartDisplay();
    }

LOCAL_C VOID OpenIconFile(VOID)
    {
    H_DI_FSEL open;
    TEXT openbuf[P_FNAMESIZE+2];
    INT newbid;
    VOID *newfcb;

    SetupFilename(open.fname=(&openbuf[0]));
    open.flags=H_FILE_PICK_SELECTOR|H_FILE_RESTRICT_LIST|H_FILE_SET_DEFEXT;
    do
        {
        if (uOpenDialog("Open icon file"))
            return;
        if (uAddDialogItem(H_DIALOG_FSEL,"File:",&open))
            return;
        if (RunDialog()<=0)
            return; 
        SaveIfChanged();
        openbuf[1+openbuf[0]]=0;
        newbid=TryOpenIconFile(&openbuf[1],&newfcb);
        } while (newbid<=0);
    ChangeOverTo(newbid,newfcb,&openbuf[1]);
    }

LOCAL_C INT AddNoYesChoice(TEXT *prompt,UWORD *nsel)
    {
    return(uAddChoiceList(prompt,nsel,"No","Yes",NULL));
    }

LOCAL_C VOID SaveIconAs(VOID)
    {
    H_DI_FSEL save;
    TEXT savebuf[P_FNAMESIZE+2];
    UWORD usenew;

    if (uOpenDialog("Save icon as"))
        return;
    SetupFilename(save.fname=(&savebuf[0]));
    save.flags=H_FILE_NEW_EDITOR|H_FILE_SET_DEFEXT;
    if (uAddDialogItem(H_DIALOG_FSEL,"File:",&save))
        return;
    usenew=1;
    if (AddNoYesChoice("Use new file",&usenew))
        return;
    if (RunDialog())
        {
        savebuf[1+savebuf[0]]=0;
        hEnsurePath(&savebuf[1]);
        DoSave(&savebuf[1],usenew==2);
        wInfoMsg("Saved");
        }
    }

LOCAL_C VOID SaveIcon(VOID)
    {
    if (!changed)
        wInfoMsg("Not changed");
    else
        {
        DoSave(DatUsedPathNamePtr,FALSE);
        wInfoMsg("Saved");
        changed=FALSE;
        }
    }

LOCAL_C VOID TryToRevert(VOID)
    {
    INT newbid;

    p_close(fcb);
    while ((newbid=TryOpenIconFile(DatUsedPathNamePtr,&fcb))<=0)
        RetryOrExit("Revert has failed");
    wFree(bid);
    bid=newbid;
    ToggleCursor();
    RestartDisplay();
    }

LOCAL_C INT AddNoYesButs(VOID)
    {
    return(uAddButtonList("No",-'n',"Yes",'y',0));
    }

LOCAL_C INT UserConfirms(TEXT *message)
    {
    if (uOpenDialog(message) || AddNoYesButs())
        return(0);
    return(uRunDialog()=='y');
    }

LOCAL_C INT CheckUserConfirms(TEXT *message)
    {
    return(require_query? UserConfirms(message): TRUE);
    }

LOCAL_C VOID RevertIfConfirmed(VOID)
    {
    if (!changed)
        {
        wInfoMsg("Not changed");
        return;
        }
    if (UserConfirms("Revert to saved"))
        TryToRevert();
    }

LOCAL_C VOID ToggleFrame(VOID)
    {
    P_RECT box;

    box.tl.x=LARGE_ICON_X-1;
    box.tl.y=LARGE_ICON_Y-1;
    box.br.x=LARGE_ICON_X+ICON_SIZE*LARGE_ICON_SCALE+1;
    box.br.y=LARGE_ICON_Y+ICON_SIZE*LARGE_ICON_SCALE+1;
    gDrawBox(&box);
    cmds[FRAME_ITEM]=((show_frame^=TRUE)? "fFrame off": "fFrame on");
    }

LOCAL_C VOID ToggleStatusWindow(VOID)
    {
    if (show_statwin^=TRUE)
        wsEnable();
    else
        wsDisable();
    }

LOCAL_C INT TryNewIconFile(TEXT *newname,VOID **pfcb)
    {
    INT newbid;
    P_RECT rect;
    INT ret;

    hEnsurePath(newname);
    rect.tl.x=rect.tl.y=0;
    rect.br.x=rect.br.y=ICON_SIZE;
    newbid=gCreateBit(0,&rect.br);
    if (newbid<0)
        uErrorValue(newbid);
    else
        {
        gCreateTempGC0(newbid);
        gClrRect(&rect,G_TRMODE_CLR);
        gFreeTempGC();
        ret=gSaveBit(newname,newbid);
        if (ret!=0 || (ret=p_open(pfcb,newname,P_FOPEN))<0)
            {
            uErrorValue(ret);
            wFree(newbid);
            newbid=0;
            }
        else
            changed=FALSE;
        }
    return(newbid);
    }

LOCAL_C VOID NewIconFile(VOID)
    {
    H_DI_FSEL new;
    TEXT newbuf[P_FNAMESIZE+2];
    INT newbid;
    VOID *newfcb;

    SetupFilename(new.fname=(&newbuf[0]));
    new.flags=H_FILE_NEW_EDITOR|H_FILE_SET_DEFEXT;
    do
        {
        if (uOpenDialog("New icon"))
            return;
        if (uAddDialogItem(H_DIALOG_FSEL,"File:",&new))
            return;
        if (RunDialog()<=0)
            return;
        SaveIfChanged();
        newbuf[1+newbuf[0]]=0;
        newbid=TryNewIconFile(&newbuf[1],&newfcb);
        } while (newbid<=0);
    ChangeOverTo(newbid,newfcb,&newbuf[1]);
    }

LOCAL_C VOID ExitApplication(VOID)
    {
    SaveIfChanged();
    p_exit(0);
    }

LOCAL_C VOID GetWholeIcon(P_RECT *pbox)
    {
    pbox->tl.x=pbox->tl.y=0;
    pbox->br.x=pbox->br.y=ICON_SIZE;
    }

LOCAL_C VOID InvertOrGrey(VOID)
    {
    INT keycode;
    P_RECT box;

    if (uOpenDialog(issel? "Plaster selection": "Plaster whole icon"))
        return;
    if (uAddButtonList("Cancel",W_KEY_ESCAPE,"Grey",'g',"Invert",'i',NULL))
        return;
    if (!(keycode=RunDialog()))
        return;
    if (!issel)
        GetWholeIcon(&box);
    else
        {
        ToggleSelectBox();
        GetSmallSelectRegion(&box);
        }
    gCreateTempGC0(bid);
    if (keycode=='i')
        gClrRect(&box,G_TRMODE_INV);
    else
        gFillPattern(&box,WS_BITMAP_GREY,G_TRMODE_INV);
    changed=TRUE;
    gFreeTempGC();
    DrawIcons();
    if (issel)
        ToggleSelectBox();
    }

LOCAL_C VOID ToggleCursorVisibility(VOID)
    {
    cmds[CURSOR_ITEM]=(show_cursor? "kCursor on": "kCursor off");
    if (show_cursor)
        {
        ToggleCursor();
        show_cursor=FALSE;
        wEraseTextCursor();
        }
    else
        {
        show_cursor=TRUE;
        ToggleCursor();
        }
    }

LOCAL_C VOID WriteText(VOID)
    {
    H_DI_EDIT edit;
    G_GC gc;
    INT gcflags;

    if (uOpenDialog("Write text"))
        return;
    edit.str=(&ToWrite[0]);
    edit.len=9;
    if (uAddDialogItem(H_DIALOG_EDIT,"Text",&edit))
        return;
    if (uAddChoiceList("Font",&font,"Standard","Bold","Small digits",NULL))
        return;
    if (AddNoYesChoice("Italic",&italic))
        return;
    if (RunDialog()<=0)
        return;
    gCreateTempGC0(bid);
    gcflags=G_GC_MASK_TEXTMODE|G_GC_MASK_FONT;
    gc.textmode=brush;
    gc.font=WS_FONT_BASE+font-1;
    if (italic==2)
        {
        gcflags|=G_GC_MASK_STYLE;
        gc.style=G_STY_ITALIC;
        }
    gSetGC(0,gcflags,&gc);
    gPrintText(curs.x,curs.y,&ToWrite[1],ToWrite[0]);
    changed=TRUE;
    gFreeTempGC();
    DrawIcons();
    }

LOCAL_C VOID DrawBorder(VOID)
    {
    INT bflags;
    G_GC gc;

    if (uOpenDialog("Draw border"))
        return;
    if (uAddChoiceList("Corner",&BordCorner,"Simple","Gentle","Round",NULL))
        return;
    if (uAddChoiceList("Shadow",&BordShadow,"None","Single","Double",NULL))
        return;
    if (AddNoYesChoice("Cushion",&BordCushion))
        return;
    if (RunDialog()<=0)
        return;
    gc.gmode=brush;
    gCreateTempGC(bid,G_GC_MASK_GMODE,&gc);
    bflags=W_BORD_SHADOW_ON;
    if (BordCorner==1)
        bflags|=W_BORD_CORNER_1;
    else if (BordCorner==3)
        bflags|=W_BORD_CORNER_4;
    if (BordShadow==2)
        bflags|=W_BORD_SHADOW_S;
    else if (BordShadow==3)
        bflags|=W_BORD_SHADOW_D;
    if (BordCushion==2)
        bflags|=W_BORD_CUSHION;
    gBorder(bflags);
    changed=TRUE;
    gFreeTempGC();
    DrawIcons();
    }

LOCAL_C VOID ChangeClipboardUsed(VOID)
    {
    if (uOpenDialog("Use"))
        return;
    if (uAddChoiceList("Clipboard",&using,"1","2","3","4",NULL))
        return;
    if (RunDialog()<=0)
        return;
    WriteClipboardText();
    }

LOCAL_C VOID CancelSelect(VOID)
    {
    ToggleSelectBox();
    issel=FALSE;
    WriteCursorText();
    }

LOCAL_C VOID CheckIfSelectStatusChanging(VOID)
    {
    if (!(key.modifiers&W_SHIFT_MODIFIER))
        issel=FALSE;
    else if (!issel)
        {
        issel=TRUE;
        anch=curs;
        }
    }

LOCAL_C VOID CheckForNullSelect(VOID)
    {
    if (anch.x==curs.x && anch.y==curs.y)
        issel=FALSE;
    }

LOCAL_C VOID MoveCursorAbsolute(INT newx,INT newy)
    {
    ToggleCursor();
    CheckIfSelectStatusChanging();
    if (newx>=0)
        curs.x=newx;
    if (newy>=0)
        curs.y=newy;
    CheckForNullSelect();
    ToggleCursor();
    WriteCursorText();
    }

LOCAL_C VOID RepositionCursor(VOID)
    {
    H_DI_NUMBER posx;
    H_DI_NUMBER posy;
    LONG curx;
    LONG cury;
    UWORD extend;

    curx=curs.x;
    cury=curs.y;
    posx.low=0;
    posx.high=23;
    posx.value=(&curx);
    posy.low=0;
    posy.high=23;
    posy.value=(&cury);
    extend=(issel? 2: 1);
    if (uOpenDialog("Go to"))
        return;
    if (uAddDialogItem(H_DIALOG_NUMBER,"Cursor x",&posx))
        return;
    if (uAddDialogItem(H_DIALOG_NUMBER,"Cursor y",&posy))
        return;
    if (AddNoYesChoice("Extend select",&extend))
        return;
    if (RunDialog()<=0)
        return;
    key.modifiers=(extend==2? W_SHIFT_MODIFIER: 0);
    MoveCursorAbsolute((INT)curx,(INT)cury);
    }

LOCAL_C VOID FlashHighlightRegion(VOID)
    {
    P_RECT select;

    if (!issel)
        {
        wInfoMsg("No select region");
        return;
        }
    GetSelectRegion(&select);
    wInfoMsgCorner("Showing select region",W_CORNER_TOP_LEFT);
    gFillPattern(&select,WS_BITMAP_GREY,G_TRMODE_INV);
    wFlush();
    p_sleep(20L);
    gFillPattern(&select,WS_BITMAP_GREY,G_TRMODE_INV);
    }

LOCAL_C INT DoCopy(P_RECT *prect,INT clipindex)
    {
    P_POINT size;
    P_POINT origin;
    INT newclipbid;

    size.x=prect->br.x-prect->tl.x;
    size.y=prect->br.y-prect->tl.y;
    newclipbid=gCreateBit(0,&size);
    if (newclipbid<0)
        {
        uErrorValue(newclipbid);
        return(-1);
        }
    gCreateTempGC0(newclipbid);
    origin.x=origin.y=0;
    gCopyBit(&origin,bid,prect,G_TRMODE_REPL);
    gFreeTempGC();
    wFree(clipbid[clipindex]);
    clipbid[clipindex]=newclipbid;
    clipsize[clipindex]=size;
    return(0);
    }

LOCAL_C VOID CopyToClipboard(VOID)
    {
    P_RECT rect;

    if (issel)
        {
        if (!CheckUserConfirms("Copy select region to clipboard"))
            return;
        GetSmallSelectRegion(&rect);
        }
    else
        {
        if (!CheckUserConfirms("Copy whole icon to clipboard"))
            return;
        GetWholeIcon(&rect);
        }
    if (DoCopy(&rect,using))
        return;
    wInfoMsg("Copied");
    }

LOCAL_C VOID DoDelete(P_RECT *prect)
    {
    gCreateTempGC0(bid);
    gClrRect(prect,G_TRMODE_CLR);
    gFreeTempGC();
    }

LOCAL_C VOID CutToClipboard(VOID)
    {
    P_RECT rect;

    if (issel)
        {
        if (!CheckUserConfirms("Cut select region into clipboard"))
            return;
        GetSmallSelectRegion(&rect);
        }
    else
        {
        if (!CheckUserConfirms("Cut whole icon into clipboard"))
            return;
        GetWholeIcon(&rect);
        }
    if (DoCopy(&rect,using))
        return;
    key.modifiers=0;
    MoveCursorAbsolute(rect.tl.x,rect.tl.y);
    DoDelete(&rect);
    changed=TRUE;
    DrawIcons();
    }

LOCAL_C VOID DoInsert(P_POINT *target,INT clipindex,INT mode)
    {
    P_RECT rect;

    rect.tl.x=rect.tl.y=0;
    rect.br.x=clipsize[clipindex].x;
    rect.br.y=clipsize[clipindex].y;
    gCreateTempGC0(bid);
    gCopyBit(target,clipbid[clipindex],&rect,mode);
    gFreeTempGC();
    }

LOCAL_C VOID InsertFromClipboard(VOID)
    {
    if (!clipbid[using])
        {
        wInfoMsg("Current clipboard is empty");
        return;
        }
    if (!CheckUserConfirms("Insert from clipboard"))
        return;
    if (issel)
        CancelSelect();
    DoInsert(&curs,using,brush);
    DrawIcons();
    changed=TRUE;
    }

LOCAL_C VOID ConsiderInterrogation(VOID)
    {
    UWORD nsel;

    if (uOpenDialog("Before cut/copy/insert"))
        return;
    nsel=(require_query? 2: 1);
    if (AddNoYesChoice("Request confirmation",&nsel))
        return;
    if (RunDialog()<=0)
        return;
    require_query=(nsel==2);
    }

LOCAL_C VOID ResetStateBeforeMove(VOID)
    {
    P_POINT origin;

    origin.x=origin.y=0;
    DoInsert(&origin,MOVE_BACKUP,G_TRMODE_REPL);
    }

LOCAL_C VOID MoveBy(INT offx,INT offy)
    {
    if (issel || !MoveSingle)
        ResetStateBeforeMove();
    if (MoveSingle)
        DoDelete(&moveorigin);
    p_offrec(&movedto,offx,offy);
    DoInsert(&movedto.tl,MOVE_RAFT,G_TRMODE_REPL);
    MoveUpdatePending=TRUE;
    }

LOCAL_C VOID EnterMoveState(VOID)
    {
    TEXT *title;
    INT keycode;

    GetWholeIcon(&moveorigin);
    if (DoCopy(&moveorigin,MOVE_BACKUP))
        return;
    if (issel)
        {
        title="Move select region";
        GetSmallSelectRegion(&moveorigin);
        }
    else
        title="Move whole icon";
    if (uOpenDialog(title))
        goto TidyUp;
    if (uAddButtonList("Single copy",'s',"Double copy",'d',NULL))
        goto TidyUp;
    if (!(keycode=RunDialog()))
        goto TidyUp;
    MoveSingle=(keycode=='s');
    if (DoCopy(&moveorigin,MOVE_RAFT))
        goto TidyUp;
    movedto=moveorigin;
    ToggleCursor();
    wEraseTextCursor();
    DatLocked=TRUE;
    wSetBusyMsg("Moving",W_CORNER_BOTTOM_LEFT);
    MoveUpdatePending=FALSE;
    FOREVER
        {
        uGetKey(&key);
        switch (key.keycode)
            {
        case W_KEY_UP:
            MoveBy(0,-1);
            break;
        case W_KEY_DOWN:
            MoveBy(0,1);
            break;
        case W_KEY_LEFT:
            MoveBy(-1,0);
            break;
        case W_KEY_RIGHT:
            MoveBy(1,0);
            break;
        case W_KEY_ESCAPE:
            ResetStateBeforeMove();
            DrawIcons();
            goto ExitMoveState;
        case W_KEY_RETURN:
            changed=TRUE;
            goto ExitMoveState;
        default:
            p_sound(-5,320);
            wInfoMsg("ENTER or ESC exits `Moving'");
            }
        if (MoveUpdatePending && !uKeyPressOutstanding())
            {
            DrawIcons();
            MoveUpdatePending=FALSE;
            }
        }
ExitMoveState:
    DatLocked=FALSE;
    ToggleCursor();
    wCancelBusyMsg();
    wFree(clipbid[MOVE_RAFT]);
    clipbid[MOVE_RAFT]=0;
TidyUp:
    wFree(clipbid[MOVE_BACKUP]);
    clipbid[MOVE_BACKUP]=0;
    }

LOCAL_C INT IncrementAndFix(INT value,INT delta)
    {
    value+=delta;
    if (value<0)
        value+=ICON_SIZE;
    else if (value>=ICON_SIZE)
        value-=ICON_SIZE;
    return(value);
    }

LOCAL_C VOID AlterBit(INT gmode)
    {
    P_RECT box;

    gCreateTempGC0(bid);
    box.tl=curs;
    box.br.x=curs.x+1;
    box.br.y=curs.y+1;
    gClrRect(&box,gmode);
    changed=TRUE;
    gFreeTempGC();
    DrawSmallIcon();
    ScaleUp(&box);
    gClrRect(&box,gmode);
    }

LOCAL_C VOID MoveCursorRelative(INT delx,INT dely)
    {
    if (key.modifiers&W_CTRL_MODIFIER)
        AlterBit(brush);
    ToggleCursor();
    CheckIfSelectStatusChanging();
    curs.x=IncrementAndFix(curs.x,delx);
    curs.y=IncrementAndFix(curs.y,dely);
    CheckForNullSelect();
    ToggleCursor();
    WriteCursorText();
    }

LOCAL_C VOID ReduceScreenSize(VOID)
    {
    W_WINDATA wd;

    wd.extent.tl.x=wd.extent.tl.y=0;
    wd.extent.width=189;
    wd.extent.height=80;
    wSetWindow(MainWid,W_WIN_EXTENT,&wd);
    if (uErrorValue(wCheckPoint()))
        p_exit(0);
    }

LOCAL_C INT ObeySystemCommand(INT cmd,TEXT *pb,VOID **newfcb)
    {
    return((cmd=='O'? TryOpenIconFile: TryNewIconFile)(pb,newfcb));
    }

LOCAL_C VOID CreateGC(VOID)
    {
    G_GC gc;
    INT hgc;

    gc.gmode=G_TRMODE_INV;
    hgc=gCreateGC(MainWid,G_GC_MASK_GMODE,&gc);
    if (hgc<0)
        p_exit(hgc);
    }

LOCAL_C VOID SpecificInit(VOID)
    {
    INT command;

    MainWid=uFindMainWid();
    CreateGC();
    ToggleFrame();
    command=hCrackCommandLine();
    bid=ObeySystemCommand(command,DatUsedPathNamePtr,&fcb);
    if (bid<=0)
        p_exit(0);
    ReduceScreenSize();
    ToggleStatusWindow();
    RestartDisplay();
    WriteBrushText();
    WriteClipboardText();
    wInfoMsg("Press `Help' for tips on usage");
    }

LOCAL_C VOID ProcessSystemCommand(VOID)
    {
    TEXT buf[P_FNAMESIZE+2];
    INT newbid;
    VOID *newfcb;

    wGetCommand((UBYTE *)&buf[0]);
    if (buf[0]=='X')
        ExitApplication();
    SaveIfChanged();
    newbid=ObeySystemCommand(buf[0],&buf[1],&newfcb);
    if (newbid>0)
        ChangeOverTo(newbid,newfcb,&buf[1]);
    }

LOCAL_C VOID DisplayHelpText(VOID)
    {
    INT choice;

    FOREVER
        {
        choice=uDialogMenu("Help on which topic",
                            "Cursor position",
                            "Select regions",
                            "Drawing lines",
                            "Brushes",
                            "Using clipboards",
                            "Borders and frames",NULL);
        switch (choice)
            {
        case 0:
            return;
        case 2:
            uDisplayText("To move the cursor:",
                            "use the usual cursor keys",
                            "including Home, End, PgUp and PgDn",
                            "or use `Goto' in the `Edit' menu.",
                            "Use Enter or Space to toggle the pixel",
                            "at the cursor position.",NULL);
            break;
        case 3:
            uDisplayText("To create a select region:",
                            "use SHIFTed cursor keys.",
                            "To emphasise a select region:",
                            "use `Highlight' in the `Special' menu.",
                            "`Move', Delete, `Copy' and `Plaster'",
                            "all operate on the current select region.",NULL);
            break;
        case 4:
            uDisplayText("To draw vertical or horizontal lines:",
                            "use the cursor keys plus CONTROL.",
                            "Lines are drawn in the current Brush.",NULL);
            break;
        case 5:
            uDisplayText("The Brush setting affects:",
                            "horizontal and vertical lines drawn",
                            "borders drawn using `Border'",
                            "text written using `Write'",
                            "and contents inserted from clipboards.",
                            "`Toggle' is the most useful setting.",NULL);
            break;
        case 6:
            uDisplayText("Choose between 4 clipboards with `Use'.",
                            "Cut into a clipboard with the Delete key.",
                            "To eliminate `Are you sure?' queries:",
                            "use `Interrogation' in the `Special' menu.",NULL);
            break;
        case 7:
            uDisplayText("To apply one of many types of border:",
                            "use `Border' in the `Special' menu.",
                            "To hide or show a frame round the icon:",
                            "use `Frame' in the `Special' menu.",
                            "To control cursor indicators in the frame:",
                            "use `Cursor' in the `Special' menu.",NULL);
            break;
            }
        }
    }

LOCAL_C VOID ManageCommand(INT index)
    {
    switch (index)
        {
    case ITEMS_BEFORE_FILE:
        NewIconFile();
        break;
    case ITEMS_BEFORE_FILE+1:
        OpenIconFile();
        break;
    case ITEMS_BEFORE_FILE+2:
        SaveIconAs();
        break;
    case ITEMS_BEFORE_FILE+3:
        SaveIcon();
        break;
    case ITEMS_BEFORE_FILE+4:
        RevertIfConfirmed();
        break;
    case ITEMS_BEFORE_FILE+5:
        ExitApplication();
        break;
    case ITEMS_BEFORE_EDIT:
        InsertFromClipboard();
        break;
    case ITEMS_BEFORE_EDIT+1:
        CopyToClipboard();
        break;
    case ITEMS_BEFORE_EDIT+2:
        ChangeClipboardUsed();
        break;
    case ITEMS_BEFORE_EDIT+3:
        InvertOrGrey();
        break;
    case ITEMS_BEFORE_EDIT+4:
        EnterMoveState();
        break;
    case ITEMS_BEFORE_EDIT+5:
        RepositionCursor();
        break;
    case ITEMS_BEFORE_BRUSH:
    case ITEMS_BEFORE_BRUSH+1:
    case ITEMS_BEFORE_BRUSH+2:
        brush=index-ITEMS_BEFORE_BRUSH;
        WriteBrushText();
        break;
    case ITEMS_BEFORE_SPECIAL:
        ToggleFrame();
        break;
    case ITEMS_BEFORE_SPECIAL+1:
        ToggleCursorVisibility();
        break;
    case ITEMS_BEFORE_SPECIAL+2:
        FlashHighlightRegion();
        break;
    case ITEMS_BEFORE_SPECIAL+3:
        WriteText();
        break;
    case ITEMS_BEFORE_SPECIAL+4:
        DrawBorder();
        break;
    case ITEMS_BEFORE_SPECIAL+5:
        ConsiderInterrogation();
        break;
        }
    }

LOCAL_C VOID TryExecuteCommand(INT keycode)
    {
    keycode=uLocateCommand(keycode);
    if (keycode>=0)
        ManageCommand(keycode);
    }

LOCAL_C VOID MainLoop(VOID)
    {
    INT ret;

    FOREVER
        {
        uGetKey(&key);
        if (key.keycode&W_EVENT_KEY)
            {
            if (key.keycode==CONS_EVENT_COMMAND)
                ProcessSystemCommand();
            }
        else if (key.keycode&W_SPECIAL_KEY)
            TryExecuteCommand(key.keycode&(~W_SPECIAL_KEY));
        else
            {
            switch (key.keycode)
                {
            case W_KEY_MENU:
                if (key.modifiers&W_CTRL_MODIFIER)
                    ToggleStatusWindow();
                else
                    {
                    ret=uPresentMenus();
                    if (ret>0)
                        TryExecuteCommand(ret);
                    }
                break;
            case W_KEY_RETURN:
            case ' ':
                AlterBit(G_TRMODE_INV);
                break;
            case W_KEY_LEFT:
                MoveCursorRelative(-1,0);
                break;
            case W_KEY_RIGHT:
                MoveCursorRelative(1,0);
                break;
            case W_KEY_UP:
                MoveCursorRelative(0,-1);
                break;
            case W_KEY_DOWN:
                MoveCursorRelative(0,1);
                break;
            case W_KEY_HOME:
                MoveCursorAbsolute(0,-1);
                break;
            case W_KEY_END:
                MoveCursorAbsolute(ICON_SIZE-1,-1);
                break;
            case W_KEY_PAGE_UP:
                MoveCursorAbsolute(-1,0);
                break;
            case W_KEY_PAGE_DOWN:
                MoveCursorAbsolute(-1,ICON_SIZE-1);
                break;
            case W_KEY_ESCAPE:
                if (issel)
                    CancelSelect();
                break;
            case W_KEY_DELETE_LEFT:
                CutToClipboard();
                break;
            case W_KEY_HELP:
                DisplayHelpText();
                }
            }
        }
    }

GLDEF_C VOID main(VOID)
    {
    uCommonInit();
    SpecificInit();
    MainLoop();
    }
