Пример 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
Си в UNIX
Назад | Содержание | Вперед