Пример 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) */
}
© Copyright А. Богатырев, 1992-95
Си в UNIX
Назад | Содержание | Вперед