
//  ------------------------------------------------------------------
//  GoldED+
//  Copyright (C) 1990-1999 Odinn Sorensen
//  Copyright (C) 1999-2000 Alexander S. Aganichev
//  ------------------------------------------------------------------
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License as
//  published by the Free Software Foundation; either version 2 of the
//  License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
//  MA 02111-1307 USA
//  ------------------------------------------------------------------
//  $Id$
//  ------------------------------------------------------------------
//  Arealister and other area-specific code
//  ------------------------------------------------------------------

#include <golded.h>
#include <gutlos.h>


//  ------------------------------------------------------------------

extern int arealistnumgrps;

bool in_arealist = false;

GPickArealist* PickArealist;

Echo area_maybe;
int areaswithgroupid = 0;
uint* areanumbers = NULL;

//            111111111122222222223333333333444444444455555555556666666666777777777
//  0123456789012345678901234567890123456789012345678901234567890123456789012345678
// ÚÄAreaÄDescriptionÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄMsgsÄÄÄNewÄÄEchoIDÄÄÄÄÄÄÄÄÄÄÄÄÄÄGrpÄ¿
// ³   1> NET FidoNet Z3/4/5/6                 118+    3* NET.FIDOZX            A ³

// AREALISTFORMAT "AM D CPUN E G"

int area_pos      = -1;
int area_width    = 4;
int marked_pos    = -1;
int marked_width  = 1;
const char marked_char   = MMRK_MARK;
int desc_pos      = -1;
int desc_width    = -1;
int count_pos     = -1;
int count_width   = 6;
int pmark_pos     = -1;
int pmark_width   = 1;
const char pmark_char    = '+';
int unread_pos    = -1;
int unread_width  = 6;
int changed_pos   = -1;
int changed_width = 1;
const char changed_char  = '*';
int echoid_pos    = -1;
int echoid_width  = 0;
int groupid_pos   = -1;
int groupid_width = 0;


//  ------------------------------------------------------------------

GPickArealist::GPickArealist()
{

    areawin1 = 0;
    areawin2 = 0;
    tempwin1 = 0;
    tempwin2 = 0;

    pmails   = 0;
    pmareas  = 0;
    pmscan   = false;

    area_fuzidx = 0;

}


//  ------------------------------------------------------------------

void GPickArealist::AreasRenumber()
{

    for(int n=0,a=1,t=AL.size(); n<t; n++)
        areanumbers[n] = AL[n]->isseparator() ? 0 : a++;
}


//  ------------------------------------------------------------------

bool GPickArealist::is_selectable(uint idx)
{

    Area* area = AL.AreaNoToPtr(idx);
    return (not area->isseparator());

}


//  ------------------------------------------------------------------

void GPickArealist::do_delayed()
{

    Area* area = AL.AreaNoToPtr(index);

    const size_t buflen=strlen(title)+strlen(area_maybe);
    const size_t tmplen=(buflen>MAXCOL?buflen:MAXCOL);
    char * buf = new char[buflen+3];
    buf[buflen] = '\0';
    buf[buflen+1] = '\xFF';
    buf[buflen+2] = '\0';
    char * tmp = new char[tmplen+3];
    tmp[tmplen] = '\0';
    tmp[tmplen+1] = '\xFF';
    tmp[tmplen+2] = '\0';

    update_statuslinef("%s: %u %s, %u %s, %u %s", "", area->echoid(), area->Msgn.Count(),
                       (area->Msgn.Count() == 1 ? LNG->msg : LNG->msgs), area->unread,
                       LNG->unread, area->PMrk.Count(), LNG->personal);

    strcpy(stpcpy(buf, title), area_maybe);
    strsetsz(strcpy(tmp, buf), MAXCOL);
    wwprintstr(tempwin, 0, 0, wattr, tmp);

    if(CFG->switches.get(arealistpagebar))
        wscrollbar(W_VERT, maximum_index+1, maximum_index, index);

    if (buf[buflen] || buf[buflen+1]!='\xFF' || buf[buflen+2])
    {
        LOG.errpointer(__FILE__,__LINE__);
        LOG.printf("! Buffer overflow: buf in GPickArealist::do_delayed(), 8 or 9 lines above");
        PointerErrorExit();
    }
    if (tmp[tmplen] || tmp[tmplen+1]!='\xFF' || tmp[tmplen+2])
    {
        LOG.errpointer(__FILE__,__LINE__);
        LOG.printf("! Buffer overflow: tmp in GPickArealist::do_delayed(), 15 or 16 lines above");
        PointerErrorExit();
    }
    delete[] buf;
    delete[] tmp;
}


//  ------------------------------------------------------------------

void GPickArealist::close_all()
{

    if(areawin1)
        wunlink(areawin1);
    if(areawin2)
        wunlink(areawin2);
    if(tempwin1)
        wunlink(tempwin1);
    if(tempwin2)
        wunlink(tempwin2);
}


//  ------------------------------------------------------------------

void GPickArealist::dispbuf(char* buf, int areano)
{

    Area* area = AL.AreaNoToPtr(areano);

    memset(buf, ' ', MAXCOL-2);
    buf[MAXCOL-2] = NUL;

    char areabuf[33];
    gsprintf(PRINTF_DECLARE_BUFFER(areabuf), "%u", CFG->switches.get(arealistnos) ? area->board() : areanumbers[areano]);
    int areawidth = strlen(areabuf);

    char markedbuf[2] = { " " };
    *markedbuf = area->ismarked() ? marked_char : ' ';
    int markedwidth = 1;

    char descbuf[100];
    int descwidth = strlen(strcpy(descbuf, area->desc()));

    char countbuf[33];
    if (area->isscanned)
        gsprintf(PRINTF_DECLARE_BUFFER(countbuf), "%u", (uint)area->Msgn.Count());
    else
        strcpy(countbuf,  "-");
    int countwidth = strlen(countbuf);

    char pmarkbuf[2] = { " " };
    *pmarkbuf = area->PMrk.Count() ? pmark_char : ' ';
    int pmarkwidth = 1;

    char unreadbuf[33];
    if (area->isscanned)
        gsprintf(PRINTF_DECLARE_BUFFER(unreadbuf), "%u", (uint)((CFG->arealisttype == AL_TOTNEW) ? area->unread : area->lastread()));
    else
        strcpy(unreadbuf, "-");
    int unreadwidth = strlen(unreadbuf);

    char changedbuf[2] = { " " };
    *changedbuf = area->isunreadchg ? changed_char : ' ';
    int changedwidth = 1;

    char echoidbuf[100];
    int echoidwidth = strlen(strcpy(echoidbuf, area->echoid()));

    char groupidbuf[10] = { "" };
    if (groupid_width)
    {
        if (area->groupid() & 0x8000u)
        {
            if (groupid_width > 2)
                gsprintf(PRINTF_DECLARE_BUFFER(groupidbuf), "%u", area->groupid()&0x7FFF);
        }
        else if (g_isupper(area->groupid()))
            gsprintf(PRINTF_DECLARE_BUFFER(groupidbuf), "%c", (char)area->groupid());
    }
    int groupidwidth = strlen(groupidbuf);

    areawidth    = MinV(areawidth,    area_width);
    markedwidth  = MinV(markedwidth,  marked_width);
    descwidth    = MinV(descwidth,    desc_width);
    countwidth   = MinV(countwidth,   count_width);
    pmarkwidth   = MinV(pmarkwidth,   pmark_width);
    unreadwidth  = MinV(unreadwidth,  unread_width);
    changedwidth = MinV(changedwidth, changed_width);
    echoidwidth  = MinV(echoidwidth,  echoid_width);
    groupidwidth = MinV(groupidwidth, groupid_width);

    memcpy(buf+area_pos+area_width-areawidth,          areabuf,    areawidth);
    memcpy(buf+marked_pos,                             markedbuf,  markedwidth);
    memcpy(buf+desc_pos,                               descbuf,    descwidth);
    memcpy(buf+count_pos+count_width-countwidth,       countbuf,   countwidth);
    memcpy(buf+pmark_pos,                              pmarkbuf,   pmarkwidth);
    memcpy(buf+unread_pos+unread_width-unreadwidth,    unreadbuf,  unreadwidth);
    memcpy(buf+changed_pos,                            changedbuf, changedwidth);
    memcpy(buf+echoid_pos,                             echoidbuf,  echoidwidth);
    memcpy(buf+groupid_pos+groupid_width-groupidwidth, groupidbuf, groupidwidth);
}

//  ------------------------------------------------------------------

void GPickArealist::center()
{

    uint room, toproom, botroom;

    toproom = index;
    botroom = maximum_index - index;
    if(toproom > maximum_position/2)
    {
        if(botroom > maximum_position/2)
            room = maximum_position/2;
        else if(botroom)
            room = maximum_position - botroom;
        else
            room = maximum_position;
    }
    else
        room = toproom;

    position = room;
    display_page();
}


//  ------------------------------------------------------------------

void GPickArealist::jump_to()
{

    uint n = index+1;
    bool found = false;
    // Search for next marked area, wrapping 'round if needed
    for(; n<=maximum_index; n++)
    {
        if(not AL[n]->isseparator() and AL[n]->ismarked())
        {
            found = true;
            break;
        }
    }

    if(not found)
    {
        for(n = minimum_index; n <= index; n++)
        {
            if(not AL[n]->isseparator() and AL[n]->ismarked())
                break;
        }
    }

    if(n > maximum_index)
        n = minimum_index;

    while(AL[n]->isseparator())
    {
        if((++n) > maximum_index)
            n = minimum_index;
    }

    index = n;
    center();
}


//  ------------------------------------------------------------------

void GPickArealist::jumpmatch()
{

    uint n = index+1;
    bool found = false;
    // Search for next marked area, wrapping 'round if needed
    for(; n<=maximum_index; n++)
    {
        if(not AL[n]->isseparator() and (strnieql(area_maybe, AL[n]->echoid(), area_fuzidx) or striinc(area_maybe, AL[n]->echoid())))
        {
            found = true;
            break;
        }
    }

    if(not found)
    {
        for(n = minimum_index; n <= index; n++)
        {
            if(not AL[n]->isseparator() and (strnieql(area_maybe, AL[n]->echoid(), area_fuzidx) or striinc(area_maybe, AL[n]->echoid())))
                break;
        }
    }

    if(n > maximum_index)
        n = minimum_index;

    while(AL[n]->isseparator())
    {
        if((++n) > maximum_index)
            n = minimum_index;
    }

    index = n;
    center();

}


//  ------------------------------------------------------------------

void GPickArealist::open()
{

    int active=NO;

#if defined(GUTLOS_FUNCS)
    g_set_ostitle_name("Arealist",0);
#endif

    if(ypos)
    {
        if(tempwin1)
        {
            tempwin = tempwin1;
            areawin = areawin1;
            active = YES;
        }
    }
    else
    {
        if(tempwin2)
        {
            tempwin = tempwin2;
            areawin = areawin2;
            active = YES;
        }
    }

    if(active)
    {
        wunhide(tempwin);
        wprints(0,0, wattr, title);
        wunhide(areawin);
    }
    else
    {
        tempwin = wopen_(ypos, xpos, 1, MAXCOL, 5, battr, wattr, sbattr);
        wprints(0,0, wattr, title);
        areawin = wopen_(ypos+1, xpos, ylen+2, MAXCOL, btype, battr, wattr, sbattr);

        if(ypos)
        {
            tempwin1 = tempwin;
            areawin1 = areawin;
        }
        else
        {
            tempwin2 = tempwin;
            areawin2 = areawin;
        }

        if(area_width)
            wmessage(LNG->Area, TP_BORD, 1+area_pos+(marked_pos==area_pos+area_width?1:0), tattr);
        if(desc_width)
            wmessage(LNG->Description, TP_BORD, 1+desc_pos, tattr);
        if(count_width)
            wmessage(LNG->Msgs, TP_BORD, 1+count_pos+count_width-strlen(LNG->Msgs), tattr);
        if(unread_width)
        {
            if(CFG->arealisttype == AL_TOTLST)
                wmessage(LNG->Last, TP_BORD, 1+unread_pos+unread_width-strlen(LNG->Last), tattr);
            else if(CFG->arealisttype == AL_TOTNEW)
                wmessage(LNG->New, TP_BORD, 1+unread_pos+unread_width-strlen(LNG->New), tattr);
        }
        if(echoid_width)
            wmessage(LNG->EchoID, TP_BORD, 1+echoid_pos, tattr);
        if(groupid_width)
        {
            char grpbuf[40];
            if(groupid_width > 2)
                strcpy(grpbuf, LNG->Grp);
            else
            {
                *grpbuf = *(LNG->Grp);
                grpbuf[1] = NUL;
            }
            wmessage(grpbuf, TP_BORD, 1+groupid_pos, tattr);
        }

    }
    if(CFG->switches.get(areaautonext) and ypos == 0 and index >= minimum_index and not AL[index]->ismarked())
    {
        jump_to();
    }
    else
        center();
}


//  ------------------------------------------------------------------

void GPickArealist::close()
{

    whide();
    whide();
}

//  ------------------------------------------------------------------

void GPickArealist::precursor()
{

    *area_maybe = 0;
    area_fuzidx = 0;
}


//  ------------------------------------------------------------------

void GPickArealist::print_line(uint idx, uint pos, bool isbar)
{
    CREATEBUFFER(char, buf, MAXCOL);
    CREATEBUFFER(vchar, vbuf, MAXCOL*2);

    if(AL[idx]->isseparator())
    {
        Area* area = AL.AreaNoToPtr(idx);

        int sep_pos = (desc_pos != -1) ? desc_pos : echoid_pos;

        {
            for(int c = 0; c < sep_pos; c++)
                vbuf[c] = _box_table(btype, 1);
        }
        vbuf[sep_pos] = NUL;
        wprintvs(pos, 0, battr|ACSET, vbuf);
        wprints(pos, sep_pos, tattr, area->desc());

        int l = strlen(area->desc());
        int n = MAXCOL-2-sep_pos-l;

        {
            for(int c = 0; c < n; c++)
                vbuf[c] = _box_table(btype,1);
        }
        vbuf[n] = NUL;
        wprintvs(pos, sep_pos+l, battr|ACSET, vbuf);
    }
    else
    {
        dispbuf(buf, idx);
        wprints(pos, 0, isbar ? sattr : wattr, buf);
        if(AL[idx]->ismarked())
            wprintc(pos, marked_pos, isbar ? sattr : hattr, marked_char);
    }
}


//  ------------------------------------------------------------------

void GPickArealist::AreaCatchUp(uint n)
{

    // Do not do catch up if there's active area
    if(AA->isopen())
        return;

    GMenuAreaCatchup MenuAreaCatchup;
    GMsg* msg = new GMsg();
    throw_new(msg);

    int mode = MenuAreaCatchup.Run();

    if(mode != SCAN_QUIT)
    {
        for(AL.item = AL.idx.begin(); AL.item != AL.idx.end(); AL.item++)
        {

            if((mode == SCAN_MARKED and (*AL.item)->ismarked()) or mode == SCAN_ALL or (mode == SCAN_CURRENT and (*AL.item) == AL[n]))
            {

                if((*AL.item)->isseparator())
                    continue;

                update_statuslinef("%s ...", "", (*AL.item)->echoid());

                AA = (*AL.item);
                AA->Open();

                if(CFG->switches.get(highlightunread) and CFG->switches.get(areacatchupread))
                {
                    w_info(LNG->Wait);
                    for(uint i=AA->lastread()+1; i <= AA->Msgn.Count(); i++)
                    {
                        AA->LoadHdr(msg, AA->Msgn.CvtReln(i), false);
                        if(msg->timesread++ == 0)
                            AA->UpdateTimesread(msg);
                    }
                    w_info(NULL);
                }

                AA->set_lastread(AA->Msgn.Count());
                AA->isvalidchg = false;
                AA->Close();
            }
        }
    }
    throw_delete(msg);
}


//  ------------------------------------------------------------------

void GPickArealist::AreaDropMsgMarks(uint n)
{

    GMenuAreaDropMarks MenuAreaDropMarks;

    uint nummarks = 0;

    for(AL.item = AL.idx.begin(); AL.item != AL.idx.end(); AL.item++)
        nummarks += (*AL.item)->Mark.Count();

    CREATEBUFFER(char, buf, MAXCOL);
    gsprintf(PRINTF_DECLARE_BUFFER_AUTO(buf, MAXCOL), LNG->DropMarksInfo, longdotstr(nummarks));

    w_info(buf);
    int mode = MenuAreaDropMarks.Run();
    w_info(NULL);

    if(mode != SCAN_QUIT)
    {
        for(AL.item = AL.idx.begin(); AL.item != AL.idx.end(); AL.item++)
        {
            if((mode == SCAN_MARKED and (*AL.item)->ismarked()) or mode == SCAN_ALL or (mode == SCAN_CURRENT and (*AL.item) == AL[n]))
            {

                if((*AL.item)->isseparator())
                    continue;

                (*AL.item)->Mark.ResetAll();
            }
        }
    }
}


//  ------------------------------------------------------------------

bool GPickArealist::handle_key()
{

    uint n;
    uint x;
    const char* adesc;

    CREATEBUFFER(char, buf, MAXCOL+1);
    CREATEBUFFER(char, tmp, MAXCOL+1);

    int mode, changed, currno;

    gkey kk;

    if(key < KK_Commands)
    {
        key = key_tolower(key);
        if((key == Key_Esc) and esc_abort)
            key = KK_AreaAbort;
        else
        {
            kk = SearchKey(key, AreaKey, AreaKeys);
            if(kk)
                key = kk;
        }
    }


    switch(key)
    {

    case KK_AreaDropMsgMarks:
        AreaDropMsgMarks(index);
        break;

    case KK_AreaSelectMarks:
        AL.Select_Mask();
        break;

    case KK_AreaAskExit:
    {
        GMenuQuit MenuQuit;
        aborted = gkbd.quitall = (MenuQuit.Run()!=0);
        if(gkbd.quitall)
        {
            precursor();
            return false;
        }
    }
    break;

    case KK_AreaAbort:
        aborted = true;
    // Drop Through

    case KK_AreaSelect:
        if(AL[index]->isseparator())
        {
            if(not PlayMacro(key, KT_A))
                SayBibi();
            break;
        }
        precursor();
        return false;

    case KK_AreaQuitNow:
        aborted = gkbd.quitall = true;
        precursor();
        return false;

    case KK_AreaToggle:
        AL[index]->set_marked(not AL[index]->ismarked());
        display_bar();
        precursor();
        cursor_down();
        break;

    case KK_AreaMark:
        AL[index]->set_marked(true);
        display_bar();
        precursor();
        cursor_down();
        break;

    case KK_AreaUnmark:
        AL[index]->set_marked(false);
        display_bar();
        precursor();
        cursor_down();
        break;

    case KK_AreaBoardnos:
        CFG->switches.set(arealistnos, not CFG->switches.get(arealistnos));
        update();
        break;

    case KK_AreaCatchUp:
        AreaCatchUp(index);
        update();
        break;

    case KK_AreaJumpNextMatch:
        jumpmatch();
        break;

    case KK_AreaJump:
    {
        precursor();
        jump_to();
    }
    break;

    case KK_AreaDosShell:
        DosShell();
        break;

    case KK_AreaGotoPrev:
        precursor();
        cursor_up();
        break;
    case KK_AreaGotoNext:
        precursor();
        cursor_down();
        break;
    case KK_AreaGotoFirst:
        precursor();
        cursor_first();
        break;
    case KK_AreaGotoLast:
        precursor();
        cursor_last();
        break;

    case KK_AreaSoundkill:
        HandleGEvent(EVTT_STOPVOICE);
        break;

    case KK_AreaTouchNetscan:
        TouchNetscan();
        break;

    case KK_AreaHeat:
    {
        GMenuAreaHeat MenuAreaHeat;
        mode = MenuAreaHeat.Run();
        if(mode != SCAN_QUIT)
        {
            for(AL.item = AL.idx.begin(); AL.item != AL.idx.end(); AL.item++)
            {
                if((mode == SCAN_MARKED and (*AL.item)->ismarked()) or mode == SCAN_ALL or (mode == SCAN_CURRENT and (*AL.item) == AL[index-1]))
                {
                    update_statuslinef("%s %s", "", 1+LNG->ScanningArea, (*AL.item)->echoid());
                    (*AL.item)->SetHighwaterMark();
                }
            }
        }
    }
    break;

    case KK_AreaZap:
    {
        GMenuAreaZap MenuAreaZap;
        mode = MenuAreaZap.Run();
        if(mode != SCAN_QUIT)
        {
            for(AL.item = AL.idx.begin(); AL.item != AL.idx.end(); AL.item++)
            {
                if((mode == SCAN_MARKED and (*AL.item)->ismarked()) or mode == SCAN_ALL or (mode == SCAN_CURRENT and (*AL.item) == AL[index-1]))
                {
                    update_statuslinef("%s %s", "", 1+LNG->ScanningArea, (*AL.item)->echoid());
                    (*AL.item)->ResetHighwaterMark();
                }
            }
        }
    }
    break;

    case KK_AreaScanPM:
        pmscan = true;
        pmails = 0;
        pmareas = 0;
    // DROP THROUGH //

    case KK_AreaScan:
    {
        currno = index;
        GMenuAreaScan MenuAreaScan;
        mode = MenuAreaScan.Run(pmscan);
        if(mode != SCAN_QUIT)
        {

            w_info(LNG->Wait);

            strcpy(stecho, AL[index]->echoid());

            if(mode == SCAN_IMPORTQWK)
                changed = ImportQWK();
            else if(mode == SCAN_EXPORTQWK)
                changed = ExportQWK();
            else if(mode == SCAN_IMPORTSOUP)
                changed = ImportSOUP();
            else if(mode == SCAN_EXPORTSOUP)
                changed = ExportSOUP();
            else
            {
                if(cmdlinedebughg)
                    LOG.printf("- AreaScan from arealist");
                changed = AL.AreaScan(mode, currno, pmscan, pmails, pmareas);
            }

            w_info();

            AL.SetActiveAreaNo(currno);
            if(changed)
            {
                AL.Sort();
                AreasRenumber();
                index = AL.AreaEchoToNo(stecho);
                center();
            }

            if(pmscan)
            {
                if(pmails)
                {
                    w_infof(LNG->FoundPersonal,
                            pmails, (pmails==1?"":"s"),
                            pmareas, (pmareas==1?"":"s")
                           );
                }
                else
                {
                    w_info(LNG->NoPersonal);
                }
                waitkeyt(10000);
                w_info();
            }
        }
        pmscan = false;
    }
    break;

    case Key_Tick:
        CheckTick(KK_AreaQuitNow);
        break;

    case KK_AreaUndefine:
        break;

    case KK_AreaWriteGoldlast:
        w_info(LNG->Wait);
        AL.WriteGoldLast();
        w_info();
        break;

    default:
        if(key < KK_Macro)
        {
            n = g_toupper(key & 0xFF);
            if((area_fuzidx < area_maxfuz) or (key == Key_BS))
            {

                // Incremental search in the echoids
                if(not iscntrl(n) or (key == Key_BS))
                {
                    if(n == ' ')
                        n = '_';
                    if(key != Key_BS)
                        area_maybe[area_fuzidx++] = (char)n;
                    else if(area_fuzidx)
                        area_fuzidx--;
                    area_maybe[area_fuzidx] = NUL;
                    strcpy(stpcpy(buf, title), area_maybe);
                    strsetsz(strcpy(tmp, buf), MAXCOL);
                    wwprintstr(tempwin, 0, 0, wattr, tmp);
                    if(area_fuzidx)
                    {

                        x = atoi(area_maybe);
                        if(x)
                        {
                            if(CFG->switches.get(arealistnos) and (isdigit(area_maybe[1]) or (area_maybe[1] == NUL)))
                            {
                                for(n=0; n<AL.size(); n++)
                                {
                                    if(AL[n]->board() == x)
                                    {
                                        x = n;
                                        goto RedrawAreas;
                                    }
                                }
                                x = 0;  // No match found
                            }
                            else
                            {
                                for(n=0; n<area_fuzidx; n++)
                                {
                                    if(not isdigit(area_maybe[n]))
                                    {
                                        x = 0;
                                        break;
                                    }
                                }
                            }
                        }

                        if(x >= 1 and x<=AL.size())
                        {
                            {
                                for(int a=0, at=AL.size(); a<at; a++)
                                {
                                    if(areanumbers[a] == x)
                                    {
                                        x = a;
                                        goto RedrawAreas;
                                    }
                                }
                            }
                            x = (uint)-1;

RedrawAreas:

                            if(x == (uint)-1)
                                x = index;
                            while(AL[x]->isseparator())
                            {
                                x++;
                                if(x > maximum_index)
                                    x = minimum_index;
                            }
                            if(x != index)
                                display_line();
#define _topidx (index-position)                    // Shorthand..
#define _botidx (index+(maximum_position-position)) // Shorthand..
                            if((_topidx <= x) and (x <= _botidx))
                            {
                                position += (x-index);
                                index = x;
                                display_page();    /////////// NEW
                            }
                            else
                            {
                                index = x;
                                center();
                            }
                            break;
                        }
                        else
                        {
                            // Regular search
                            for(n=0; n<AL.size(); n++)
                            {
                                adesc = AL[n]->echoid();
                                if(strnicmp(area_maybe, adesc, area_fuzidx) == 0)
                                {
                                    AL.Sort();
                                    AreasRenumber();
                                    x = AL.AreaEchoToNo(adesc);
                                    goto RedrawAreas;
                                }
                            }
                            // Search inside
                            for(n=0; n<AL.size(); n++)
                            {
                                adesc = AL[n]->echoid();
                                if(striinc(area_maybe, adesc))
                                {
                                    AL.Sort();
                                    AreasRenumber();
                                    x = AL.AreaEchoToNo(adesc);
                                    goto RedrawAreas;
                                }
                            }
                        }
                    }
                    else
                    {
                        adesc = AL[index]->echoid();
                        AL.Sort();
                        AreasRenumber();
                        index = AL.AreaEchoToNo(adesc);
                        center();
                    }
                    break;
                }
            }
        }
        if(not PlayMacro(key, KT_A))
            SayBibi();
    }
    return true;
}

//  ------------------------------------------------------------------

int GPickArealist::Run(const char* _title, int wpos, int& idx)
{

    HandleGEvent(EVTT_AREALIST);

    xpos     = 0;                             // Window starting coloumn
    ypos     = wpos;                          // Window starting row
    xlen     = MAXCOL-2;                      // Window ending coloumn
    ylen     = MAXROW-wpos-4;                 // Window ending row
    title    = _title;                        // Window title
    btype    = W_BAREA;                       // Window border type
    battr    = C_AREAB;                       // Window border attributes
    wattr    = C_AREAW;                       // Window Color
    tattr    = C_AREAT;                       // Window Title Color
    sattr    = C_AREAS;                       // Window Selection Bar Color
    hattr    = C_AREAQ;                       // Window Highlight Color
    sbattr   = C_AREAPB;                      // Window Scrollbar Color
    helpcat  = H_Area;                        // Window Help Category

    maximum_index    = AL.size() - 1;             // List Entries - 1
    maximum_position = MinV((size_t)ylen-1, AL.size()-1); // Display Pos
    index            = AL.AreaIdToNo(idx);        // List Index
    listwrap         = CFG->switches.get(displistwrap);  // True if wrap-around is supported
    esc_abort        = (wpos!=0);
    area_maxfuz      = MinV(sizeof(Echo), MAXCOL-strlen(title)-1);

    goldmark = ' ';

    areanumbers = (uint*)throw_calloc(AL.size(), sizeof(uint));
    AreasRenumber();

    run_picker();

    throw_release(areanumbers);

    if(not aborted)
        return AL[index]->areaid();

    idx = AL[index]->areaid();

    return -1;
}


//  ------------------------------------------------------------------

int AreaPick(char* title, int wpos, int* idx)
{

    GPickArealist p;
    int new_area;

    if(gkbd.quitall)
        return -1;

    PickArealist = &p;

    in_arealist = true;
    new_area = p.Run(title, wpos, *idx);
    in_arealist = false;

    PickArealist = NULL;

    return new_area;
}


//  ------------------------------------------------------------------

