Пример 22

/* ______________________________________________________________ */ /* ПРЯМОУГОЛЬНАЯ ТАБЛИЦА-МЕНЮ */ /* _______________________ файл table.h _________________________ */ typedef struct _Table { /* Паспорт таблицы */ int nitems; /* количество элементов в таблице */ Info *items; /* массив элементов */ char *fmt; /* формат вывода */ int key; /* кнопка, завершившая выбор в таблице */ int current; /* номер выбранного элемента */ int shift; /* число элементов перед окном */ WINDOW *win; /* окно в котором размещена таблица */ int left, top, height, width; /* размеры и расположение таблицы в окне */ int space; /* интервал между колонками */ int elen; /* макс. ширина колонки */ int tcols; /* число колонок в таблице */ int cols; /* число колонок в видимой части таблицы */ int cutpos; /* позиция для обрубания слишком длинных строк */ int scrollok; /* роллируется ? */ int exposed; /* нарисована ? */ int elems; /* текущее число эл-тов в подокне */ int maxelems; /* максимальное число эл-тов в подокне */ int maxshift; /* максимальный сдвиг */ int bg_attrib, sel_attrib; /* цвет фона и выбранного элемента */ Point savep; /* Функции проявки/спрятывания окна */ int (*showMe)(struct _Table *tbl); void (*hideMe)(struct _Table *tbl); void (*scrollBar)(struct _Table *tbl, int whichbar, int n, int among); /* Обработчик специальных клавиш */ int *hitkeys; int (*handler)(struct _Table *tbl, int c, HandlerReply *reply); } Table; #define T_BOLD M_BOLD #define T_NOSEL I_NOSEL #define T_HATCH M_HATCH #define T_LABEL M_LABEL #define T_SET(m, i, flg) (((m)->items)[i]).fl |= (flg) #define T_CLR(m, i, flg) (((m)->items)[i]).fl &= ~(flg) #define T_TST(m, i, flg) ((((m)->items)[i]).fl & (flg)) #define T_ITEM(m, i) ((((m)->items)[i]).s) /* Формат 'd' ниже вставлен лишь для текущего состояния использования * форматов в нашем проекте: */ #define T_ITEMF(m, i, cut) \ ((m)->fmt && *(m)->fmt != 'd' ? \ TblConvert(T_ITEM((m), i), (m)->fmt, (cut)) : T_ITEM((m), i)) #define T_VISIBLE(tbl, new) ((tbl)->exposed == YES && \ (new) >= (tbl)->shift && (new) < (tbl)->shift + (tbl)->elems) #define TLABSIZE 2 /* ширина поля меток */ #define T_REFUSED(t) ((t)->key < 0 || (t)->key == ESC ) int TblCount( Table *tbl ); void TblInit( Table *tbl, int forcedOneColumn ); void TblChkCur ( Table *tbl ); int TblChkShift( Table *tbl ); void TblChk ( Table *tbl ); char *TblConvert( char *s, char *fmt, int cutpos ); void TblPointAt ( Table *tbl, int snew ); void TblPoint ( Table *tbl, int snew, int eraseOld ); void TblDraw ( Table *tbl ); void TblDrawItem( Table *tbl, int at, int reverse, int selection); void TblBox ( Table *tbl, int at, int reverse, int hatched, int width, int axl, int axi, int ay); int TblClear( Table *tbl ); int TblPlaceByName( Table *tbl, char *p ); void TblReport( Table *tbl ); void TblTag ( Table *tbl, int at, int flag); void TblUntag( Table *tbl, int at, int flag); void TblRetag( Table *tbl, int at, int flag); void TblTagAll( Table *tbl, char *pattern, int flag ); void TblUntagAll( Table *tbl, char *pattern, int flag ); int TblUsualSelect( Table *tbl ); /* _______________________ файл table.c _________________________ */ #include "w.h" #include "glob.h" #include "menu.h" #include "table.h" extern char STRING_BUFFER[MAXLEN]; /* imported from menu.c */ /* надо указать размер, чтоб работал sizeof(STRING_BUFFER) */ /* Переформатировать строку по формату fmt для выдачи в таблицу. * Пока предложена простейшая интерпретация. */ char *TblConvert( char *s, char *fmt, int cutpos ){ if( fmt && *fmt == 'd'){ register i, j, len; char *p = strrchr(s, '.'); if((len = strlen(s)) < DIR_SIZE && *s != '.' && p ){ int sufxlen = strlen(p); for(i=0; i < len - sufxlen ; ++i) STRING_BUFFER[i] = s[i]; for(; i < DIR_SIZE - sufxlen; ++i) STRING_BUFFER[i] = ' '; for(j=0; i < DIR_SIZE; j++, ++i) STRING_BUFFER[i] = p[j]; STRING_BUFFER[i] = '\0'; } else strcpy(STRING_BUFFER, s); if(cutpos > 0 && cutpos < sizeof(STRING_BUFFER)) STRING_BUFFER[cutpos] = '\0'; } else { /* без формата, только обрубание */ if( cutpos <= 0 ) cutpos = 32000; /* Обрубание выключено */ strncpy(STRING_BUFFER, s, MIN(sizeof(STRING_BUFFER) - 1, cutpos)); } return STRING_BUFFER; } /* Обрубить s до длины cutpos букв */ char *TblCut( char *s, int cutpos ){ if( cutpos <= 0 ) return s; strncpy(STRING_BUFFER, s, MIN(sizeof(STRING_BUFFER) - 1, cutpos)); return STRING_BUFFER; } /* Подсчет элементов таблицы и ширины столбца */ int TblCount( Table *tbl ){ register i, L, LL; char *s; L = i = 0; if( tbl->items) while((s = T_ITEM(tbl, i)) != NULL ){ if( tbl->fmt ) s = TblConvert(s, tbl->fmt, 0); LL = strlen(s); if( LL > L ) L = LL; i++; } tbl->nitems = i; return L; } /* Разметка таблицы. На входе: t->items Массив данных, показываемый в меню. t->exposed = NO Таблица уже нарисована ? t->fmt Формат строк, выводимых в таблицу. t->win Окно для размещения таблицы. t->showMe Функция проявки окна. t->hideMe Функция упрятывания окна. t->hitkeys Специальные клавиши []. Конец -1. t->handler Обработчик или NULL. t->width Ширина поля таблицы. t->height Высота поля таблицы. t->left Левый край таблицы в окне. t->top Верхний край таблицы в окне. t->scrollBar Функция рисования scroll-bar-а или NULL. t->bg_attrib Цвет фона (== цвету фона окна). t->sel_attrib Цвет выбранного элемента. forcedOneColumn == YES делает таблицу в 1 колонку. */ void TblInit( Table *tbl, int forcedOneColumn ){ int mlen = TblCount( tbl ); /* самый широкий элемент таблицы */ /* усечь до ширины таблицы */ if( mlen > tbl->width || forcedOneColumn ) mlen = tbl->width; /* слишком широко */ /* ширина столбца таблицы = ширина элемента + поле меток + разделитель */ tbl->elen = mlen + TLABSIZE + 1; /* #####строка_элемент| */ /* метки элемент 1 */ /* число столбцов во всей таблице */ tbl->tcols = (tbl->nitems + tbl->height - 1) / tbl->height; /* число столбцов, видимых через окно (+1 для ошибок округления) */ tbl->cols = tbl->width / (tbl->elen + 1); if( tbl->cols == 0 ){ /* слишком широкая таблица */ tbl->cols = 1; /* таблица в одну колонку */ tbl->elen = tbl->width - 2; mlen = tbl->elen - (TLABSIZE + 1); tbl->cutpos = mlen; /* и придется обрубать строки */ } else tbl->cutpos = 0; /* без обрубания */ tbl->cols = MIN(tbl->cols, tbl->tcols); /* интервал между колонками */ tbl->space = (tbl->width - tbl->cols * tbl->elen)/(tbl->cols+1); if( tbl->space < 0 ){ beep(); tbl->space = 0; } /* сколько элементов умещается в окно */ tbl->maxelems = tbl-> cols * tbl->height; tbl->maxshift = (tbl->tcols * tbl->height) - tbl->maxelems; if( tbl->maxshift < 0 ) tbl->maxshift = 0; /* требуется ли роллирование таблицы через окно */ tbl->scrollok = (tbl->nitems > tbl->maxelems); tbl->elems = tbl->shift = tbl->current = 0; /* пока */ tbl->exposed = NO; /* таблица еще не нарисована */ tbl->key = (-1); } /* Проверить корректность текущей позиции */ void TblChkCur( Table *tbl ){ if( tbl->current >= tbl->nitems ) tbl->current = tbl->nitems - 1; if( tbl->current < 0 ) tbl->current = 0; } /* Проверить корректность сдвига (числа элементов ПЕРЕД окном) */ int TblChkShift( Table *tbl ){ register int oldshift = tbl->shift; /* в колонке должно быть видно достаточно много элементов */ if( tbl->cols == 1 && /* таблица в 1 колонку */ tbl->tcols > 1 && /* но всего в ней не одна колонка */ tbl->nitems - tbl->shift < tbl->height / 2 + 1 ) tbl->shift = tbl->nitems - (tbl->height/2 + 1); if( tbl->shift > tbl->maxshift ) tbl->shift = tbl->maxshift; if( tbl->shift < 0 ) tbl->shift = 0; return tbl->shift != oldshift; /* скорректировано ? */ } /* Проверить корректность параметров таблицы */ void TblChk( Table *tbl ){ again: TblChkCur( tbl ); TblChkShift( tbl ); if( tbl -> maxelems ){ if( tbl -> current >= tbl->shift + tbl->maxelems ){ tbl->shift = tbl->current - (tbl->maxelems - 1); goto again; } if( tbl->current < tbl->shift ){ tbl->shift = tbl->current; goto again; } } } /* Указать на snew-тый элемент списка, перерисовать картинку */ void TblPointAt( Table *tbl, int snew ){ int curCol; /* текущий столбец всей таблицы (для current) */ int newCol; /* нужный столбец таблицы (для snew) */ int colw; /* нужный столбец ОКНА (для snew) */ int gap; /* зазор */ int newshift = tbl->shift; /* новый сдвиг окна от начала массива */ int drawn = NO; /* таблица целиком перерисована ? */ /* ПРоверить корректность номера желаемого элемента */ if( snew < 0 ) snew = 0; if( snew >= tbl->nitems ) snew = tbl->nitems - 1; if( tbl->current == snew && tbl->exposed == YES) return; /* уже стоим на требуемом элементе */ #define WANTINC 1 #define WANTDEC (tbl->cols-1-WANTINC) gap = (tbl->height - (tbl->shift % tbl->height)) % tbl->height; /* gap - это смещение, которое превращает строгую постолбцовую структуру --0-- --3- --1-- --4- --2-- --5- в сдвинутую структуру ____ |----- gap=2___/ пусто g0 --1-- g3 | --4-- g6 .... \____пусто g1 --2-- g4 | --5-- g7 --0-- g2 --3-- g5 | --6-- g8 |------ shift=4 */ /* операция прокрутки данных через таблицу: TblRoll() _________________*/ /* Элемент уже виден в текущем окне ? */ /* Параметр elems вычисляется в TblDraw() */ if( T_VISIBLE(tbl, snew)) goto ThisWindow; /* smooth scrolling (гладкое роллирование) */ if( snew == tbl->shift + tbl->elems && /* элемент непосредственно следующий ЗА окном */ tbl->current == tbl->shift + tbl->elems - 1 /* курсор стоит в нижнем правом углу окна */ ){ newshift++; gap--; if ( gap < 0 ) gap = tbl->height - 1 ; goto do_this; } if( snew == tbl->shift - 1 && /* элемент непосредственно стоящий ПЕРЕД окном */ tbl->current == tbl->shift /* и курсор стоит в верхнем левом углу окна таблицы */ ){ newshift --; gap = (gap + 1) % tbl->height; goto do_this; } /* jump scrolling (прокрутка скачком) */ curCol = (tbl->current+gap) / tbl->height; newCol = (snew +gap) / tbl->height; if( tbl->cols > 1 ){ if( newCol > curCol ) colw = WANTINC; else colw = WANTDEC; } else colw = 0; newshift = (newCol - colw) * tbl->height - gap ; do_this: if( tbl->shift != newshift || tbl->exposed == NO){ tbl->shift = newshift; TblChkShift( tbl ); /* >= 0 && <= max */ TblDraw( tbl ); /* перерисовать все окно с нового места */ drawn = YES; /* перерисовано целиком */ } ThisWindow: /* поставить курсор в текущем окне без перерисовки окна */ TblPoint( tbl, snew, !drawn ); /* tbl->current = snew; сделается в TblPoint() */ } /* Поставить курсор на элемент в текущем окне */ void TblPoint ( Table *tbl, int snew, int eraseOld ){ if( ! T_VISIBLE(tbl, snew)){ beep(); /* ERROR !!! */ return; } if( eraseOld && tbl->current != snew ) TblDrawItem( tbl, tbl->current, NO, YES ); TblDrawItem( tbl, snew, YES, YES ); tbl->current = snew; TblReport( tbl ); } /* Нарисовать scroll bar в нужной позиции. Кроме того, * в эту функцию можно включить и другие действия, например * выдачу имени T_ITEM(tbl, tbl->current) на рамке окна. */ void TblReport( Table *tbl ){ if ( tbl->scrollBar ) (*tbl->scrollBar)( tbl, BAR_VER|BAR_HOR, tbl->current, tbl->nitems); GetBack( tbl->savep, tbl->win ); /* курсор на место ! */ } /* Перерисовать все окно таблицы */ void TblDraw( Table *tbl ){ register next; /* число элементов в таблице (может остаться незанятое * место в правой нижней части окна */ tbl->elems = MIN(tbl->nitems - tbl->shift, tbl->maxelems ); for( next = 0; next < tbl->maxelems; next++ ) TblDrawItem(tbl, next + tbl->shift, NO, tbl->scrollok ? YES : NO); tbl->exposed = YES; /* окно изображено */ } /* Нарисовать элемент таблицы */ void TblDrawItem( Table *tbl, int at, int reverse, int selection){ register WINDOW *w = tbl->win; int pos; char *s; int hatch, bold, label, under; int ax, axl, ay, column; if( at >= 0 && at < tbl->nitems ){ s = T_ITEM( tbl, at ); if( tbl->fmt ) s = TblConvert(s, tbl->fmt, tbl->cutpos); else if( tbl->cutpos > 0 ) s = TblCut(s, tbl->cutpos); /* выделения */ hatch = T_TST( tbl, at, T_HATCH ); bold = T_TST( tbl, at, T_BOLD ); label = T_TST( tbl, at, T_LABEL ); under = T_TST( tbl, at, I_EXE ); } else { s = "~"; label = hatch = bold = under = NO; } at -= tbl->shift; /* координату в списке перевести в коорд. окна */ ay = tbl->top + at % tbl->height; column = at / tbl->height; /* начало поля меток */ axl = tbl->left + tbl->space + column * (tbl->space + tbl->elen); /* начало строки-элемента */ ax = axl + TLABSIZE; if(selection) TblBox( tbl, at, reverse, reverse && hatch, strlen(s), axl, ax, ay ); wattrset (w, reverse ? tbl->sel_attrib : tbl->bg_attrib); if( hatch ) wattron(w, A_ITALICS); if( bold ) wattron(w, A_BOLD); if( under ) wattron(w, A_UNDERLINE); mvwaddstr(w, ay, ax, s); wattrset(w, tbl->bg_attrib | (bold ? A_BOLD:0)); if( label ) mvwaddch(w, ay, axl, LABEL); if( under ){ wattron(w, A_BOLD); mvwaddch(w, ay, axl+1, BOX_HATCHED);} wattrset(w, tbl->bg_attrib); if( column != tbl->cols-1 ) /* не последний столбец */ mvwaddch(w, ay, axl+tbl->elen-1 + (tbl->space+1)/2, VER_LINE); wmove(w, ay, ax-1); /* курсор перед началом строки */ SetPoint(tbl->savep, ay, ax-1); /* запомнить координаты курсора */ } /* Зачистить область окна для рисования элемента таблицы */ void TblBox(Table *tbl, int at, int reverse, int hatched, int width, int axl, int axi, int ay){ register WINDOW *w = tbl->win; int len = tbl->elen; wattrset (w, tbl->bg_attrib); wboxerase(w, axl, ay, axl+len-1, ay); wattrset (w, reverse ? tbl->sel_attrib : tbl->bg_attrib); /* если ниже задать axl+len+1, то подсвеченный * прямоугольник будет фиксированного размера */ wboxerase(w, axi, ay, axl+width-1, ay); wattrset (w, tbl->bg_attrib); } /* Зачистить прямоугольную рабочую область окна tbl->win, * в которой будет изображаться таблица. * Эта функция нигде не вызывается ЯВНО, поэтому ВЫ должны * вызывать ее сами после каждого TblInit() * для этого удобно поместить ее в демон (*showMe)(); */ int TblClear( Table *tbl ){ tbl->exposed = NO; tbl->elems = 0; /* Это всегда происходит при exposed:= NO */ wboxerase( tbl->win, tbl->left, tbl->top, tbl->left + tbl->width - 1, tbl->top + tbl->height - 1); return 1; } /* Пометить элемент в таблице */ void TblTag( Table *tbl, int at, int flag){ if( T_TST(tbl, at, flag)) return; T_SET(tbl, at, flag); if( T_VISIBLE(tbl, at)) TblDrawItem(tbl, at, tbl->current == at ? YES:NO, YES ); } /* Снять пометку с элемента таблицы */ void TblUntag( Table *tbl, int at, int flag){ if( ! T_TST(tbl, at, flag)) return; T_CLR(tbl, at, flag); if( T_VISIBLE(tbl, at)) TblDrawItem(tbl, at, tbl->current == at ? YES:NO, YES ); } /* Изменить пометку элемента таблицы */ void TblRetag( Table *tbl, int at, int flag){ if( T_TST(tbl, at, flag)) T_CLR(tbl, at, flag); else T_SET(tbl, at, flag); if( T_VISIBLE(tbl, at)) TblDrawItem(tbl, at, tbl->current == at ? YES:NO, YES ); } /* Используется в match() для выдачи сообщения об ошибке */ void TblMatchErr(){} /* Пометить элементы, чьи имена удовлетворяют шаблону */ void TblTagAll( Table *tbl, char *pattern, int flag ){ register i; for(i=0; i < tbl->nitems; i++) if( !T_TST(tbl, i, I_DIR) && match( T_ITEMF(tbl, i, 0), pattern)) TblTag( tbl, i, flag ); } /* Снять пометки с элементов по шаблону имени */ void TblUntagAll( Table *tbl, char *pattern, int flag ){ register i; for(i=0; i < tbl->nitems; i++) if( match( T_ITEMF(tbl, i, 0), pattern)) TblUntag( tbl, i, flag ); } /* Указать на элемент по шаблону его имени */ int TblPlaceByName( Table *tbl, char *p ){ register i; char *s; for( i=0; i < tbl->nitems; i++ ){ s = T_ITEMF(tbl, i, 0); if( match( s, p )){ if( tbl->exposed == NO ){ /* Задать некорректный shift, * чтобы окно полностью перерисовалось */ tbl->shift = tbl->nitems+1; tbl->elems = 0; } TblPointAt( tbl, i ); return i; } } return (-1); } /* Перемещение по таблице набором первых букв названия элемента */ static int TblTrack( Table *tbl, int c){ char *s; register i; int from; /* с какого элемента начинать поиск */ int found = 0; /* сколько было найдено */ int plength = 0; int more = 0; char pattern[20]; if( c >= 0400 || iscntrl(c)){ beep(); return 0; } AddCh: from = 0; pattern[plength] = c; pattern[plength+1] = '*'; pattern[plength+2] = '\0'; plength++; More: for(i = from; i < tbl->nitems; i++){ s = T_ITEMF(tbl, i, 0); if( match(s, pattern)){ ++found; from = i+1; TblPointAt( tbl, i ); c = WinGetch( tbl->win ); switch(c){ case '\t': /* find next matching */ more++; goto More; case KEY_BACKSPACE: case '\177': case '\b': if( plength > 1 ){ plength--; pattern[plength] = '*'; pattern[plength+1] = '\0'; from = 0; more++; goto More; } else goto out; default: if( c >= 0400 || iscntrl(c)) return c; if( plength >= sizeof pattern - 2 ) goto out; goto AddCh; } } } /* не найдено */ if(more && found){ /* нет БОЛЬШЕ подходящих, но ВООБЩЕ - есть */ beep(); more = found = from = 0; goto More; } out: beep(); return 0; } /* Выбор в таблице */ int TblUsualSelect( Table *tbl ){ int c, want; tbl->key = (-1); if( tbl->items == NULL || tbl->nitems <= 0 ) return TOTAL_NOSEL; TblChk( tbl ); if( tbl->showMe ) if((*tbl->showMe)(tbl) <= 0 ) return (-1); if( !tbl->win ) return TOTAL_NOSEL; if( tbl->exposed == NO ){ TblDraw ( tbl ); } /* Указать текущий элемент */ TblPoint( tbl, tbl->current, NO); TblReport( tbl ); for( ;; ){ c = WinGetch(tbl->win); INP: if( tbl->hitkeys && tbl->handler ){ HandlerReply reply; if( is_in(c, tbl->hitkeys)){ c = (*tbl->handler)(tbl, c, &reply); TblReport( tbl ); /* restore scroll bar */ switch( reply ){ case HANDLER_CONTINUE: continue; case HANDLER_NEWCHAR: goto INP; case HANDLER_OUT: goto out; case HANDLER_SWITCH: default: break; /* goto switch(c) */ } } } sw: switch( c ){ case KEY_LEFT: want = tbl->current - tbl->height; goto mv; case KEY_RIGHT: want = tbl->current + tbl->height; goto mv; case KEY_UP: want = tbl->current - 1; goto mv; case KEY_DOWN: next: want = tbl->current + 1; goto mv; case KEY_HOME: want = 0; goto mv; case KEY_END: want = tbl->nitems - 1; goto mv; case KEY_NPAGE: want = tbl->current + tbl->elems; goto mv; case KEY_PPAGE: want = tbl->current - tbl->elems; goto mv; case KEY_IC: if( T_TST(tbl, tbl->current, T_LABEL )) T_CLR(tbl, tbl->current, T_LABEL ); else T_SET(tbl, tbl->current, T_LABEL); if( tbl->current == tbl->nitems - 1 /* LAST */){ TblPoint(tbl, tbl->current, NO ); break; } TblPointAt(tbl, tbl->current ); /* if not goto next; * but break; * then use * TblPoint(tbl, tbl->current, NO); * here */ goto next; case KEY_DC: if( T_TST(tbl, tbl->current, T_HATCH )) T_CLR(tbl, tbl->current, T_HATCH ); else T_SET(tbl, tbl->current, T_HATCH); if( tbl->current == tbl->nitems - 1 /* LAST */){ TblPoint(tbl, tbl->current, NO ); break; } TblPointAt(tbl, tbl->current ); goto next; case ESC: case '\r': case '\n': goto out; case 0: break; default: c = TblTrack(tbl, c); if( c ) goto INP; break; } continue; mv: TblPointAt( tbl, want ); } out: wnoutrefresh( tbl->win ); if( tbl->hideMe ) (*tbl->hideMe)(tbl); return ((tbl->key = c) == ESC ? -1 : tbl->current ); }

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

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