#include "alg.h"
#include "mylib.h"

#define cpComboBox "\x16\x17"
const int MAXSTR = 1024;


//================================================================================
//==================== TCompCollect ==============================================
//================================================================================

TCompCollect::TCompCollect( ccIndex aLimit, ccIndex aDelta ) :
    count( 0 ), items( 0 ), limit( 0 ), delta( aDelta ),
    shouldDelete( True )
{
    setLimit( aLimit );
}

TCompCollect::TCompCollect() :
    count( 0 ), items( 0 ), limit( 0 ), delta( 0 ),
    shouldDelete( True )
{
    items = 0;
}

TCompCollect::~TCompCollect()
{
  delete items;
}

void TCompCollect::shutDown()
{
    if( shouldDelete )
        freeAll();
    else
        count=0;
    setLimit(0);
    TObject::shutDown();
}

void *TCompCollect::at( ccIndex index )
{
    if( index < 0 || index >= count ) error(1,0);
    return items[index].item;
}

void TCompCollect::atRemove( ccIndex index )
{
    if( index < 0 || index >= count ) error(1,0);

    count--;
//    memmove( &items[index], &items[index+1], (count-index)*sizeof(void *) );
    memmove( &items[index], &items[index+1], (count-index)*sizeof(CollIt) );
}

void TCompCollect::atFree( ccIndex index )
{
    void *item = at( index );
    atRemove( index );
    freeItem( item );
}

void TCompCollect::atInsert(ccIndex index, void *item)
{
    if( index < 0 ) error(1,0);
    if( count == limit ) setLimit(count + delta);

//    memmove( &items[index+1], &items[index], (count-index)*sizeof(void *) );
    memmove( &items[index+1], &items[index], (count-index)*sizeof(CollIt) );
    count++;

    items[index].item = item;
}

void TCompCollect::atPut( ccIndex index, void *item )
{
    if( index >= count ) error(1,0);
    items[index].item = item;
}

void TCompCollect::remove( void *item )
{
    atRemove( indexOf(item) );
}

void TCompCollect::removeAll()
{
    count = 0;
}

void TCompCollect::error( ccIndex code, ccIndex )
{
    exit(212 - code);
}

void *TCompCollect::firstThat( ccTestFunc Test, void *arg )
{
 for( ccIndex i = 0; i < count; i++ )
  if( Test(items[i].item, arg ) == True ) return items[i].item;
 return 0;
}

void *TCompCollect::lastThat( ccTestFunc Test, void *arg )
{
 for( ccIndex i = count; i > 0; i-- )
  if( Test( items[i-1].item, arg ) == True ) return items[i-1].item;
 return 0;
}

void TCompCollect::forEach( ccAppFunc action, void *arg )
{
 for( ccIndex i = 0; i < count; i++ ) action( items[i].item, arg );
}

void TCompCollect::free( void *item )
{
 remove( item );
 freeItem( item );
}

void TCompCollect::freeAll()
{
 for( ccIndex i = 0; i < count; i++ ) {items[i].opt=0; freeItem( at(i) );}
 count = 0;
}

void TCompCollect::freeItem( void *item )
{
 delete item;
}

ccIndex TCompCollect::indexOf(void *item)
{
 for( ccIndex i = 0; i < count; i++ ) if( item == items[i].item ) return i;
 error(1,0);
 return 0;
}

ccIndex TCompCollect::insert( void *item )
{
 ccIndex loc = count;
 atInsert( count, item );
 return loc;
}

void TCompCollect::pack()
{
 CollIt *curDst = items;
 CollIt *curSrc = items;
 CollIt *last = items + count;
 while( curSrc < last )
 {
  if( curSrc->item != 0 ) {curDst++; curDst->item = curSrc->item;curDst->opt = curSrc->opt;}
  curSrc++;
 }
}

void TCompCollect::setLimit(ccIndex aLimit)
{
 if( aLimit < count ) aLimit =  count;
 if( aLimit > maxCollectionSize) aLimit = maxCollectionSize;
 if( aLimit != limit )
 {
  CollIt *aItems;
  if (aLimit == 0 ) aItems = 0;
  else
  {
   aItems = (CollIt *)calloc(sizeof(CollIt),aLimit);
//   aItems = new CollIt[aLimit];
   if(!aItems) return; // BF:TNSCollection-2
   if( count !=  0 ) memcpy( aItems, items, count*sizeof(CollIt) );
  }
  delete items;
  items =  aItems;
  limit =  aLimit;
 }
}



//==================================================================================
//= TMyTListBox                                                                    =
//==================================================================================
struct TMyTListBoxRec
{
    TCompCollect *items;
    int selection;
};

TMyTListBox::TMyTListBox( const TRect& bounds, ushort aNumCols, TScrollBar *aScrollBar, funcptr func ) :
    TListViewer(bounds, aNumCols, 0, aScrollBar), items( 0 ), funcEvent(func)
{
    setRange(0);
}

TMyTListBox::~TMyTListBox()
{
}

size_t TMyTListBox::dataSize()
{
    return sizeof(TMyTListBoxRec);
}

void TMyTListBox::getData( void * rec )
{
    TMyTListBoxRec *p = (TMyTListBoxRec *)rec;
    p->items = items;
    p->selection = focused;
}

void TMyTListBox::getText( char *dest, int item, int maxChars )
{
        if (items != 0 )
                {
                strncpy( dest, (const char *)(items->at(item)), maxChars );
                dest[maxChars] = '\0';
                }
    else
        *dest = EOS;
}

void TMyTListBox::setData( void *rec )
{
    TMyTListBoxRec *p = (TMyTListBoxRec *)rec;
    newList(p->items);
    focusItem(p->selection);
    drawView();
}

void TMyTListBox::handleEvent ( TEvent& event )
{
 Boolean mousOk=False;
 Boolean spaceOk=False;

 if(event.what == evMouseDown && event.mouse.buttons == mbLeftButton) mousOk=True;
 if(event.what == evKeyboard && event.keyDown.keyCode == 0x3920)
 {
  spaceOk=True;
  if(list()->GetOptItem(focused)==1)list()->SetOptItem(focused,0);
  else list()->SetOptItem(focused,1);
  focusItemNum(focused+1);
  drawView();
 }
 TListViewer::handleEvent(event);
 if (funcEvent!=NULL) funcEvent( this, event);

 if(mousOk) message(owner,evBroadcast,cmDownLeftMouse,this);
 if(spaceOk) message(owner,evBroadcast,cmDownSpace,this);
}

void TMyTListBox::draw()
{
 int i, j, item;
 ushort normalColor, selectedColor, focusedColor, color;
 short colWidth, curCol, indent;
 TDrawBuffer b;
 uchar scOff;

 if( (state&(sfSelected | sfActive)) == (sfSelected | sfActive))
 { normalColor = getColor(1); focusedColor = getColor(3); selectedColor = getColor(4); }
 else { normalColor = getColor(2); selectedColor = getColor(4); }

 if( hScrollBar != 0 ) indent = hScrollBar->value;
 else indent = 0;

 colWidth = size.x / numCols + 1;
 for( i = 0; i < size.y; i++ )
 {
  for( j = 0; j < numCols; j++ )
  {
   item =  j * size.y + i + topItem;
   curCol = j * colWidth;
   if( (state & (sfSelected | sfActive)) == (sfSelected | sfActive) && focused == item && range > 0)
   {
    color = focusedColor;
    setCursor( curCol + 1, i );
    scOff = 0;
   }
   else if( item < range && isSelected(item) ) { color = selectedColor; scOff = 2; }
   else { color = normalColor; scOff = 4; }

   b.moveChar( curCol, ' ', color, colWidth );
   if( item < range )
   {
    char text[MAXSTR];
    getText( text, item, colWidth + indent );
    char buf[MAXSTR];
    buf[0] = '\0';
    if ( strlen(text) > indent )
    { memcpy( buf, text+indent, colWidth ); buf[colWidth] = EOS; }
    b.moveStr( curCol+1, buf, color );
    if( showMarkers )
    {
     b.putChar( curCol, specialChars[scOff] );
     b.putChar( curCol+colWidth-2, specialChars[scOff+1] );
    }
    if(list()->GetOptItem(item)==1) b.moveStr( curCol,"*",getColor(4));
   }
   else if( i == 0 && j == 0 ) b.moveStr( curCol+1, "<empty>",getColor(1));
   b.moveChar( curCol+colWidth-1, 179, getColor(5), 1 );
  }
  writeLine( 0, i, size.x, 1, b );
 }
}


void TMyTListBox::newList( TCompCollect *aList ,int optFre)
{
    if(optFre) destroy( items );
    items = aList;
    if( aList ) setRange( aList->getCount() );
    else setRange(0);
    if( range > 0 ) focusItem(0);
    drawView();
}

//==================================================================================
//= TMyLabel                                                                       =
//==================================================================================
void TMyLabel::SetText(char *aText)
{
 delete (char *)text;
 text=newStr( aText );
 draw();
};

void TMyLabel::draw()
{
    ushort color;
    TDrawBuffer b;
    uchar scOff;
    int strt_x;


    if( light )
        {
        color = getColor(0x0402);
        scOff = 0;
        }
    else
        {
        color = getColor(0x0301);
        scOff = 4;
        }
    b.moveChar( 0, ' ', color, size.x );
    if( text != 0 )
    {
     strt_x=0;
     if(align && size.x>strlen(text))strt_x=(size.x-strlen(text))/2;
     b.moveCStr( strt_x, text, color );
    }
    if( showMarkers ) b.putChar( 0, specialChars[scOff] );
    writeLine( 0, 0, size.x, 1, b );
};

void TMyLabel::handleEvent( TEvent& event )
{
    TStaticText::handleEvent(event);
    if( event.what == evMouseDown )
        {
        if( link && (link->options & ofSelectable) && !(link->state & sfDisabled) )
            link->select();
        clearEvent( event );
        }
    else if( event.what == evKeyDown )
        {
        char c = hotKey( text );
#ifdef __DOS32__
        if(getAltCode(c) == event.keyDown.keyCode ||
#else
        if(TGKey::GetAltCode(c) == event.keyDown.keyCode ||                //Q
#endif
           ( c != 0 && owner->phase == TGroup::phPostProcess &&
           toupper(event.keyDown.charScan.charCode) ==  c )
          )
            {
            if( link && (link->options & ofSelectable) && !(link->state & sfDisabled) )
                link->select();
            clearEvent( event );
            }
        }
    else if( event.what == evBroadcast &&
            ( event.message.command == cmReceivedFocus ||
              event.message.command == cmReleasedFocus )
           )
            {
             if(link)
             {
              light = Boolean( (link->state & sfFocused) != 0 );
              drawView();
             }
            }
}


//==================================================================================
//= TEdit                                                                          =
//==================================================================================
void TEdit::draw()
{
    int l, r;
    TDrawBuffer b;

    uchar color = (state & sfFocused) ? getColor( 2 ) : getColor( 1 );

    b.moveChar( 0, ' ', color, size.x );
    char buf[MAXSTR];
    strncpy( buf, data+firstPos, size.x);
    buf[size.x] = EOS;
    b.moveStr( 0, buf, color );

    if( canScroll(1) )
        b.moveChar( size.x-1, rightArrow, getColor(4), 1 );
    if( (state & sfSelected) != 0 )
        {
        if( canScroll(-1) )
            b.moveChar( 0, leftArrow, getColor(4), 1 );
        l = selStart - firstPos;
        r = selEnd - firstPos;
        l = max( 0, l );
        r = min( size.x , r );
        if (l <  r)
            b.moveChar( l, 0, getColor(3), r - l );
        }
    writeLine( 0, 0, size.x, size.y, b );
    setCursor( curPos-firstPos, 0);
}
Boolean TEdit::canScroll( int delta )
{

    if( delta < 0 )
        return Boolean( firstPos > 0 );
    else
        if( delta > 0 )
            return Boolean( strlen(data) - firstPos > size.x );
        else
            return False;
}
const char near TEdit::rightArrow = '\x10';
const char near TEdit::leftArrow = '\x11';
const int CONTROL_Y = 25;

void  TEdit::handleEvent( TEvent& event )
{
 char *str;
//-----------------------------------------
#ifdef __DOS32__
    if(event.what == evKeyboard)
    {
     switch(event.keyDown.keyCode)
     {
      case kbCtrlIns:
       put_buf(data);
       message(owner,evBroadcast,cmPutBuf,this);
       clearEvent( event );
       break;
      case kbShiftIns:
       str=get_buf();
       if(str!=NULL) {setData(str); message(owner,evBroadcast,cmGetBuf,this);}
       clearEvent( event );
       break;
      default:
       break;
     }
    }
#endif
//-----------------------------------------

    TView::handleEvent(event);

    if (funcEvent!=NULL) if(funcEvent( this, event)==1) return;

    int delta, anchor, i;
    if( (state & sfSelected) != 0 )
        switch( event.what )
            {
            case  evMouseDown:
                if( canScroll(delta = mouseDelta(event)) )
                    do  {
                        if( canScroll(delta) )
                            {
                            firstPos += delta;
                            drawView();
                            }
                        } while( mouseEvent( event, evMouseAuto ) );
                else if (event.mouse.doubleClick)
                        selectAll(True);
                else
                    {
                    anchor =  mousePos(event);
                    do  {
                        if( event.what == evMouseAuto &&
                            canScroll( delta = mouseDelta(event) )
                          )
                            firstPos += delta;
                        curPos = mousePos(event);
                        if( curPos < anchor )
                            {
                            selStart = curPos;
                            selEnd = anchor;
                            }
                        else
                            {
                            selStart = anchor;
                            selEnd = curPos;
                            }
                        drawView();
                        } while (mouseEvent(event, evMouseMove | evMouseAuto));
                    }
                clearEvent(event);
                break;
            case  evKeyDown:
                switch( ctrlToArrow(event.keyDown.keyCode) )
                    {
                    case kbLeft:
                        if( curPos > 0 )
                            curPos--;
                        break;
                    case kbRight:
                        if(strlen(data) > curPos)curPos++;
                        else curPos=strlen(data);
                        break;
                    case kbHome:
                        curPos =  0;
                        break;
                    case kbEnd:
                        curPos = strlen(data);
                        break;
                    case kbBack:
                        if( curPos > 0 )
                            {
                             strcpy( data+curPos-1, data+curPos );
                             curPos--;
                             if( firstPos > 0 )
                                firstPos--;
                            }
                        break;
                    case kbDel:
                        if( selStart == selEnd )
                            if( curPos < strlen(data) )
                                {
                                selStart = curPos;
                                selEnd = curPos + 1;
                                }
                        deleteSelect();
                        break;
                    case kbIns:
                        setState(sfCursorIns, Boolean(!(state & sfCursorIns)));
                        break;
                    default:
                        if( event.keyDown.charScan.charCode >= ' ' )
                            {
                            deleteSelect();
                            if( (state & sfCursorIns) != 0 && (curPos < strlen(data)) )
                                strcpy( data + curPos, data + curPos + 1 );
                            if( strlen(data) < maxLen )
                                {
                                if( firstPos > curPos )
                                    firstPos = curPos;
                                memmove( data + curPos + 1, data + curPos,
                                         strlen(data+curPos)+1 );
                                data[curPos++] = event.keyDown.charScan.charCode;
                                }
                            }
                        else if( event.keyDown.charScan.charCode == CONTROL_Y)
                            {
                            *data = EOS;
                            curPos = 0;
                            }
                        else
                            return;
                    }
                    selStart = 0;
                    selEnd = 0;
                    if( firstPos > curPos )
                        firstPos = curPos;
                    i = curPos - size.x + 1;
                    if( firstPos < i )
                        firstPos = i;

                    drawView();
                    clearEvent( event );
                    break;
            }
}
int TEdit::mouseDelta( TEvent& event )
{
    TPoint mouse = makeLocal( event.mouse.where );

    if( mouse.x <= 0 )
        return -1;
    else
        if( mouse.x >= size.x-1)
            return 1;
        else
            return 0;
}
int TEdit::mousePos( TEvent& event )
{


    TPoint mouse = makeLocal( event.mouse.where );
    mouse.x = max( mouse.x, 0 );
    int pos = mouse.x + firstPos ;
    pos = max( pos, 0 );
    pos = min( pos, strlen(data) );
    return pos;
}
void  TEdit::deleteSelect()
{
    if( selStart < selEnd )
        {
        strcpy( data+selStart, data+selEnd );
        curPos = selStart;
        }
}
void TEdit::selectAll( Boolean enable )
{
    selStart = 0;
    if( enable )
        curPos = selEnd = min(size.x,strlen(data));
    else
        curPos = selEnd = 0;
    firstPos = max( 0, curPos-size.x);
    drawView();
}
void TEdit::setData( void *rec )
{
    strncpy(data,(char *)rec,dataSize()-1);
//    memcpy( data, rec, dataSize()-1 );
    data[dataSize()-1] = EOS;
    selectAll( True );
}
void TEdit::setState( ushort aState, Boolean enable )
{
    TView::setState( aState, enable );
    if( aState == sfSelected ||
        ( aState == sfActive && (state & sfSelected) != 0 )
      )
        selectAll( enable );
}

//==================================================================================
//= TComboBox                                                                      =
//==================================================================================
TComboBox::TComboBox( const TRect& bounds, TEdit *aLink, TCompCollect *list, funcptr func) :
                      TView(bounds), link( aLink ), items(list), posit(0), funcEvent(func)

{
    options |= ofPostProcess;
    eventMask |= evBroadcast;
}

const char * near TComboBox::icon = "\x19";

void TComboBox::draw()
{
    TDrawBuffer b;

    b.moveCStr( 0, icon, getColor(0x0102) );
    writeLine( 0, 0, size.x, size.y, b );
}

TPalette& TComboBox::getPalette() const
{
    static TPalette palette( cpComboBox, sizeof( cpComboBox )-1 );
    return palette;
}

void TComboBox::handleEvent( TEvent& event )
{
    TComboBoxWindow *ComboBoxWindow;
    TRect  r, p, r1;
    ushort c;

    TView::handleEvent( event );
    if( event.what == evMouseDown ||
          ( event.what == evKeyDown &&
            ctrlToArrow( event.keyDown.keyCode ) ==  kbDown &&
            (link->state & sfFocused) != 0
          )
      )
        {
        link->select();
        r = link->getBounds();
        r1 = getBounds();
        r.a.x = min(r.a.x,r1.a.x);
        r.b.x = max(r.b.x,r1.b.x);
        r.a.x--;
        r.b.x++;
        r.b.y += 7;
        r.a.y--;
        p = owner->getExtent();
        r.intersect( p );
        r.b.y--;
//        ComboBoxWindow = initComboBoxWindow( r );
//----------------
        ComboBoxWindow = new TComboBoxWindow( r, items, posit);
        ComboBoxWindow->helpCtx = link->helpCtx;
//----------------

        if( ComboBoxWindow != 0 )
        {
         c = owner->execView(ComboBoxWindow);
         if( c == cmOK )
         {
          strncpy( link->data,(char *)items->at(ComboBoxWindow->selpos), link->maxLen );
          link->selectAll( True );
          link->drawView();
          posit=ComboBoxWindow->selpos;
          if(funcEvent!=NULL)funcEvent(this,event);
         }
         destroy( ComboBoxWindow );
        }
       clearEvent( event );
       }
}

//TComboBoxWindow *TComboBox::initComboBoxWindow( const TRect& bounds )
//{
//    TComboBoxWindow *p = new TComboBoxWindow( bounds, items);
//    p->helpCtx = link->helpCtx;
//    return p;
//}



//==================================================================================
//= TComboBoxWindow                                                                =
//==================================================================================
TComboBoxWindow::TComboBoxWindow(const TRect& bounds, TCompCollect * items,int st_pos) :
           TDialog( bounds, 0 ),
//           TWindow( bounds, 0, wnNoNumber),
           TWindowInit( TComboBoxWindow::initFrame )
{
 flags = wfClose;
 selpos=st_pos;
 MyTListBox = new TMyTListBox(TRect( 1, 1,bounds.b.x-bounds.a.x-1,bounds.b.y-bounds.a.y-1), 1, standardScrollBar( sbVertical | sbHandleKeyboard ),NULL/*SelCombobox*/);
 MyTListBox->newList(items);
 MyTListBox->focusItem(selpos);
 insert(MyTListBox);
}

void TComboBoxWindow::handleEvent( TEvent& event )
{
 if((event.what == evBroadcast && event.message.command == cmDownLeftMouse) ||
    (event.what == evKeyDown && event.keyDown.keyCode == kbEnter))
 {
  endModal( cmOK ); clearEvent( event );
  selpos=MyTListBox->focused;
 }
 else
 if( (event.what ==  evKeyDown && event.keyDown.keyCode == kbEsc) ||
     (event.what ==  evCommand && event.message.command ==  cmCancel)
   )
 {
  endModal( cmCancel );
  clearEvent( event );
 }
 TDialog::handleEvent( event );
}

//==================================================================================
//= TMyCheckBoxes                                                                  =
//==================================================================================
void TMyCheckBoxes::press(int item)
{
    value = value^(1 << item);
    message(owner,evBroadcast,cmClustChange,this); // Clicked
}


//==================================================================================
//= TBasaDBF для работы с DBF                                                      =
//==================================================================================
TBasaDBF::TBasaDBF()
{
 db_head_ptr =(db_head *)calloc(sizeof(db_head),1);
 db_head_ptr->ver=3;

 db_head_ptr->numb_rec=0;
 db_head_ptr->len_head=sizeof(db_head)+2;
 db_head_ptr->len_rec=1;

 db_field_ptr = NULL;
 items = NULL;
}

TBasaDBF::~TBasaDBF()
{
 int i;

 if(db_field_ptr){free(db_field_ptr); db_field_ptr=NULL;}
 if(items)
 {
  for(i=0;i<db_head_ptr->numb_rec;i++) free(items[i]);
  free(items); items = NULL;
 }
 free(db_head_ptr);
}

int TBasaDBF::LoadFile(char *Name)
{
 int hd,i;
 db_head db_head_temp;

 if((hd=open(Name,O_BIN|O_RDONLY))<=0) return(-1);
 lseek(hd,0,SEEK_SET);
 read(hd, &db_head_temp, sizeof(db_head));                /* читать заголовок  dbf-файла */
 if(filelength_(hd)!=(db_head_temp.len_head+(db_head_temp.len_rec*db_head_temp.numb_rec)+1)){close(hd); return(-1);}

 if(db_field_ptr){ free(db_field_ptr); db_field_ptr=NULL;}
 if(items)
 {
  for(i=0;i<db_head_ptr->numb_rec;i++) free(items[i]);
  free(items); items = NULL;
 }

 lseek(hd,0,SEEK_SET);
 read(hd, db_head_ptr, sizeof(db_head));                /* читать заголовок  dbf-файла */

 db_field_ptr = (db_str_rec *)calloc(db_head_ptr->len_head-sizeof(db_head)-2,1);
 read(hd,db_field_ptr,db_head_ptr->len_head-sizeof(db_head)-2);
 lseek(hd,2,SEEK_CUR);

 items=(void **)calloc(db_head_ptr->numb_rec, sizeof(void **));
 for(i=0;i<db_head_ptr->numb_rec;i++)
 {
  items[i]=(void *)calloc(db_head_ptr->len_rec,1);
  read(hd,items[i],db_head_ptr->len_rec);
 }
 close(hd);
 return(db_head_ptr->numb_rec);
}

int TBasaDBF::SaveFile(char *Name)
{
 int i,hd;

 if((hd=open(Name,O_BIN|O_RDWR|O_CREAT|O_TRUNC,S_IWRITE))<=0) return(-1);
 ::write(hd,db_head_ptr,sizeof(db_head));
 ::write(hd,db_field_ptr,db_head_ptr->len_head-sizeof(db_head)-2);
 ::write(hd,"\x0D\x00",2);
 for(i=0;i<db_head_ptr->numb_rec;i++)

 {
  ::write(hd,items[i],db_head_ptr->len_rec);
 }
 ::write(hd,"\x1A",1);
 close(hd);
 return(0);
}

int TBasaDBF::GetCountItems()
{
 return(db_head_ptr->numb_rec);
}

int TBasaDBF::LoadFields(db_str_rec *fields,int number)
{
 int i;

 if(db_field_ptr){ free(db_field_ptr); db_field_ptr=NULL;}
 db_field_ptr = (db_str_rec *)calloc(number,sizeof(db_str_rec));
 memcpy(db_field_ptr,fields,number*sizeof(db_str_rec));

 if(items)
 {
  for(i=0;i<db_head_ptr->numb_rec;i++) free(items[i]);
  free(items); items = NULL;
 }

 db_head_ptr->numb_rec=0;
 db_head_ptr->len_head=sizeof(db_head)+2+number*sizeof(db_str_rec);
 db_head_ptr->len_rec=1;
 for(i=0;i<number;i++) db_head_ptr->len_rec+=(db_field_ptr+i)->len_fild;

 return(0);
}

int TBasaDBF::DelField(int pos)
{
 int number,rec_len=1,i,len_fild;
 db_str_rec *field_ptr;

 number=(db_head_ptr->len_head-sizeof(db_head)-2)/sizeof(db_str_rec);
 if(pos>=number)  return(-1);
 if(db_head_ptr->numb_rec)
 {
  field_ptr=db_field_ptr+pos;
  len_fild=field_ptr->len_fild;
  if(pos==number-1) for(i=0;i<db_head_ptr->numb_rec;i++) items[i]=realloc(items[i],db_head_ptr->len_rec-len_fild);
  else
  {
   for(i=0;i<pos;i++) rec_len+= (db_field_ptr+i)->len_fild;
   for(i=0;i<db_head_ptr->numb_rec;i++)
   {
    memmove((char *)items[i]+rec_len,(char *)items[i]+rec_len+len_fild,db_head_ptr->len_rec-rec_len);
    items[i]=realloc(items[i],db_head_ptr->len_rec-len_fild);
   }
  }
 }
 if(pos!=number-1) memmove(db_field_ptr+pos,db_field_ptr+pos+1,(number-pos)*sizeof(db_str_rec));
 db_field_ptr=(db_str_rec *)realloc(db_field_ptr,(number-1)*sizeof(db_str_rec));

 db_head_ptr->len_head-=sizeof(db_str_rec);
 db_head_ptr->len_rec-=len_fild;

 return(0);
}

int TBasaDBF::DelField(char *NameField)
{
 int number,rec_len=1,i,pos,len_fild;
 db_str_rec *field_ptr;

 number=(db_head_ptr->len_head-sizeof(db_head)-2)/sizeof(db_str_rec);
 for(i=0;i<number;i++) if(!strcmp(NameField,(db_field_ptr+i)->name)){pos=i;break;}
 if(pos== -1) return(-1);
 if(db_head_ptr->numb_rec)
 {
  field_ptr=db_field_ptr+pos;
  len_fild=field_ptr->len_fild;
  if(pos==number-1) for(i=0;i<db_head_ptr->numb_rec;i++) items[i]=realloc(items[i],db_head_ptr->len_rec-len_fild);
  else
  {
   for(i=0;i<pos;i++) rec_len+= (db_field_ptr+i)->len_fild;
   for(i=0;i<db_head_ptr->numb_rec;i++)
   {
    memmove((char *)items[i]+rec_len,(char *)items[i]+rec_len+len_fild,db_head_ptr->len_rec-rec_len);
    items[i]=realloc(items[i],db_head_ptr->len_rec-len_fild);
   }
  }
 }
 if(pos!=number-1) memmove(db_field_ptr+pos,db_field_ptr+pos+1,(number-pos)*sizeof(db_str_rec));
 db_field_ptr=(db_str_rec *)realloc(db_field_ptr,(number-1)*sizeof(db_str_rec));

 db_head_ptr->len_head-=sizeof(db_str_rec);
 db_head_ptr->len_rec-=len_fild;

 return(0);
}

int TBasaDBF::addField(int pos,db_str_rec *field_ptr)
{
 int number,rec_len=1,i;
 char *str_tmp;

 number=(db_head_ptr->len_head-sizeof(db_head)-2)/sizeof(db_str_rec);
 if(pos<number-1)
 {
  db_field_ptr=(db_str_rec *)realloc(db_field_ptr,(number+1)*sizeof(db_str_rec));
  memmove(db_field_ptr+pos+1,db_field_ptr+pos,(number-pos)*sizeof(db_str_rec));
  memcpy(db_field_ptr+pos,field_ptr,sizeof(db_str_rec));

  if(db_head_ptr->numb_rec)
  {
   for(i=0;i<pos;i++) rec_len+= (db_field_ptr+i)->len_fild;
   for(i=0;i<db_head_ptr->numb_rec;i++)
   {
//    items[i]=realloc(items[i],db_head_ptr->len_rec+field_ptr->len_fild);
    str_tmp=(char *)malloc(db_head_ptr->len_rec+field_ptr->len_fild);
    memmove(str_tmp,items[i],db_head_ptr->len_rec); free(items[i]); items[i]=str_tmp;

    memmove((char *)items[i]+rec_len+field_ptr->len_fild,(char *)items[i]+rec_len,db_head_ptr->len_rec-rec_len);
    memset((char *)items[i]+rec_len,' ',field_ptr->len_fild);
   }
  }
 }
 else
 {
  if(db_field_ptr)db_field_ptr=(db_str_rec *)realloc(db_field_ptr,(number+1)*sizeof(db_str_rec));
  else db_field_ptr=(db_str_rec *)calloc(1,sizeof(db_str_rec));
  memcpy(db_field_ptr+number,field_ptr,sizeof(db_str_rec));
  if(items)
  {
   for(i=0;i<db_head_ptr->numb_rec;i++)
   {
    str_tmp=(char *)malloc(db_head_ptr->len_rec+field_ptr->len_fild);
    memmove(str_tmp,items[i],db_head_ptr->len_rec); free(items[i]); items[i]=str_tmp;
//    items[i]=realloc(items[i],db_head_ptr->len_rec+field_ptr->len_fild);
    memset((char *)items[i]+db_head_ptr->len_rec,' ',field_ptr->len_fild);
   }
  }
 }
 db_head_ptr->len_head+=sizeof(db_str_rec);
 db_head_ptr->len_rec+=field_ptr->len_fild;

 return(0);
}

db_str_rec *TBasaDBF::getField(int posField)
{
 int number;

 number=(db_head_ptr->len_head-sizeof(db_head)-2)/sizeof(db_str_rec);
 if(posField >= number) return(NULL);
 return(db_field_ptr+posField);
}

db_str_rec *TBasaDBF::getField(char *NameField)
{
 int number, i, posField= -1;

 number=(db_head_ptr->len_head-sizeof(db_head)-2)/sizeof(db_str_rec);
 for(i=0;i<number;i++) if(!strcmp(NameField,(db_field_ptr+i)->name)){posField=i;break;}
 if(posField== -1) return(NULL);
 return(db_field_ptr+posField);
}

int TBasaDBF::CreateItems(int pos)
{
 void *temp;
 int number=db_head_ptr->numb_rec;

 if(pos<number)
 {
  temp=(void *)calloc(number-pos,sizeof(void *));
  items=(void **)realloc(items,(number+1)*sizeof(void **));
  memcpy(temp,items+pos,(number-pos)*sizeof(void *));
  items[pos]=(void *)calloc(db_head_ptr->len_rec,1);
  memset(items[pos],' ',db_head_ptr->len_rec);
  memcpy(items+pos+1,temp,(number-pos)*sizeof(void *));
  free(temp);
 }
 else
 {
  if(items)items=(void **)realloc(items,(number+1)*sizeof(void **));
  else items=(void **)calloc(1,sizeof(void **));
  items[db_head_ptr->numb_rec]=(void *)calloc(db_head_ptr->len_rec,1);
  memset(items[number],' ',db_head_ptr->len_rec);
 }
 db_head_ptr->numb_rec++;
 return(0);
}

int TBasaDBF::ModifiFieldIt(int posItems,int posField,char *str)
{
 int rec_len=1,number,i;

 number=(db_head_ptr->len_head-sizeof(db_head)-2)/sizeof(db_str_rec);
 if(posField>=number) return(-1);
 for(i=0;i<posField;i++) rec_len+= (db_field_ptr+i)->len_fild;
 if(posItems>=db_head_ptr->numb_rec) return(-1);
 strncpy((char *)items[posItems]+rec_len,str,(db_field_ptr+posField)->len_fild);
 return(0);
}

int TBasaDBF::ModifiFieldIt(int posItems,char *NameField,char *str)
{
 int rec_len=1,number,i,posField= -1;
// float temp;

 number=(db_head_ptr->len_head-sizeof(db_head)-2)/sizeof(db_str_rec);
 for(i=0;i<number;i++) if(!strcmp(NameField,(db_field_ptr+i)->name)){posField=i;break;}
 if(posField== -1) return(-1);
 for(i=0;i<posField;i++) rec_len+= (db_field_ptr+i)->len_fild;
 if(posItems>=db_head_ptr->numb_rec) return(-1);

// if((db_field_ptr+posField)->tip_fild=='N')
// {
//  temp=atof(str); fcvt(temp,(db_field_ptr+posField)->dec_field,str);
// }
 strncpy((char *)items[posItems]+rec_len,str,(db_field_ptr+posField)->len_fild);
 return(0);
}

int TBasaDBF::GetFieldIt(int posItems,int posField, char *str)
{
 int rec_len=1,number,i;

 number=(db_head_ptr->len_head-sizeof(db_head)-2)/sizeof(db_str_rec);
 if(posField>=number) return(-1);
 for(i=0;i<posField;i++) rec_len+= (db_field_ptr+i)->len_fild;
 if(posItems>=db_head_ptr->numb_rec) return(-1);
 strncpy(str,(char *)items[posItems]+rec_len,(db_field_ptr+posField)->len_fild);
 str[(db_field_ptr+posField)->len_fild]=0;
 return(0);
}

int TBasaDBF::GetFieldIt(int posItems,char *NameField, char *str)
{
 int rec_len=1,number,i,posField= -1;

 number=(db_head_ptr->len_head-sizeof(db_head)-2)/sizeof(db_str_rec);
 for(i=0;i<number;i++) if(!strcmp(NameField,(db_field_ptr+i)->name)){posField=i;break;}
 if(posField== -1) return(-1);
 for(i=0;i<posField;i++) rec_len+= (db_field_ptr+i)->len_fild;
 if(posItems>=db_head_ptr->numb_rec) return(-1);
 strncpy(str,(char *)items[posItems]+rec_len,(db_field_ptr+posField)->len_fild);
 str[(db_field_ptr+posField)->len_fild]=0;
 return(0);
}

int TBasaDBF::DeleteItems(int pos,int fr)
{
 void *temp;
 int number=db_head_ptr->numb_rec;

 if(pos>=number) return(-1);
 if(pos!=number-1)
 {
  temp=(void *)calloc(number-pos-1,sizeof(void *));
  memcpy(temp,items+pos+1,(number-pos-1)*sizeof(void *));
  if(fr)free(items[pos]);
  items=(void **)realloc(items,(number-1)*sizeof(void **));
  memcpy(items+pos,temp,(number-pos-1)*sizeof(void *));
  free(temp);
 }
 else
 {
  if(fr)free(items[pos]);
  items=(void **)realloc(items,(number-1)*sizeof(void **));
 }
 db_head_ptr->numb_rec--;
 return(0);
}

void *TBasaDBF::getItem(int posItem)
{
 if(posItem>=db_head_ptr->numb_rec) return(items[db_head_ptr->numb_rec-1]);
 else return(items[posItem]);
}

void TBasaDBF::AddItem(int pos,void *it)
{
 void *temp;
 int number=db_head_ptr->numb_rec;

 if(pos<number)
 {
  temp=(void *)calloc(number-pos,sizeof(void *));
  items=(void **)realloc(items,(number+1)*sizeof(void **));
  memcpy(temp,items+pos,(number-pos)*sizeof(void *));
  items[pos]=it;
  memcpy(items+pos+1,temp,(number-pos)*sizeof(void *));
  free(temp);
 }
 else
 {
  if(items)items=(void **)realloc(items,(number+1)*sizeof(void **));
  else items=(void **)calloc(1,sizeof(void **));
  items[db_head_ptr->numb_rec]=it;
 }
 db_head_ptr->numb_rec++;
}


//==================================================================================
//= TInputBox                                                                      =
//==================================================================================
TExecInputLine::TExecInputLine(const TRect& bounds, int aMaxLen,funcptr func)
               :TEdit(bounds,aMaxLen,func)
{ }
void TExecInputLine::set(void *rec) { setData(rec); }

void TExecInputLine::get(void *rec) { getData(rec); }

ushort TExecInputLine::execute()
{
 TEvent e;
 Boolean leavemodal = False;
 do
 {
  getEvent(e);
  if (e.what == evKeyDown)
   switch (e.keyDown.keyCode)
   {
    case kbDown:
    case kbUp:
    case kbEsc:
    case kbEnter:
    case kbPgDn:
    case kbPgUp:
    case kbCtrlPgDn:
    case kbCtrlPgUp:
         leavemodal = True;
    break;
    default:
         leavemodal = False;
    break;
   }
  if (e.what == evMouseDown && e.mouse.buttons == mbLeftButton)
  {
   TPoint p = makeLocal(e.mouse.where);
   if (mouseInView(p) == False) leavemodal = True;
  }
  handleEvent(e);
 } while (leavemodal == False);
 return e.keyDown.keyCode;
}

TInputBox::TInputBox(const TRect& bounds, ushort aNumCols, TScrollBar *aScrollBar,funcptr func)
          :TListBox(bounds,aNumCols,aScrollBar), funcT(func)
{ }

void TInputBox::getText(char *dest, int item, int maxLen)
{
 ListBoxItem *v;
 char s[MAXVALUELEN+MAXLABELLEN+2];
 if (list() != 0 )
 {
  v = (ListBoxItem *)(list()->at(item));
  sprintf(s, "%.*s│%.*s", MAXLABELLEN,v->label, MAXVALUELEN, v->value);
  strncpy( dest, s, maxLen );
  dest[maxLen] = '\0';
 }
 else *dest = EOS;
}

ushort TInputBox::inputData( )
{
 ListBoxItem *v;
 TExecInputLine *te;
 TRect r = getBounds();
 ushort control;
 if (list() != 0 )
 {
  v  = (ListBoxItem *)(list()->at(focused));
  r.a.x+=strlen(v->label)+2; r.a.y+=focused-topItem; r.b.y=r.a.y+1;
  te = new TExecInputLine(r,MAXVALUELEN,funcT);
  te->set(v->value);
  control = owner->execView((TView *)te);
  if (control != kbEsc) te->get(v->value);
  destroy((TView *)te);
 }
 return control;
}

void TInputBox::handleEvent(TEvent& event)
{
 TEvent e = event;
 ushort item;
 TListBox::handleEvent(event);
 item = focused;
 if ( event.what == evKeyDown )
 {
  if (event.keyDown.charScan.charCode >=  32)
  {
   putEvent(event);
   if (inputData() == kbUp) item--;
   else item++;
   drawView();
   focusItemNum(item);
  }
  else if ( event.keyDown.keyCode == kbRight ||
            event.keyDown.keyCode == kbLeft  ||
            event.keyDown.keyCode == kbEnter
          )
  {
   if (inputData() == kbUp) item--;
   else item++;
   drawView();
   focusItemNum(item);
  }
 }
 else if (e.what == evMouseDown && e.mouse.buttons == mbLeftButton)
 { inputData(); drawView(); }
}

//==================================================================================
//= TTextView                                                                      =
//==================================================================================
#define cpTextView    "\x1A\x1B"

TTextView::TTextView( const TRect& bounds, TScrollBar *aHScrollBar, TScrollBar *aVScrollBar) :
           TScroller( bounds, aHScrollBar, aVScrollBar )
{
 growMode = gfGrowHiX | gfGrowHiY;
 fileLines = new TAlgCollection(5, 5);
}

TTextView::~TTextView(){ destroy (fileLines); }

TPalette& TTextView::getPalette() const
{
 static TPalette palette( cpTextView, sizeof( cpTextView )-1 );
 return palette;
}

void TTextView::draw()
{
 char *p;

 ushort c =  getColor(0x0201);
 for( int i = 0; i < size.y; i++ )
 {
  TDrawBuffer b;
  b.moveChar( 0, ' ', c, size.x );
  if( delta.y + i < fileLines->getCount() )
  {
   char s[maxLineLength+1];
   p = (char *)( fileLines->at(delta.y+i) );
   if( p == 0 || strlen(p) < delta.x ) s[0] = EOS;
   else
   {
    strncpy( s, p+delta.x, size.x );
    if( strlen( p + delta.x ) > size.x ) s[size.x] = EOS;
   }
   b.moveStr( 0, s, c );
  }
  writeBuf( 0, i, size.x, 1, b );
 }
}

void TTextView::scrollDraw() { TScroller::scrollDraw(); draw(); }

void TTextView::setData(char *str)
{
 char *str1;
 int i,ii,len;
 limit.x = 0;

 fileLines->freeAll();
 len=strlen(str);

 for(i=0;i<len;i++)
 {
//  for(ii=0;(i+ii)<len;ii++) if(str[i+ii]=='\n') break;
  for(ii=0;(i+ii)<len;ii++) if(str[i+ii]==0x0D) break;
  str1= new char[ii+1]; str1[ii]=EOS;
  strncpy(str1,str+i,ii);
  fileLines->insert( str1 ); limit.x = max(limit.x,ii);
  i+=(ii+1);
 }
 limit.y = fileLines->getCount();
 setLimit(limit.x, limit.y);
 draw();
}

void TTextView::setState( ushort aState, Boolean enable )
{
 TScroller::setState( aState, enable );
 if( enable && (aState & sfExposed) ) setLimit( limit.x, limit.y );
}

const char * const TTextView::name = "TTextViewer";