Пример 21

/* РЕДАКТОР СТРОКИ И ИСТОРИЯ РЕДАКТИРУЕМЫХ СТРОК                  */
/* _______________________ файл hist.h __________________________ */
/* ИСТОРИЯ. ЗАПОМИНАНИЕ СТРОК И ВЫДАЧА ИХ НАЗАД ПО ТРЕБОВАНИЮ.    */
/* ______________________________________________________________ */
typedef struct {        /* Паспорт истории        */
        Info *list;     /* запомненные строки     */
        int sz;         /* размер истории (макс.) */
        int len;        /* текущее число строк    */
        Menu mnu;       /* меню для выборки из истории */
} Hist;
void HistInit(Hist *h, int n);
void HistAdd (Hist *h, char *s, int fl);
Info *HistSelect(Hist *h, int x, int y);

/* _______________________ файл hist.c __________________________ */
#include "w.h"
#include "glob.h"
#include "menu.h"
#include "hist.h"
/* Проинициализировать новую "историю" емкостью n строк */
void HistInit(Hist *h, int n){
        register i;
        if( h->list ){ blkfree( h->list ); h->list = NULL; }
        h->len = 0;
        h->mnu.title      = "History";
        h->mnu.bg_attrib  = A_NORMAL;
        h->mnu.sel_attrib = A_REVERSE;
        h->list = (Info *) malloc( (n+1) * sizeof(Info));
        if( ! h->list ){
                h->sz = 0; return;
        }else   h->sz = n;
        for( i=0; i < n+1 ; i++ )
                h->list[i] = NullInfo;
}
/* Добавить строку s с меткой fl в историю */
void HistAdd (Hist *h, char *s, int fl){
        register i, j; Info tmp;

        if( h->sz == 0 ) return;
        /* А нет ли уже такой строки ? */
        for( i=0; i < h->len; i++ )
                if( !strcmp(s, h->list[i].s )){   /* есть ! */
                        if( i == 0 ) return;      /* первая */
                        /* сделать ее первой строкой */
                        tmp = h->list[i];
                        for( j=i-1; j >= 0; --j )
                                h->list[j+1] = h->list[j];
                        h->list[0] = tmp;
                        return;
                }
        if( h->len < h->sz ){
                for( i=h->len-1; i>= 0; i-- )
                        h->list[i+1] = h->list[i];
                h->len ++ ;
        }else{
        /* выкинуть самую старую строку из истории */
                free( h->list[ h->sz - 1 ].s );
                for( i=h->sz - 2; i >= 0; i-- )
                        h->list[i+1] = h->list[i];
        }
        (h->list)[0].s = strdup(s); (h->list)[0].fl = fl;
}
/* Выборка строки из истории */
Info *HistSelect(Hist *h, int x, int y){
        if( h->len == 0 ) return (Info *) NULL;
        h->mnu.top = y;
        h->mnu.left = x;
        h->mnu.items = h->list;
        MnuInit( & h->mnu );
        if( h->mnu.hotkeys ){
                register i;
                for(i=0 ; i < h->mnu.nitems; i++ )
                    h->mnu.hotkeys[i] = h->list[i].s[0] & 0377;
        }
        MnuUsualSelect( & h->mnu, 0 );
        MnuDeinit     ( & h->mnu );
        if( M_REFUSED ( & h->mnu ))
                return (Info *) NULL;
        return & h->list[ h->mnu.current ];
}

/* _______________________ файл line.h __________________________ */
/* РЕДАКТОР ДЛИННЫХ СТРОК (ВОЗМОЖНО ШИРЕ ЭКРАНА)                  */
/* ______________________________________________________________ */
typedef struct _LineEdit { /* Паспорт редактора строки        */
    WINDOW *win;         /* окно для редактирования           */
    int     width;       /* ширина поля редактирования        */
    int     left, top;   /* координаты поля редактирования в окне */
    int     pos;         /* позиция в строке                  */
    int     shift;       /* число символов скрытых левее поля */
    char   *line;        /* строка которая редактируется      */
    int     maxlen;      /* максимальная длина строки         */
    int     len;         /* текущая длина строки              */
    int     insert;      /* 1 - режим вставки; 0 - замены */
    int     nc;          /* 1 - стирать строку по первому нажатию */
    int     cursorOn;    /* курсор включен (для графики)  */
    int     bg_attrib;   /* цвет текста                   */
    int     fr_attrib;   /* цвет пустого места в поле     */
    int     wl_attrib;   /* цвет краев строки             */
    int     sel_attrib;  /* цвет символа под курсором     */
    Hist    *histIn;     /* история для выборки строк     */
    Hist    *histOut;    /* история для запоминания строк */
    int      key;        /* кнопка, завершившая редактирование */
    Point    savep;
    /* функции проявки и убирания окна (если надо)        */
    int  (*showMe)(struct _LineEdit *le); /* 1 при успехе */
    void (*hideMe)(struct _LineEdit *le);
    void (*posMe) (struct _LineEdit *le); /* установка позиции */
/* Функция рисования scroll bar-а (если надо)         */
void (*scrollBar)(struct _LineEdit *le, int whichbar, int n, int among);
    /* Специальная обработка клавиш (если надо)           */
    int *hitkeys;
    int (*handler)(struct _LineEdit *le, int c, HandlerReply *reply);
}   LineEdit;

void LePutChar( LineEdit *le, int at);
void LeCursorHide( LineEdit *le );
void LeCursorShow( LineEdit *le );
void LePointAt( LineEdit *le, int at );
void LePoint( LineEdit *le, int x, int eraseOld );
void LeDraw( LineEdit *le );
void LeReport( LineEdit *le );
void LeDelCh ( LineEdit *le );
void LeInsCh ( LineEdit *le, int c );
void LeRepCh ( LineEdit *le, int c );
int  LeInsStr( LineEdit *le, char *s);
int  LeWerase( LineEdit *le, char *to );
int  LeEdit( LineEdit *le );
#define LINE_DX 1
#define LE_REFUSED(m)    ((m)->key < 0 || (m)->key == ESC )

/* _______________________ файл line.c __________________________ */
/* Редактор строки. Эта версия была изначально написана           *
 * для графики, поэтому здесь не совсем CURSES-ные алгоритмы      */
#include "w.h"
#include "glob.h"
#include "menu.h"
#include "hist.h"
#include "line.h"

/* Удалить букву из строки */
static char cdelete(register char *s, int at) { char c;
    s += at; if((c = *s) == '\0') return c;
    while( s[0] = s[1] ) s++;     return c;
}
/* Вставить букву в строку */
static void insert(char *s, int at, int c){
        register char *p;
        s += at; p = s;
        while(*p) p++;  /* найти конец строки */
        p[1] = '\0';    /* закрыть строку     */
        for( ; p != s; p-- )
                p[0] = p[-1];
        *s = c;
}
/* Нарисовать видимую часть строки с позиции from */
static void LeDrawLine( LineEdit *le, int from ){
        LeCursorHide( le );
        for( ; from < le->width; from++ )
                LePutChar(le, from);
        /* курсор остается спрятанным */
}
/* Выдать символ строки в позиции at */
void LePutChar( LineEdit *le, int at){
     int off = le->shift + at;
     int bgcolor = le->bg_attrib, wall;
wall =  /* символ на краю поля и строка выходит за этот край ? */
   ( at == 0 && le->shift    ||
   ( at >= le->width - 1 && le->shift + le->width < le->len ));
bgcolor =
   ( off < le->len ) ?                         le->bg_attrib   :
   ( at >= le->width || off >= le->maxlen ) ? (le->bg_attrib | A_ITALICS):
    /* чистое место в поле */                  le->fr_attrib   ;
        wattrset( le->win, wall? le->wl_attrib|A_BOLD|A_ITALICS: bgcolor);
        mvwaddch( le->win, le->top, le->left + at,
                  off < le->len ? le->line[off] : ' ' );
        wattrset( le->win, le->bg_attrib);
}
/* Спрятать курсор. x в интервале 0..le->width */
void LeCursorHide( LineEdit *le ){
        int x = le->pos - le->shift;
        if( x < 0 || x > le->width || le->cursorOn == NO )
                return;
        LePutChar( le, x ); le->cursorOn = NO;
}
/* Проявить курсор */
void LeCursorShow( LineEdit *le ){
     int x = le->pos - le->shift, saveattr = le->bg_attrib;

     if( x < 0 || x >  le->width      || le->cursorOn == YES ) return;
     le->bg_attrib =   le->sel_attrib | (le->insert==NO ? A_BOLD : 0);
     LePutChar(le, x); le->bg_attrib  =  saveattr;
     wmove(le->win, le->top, le->left + x); le->cursorOn = YES;
     SetPoint(le->savep, le->top, le->left+x);
}
/* Функция прокрутки длинной строки через окошко */
static void LeRoll( LineEdit *ptr,
          int aid, int *cur, int *shift,
          int width,    /* ширина окна */
          int len, int maxlen,
          void (*go)  (LineEdit *p, int x, int eraseOld),
          void (*draw)(LineEdit *p), /* перерисовщик поля */
          int LDX
){
        int x = *cur - *shift, oldshift = *shift, newshift = oldshift;
        int AID_LFT, AID_RGT,  drawn = NO;

        if( aid < 0 || aid > len   ) return;       /* incorrect */
        if( x   < 0 || x   > width ) return;       /* incorrect */

        AID_LFT = MIN(LDX, maxlen);
        AID_RGT = MAX(0,  MIN(maxlen, width-1 - LDX));

        if( aid < *cur && x <= AID_LFT && oldshift > 0 )
                goto Scroll;
        else if( aid > *cur && x >= AID_RGT && oldshift + width < maxlen )
                goto Scroll;
        if( oldshift <= aid && aid < oldshift + width )
                /* прокрутка не нужна - символ уже видим */
                goto Position;
Scroll:
        if( aid >= *cur )
                newshift = aid - AID_RGT;
        else    newshift = aid - AID_LFT;
        if( newshift + width > maxlen || (len == maxlen && aid == len))
                newshift = maxlen - width;
        if( newshift < 0 )
                newshift = 0;
        if( newshift != oldshift ){
                *shift = newshift; (*draw)(ptr); drawn = YES;
        }
Position:
        if((x = aid - newshift) >= width && len != maxlen )
                beep();      /* ERROR */
        (*go)(ptr, x, !drawn ); *cur = aid;
}
/* Поставить курсор на at-тый символ строки */
void LePointAt( LineEdit *le, int at ){
        /* at == len допустимо */
        if( at < 0 || at > le->len ) return;
        if( le->pos == at ) return;  /* уже на месте */
        LeCursorHide( le );
        LeRoll( le, at, & le->pos, & le->shift,
                    le->width, le->len, le->maxlen,
                    LePoint,   LeDraw,
                    LINE_DX);
        le->pos = at;
        LeCursorShow( le );
}
/* Нарисовать подходящий scroll bar */
void LePoint( LineEdit *le, int x, int eraseOld ){
     if(le->scrollBar)
      (*le->scrollBar)(le, BAR_HOR, x + le->shift, le->maxlen+1 );
     GetBack( le->savep, le->win);
}
/* Нарисовать подходящий scroll bar            */
/* Вызывай это каждый раз, когда len изменится */
void LeReport( LineEdit *le ){
     if(le->scrollBar)
      le->scrollBar (le, BAR_VER, le->len, le->maxlen+1 );
     GetBack( le->savep, le->win);
}
/* Нарисовать видимую часть строки */
void LeDraw( LineEdit *le ){
     LeDrawLine( le, 0);
}
/* Удаление буквы из строки */
void LeDelCh( LineEdit *le ){
     if( le->len <= 0 || le->pos < 0 || le->pos >= le->len ) return;
     LeCursorHide( le );
     (void) cdelete( le->line, le->pos );
     le->len --;
     LeDrawLine( le, le->pos - le->shift );
     LeReport( le );
}
/* Вставка буквы в строку */
void LeInsCh( LineEdit *le, int c ){
     if( le->len < 0 || le->pos < 0 || le->pos > le->len ) return;
     LeCursorHide( le );
     insert( le->line, le->pos, c );
     le->len++;
     LeDrawLine( le, le->pos - le->shift );
     LeReport( le );
}
/* Замена буквы в строке */
void LeRepCh( LineEdit *le, int c ){
     if( le->len <= 0 || le->pos < 0 || le->pos >= le->len ) return;
     LeCursorHide( le );
     le->line[ le->pos ] = c;
     LePutChar( le, le->pos - le-> shift );
}
/* Вставка подстроки в строку редактирования */
int LeInsStr( LineEdit *le, char *s){
    int len = le->len, slen = strlen(s);
    register i;

    if( len + slen > le->maxlen )
              slen = le->maxlen - len;
    if( ! slen ) return 0;

    for( i=0; i < slen ; i ++ )
         insert( le->line, le->pos+i, s[i] );
    le->len += slen;
    LeCursorHide( le );
    LeDrawLine( le, le->pos - le->shift );
    LePointAt( le, le->pos + slen );
    LeReport( le );
    return slen ;
}
/* Стирание слова */
int LeWerase( LineEdit *le, char *to ){
        register i;
        register char *s = le->line;
        char c;

        if( to ) *to = '\0';
        i = le->pos;
        if( s[i] == ' ' || s[i] == '\0' ){
                /* найти конец слова */
                for( --i; i >= 0 ; i-- )
                        if( s[i] != ' ' ) break;
                if( i < 0 || le->len == 0 ){
                        beep(); return NO; }
        }
        /* найти начало слова */
        for( ; i >= 0 && s[i] != ' ' ; i-- );
        i++;  /* i < 0 || s[i] == ' ' */
        LeCursorHide( le );  LePointAt( le, i );
        while( s[i] != ' ' && s[i] != '\0' ){
                c = cdelete( s, i );
                if( to ) *to++ = c;
                le->len --;
        }
        /* удалить пробелы после слова */
        while( s[i] == ' ' ){
                c = cdelete( s, i );
                le->len --;
        }
        if( to ) *to = '\0';
        LeDrawLine( le, i - le->shift );
        LeReport( le );
        return YES;
}
/* Редактор строки
        le->line                что редактировать.
        le->maxlen              макс. длина строки.
        le->win                 окно, содержащее поле редактирования.
        le->width               ширина поля редактирования.
        le->top                 коорд-ты поля редактирования
        le->left                  в окне win.
        le->insert = YES        режим вставки.
        le->nc     = YES        стирать строку при первом нажатии.
        le->histIn              входная история  или NULL.
        le->histOut             выходная история или NULL.
        le->showMe              функция проявки окна или NULL.
        le->hideMe              функция спрятывания окна или NULL.
        le->hitkeys             специальные клавиши или NULL.
        le->handler             обработчик специальных клавиш или NULL.
        le->scrollBar           рисовалка scroll bar-ов или NULL.
        le->posMe               установка позиции в строке при входе.
        le->bg_attrib           цвет поля.
        le->fr_attrib           цвет незаполненной части поля.
        le->wl_attrib           цвет краев поля при продолжении.
        le->sel_attrib          цвет символа под курсором.
*/
int LeEdit( LineEdit *le ){
    int    c;
    int nchar = 0;  /* счетчик нажатых клавиш */
    Info *inf;

 /* проявить окно */
    if( le->showMe )
        if( (*le->showMe) (le) <= 0 )
                return (-1);
    if( !le->win ) return (le->key = -1);
Again:
    le -> pos = 0;
    le -> len = strlen( le->line );
    le -> shift = 0;
    le -> cursorOn = NO;
    le->key = (-1);

    LeDraw( le );
    if(le->posMe) (*le->posMe)(le);
    LePointAt(le, le->pos );
    LePoint( le,  le->pos - le->shift, NO );
    LeReport( le );

    for (;;) {
        LeCursorShow( le );

        c = WinGetch(le->win); /* прочесть символ с клавиатуры */
        nchar++;               /* число нажатых клавиш         */
INP:
        if( le->hitkeys && le->handler ){
                HandlerReply reply;
                if( is_in(c, le->hitkeys)){ /* спецсимвол ? */
                        c = (*le->handler)(le, c, &reply);
                        /* Восстановить scroll bars */
                        LePoint( le, le->pos - le->shift, NO );
                        LeReport( le );

                        switch( reply ){
                        case HANDLER_CONTINUE:  continue;
                        case HANDLER_NEWCHAR:   goto INP;
                        case HANDLER_OUT:       goto out;
                        case HANDLER_AGAIN:     /* reset */
                             LeCursorHide(le);  goto Again;
                        case HANDLER_SWITCH:
                        default:        break;  /* goto switch(c) */
                        }
                }
        }
sw:
        switch (c) {
            case KEY_RIGHT:     /* курсор вправо */
                if (le->pos != le->len && le->len > 0)
                    LePointAt( le, le->pos + 1);
                break;

            case KEY_LEFT:      /* курсор влево */
                if (le->pos > 0)
                    LePointAt(le, le->pos - 1);
                break;

            case '\t':          /* табуляция вправо */
                if (le->pos + 8 > le->len)
                    LePointAt(le, le->len);
                else
                    LePointAt(le, le->pos + 8);
                break;

            case KEY_BACKTAB:   /* табуляция влево */
            case ctrl('X'):
                if( le->pos - 8 < 0 )
                        LePointAt(le, 0);
                else    LePointAt(le, le->pos - 8 );
                break;

            case KEY_HOME:      /* в начало строки */
                LePointAt(le, 0); break;

            case KEY_END:       /* в конец строки KEY_LL */
                if( le->len > 0 )
                         LePointAt(le, le->len);
                break;

            case 0177:          /* стереть символ перед курсором */
            case KEY_BACKSPACE:
            case '\b':
                if (le->pos == 0) break;
                LePointAt(le, le->pos - 1);   /* налево */
                /* и провалиться в DC ... */

            case KEY_F (6):     /* стереть символ над курсором */
            case KEY_DC:
                if (! le->len || le->pos == le->len)
                    break;
                LeDelCh(le);
                break;

            case KEY_UP:        /* вызвать историю */
            case KEY_DOWN:
            case KEY_NPAGE:
            case KEY_PPAGE:
            case KEY_F(4):
                if( ! le->histIn ) break;
                /* иначе позвать историю */
                inf = HistSelect( le->histIn,
          wbegx(le->win) + le->pos - le->shift + 2, le->top + 1);
                if( inf == (Info *) NULL )
                        break;
                LeCursorHide( le );
                strncpy( le->line, inf->s, le->maxlen );
                goto Again;

out:        case '\r': case '\n': case ESC:
            /* ввод завершен - выйти */
                LeCursorHide( le );
                if( c != ESC && le->histOut && *le->line )
                /* запомнить строку в историю */
                        HistAdd( le->histOut, le->line, 0);
                if( le->hideMe ) /* спрятать окно */
                        (*le->hideMe)(le);
                return (le->key = c);

            case KEY_F (8):     /* стереть всю строку */
            case ctrl('U'):
                le->line[0] = '\0';
                le->len = le->pos = le->shift = 0;
                LeCursorHide( le );
                LeReport( le );
                goto REWRITE;

            case KEY_F(0):      /* F10: стереть до конца строки */
                if( le->pos == le->len ) break;
                le->line[ le->pos ] = '\0';
                le->len = strlen( le->line );
                LeCursorHide( le );
                LeDrawLine( le, le->pos - le->shift );
                LeReport( le );
                break;

            case ctrl('W'): /* стереть слово */
                LeWerase( le, NULL );
                break;

            case ctrl('A'): /* перерисовка */
                LeCursorHide(le); /* RedrawScreen(); */
REWRITE:        LeDraw(le);
                break;

            case KEY_F(7):  /* переключить режим вставки/замены */
                le->insert = ! le->insert;
                LeCursorHide( le );
                break;
#ifndef M_UNIX
            case ctrl('V'): /* ввод заэкранированного символа */
                nchar--;
                c = WinGetch(le->win);
                nchar++;
                if( c >= 0400 ) goto sw;
                goto Input;
#endif
            case 0: break;
            default:        /* ввод обычного символа */
                if (c >= 0400 || ! isprint(c)) break;
        Input:  if( le->nc && nchar == 1 && le->insert &&
                      /*le->pos == 0 &&*/ le->len != 0 ){
            /* если это первая нажатая кнопка, то
            /* удалить все содержимое строки
            /* и заменить ее нажатой буквой */
                       le->shift = 0;
                       le->len = le->pos = 1;
                       le->line[0] = c;
                       le->line[1] = '\0';
                       LeCursorHide( le );
                       LeReport( le );
                       goto REWRITE;
                }
                if (!le->insert) {
                /* REPLACE - режим замены */
                    if (le->pos == le->len)
                        goto AddChar;  /* временный INSERT */
                    LeRepCh( le, c );
                    LePointAt( le, le->pos + 1 );
                } else {
                /* INSERT - режим вставки */
AddChar:
                    if( le->len >= le->maxlen ){
                        beep();      /* строка переполнена */
                        break;
                    }
                    LeInsCh( le, c );
                    LePointAt( le, le->pos + 1 );
                }               /* endif */
        }                       /* endswitch */
    }                           /* endfor */
}                               /* endfunc */

© Copyright А. Богатырев, 1992-95 www.abyss-group.narod.ru
Си в UNIX

Назад | Содержание | Вперед

Hosted by uCoz