/*
    TELE.C

Written by DavidW
Started on 20/11/91

*/

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

GLREF_D UWORD DatLocked;
GLREF_D TEXT *DatUsedPathNamePtr;


LOCAL_D TEXT *cmds[]=
    {
    "nNew",
    "oOpen",
    "mMerge",
    "sSave as",
    "kCompress",
    "xExit",
    "iInsert",
    "cCopy",
    "uUpdate",
    "aAdd",
    "dDelete",
    "rFind previous",
    "fFind",
    "gFind next",
    "jJump to",
    "zSort",
    "lDial",
    "yPrint setup",
    "pPrint",
    NULL
    };

#define FILE_ITEMS          6
#define EDIT_ITEMS          5
#define FIND_ITEMS          3
#define SPECIAL_ITEMS       5

#define ITEMS_BEFORE_FILE       0
#define ITEMS_BEFORE_EDIT       FILE_ITEMS
#define ITEMS_BEFORE_FIND       (ITEMS_BEFORE_EDIT+EDIT_ITEMS)
#define ITEMS_BEFORE_SPECIAL    (ITEMS_BEFORE_FIND+FIND_ITEMS)

LOCAL_D H_MENU_DATA mdata[]=
    {
    "File",FILE_ITEMS,
    "Edit",EDIT_ITEMS,
    "Find",FIND_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 struct
    {
    UWORD dataStart;      
    UWORD needVersion;    
    UWORD firHeader;
    UBYTE fir[3];
    } ModelHeader=
        {
        DbfHeaderLength,
        0x100f,
        (DbfRecordTypeFir<<12)|3,
            {
            DbfFieldTypeString,
            DbfFieldTypeString,
            DbfFieldTypeWord
            }
        };

LOCAL_D TEXT filename[P_FNAMESIZE];
LOCAL_D VOID *dH;
LOCAL_D UBYTE dBuf[512];
LOCAL_D UWORD dOff;

typedef struct
    {
    TEXT nam[20];
    TEXT dep[4];
    WORD nmb;
    } ENTRY;

LOCAL_D ENTRY clip;
LOCAL_D UWORD clipfill=FALSE;

LOCAL_D TEXT find[22]={0};

LOCAL_D UWORD PrintState;
LOCAL_D TEXT PrintBuf[10];
LOCAL_D UWORD PrintRec;
LOCAL_D ENTRY PrintEnt;

#define NAME_TLY     4
#define DEPT_TLY    24
#define TELE_TLY    44
#define COUNT_TLY   58
#define TEXT_HEIGHT 18
#define TEXT_ASCENT 16

#define LABEL_TLX   15
#define LABEL_WID   30
#define FIELD_TLX   65
#define FIELD_WID   120
#define COUNT_TLX   105
#define COUNT_WID   75

#define TRN_CALL call(reg_param=>(bx,si),reg_saved=>(cx,dx,di,EPOC_SAVE))


LOCAL_C VOID DisplayPrompt(INT tly,TEXT *buf)
    {
    P_RECT box;

    box.tl.y=tly;
    box.tl.x=LABEL_TLX;
    box.br.x=LABEL_TLX+LABEL_WID;
    box.br.y=tly+TEXT_HEIGHT;
    gPrintBoxText(&box,TEXT_ASCENT,G_TEXT_ALIGN_CENTRE,0,buf,p_slen(buf));
    }

LOCAL_C VOID DisplayPrompts(VOID)
    {
    DisplayPrompt(NAME_TLY,"Name");
    DisplayPrompt(DEPT_TLY,"Dept");
    DisplayPrompt(TELE_TLY,"\05");
    }

LOCAL_C VOID DisplayField(INT tly,TEXT *buf,INT len)
    {
    P_RECT box;

    box.tl.y=tly;
    box.tl.x=FIELD_TLX;
    box.br.x=FIELD_TLX+FIELD_WID;
    box.br.y=tly+TEXT_HEIGHT;
    gPrintBoxText(&box,TEXT_ASCENT,G_TEXT_ALIGN_LEFT,0,buf,len);
    }

LOCAL_C VOID DisplayCount(TEXT *buf)
    {
    P_RECT box;

    box.tl.x=COUNT_TLX;
    box.tl.y=COUNT_TLY;
    box.br.x=COUNT_TLX+COUNT_WID;
    box.br.y=COUNT_TLY+TEXT_HEIGHT;
    gPrintBoxText(&box,TEXT_ASCENT,G_TEXT_ALIGN_RIGHT,0,buf,p_slen(buf));
    }

LOCAL_C VOID BufToEntry(ENTRY *pent)
    {
    UBYTE *psrc;
    INT len;

    psrc=(&dBuf[dOff+2]);
    len=(*psrc)+1;
    p_bcpy(&pent->nam[0],psrc,len);
    psrc+=len;
    len=(*psrc)+1;
    p_bcpy(&pent->dep[0],psrc,len);
    psrc+=len;
    pent->nmb=(*(WORD *)psrc);
    }

LOCAL_C VOID CleanEntry(ENTRY *pent)
    {
    p_bfil(pent,sizeof(ENTRY),0);
    }

LOCAL_C VOID Display(VOID)
    {
    ENTRY ent;
    UWORD count;
    TEXT buf[12];
    TEXT num[4];
    INT len;

    count=DbfCount(dH);
    if (count)
        {
        p_atos(&buf[0],"%u/%u",DbfSense(dH)+1,count);
        BufToEntry(&ent);
        }
    else
        {
        p_scpy(&buf[0],"Empty");
        CleanEntry(&ent);
        }
    DisplayField(NAME_TLY,&ent.nam[1],ent.nam[0]);
    DisplayField(DEPT_TLY,&ent.dep[1],ent.dep[0]);
    if (ent.nmb)
        len=p_itob(&num[0],ent.nmb);
    else
        len=0;
    DisplayField(TELE_TLY,&num[0],len);
    DisplayCount(&buf[0]);
    }

LOCAL_C INT EditEntry(ENTRY *pent,TEXT *title)
    {
    H_DI_EDIT edname;
    H_DI_EDIT eddept;
    LONG lnumb;
    H_DI_NUMBER numb;

    if (uOpenDialog(title))
        return(-1);
    edname.len=19;
    edname.str=(&pent->nam[0]);
    if (uAddDialogItem(H_DIALOG_EDIT,"Name",&edname))
        return(-1);
    eddept.len=3;
    eddept.str=(&pent->dep[0]);
    if (uAddDialogItem(H_DIALOG_EDIT,"Dept",&eddept))
        return(-1);
    lnumb=pent->nmb;
    numb.value=(&lnumb);
    numb.low=100;
    numb.high=999;
    if (uAddDialogItem(H_DIALOG_NUMBER,"Number",&numb))
        return(-1);
    if (uRunDialog()<=0)
        return(-1);
    pent->nmb=lnumb;
    return(0);
    }

LOCAL_C INT EntryToBuf(ENTRY *pent)
    {
    INT len1;
    INT len2;
    UBYTE *pb;

    len1=pent->nam[0]+1;
    pb=(&dBuf[2]);
    pb=p_bcpy(pb,&pent->nam[0],len1);
    len2=pent->dep[0]+1;
    pb=p_bcpy(pb,&pent->dep[0],len2);
    *(WORD *)pb=pent->nmb;
    dOff=0;
    return(len1+len2+2);
    }

LOCAL_C INT Check(INT val)
    {
    TEXT buf[64];

    if (val<0 && val!=E_FILE_EOF)
        {
        uForceToFront();
        DatLocked=TRUE;
        p_errs(&buf[0],val);
        wsAlertW(WS_ALERT_CLIENT,&buf[0],"Terminating application",0);
        p_exit(0);
        }
    return(val);
    }

LOCAL_C VOID DoUpdate(INT len)
    {
    INT state;

    state=DbfStateDisabled;
    Check(DbfUpdate(&state,dH,len));
    }

LOCAL_C VOID DoAppend(INT len)
    {
    Check(DbfAppend(dH,len));
    Display();
    }

LOCAL_C VOID AddRecord(VOID)
    {
    ENTRY new;

    CleanEntry(&new);
    if (!EditEntry(&new,"Add new record"))
        DoAppend(EntryToBuf(&new));
    }

LOCAL_C VOID UpdateRecord(VOID)
    {
    ENTRY ent;

    if (!DbfCount(dH))
        {
        wInfoMsg("No record to update");
        return;
        }
    BufToEntry(&ent);
    if (!EditEntry(&ent,"Update record"))
        {
        DoUpdate(EntryToBuf(&ent));
        Display();
        }
    }

LOCAL_C INT Transition(
#pragma save,TRN_CALL
                        INT pf(VOID *,UWORD *))
#pragma restore
    {
    return(Check(pf(dH,&dOff)));
    }

LOCAL_C VOID DeleteRecord(VOID)
    {
    INT state;

    if (!DbfCount(dH))
        {
        wInfoMsg("No record to delete");
        return;
        }
    if (uOpenDialog("Delete current record"))
        return;
    if (uAddButtonList("No",-'n',"Yes",'y',NULL))
        return;
    if (uRunDialog()!='y')
        return;
    state=DbfStateDisabled;
    if (Check(DbfEraseRead(&state,dH,&dOff))<0)
        Transition(DbfBackRead);
    Display();
    }

LOCAL_C VOID InsertFromClipboard(VOID)
    {
    if (!clipfill)
        {
        wInfoMsg("No record to insert");
        return;
        }
    DoAppend(EntryToBuf(&clip));
    }

LOCAL_C VOID CopyToClipboard(VOID)
    {
    if (!DbfCount(dH))
        {
        wInfoMsg("No record to copy");
        return;
        }
    BufToEntry(&clip);
    clipfill=TRUE;
    wInfoMsg("Record copied");
    }

LOCAL_C INT Search(INT dir)
    {
    UINT state;
    INT len;
    INT ret;

    len=find[0];
    find[0]='*';
    find[len+1]='*';
    state=DbfStateDisabled;
    ret=Check(DbfFindRead(&state,dH,&find[0],len+2,
                                    255|dir|DbfFindCaseIndependent,2,&dOff));
    find[0]=len;
    return(ret);
    }

LOCAL_C INT ResetTo(UINT current)
    {
    return(Check(DbfAbsRead(dH,current,&dOff)));
    }

LOCAL_C VOID DoFind(INT dir)
    {
    UINT current;

    current=DbfSense(dH);
    if (Search(dir)>=0)
        Display();
    else
        {
        ResetTo(current);
        wInfoMsg("Not found");
        }
    }

LOCAL_C VOID FindAgain(INT dir1,INT dir2,
#pragma save,TRN_CALL
                                        INT pf(VOID *,UWORD *))
#pragma restore
    {
    UINT current;

    current=DbfSense(dH);
    if (Transition(pf)>=0 && Search(dir1)>=0
                                || Search(dir2)>=0 && DbfSense(dH)!=current)
        Display();
    else
        {
        ResetTo(current);
        wInfoMsg("No more found");
        }
    }

LOCAL_C VOID FindNext(VOID)
    {
    FindAgain(DbfFindForwards,DbfFindFirst,DbfNextRead);
    }

LOCAL_C VOID FindPrevious(VOID)
    {
    FindAgain(DbfFindBackwards,DbfFindLast,DbfBackRead);
    }

LOCAL_C VOID FindRecord(VOID)
    {
    H_DI_EDIT edfind;

    if (uOpenDialog(NULL))
        return;
    edfind.str=(&find[0]);
    edfind.len=19;
    if (uAddDialogItem(H_DIALOG_EDIT,"Find",&edfind))
        return;
    if (uAddButtonList("Cancel",W_KEY_ESCAPE,"Find backwards",W_KEY_TAB,
                                        "Find forwards",W_KEY_RETURN,NULL))
        return;
    switch (uRunDialog())
        {
    case W_KEY_TAB:
        DoFind(DbfFindLast);
    case 0:
        return;
    case W_KEY_RETURN:
        DoFind(DbfFindFirst);
        }
    }

LOCAL_C VOID DialNumber(VOID)
    {
    ENTRY tmp;
    TEXT buf[4];

    if (!DbfCount(dH))
        {
        wInfoMsg("No number to dial");
        return;
        }
    BufToEntry(&tmp);
    buf[p_itob(&buf[0],tmp.nmb)]=0;
    if (hDTMFString(&buf[0]))
        wInfoMsg("Sound device busy");
    }

LOCAL_C VOID CompressFile(VOID)
    {
    UINT state;

    if (!hIsDbfCompressible(dH))
        wInfoMsg("Cannot compress on Flash");
    else
        {
        state=DbfStateDisabled;
        Check(DbfCompress(&state,dH));
        wInfoMsg("File compressed");
        }
    }

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

LOCAL_C VOID DisplayStart(VOID)
    {
    Transition(DbfFirstRead);
    Display();
    }

LOCAL_C VOID ChangeOverTo(VOID *newdH,TEXT *newname)
    {
    DbfClose(dH);
    dH=newdH;
    ChangeName(newname);
    DisplayStart();
    }

LOCAL_C INT doOpen(VOID **pdH,TEXT *pb,INT flags,DbfHeader *pHead)
    {
    INT state;

    state=DbfStateDisabled;
    p_scpy((TEXT *)&pHead->fileType[0],"OPLDatabaseFile");
    return(uErrorValue(DbfOpen(&state,pdH,pb,flags,pHead,&dBuf[0],512,1)));
    }

LOCAL_C INT TryOpenDbf(TEXT *pb,VOID **pdH)
    {
    DbfHeader Header;

    return(doOpen(pdH,pb,P_FOPEN|P_FUPDATE|P_FRANDOM,&Header));
    }

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

LOCAL_C VOID OpenDbf(VOID)
    {
    H_DI_FSEL open;
    TEXT openbuf[P_FNAMESIZE+2];
    VOID *newdH;
    UINT current;

    current=DbfSense(dH);
    SetupFilename(open.fname=(&openbuf[0]));
    open.flags=H_FILE_PICK_SELECTOR|H_FILE_RESTRICT_LIST|H_FILE_SET_DEFEXT;
    FOREVER
        {
        if (uOpenDialog("Open telephone file"))
            break;
        if (uAddDialogItem(H_DIALOG_FSEL,"File:",&open))
            break;
        if (uRunDialog()<=0)
            break; 
        openbuf[1+openbuf[0]]=0;
        if (!TryOpenDbf(&openbuf[1],&newdH))
            {
            ChangeOverTo(newdH,&openbuf[1]);
            return;
            }
        }
    ResetTo(current);
    }

LOCAL_C INT TryNewDbf(TEXT *pb,VOID **pdH)
    {
    DbfHeader Header;

    hEnsurePath(pb);
    Header.createVersion=DbfVersion();
    p_bcpy(&Header.dataStart,&ModelHeader,sizeof(ModelHeader));
    return(doOpen(pdH,pb,P_FREPLACE|P_FUPDATE|P_FRANDOM,&Header));
    }

LOCAL_C VOID NewDbf(VOID)
    {
    H_DI_FSEL new;
    TEXT newbuf[P_FNAMESIZE+2];
    VOID *newdH;
    UINT current;

    current=DbfSense(dH);
    SetupFilename(new.fname=(&newbuf[0]));
    new.flags=H_FILE_NEW_EDITOR|H_FILE_SET_DEFEXT;
    FOREVER
        {
        if (uOpenDialog("New telephone file"))
            break;
        if (uAddDialogItem(H_DIALOG_FSEL,"File:",&new))
            break;
        if (uRunDialog()<=0)
            break;
        newbuf[1+newbuf[0]]=0;
        if (!TryNewDbf(&newbuf[1],&newdH))
            {
            ChangeOverTo(newdH,&newbuf[1]);
            return;
            }
        }
    ResetTo(current);
    }

LOCAL_C INT DoMerge(TEXT *pb,INT flags,INT dir)
    {
    UINT state;

    state=DbfStateDisabled;
    return(uErrorValue(DbfCopyFile(&state,dH,pb,flags,1,dir)));
    }

LOCAL_C VOID MergeDbf(VOID)
    {
    H_DI_FSEL merge;
    TEXT mergebuf[P_FNAMESIZE+2];
    UINT current;

    current=DbfSense(dH);
    SetupFilename(merge.fname=(&mergebuf[0]));
    merge.flags=H_FILE_PICK_SELECTOR|H_FILE_RESTRICT_LIST|H_FILE_SET_DEFEXT;
    FOREVER
        {
        if (uOpenDialog("Merge telephone file"))
            break;
        if (uAddDialogItem(H_DIALOG_FSEL,"File:",&merge))
            break;
        if (uRunDialog()<=0)
            break; 
        mergebuf[1+mergebuf[0]]=0;
        if (!DoMerge(&mergebuf[1],P_FOPEN,DbfCopyToHandle))
            {
            wInfoMsg("File merged");
            DisplayStart();
            return;
            }
        }
    ResetTo(current);
    }

LOCAL_C VOID DoSave(TEXT *pb,INT usenew)
    {
    if (DoMerge(pb,P_FUPDATE|P_FREPLACE,DbfCopyFromHandle))
        return;
    if (usenew)
        {
        DbfClose(dH);
        ChangeName(pb);
        while (TryOpenDbf(pb,&dH))
            ;
        DisplayStart();
        }
    wInfoMsg("Saved");
    }

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

    if (uOpenDialog("Save 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 (uAddChoiceList("Use new file",&usenew,"No","Yes",NULL))
        return;
    if (uRunDialog())
        {
        savebuf[1+savebuf[0]]=0;
        hEnsurePath(&savebuf[1]);
        DoSave(&savebuf[1],usenew==2);
        }
    }

LOCAL_C INT ordf(INT r1,INT r2,VOID *base)
    {
    UBYTE *table;
    UBYTE *e1;
    UBYTE *e2;
    INT entlen;

    table=(UBYTE *)base;
    entlen=(*(UWORD *)table);
    table+=2;
    r1=(*(UWORD *)(table+r1*entlen));
    r2=(*(UWORD *)(table+r2*entlen));
    table+=2;
    e1=table+r1*entlen;
    e2=table+r2*entlen;
    return(p_bcmp(e1+1,*e1,e2+1,*e2));
    }

LOCAL_C VOID excf(INT r1,INT r2,VOID *base)
    {
    UBYTE *table;
    INT entlen;
    UWORD *p1;
    UWORD *p2;
    UWORD swap;

    table=(UBYTE *)base;
    entlen=(*(UWORD *)table);
    table+=2;
    p1=(UWORD *)(table+r1*entlen);
    p2=(UWORD *)(table+r2*entlen);
    swap=(*p1);
    *p1=(*p2);
    *p2=swap;
    }

LOCAL_C INT DoSort(UINT k,INT len)
    {
    INT entlen;
    UBYTE *table;
    INT i,j;
    INT thislen;
    UBYTE *target;
    UBYTE *source;
    UWORD moved;
    INT ret;

    entlen=len+3;
    table=p_alloc(2+k*entlen);
    if (!table)
        {
        uErrorString("Insufficient memory");
        return(-1);
        }
    *(UWORD *)table=entlen;
    Transition(DbfFirstRead);
    target=table+2;
    for (i=0; i<k; i++)
        {
        source=(&dBuf[dOff+2]);
        thislen=(*source++);
        if (thislen>len)
            thislen=len;
        *(UWORD *)target=i;
        *(target+2)=thislen;
        p_bcpy(target+3,source,thislen);
        target+=entlen;
        Transition(DbfNextRead);
        }
    if (p_qsort(k,ordf,excf,table))
        {
        uErrorString("Internal sort stack overflow");
        ret=(-1);
        }
    else
        {
        for (i=0; i<k; i++)
            {
            moved=(*(UWORD *)(table+2+i*entlen));
            ResetTo(moved);
            DoUpdate(DbfCopyDown(dH,dOff));
            target=table+2;
            for (j=0; j<k; j++)
                {
                if (*(UWORD *)target>moved)
                    (*(UWORD *)target)--;
                target+=entlen;
                }
            }
        ret=0;
        wInfoMsg("Sorted");
        DisplayStart();
        }
    p_free(table);
    return(ret);
    }

LOCAL_C VOID SortDbf(VOID)
    {
    UINT k;
    LONG len;
    H_DI_NUMBER num;
    UINT current;

    k=DbfCount(dH);
    if (!k)
        {
        wInfoMsg("No records to sort");
        return;
        }
    current=DbfSense(dH);
    len=19;
    num.low=1;
    num.high=19;
    num.value=(&len);
    FOREVER
        {
        if (uOpenDialog("Sort file by name"))
            break;
        if (uAddDialogItem(H_DIALOG_NUMBER,"Key length",&num))
            break;
        if (uRunDialog()<=0)
            break;
        if (DoSort(k,(INT)len))
            return;
        }
    ResetTo(current);
    }

LOCAL_C VOID JumpTo(VOID)
    {
    INT count;
    LONG curr;
    H_DI_NUMBER num;

    count=DbfCount(dH);
    if (!count)
        {
        wInfoMsg("No records to jump to");
        return;
        }
    if (uOpenDialog(NULL))
        return;
    curr=DbfSense(dH)+1;
    num.value=(&curr);
    num.low=1;
    num.high=count;
    if (uAddDialogItem(H_DIALOG_NUMBER,"Jump to record",&num))
        return;
    if (uRunDialog()<=0)
        return;
    ResetTo((UINT)(curr-1));
    Display();
    }

LOCAL_C INT PrintLine(H_PRINT *pr)
    {
    switch (PrintState++)
        {
    case 0:
        if (ResetTo(PrintRec++)<0)
            return(0);
        BufToEntry(&PrintEnt);
        pr->buf=(&PrintEnt.nam[1]);
        pr->blen=PrintEnt.nam[0];
        pr->style|=2;
        break;
    case 1:
        pr->buf=(&PrintEnt.dep[1]);
        pr->blen=PrintEnt.dep[0];
        break;
    case 2:
        pr->buf=(&PrintBuf[0]);
        pr->blen=p_itob(&PrintBuf[0],PrintEnt.nmb);
        break;
    case 3:
        pr->blen=0;
        PrintState=0;
        return(TRUE);
        }
    pr->flags|=0x8000;
    return(TRUE);
    }

LOCAL_C VOID DoPrint(VOID)
    {
    UINT current;

    current=DbfSense(dH);
    PrintState=0;
    PrintRec=0;
    hPrint(PrintLine);
    ResetTo(current);
    }

LOCAL_C VOID ManageCommand(INT index)
    {
    switch (index)
        {
    case ITEMS_BEFORE_FILE:
        NewDbf();
        break;
    case ITEMS_BEFORE_FILE+1:
        OpenDbf();
        break;
    case ITEMS_BEFORE_FILE+2:
        MergeDbf();
        break;
    case ITEMS_BEFORE_FILE+3:
        SaveDbfAs();
        break;
    case ITEMS_BEFORE_FILE+4:
        CompressFile();
        break;
    case ITEMS_BEFORE_FILE+5:
        p_exit(0);
    case ITEMS_BEFORE_EDIT:
        InsertFromClipboard();
        break;
    case ITEMS_BEFORE_EDIT+1:
        CopyToClipboard();
        break;
    case ITEMS_BEFORE_EDIT+2:
        UpdateRecord();
        break;
    case ITEMS_BEFORE_EDIT+3:
        AddRecord();
        break;
    case ITEMS_BEFORE_EDIT+4:
        DeleteRecord();
        break;
    case ITEMS_BEFORE_FIND:
        FindPrevious();
        break;
    case ITEMS_BEFORE_FIND+1:
        FindRecord();
        break;
    case ITEMS_BEFORE_FIND+2:
        FindNext();
        break;
    case ITEMS_BEFORE_SPECIAL:
        JumpTo();
        break;
    case ITEMS_BEFORE_SPECIAL+1:
        SortDbf();
        break;
    case ITEMS_BEFORE_SPECIAL+2:
        DialNumber();
        break;
    case ITEMS_BEFORE_SPECIAL+3:
        hPrintSetupDialog();
        break;
    case ITEMS_BEFORE_SPECIAL+4:
        DoPrint();
        }
    }

LOCAL_C INT ObeySystemCommand(INT cmd,TEXT *pb,VOID **pdH)
    {
    return((cmd=='O'? TryOpenDbf: TryNewDbf)(pb,pdH));
    }

LOCAL_C VOID ProcessSystemCommand(VOID)
    {
    TEXT buf[P_FNAMESIZE+2];
    VOID *newdH;

    wGetCommand((UBYTE *)&buf[0]);
    if (buf[0]=='X')
        p_exit(0);
    if (!ObeySystemCommand(buf[0],&buf[1],&newdH))
        ChangeOverTo(newdH,&buf[1]);
    }

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

LOCAL_C VOID MainLoop(VOID)
    {
    INT ret;
    WMSG_KEY key;

    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))
                    {
                    ret=uPresentMenus();
                    if (ret>0)
                        TryExecuteCommand(ret);
                    }
                break;
            case W_KEY_LEFT:
                if (Transition(DbfBackRead)>=0)
                    {
                    Display();
                    break;
                    }
            case W_KEY_HOME:
                Transition(DbfFirstRead);
                Display();
                break;
            case W_KEY_RIGHT:
                if (Transition(DbfNextRead)>=0)
                    {
                    Display();
                    break;
                    }
            case W_KEY_END:
                Transition(DbfLastRead);
                Display();
                break;
            case W_KEY_DELETE_LEFT:
                DeleteRecord();
                break;
            case W_KEY_TAB:
                key.modifiers^=W_SHIFT_MODIFIER;
            case W_KEY_RETURN:
                if (key.modifiers&W_SHIFT_MODIFIER)
                    FindPrevious();
                else
                    FindNext();
                break;
            case W_KEY_HELP:
                if (key.modifiers&W_PSION_MODIFIER)
                    DialNumber();
                }
            }
        }
    }

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 VOID CreateGC(VOID)
    {
    G_GC gc;
    INT hgc;

    gc.style=G_STY_DOUBLE;
    hgc=gCreateGC(MainWid,G_GC_MASK_STYLE,&gc);
    if (hgc<0)
        p_exit(hgc);
    }

LOCAL_C VOID SpecificInit(VOID)
    {
    INT command;

    MainWid=uFindMainWid();
    CreateGC();
    command=hCrackCommandLine();
    if (ObeySystemCommand(command,DatUsedPathNamePtr,&dH))
        p_exit(0);
    ReduceScreenSize();
    gBorder(W_BORD_CORNER_4);
    wsEnable();
    DisplayPrompts();
    DisplayStart();
    }

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