Пример 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

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