Хрестоматия по программированию на Си в Unix
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; /* ошибка! */