(sv).c /* _______________________ файл glob.c __________________________ */ #include "glob.h" int in_the_root = NO; /* читаем корневой каталог ? */ Sort sorttype = SORT_SUFX; /* сортировка имен по суффиксу */ Info NullInfo = { NULL, 0 }; /* и прочие поля = 0 (если есть) */ char *strdup(const char *s){ char *p = malloc(strlen(s)+1); if(p)strcpy(p, s); return p; } /* Содержится ли любой из символов в строке ? */ int any(register char *s, register char *p){ while( *s ){ if( strchr(p, *s)) return YES; s++; } return NO; } /* Найти последнюю точку в имени */ static char *lastpoint (char *s) { register char *last; static char no[] = ""; if((last = strchr(s, '.')) == NULL) return no; /* если имя начинается с точки - не считать ее */ return( last == s ? no : last ); } /* Сравнение строк с учетом их суффиксов */ int strsfxcmp (register char *s1, register char *s2){ char *p1, *p2, c1, c2; int code; p1 = lastpoint (s1); p2 = lastpoint (s2); if (code = strcmp (p1, p2)) return code; /* суффиксы разные */ /* иначе: суффиксы равны. Сортируем по головам */ c1 = *p1; c2 = *p2; *p1 = '\0'; *p2 = '\0'; /* временно */ code = strcmp (s1, s2); *p1 = c1; *p2 = c2; return code; } /* Функция сортировки */ int gcmps(const void *p1, const void *p2){ Info *s1 = (Info *) p1, *s2 = (Info *) p2; switch( sorttype ){ default: case SORT_ASC: return strcmp(s1->s, s2->s); case SORT_DESC: return -strcmp(s1->s, s2->s); case SORT_SUFX: return strsfxcmp(s1->s, s2->s); case SORT_NOSORT: return (-1); #ifdef FILF case SORT_SIZE: return (s1->size < s2->size ? -1 : s1->size == s2->size ? 0 : 1 ); #endif } } /* Копирование блока */ Info *blkcpy(Info *v){ register i, len; Info *vect = (Info *) malloc(((len=blklen(v)) + 1) * sizeof(Info)); for(i=0; i < len; i++ ) vect[i] = v[i]; vect[len] = NullInfo; return vect; } /* Измерение длины блока */ int blklen(Info *v){ int i = 0; while( v->s ) i++, v++; return i; } /* Очистка блока (уничтожение) */ void blkfree(Info *v){ Info *all = v; while( v->s ) free((char *) v->s ), v++; free((char *) all ); } /* Сравнение двух блоков */ int blkcmp( register Info *p, register Info *q ){ while( p->s && q->s && !strcmp(p->s, q->s) && (p->fl & I_SYS) == (q->fl & I_SYS)){ p++; q++; } if( p->s == NULL && q->s == NULL ) return 0; /* совпадают */ return 1; /* различаются */ } char globchars [] = "*?["; Info gargv[MAX_ARGV]; int gargc; static short readErrors; void greset() { gargc = 0; readErrors = 0; } /* Расширить шаблон имен файлов в сами имена */ static void globone(char *pattern, char dirname[]){ extern char *strdup(); struct stat st; DIR *dirf; struct dirent *d; if( any(pattern, globchars) == NO ){ /* no glob */ gargv[gargc] = NullInfo; gargv[gargc].s = strdup(pattern); gargc++; gargv[gargc] = NullInfo; return; } if((dirf = opendir(dirname)) == NULL){ readErrors++; goto out; } while(d = readdir(dirf)){ if(match(d->d_name, pattern)){ char fullname[512]; if( sorttype != SORT_NOSORT && !strcmp(d->d_name, ".")) continue; /* В корневом каталоге имя ".." следует пропускать */ if( in_the_root && !strcmp(d->d_name, "..")) continue; /* Проверка на переполнение */ if( gargc == MAX_ARGV - 1){ free(gargv[gargc-1].s); gargv[gargc-1].s = strdup(" Слишком много файлов!!!"); gargv[gargc-1].fl = I_SYS; break; } gargv[gargc] = NullInfo; gargv[gargc].s = strdup(d->d_name); sprintf(fullname, "%s/%s", dirname, d->d_name); if(stat(fullname, &st) < 0) gargv[gargc].fl |= I_NOSEL; else if(isdir(st)) gargv[gargc].fl |= I_DIR; else if(isexe(st)) gargv[gargc].fl |= I_EXE; #ifdef FILF gargv[gargc].size = st.st_size; gargv[gargc].uid = st.st_uid; gargv[gargc].gid = st.st_gid; gargv[gargc].mode = st.st_mode; #endif gargc++; } } closedir(dirf); out: gargv[ gargc ] = NullInfo; } /* Расширить несколько шаблонов */ Info *glob(char **patvec, char *dirname){ greset(); while(*patvec){ globone(*patvec, dirname); patvec++; } qsort(gargv, gargc, sizeof(Info), gcmps); return blkcpy(gargv); } Info *glb(char *pattern, char *dirname){ char *pv[2]; pv[0] = pattern; pv[1] = NULL; return glob(pv, dirname); } /* Прочесть содержимое каталога, если оно изменилось: * Вернуть: 0 - каталог не менялся; * 1 - изменился; * 1000 - изменился рабочий каталог (chdir); * -1 - каталог не существует; */ int ReadDir(char *dirname, DirContents *d){ struct stat st; Info *newFiles; int save = YES; /* сохранять метки у файлов ? */ int dirchanged = NO; /* сделан chdir() ? */ /* каталог мог быть удален, а мы об этом не извещены */ if( stat(dirname, &st) < 0 ){ d->valid = NO; d->lastRead = 0L; if(d->files) blkfree(d->files); d->files = blkcpy( &NullInfo ); return (-1); /* не существует */ } else d->valid = YES; /* не изменился ли адрес каталога, хранимого в *d ? */ if(d->ino != st.st_ino || d->dev != st.st_dev){ /* изменился */ d->ino = st.st_ino; d->dev = st.st_dev; save = NO; d->lastRead = 0L; dirchanged = YES; } /* не изменилось ли имя каталога ? */ if( !d->name || strcmp(d->name, dirname)){ if(d->name) free(d->name); d->name = strdup(dirname); /* save=NO; d->lastRead = 0; */ } /* проверим, был ли модифицирован каталог ? */ if( save==YES && d->files && st.st_mtime == d->lastRead ) return 0; /* содержимое каталога не менялось */ d->lastRead = st.st_mtime; newFiles = glb("*", d->name); /* прочесть содержимое каталога */ if(save == YES && d->files){ register Info *p, *q; if( !blkcmp(newFiles, d->files)){ blkfree(newFiles); return 0; /* не изменилось */ } /* иначе сохранить пометки */ for(p= d->files; p->s; p++) for(q= newFiles; q->s; ++q) if( !strcmp(p->s, q->s)){ q->fl |= p->fl & ~I_SYS; break; } } if(d->files) blkfree(d->files); d->files = newFiles; d->readErrors = readErrors; return 1 + (dirchanged ? 999:0); /* каталог изменился */ } /* Пример 19 */ /* ________________________файл menu.h __________________________ */ /* РОЛЛИРУЕМОЕ МЕНЮ */ /* _______________________________________________________________*/ #include <ctype.h> #include <sys/param.h> #define M_HOT '\\' /* горячий ключ */ #define M_CTRL '\1' /* признак горизонтальной черты */ #define MXEND(m) XEND((m)->win,(m)->scrollok) #define NOKEY (-33) /* горячего ключа нет */ #define MAXLEN MAXPATHLEN /* макс. длина имен файлов */ typedef enum { /* Коды, возвращаемые handler-ом (HandlerReply *reply) */ HANDLER_OUT = 0, /* выйти из функции выбора */ HANDLER_CONTINUE = 1, /* читать очередную букву */ HANDLER_NEWCHAR = 2, /* пойти на анализ кода handler-ом. */ HANDLER_SWITCH = 3, /* пойти на switch() */ HANDLER_AGAIN = 4 /* перезапустить всю функцию выбора */ } HandlerReply; typedef struct _Menu { /* паспорт меню */ int nitems; /* число элементов меню */ Info *items; /* сам массив элементов */ int *hotkeys; /* "горячие" клавиши */ int key; /* клавиша, завершившая выбор */ int current; /* текущая строка списка */ int shift; /* сдвиг окна от начала меню */ int scrollok; /* окно роллируемое ? */ WINDOW *win; /* окно для меню */ int left, top, height, width; /* координаты меню на экране и размер окна win */ int textwidth, textheight; /* размер подокна выбора */ int bg_attrib; /* атрибут фона окна */ int sel_attrib; /* атрибут выбранной строки */ char *title; /* заголовок меню */ Point savep; void (*showMe) (struct _Menu *m); void (*scrollBar) (struct _Menu *m, int n, int among); int *hitkeys; /* клавиши, обрабатываемые особо */ int (*handler) (struct _Menu *m, int c, HandlerReply *reply); } Menu; /* Структура окна с меню: *--------------* +0 | ЗАГОЛОВОК | +1 *-----------*--* +2 |+ стр1ааа | | +3 | стр2ббб |##| <- scroll bar шириной BARWIDTH | стр3ввв | | *___________|__* |DX| len |DX|BS| */ /* Метки у элементов меню */ #define M_BOLD I_DIR /* яркая строка */ #define M_HATCH 0x08 /* строка тусклая */ #define M_LFT 0x10 /* для использования в pulldown menu */ #define M_RGT 0x20 /* для использования в pulldown menu */ #define M_LABEL 0x40 /* строка имеет метку */ #define M_LEFT (-111) #define M_RIGHT (-112) #define TOTAL_NOSEL (-I_NOSEL) #define M_SET(m, i, flg) (((m)->items)[i]). fl |= (flg) #define M_CLR(m, i, flg) (((m)->items)[i]). fl &= ~(flg) #define M_TST(m, i, flg) ((((m)->items)[i]).fl & (flg)) #define M_ITEM(m, i) ((((m)->items)[i]).s) /* Прототипы */ int MnuInit (Menu *m); void MnuDeinit (Menu *m); void MnuDrawItem (Menu * m, int y, int reverse, int selection); int MnuNext (Menu *m); int MnuPrev (Menu *m); int MnuFirst(Menu *m); int MnuLast (Menu *m); int MnuPgUp (Menu *m); int MnuPgDn (Menu *m); int MnuThis (Menu *m); int MnuHot (Menu *m, unsigned c); int MnuName (Menu *m, char *name); void MnuDraw (Menu *m); void MnuHide(Menu *m); void MnuPointAt (Menu *m, int y); void MnuPoint (Menu *m, int line, int eraseOld); int MnuUsualSelect (Menu *m, int block); int is_in(register int c, register int s[]); char *MnuConvert (char *s, int *pos); #define M_REFUSED(m) ((m)->key < 0 || (m)->key == ESC ) #define MNU_DY 1 /* _______________________ файл menu.c __________________________ */ #include "w.h" #include "glob.h" #include "menu.h" #include <signal.h> /* ---------------- implementation module ------------------------- */ /* Не входит ли символ в специальный набор? Массив завершается (-1) */ int is_in(register int c, register int s[]){ while (*s >= 0) { if(*s == c) return YES; s++; } return NO; } char STRING_BUFFER[ MAXLEN ]; /* временный буфер */ /* Снять пометку с "горячей" клавиши. */ char *MnuConvert (char *s, int *pos){ int i = 0; *pos = (-1); while (*s) { if (*s == M_HOT) { *pos = i; s++; } else STRING_BUFFER[i++] = *s++; } STRING_BUFFER[i] = '\0'; return STRING_BUFFER; } /* Рамка вокруг окна с меню */ static void MnuWin (Menu *m) { WinBorder(m->win, m->bg_attrib, m->sel_attrib, m->title, m->scrollok, YES); } /* Нарисовать scroll bar в нужной позиции */ static void MnuWinBar (Menu *m) { WINDOW *w = m -> win; /* окно */ WinScrollBar(m->win, m->scrollok, m->current, m->nitems, m->title, m->bg_attrib); if(m->scrollBar) /* может быть еще какие-то наши действия */ m->scrollBar(m, m->current, m->nitems); } /* Роллирование меню */ /* +---+----->+-МАССИВ--+<-----+ | n|всего |;;;;;;;;;| | shift сдвиг до окна cur| | |;;;;;;;;;| | текущий| | =ОКНО============<---------| элемент| | I ;;;;;;;;; I | y строка окна 0..n-1 | | I ;;;;;;;;; I | | +------>I###:::::::::###I<--+ |h высота окна | I ;;;;;;;;; I | | =================<---------+ | |;;;;;;;;;| +----->|_________| */ static void MnuRoll (Menu *ptr, int aid, /* какой новый элемент выбрать (0..n-1) */ int *cur, int *shift, int h, /* высота окна (строк) */ int n, /* высота items[] (элементов) */ void (*go) (Menu *p, int y, int eraseOld), void (*draw) (Menu *p), int DY ) { int y = *cur - *shift; /* текущая строка окна */ int newshift; /* новый сдвиг */ int AID_UP, AID_DN; if (aid < 0 || aid >= n) return; /* incorrect */ if (y < 0 || y >= h) return; /* incorrect */ AID_UP = MIN (DY, n); AID_DN = MAX (0, MIN (n, h - 1 - DY)); if (aid < *cur && y <= AID_UP && *shift > 0) goto scroll; /* down */ if (aid > *cur && y >= AID_DN && *shift + h < n) goto scroll; /* up */ if (*shift <= aid && aid < *shift + h) { /* роллировать не надо, а просто пойти в нужную строку окна */ (*go) (ptr, aid - *shift, YES); *cur = aid; /* это надо изменять ПОСЛЕ (*go)() !!! */ return; } scroll: if (aid > *cur) newshift = aid - AID_DN; /* вверх up */ else if (aid < *cur) newshift = aid - AID_UP; /* вниз down */ else newshift = *shift; if (newshift + h > n) newshift = n - h; if (newshift < 0) newshift = 0; *shift = newshift; *cur = aid; (*draw) (ptr); /* перерисовать окно */ (*go) (ptr, aid - newshift, NO); /* встать в нужную строку окна */ } /* Инициализация и разметка меню. На входе: m->items Массив строк. m->title Заголовок меню. m->top Верхняя строка окна (y). m->left Левый край (x). m->handler Обработчик нажатия клавиш или NULL. m->hitkeys Специальные клавиши [] или NULL. m->bg_attrib Цвет фона окна. m->sel_attrib Цвет селекции. */ int MnuInit (Menu *m) { int len, pos; char *s; register i; m -> current = m -> shift = 0; m -> scrollok = m -> key = 0; if (m -> hotkeys) { /* уничтожить старые "горячие" ключи */ free ((char *) m -> hotkeys); m -> hotkeys = (int *) NULL; } /* подсчет элементов меню */ for (i = 0; M_ITEM (m, i) != (char *) NULL; i++); m -> nitems = i; /* отвести массив для "горячих" клавиш */ if (m -> hotkeys = (int *) malloc (sizeof (int) * m -> nitems)) { for (i = 0; i < m -> nitems; i++) m -> hotkeys[i] = NOKEY; } /* подсчитать ширину текста */ len = m -> title ? strlen (m -> title) : 0; for (i = 0; i < m -> nitems; i++) { if (*(s = M_ITEM (m, i)) == M_CTRL) continue; s = MnuConvert (s, &pos); if (m -> hotkeys && pos >= 0) m -> hotkeys[i] = isupper (s[pos]) ? tolower (s[pos]) : s[pos]; if ((pos = strlen (s)) > len) len = pos; } /* сформировать окно */ #define BORDERS_HEIGHT (2 + (m -> title ? 2 : 0)) #define BORDERS_WIDTH (2 + 2*DX + (m -> scrollok ? BARWIDTH + 1 : 0)) m -> height = m->nitems + BORDERS_HEIGHT; if (m -> height > LINES * 2 / 3) { /* слишком высокое меню */ m -> scrollok = BAR_VER; /* будет роллироваться */ m -> height = LINES * 2 / 3; } if((m -> width = len + BORDERS_WIDTH) > COLS ) m->width = COLS; m -> textheight = m->height - BORDERS_HEIGHT; m -> textwidth = m->width - BORDERS_WIDTH; /* окно должно лежать в пределах экрана */ if( m->top + m->height > LINES ) m->top = LINES - m->height; if( m->left + m->width > COLS ) m->left = COLS - m->width; if( m->top < 0 ) m->top = 0; if( m->left < 0 ) m->left = 0; if( m->win ){ /* уничтожить старое окно */ KillWin( m->win ); m->win = NULL; } if( m->win == NULL ){ /* создать окно и нарисовать основу */ if((m->win = newwin(m->height, m->width, m->top, m->left)) == NULL) return 0; keypad(m->win, TRUE); MnuWin(m); MnuDraw(m); /* но окно пока не вставлено в список активных окон */ } return ( m->win != NULL ); } /* Деинициализировать меню */ void MnuDeinit (Menu *m) { if( m->win ){ KillWin (m->win); m->win = NULL; } if( m->hotkeys ){ free ((char *) m -> hotkeys); m -> hotkeys = (int *) NULL; } } /* Спрятать меню */ void MnuHide (Menu *m){ if( m->win ) HideWin(m->win); } /* Зачистить место для line-той строки окна меню */ static void MnuBox (Menu *m, int line, int attr) { register WINDOW *w = m -> win; register i, xend = MXEND(m); wattrset (w, attr); for (i = 1; i < xend; i++) mvwaddch (w, line, i, ' '); /* ликвидировать последствия M_CTRL-линии */ wattrset (w, m->bg_attrib); mvwaddch (w, line, 0, VER_LINE); mvwaddch (w, line, xend, VER_LINE); wattrset (w, m->bg_attrib); } /* Нарисовать строку меню в y-ой строке окна выбора */ void MnuDrawItem (Menu *m, int y, int reverse, int selection) { register WINDOW *w = m -> win; int pos, l, attr; int ay = WY (m->title, y), ax = WX (0); char *s, c; int hatch, bold, label, cont = NO, under; if (y + m -> shift >= 0 && y + m -> shift < m -> nitems) { s = M_ITEM (m, y + m -> shift); hatch = M_TST (m, y + m -> shift, I_NOSEL) || M_TST (m, y + m -> shift, M_HATCH); bold = M_TST (m, y + m -> shift, M_BOLD); label = M_TST (m, y + m -> shift, M_LABEL); under = M_TST (m, y + m -> shift, I_EXE); } else { /* строка вне допустимого диапазона */ s = "~"; label = hatch = bold = NO; } if (*s == M_CTRL) { /* нарисовать горизонтальную черту */ int x, xend = MXEND(m); wattrset(w, m->bg_attrib); for(x=1; x < xend; x++) mvwaddch(w, ay, x, HOR_LINE); mvwaddch (w, ay, 0, LEFT_JOIN); mvwaddch (w, ay, xend, RIGHT_JOIN); wattrset (w, m->bg_attrib); return; } l = strlen(s = MnuConvert (s, &pos)); c = '\0'; if (l > m -> textwidth) { /* слишком длинная строка */ c = s[m -> textwidth]; s[m -> textwidth] = '\0'; cont = YES; if (pos > m -> textwidth) pos = (-1); } if (selection) MnuBox (m, ay, reverse ? m->sel_attrib : m->bg_attrib); wattrset (w, attr = (bold ? A_BOLD : 0) | (hatch ? A_ITALICS : 0) | (under ? A_UNDERLINE : 0) | (reverse ? m->sel_attrib : m->bg_attrib)); mvwaddstr (w, ay, ax, s); if( cont ) mvwaddch(w, ay, ax+m->textwidth, RIGHT_TRIANG); /* Hot key letter */ if (pos >= 0) { wattron (w, bold ? A_ITALICS : A_BOLD); mvwaddch (w, ay, WX(pos), s[pos]); } if (label){ /* строка помечена */ wattrset (w, attr | A_BOLD); mvwaddch (w, ay, 1, LABEL); } if (under){ wattrset (w, A_BOLD); mvwaddch (w, ay, ax-1, BOX_HATCHED); } if (c) s[m->textwidth] = c; wattrset (w, m->bg_attrib); SetPoint (m->savep, ay, ax-1); /* курсор поставить перед словом */ } /* Выбор в меню подходящего элемента */ int MnuNext (Menu *m) { char *s; register y = m -> current; for (++y; y < m -> nitems; y++) if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL)) return y; return (-1); } int MnuPrev (Menu *m) { char *s; register y = m -> current; for (--y; y >= 0; --y) if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL)) return y; return (-1); } int MnuPgUp (Menu *m) { char *s; register n, y = m -> current; for (--y, n = 0; y >= 0; --y) { if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL)) n++; if (n == m -> textheight) return y; } return MnuFirst (m); } int MnuPgDn (Menu *m) { char *s; register n, y = m -> current; for (++y, n = 0; y < m -> nitems; y++) { if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL)) n++; if (n == m -> textheight) return y; } return MnuLast (m); } int MnuFirst (Menu *m) { char *s; register y; for (y = 0; y < m -> nitems; y++) if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL)) return y; return (-1); } int MnuLast (Menu *m) { char *s; register y; for (y = m -> nitems - 1; y >= 0; --y) if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL)) return y; return (-1); } int MnuThis (Menu *m) { char *s; if (m -> current < 0 || m -> current >= m -> nitems) return (-1); /* error */ if ((s = M_ITEM (m, m -> current)) && *s != M_CTRL && !M_TST (m, m -> current, I_NOSEL)) return m -> current; return (-1); } int MnuName (Menu *m, char *name) { char *s; register y; int pos; for(y = 0; y < m -> nitems; ++y) if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL) && strcmp(name, MnuConvert(s, &pos)) == 0 ) return y; return (-1); } int MnuHot (Menu *m, unsigned c) { register y; char *s; if (m -> hotkeys == (int *) NULL) return (-1); if (c < 0400 && isupper (c)) c = tolower (c); for (y = 0; y < m -> nitems; y++) if (c == m -> hotkeys[y] && (s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL)) return y; return (-1); } /* Нарисовать содержимое меню для выбора */ void MnuDraw (Menu *m) { register i, j; for (i = 0; i < m -> textheight; i++) MnuDrawItem (m, i, NO, m -> scrollok ? YES : NO); } /* Поставить курсор в line-тую строку окна. */ void MnuPoint(Menu *m, int line, int eraseOld /* стирать старую селекцию? */){ int curline = m->current - m->shift; /* текущая строка окна */ if (line < 0 || line >= m -> textheight) return; /* ошибка */ if (eraseOld && curline != line) /* стереть старый выбор */ MnuDrawItem (m, curline, NO, YES); MnuDrawItem (m, line, YES, YES); /* подсветить новую строку */ } /* Перейти к y-той строке массива элементов, изменить картинку */ void MnuPointAt (Menu *m, int y) { char *s; if (y < 0 || y >= m->nitems) return; /* ошибка! */ if ((s = M_ITEM (m, y)) == NULL || *s == M_CTRL) return; MnuRoll (m, y, &m -> current, &m -> shift, m -> textheight, m -> nitems, MnuPoint, MnuDraw, MNU_DY); if (m -> scrollok) MnuWinBar(m); /* сдвинуть scroll bar */ GetBack(m->savep, m->win); /* вернуть курсор в начало строки селекции, * откуда он был сбит MnuWinBar-ом */ } /* Выбор в меню без участия "мыши". */ int MnuUsualSelect (Menu *m, int block) { int sel, snew, c, done = 0; m -> key = (-1); if( ! m->win ) return TOTAL_NOSEL; if((sel = MnuThis (m)) < 0) if((sel = MnuFirst (m)) < 0) return TOTAL_NOSEL; /* в меню нельзя ничего выбрать */ RaiseWin (m->win); /* сделать окно верхним */ MnuPointAt (m, sel); /* проявить */ if(m->showMe) m->showMe(m); /* может быть изменить позицию ? */ for (;;) { c = WinGetch (m->win); INP: if (m -> hitkeys && m -> handler) { HandlerReply reply; if (is_in (c, m -> hitkeys)) { c = (*m -> handler) (m, c, &reply); /* восстановить scroll bar */ MnuPointAt (m, m -> current); switch (reply) { case HANDLER_CONTINUE: continue; case HANDLER_NEWCHAR: goto INP; case HANDLER_OUT: goto out; case HANDLER_SWITCH: default: break; /* goto switch(c) */ } } } switch (c) { case KEY_UP: if ((snew = MnuPrev (m)) < 0) break; goto mv; case KEY_DOWN: next: if ((snew = MnuNext (m)) < 0) break; goto mv; case KEY_HOME: if ((snew = MnuFirst (m)) < 0) break; goto mv; case KEY_END: if ((snew = MnuLast (m)) < 0) break; goto mv; case KEY_NPAGE: if ((snew = MnuPgDn (m)) < 0) break; goto mv; case KEY_PPAGE: if ((snew = MnuPgUp (m)) < 0) break; goto mv; case KEY_IC: /* поставить/снять пометку */ if (M_TST (m, sel, M_LABEL)) M_CLR (m, sel, M_LABEL); else M_SET (m, sel, M_LABEL); MnuPointAt (m, sel); /* Если вы вычеркнете goto next; * и оставите просто break; * то вставьте в это место * MnuPoint( m, m->current - m->shift, NO ); */ goto next; case KEY_DC: if (M_TST (m, sel, M_HATCH)) M_CLR (m, sel, M_HATCH); else M_SET (m, sel, M_HATCH); MnuPointAt (m, sel); goto next; case KEY_LEFT: if (block & M_LFT) { sel = M_LEFT; goto out; } break; case KEY_RIGHT: if (block & M_RGT) { sel = M_RIGHT; goto out; } break; case 0: break; default: if (c == '\n' || c == '\r' || c == ESC) goto out; if ((snew = MnuHot (m, c)) < 0) { beep(); break; } /* иначе найден HOT KEY (горячая клавиша) */ done++; goto mv; } continue; mv: MnuPointAt (m, sel = snew); if(done){ wrefresh(m->win); /* проявить новую позицию */ break; } } out: wnoutrefresh(m->win); return((m->key = c) == ESC ? -1 : sel); /* Меню автоматически НЕ ИСЧЕЗАЕТ: если надо - * явно делайте MnuHide(m); после MnuUsualSelect(); */ } /* Пример 20 */ /* ______________________________________________________________ */ /* PULL_DOWN меню (меню-строка) */ /* _______________________ файл pull.h __________________________ */ typedef struct { Info info; /* строка в меню */ Menu *menu; /* связанное с ней вертикальное меню */ char *note; /* подсказка */ } PullInfo; typedef struct _Pull { /* Паспорт меню */ int nitems; /* количество элементов в меню */ PullInfo *items;/* элементы меню */ int *hotkeys; /* горячие ключи */ int key; /* клавиша, завершившая выбор */ int current; /* выбранный элемент */ int space; /* интервал между элементами меню */ int bg_attrib; /* цвет фона строки */ int sel_attrib; /* цвет выбранного элемента */ Point savep; void (*scrollBar) (struct _Pull *m, int n, int among); } PullMenu; #define PYBEG 0 /* строка, в которой размещается меню */ #define PM_BOLD I_DIR #define PM_NOSEL I_NOSEL #define PM_LFT M_LFT #define PM_RGT M_RGT #define PM_SET(m, i, flg) (m)->items[i].info.fl |= (flg) #define PM_CLR(m, i, flg) (m)->items[i].info.fl &= ~(flg) #define PM_TST(m, i, flg) ((m)->items[i].info.fl & (flg)) #define PM_ITEM(m, i) ((m)->items[i].info.s) #define PM_MENU(m, i) ((m)->items[i].menu) #define PM_NOTE(m, i) ((m)->items[i].note) #define COORD(m, i) ((m)->space * (i+1) + PullSum(m, i)) int PullInit(PullMenu *m); int PullSum(PullMenu *m, int n); void PullDraw(PullMenu *m); int PullShow(PullMenu *m); void PullHide(PullMenu *m); void PullDrawItem(PullMenu *m, int i, int reverse, int selection); void PullPointAt(PullMenu *m, int y); int PullHot(PullMenu *m, unsigned c); int PullPrev(PullMenu *m); int PullNext(PullMenu *m); int PullFirst(PullMenu *m); int PullThis(PullMenu *m); int PullUsualSelect(PullMenu *m); #define PullWin stdscr #define PM_REFUSED(m) ((m)->key < 0 || (m)->key == ESC ) /* _______________________ файл pull.c __________________________ */ #include "glob.h" #include "w.h" #include "menu.h" #include "pull.h" int PullSum(PullMenu *m, int n){ register i, total; int pos; for(i=0, total = 0; i < n; i++ ) total += strlen( MnuConvert(PM_ITEM(m, i), &pos )); return total; } /* Разметка меню. На входе: p->items массив элементов с M_HOT-метками и связанных меню. p->bg_attrib цвет фона строки. p->sel_attrib цвет выбранного элемента. Меню всегда размещается в окне stdscr (PullWin). */ int PullInit(PullMenu *m){ /* подменю не должны быть инициализированы, * т.к. все равно будут сдвинуты в другое место */ int total, pos; char *s; register i; m->key = m->current = 0; if(m->hotkeys){ free((char *) m->hotkeys); m->hotkeys = (int *) NULL; } /* подсчитать элементы меню */ m->nitems = 0; for( i=0, total = 0; PM_ITEM(m, i) != NULL; i++ ){ total += strlen(s = MnuConvert(PM_ITEM(m, i), &pos)); m->nitems++; } if( total > wcols(PullWin)){ /* меню слишком широкое */ err: beep(); return 0; } m->space = (wcols(PullWin) - total - 2) / (m->nitems + 1); if( m->space <= 0 ) goto err; /* разметить горячие клавиши */ if( m-> hotkeys = (int *) malloc( sizeof(int) * m->nitems )){ for(i=0; i < m->nitems; i++ ) m->hotkeys[i] = NOKEY; } for( i=0; i < m->nitems; i++ ){ if( PM_MENU(m,i)){ PM_MENU(m,i)->left = COORD(m, i) - 1; PM_MENU(m,i)->top = PYBEG + 1; PM_MENU(m,i)->bg_attrib = m-> bg_attrib; PM_MENU(m,i)->sel_attrib = m-> sel_attrib; if( PM_MENU(m,i)->win ) MnuDeinit( PM_MENU(m,i)); MnuInit( PM_MENU(m,i)); } if( m->hotkeys ){ s = MnuConvert(PM_ITEM(m, i), &pos); if( pos >= 0 ) m->hotkeys[i] = isupper(s[pos]) ? tolower(s[pos]) : s[pos]; } } keypad(PullWin, TRUE); return 1; } /* Проявить pull-down меню */ int PullShow(PullMenu *m){ register i; int first, last; first = last = (-1); for(i=0; i < m->nitems; i++ ){ PM_SET(m, i, PM_LFT | PM_RGT ); if( !PM_TST(m, i, PM_NOSEL)){ if( first < 0 ) first = i; last = i; } } if( first < 0 ) return (TOTAL_NOSEL); if(first == last ){ PM_CLR(m, first, PM_LFT | PM_RGT ); }else{ PM_CLR(m, first, PM_LFT); PM_CLR(m, last, PM_RGT); } wmove(PullWin, PYBEG, 0); wattrset(PullWin, m->bg_attrib); wclrtoeol(PullWin); PullDraw(m); return 1; } void PullDraw(PullMenu *m){ register i; for(i=0; i < m->nitems; i++ ) PullDrawItem(m, i, NO, NO); } /* Спрятать pull-down меню. Сама строка остается, подменю исчезают */ void PullHide(PullMenu *m){ register i; for(i=0; i < m->nitems; i++ ) if( PM_MENU(m, i)) MnuHide( PM_MENU(m, i)); PullDraw(m); } /* Нарисовать элемент меню */ void PullDrawItem(PullMenu *m, int i, int reverse, int selection){ int x, pos, hatch = PM_TST(m, i, PM_NOSEL ); char *s; x = COORD(m, i); s = MnuConvert( PM_ITEM(m, i), &pos ); wattrset(PullWin, (reverse ? m->sel_attrib : m->bg_attrib) | (hatch ? A_ITALICS : 0 )); /*mvwaddch(PullWin, PYBEG, x-1, reverse ? LEFT_TRIANG : ' ');*/ mvwaddstr(PullWin, PYBEG, x, s); /*waddch(PullWin, reverse ? RIGHT_TRIANG : ' ');*/ if( pos >= 0 ){ /* Hot key letter */ wattron(PullWin, A_BOLD); mvwaddch(PullWin, PYBEG, x + pos, s[pos]); } wmove (PullWin, PYBEG, x-1); SetPoint(m->savep, PYBEG, x-1); wattrset(PullWin, m->bg_attrib); } int PullPrev(PullMenu *m){ register y; for( y = m->current - 1; y >= 0; y-- ) if( !PM_TST(m, y, PM_NOSEL )) return y; return (-1); } int PullNext(PullMenu *m){ register y; for( y = m->current+1; y < m->nitems; y++ ) if( !PM_TST(m, y, PM_NOSEL)) return y; return (-1); } int PullFirst(PullMenu *m){ register y; for( y = 0; y < m->nitems; y++ ) if( !PM_TST(m, y, PM_NOSEL)) return y; return (-1); } int PullThis(PullMenu *m){ register y; if( m->current < 0 || m->current >= m->nitems ) return (-1); if( PM_TST(m, m->current, PM_NOSEL)) return (-1); return m->current; } int PullHot(PullMenu *m, unsigned c){ register y; if( m-> hotkeys == (int *) NULL ) return (-1); if( c < 0400 && isupper(c)) c = tolower(c); for( y=0; y < m->nitems; y++ ) if( c == m->hotkeys[y] && !PM_TST(m, y, PM_NOSEL)) return y; return (-1); } /* Указать на элемент n */ void PullPointAt( PullMenu *m, int n){ if( n < 0 || n >= m->nitems ) return ; /* error */ if( n != m->current ){ if( PM_MENU(m, m->current)) MnuHide( PM_MENU(m, m->current)); PullDrawItem( m, m->current, NO, YES ); } m -> current = n; PullDrawItem( m, n, YES, YES ); if( m->scrollBar ){ m->scrollBar( m, n, m->nitems ); GetBack(m->savep, PullWin); } } /* Выбор в меню */ int PullUsualSelect(PullMenu *m){ int autogo = NO, c, code, done = 0, snew, sel, reply = (-1); m->key = (-1); if((sel = PullThis(m)) < 0 ) if((sel = PullFirst(m)) < 0 ) return TOTAL_NOSEL; if( PullShow(m) < 0 ) return TOTAL_NOSEL; PullPointAt(m, sel); /* начальная позиция */ for(;;){ if( autogo ){ /* Автоматическая проявка подменю */ if( PM_MENU(m, m->current) == NULL) goto ask; code = MnuUsualSelect(PM_MENU(m, m->current), PM_TST(m, m->current, PM_LFT) | PM_TST(m, m->current, PM_RGT)); MnuHide(PM_MENU(m, m->current)); c = PM_MENU(m, m->current)->key; if(code == (-1)){ reply = (-1); goto out; } /* в подменю ничего нельзя выбрать */ if( code == TOTAL_NOSEL) goto ask; /* MnuUsualSelect выдает специальные коды для * сдвигов влево и вправо */ if( code == M_LEFT ) goto left; if( code == M_RIGHT ) goto right; reply = code; goto out; } else ask: c = WinGetch(PullWin); switch(c){ case KEY_LEFT: left: if((snew = PullPrev(m)) < 0 ) goto ask; goto mv; case KEY_RIGHT: right: if((snew = PullNext(m)) < 0 ) goto ask; goto mv; case ESC: reply = (-1); goto out; case '\r': case '\n': if( PM_MENU(m, m->current) == NULL){ reply = 0; goto out; } autogo = YES; break; default: if((snew = PullHot(m, c)) < 0 ) break; if( PM_MENU(m, snew) == NULL){ reply=0; done++; } autogo = YES; goto mv; } continue; mv: PullPointAt(m, sel = snew); if( done ) break; } out: wnoutrefresh(PullWin); PullHide(m); m->key = c; wattrset(PullWin, A_NORMAL); /* NOT bg_attrib */ return reply; /* номер элемента, выбранного в меню PM_MENU(m, m->current) */ } /* Пример 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 )