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

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

Используются технологии uCoz