河南省林州市信用联社科技部 李连卿
在开发程序时,程序的界面、开放性是两个非常重要的方面,目前,在Unix系统下运行的程序的界面大都 比较死板,而且,在进行功能扩充时也不是很方便。那么,能不能设计一个象 Windows那样能够按照用户要求 随意调整界面,功能扩充方便的程序呢?答案是肯定的。笔者通过实践,设计了一个菜单程序,使用户在对菜 单的显式样式不满意时,只需通过对菜单参数文件进行操作即可完成对菜单位置、宽度、长度、是否有边框等 进行调整;在用户需要进行功能扩充时,也无须改动源程序,只须对参数文件进行操作就可将新增功能挂到菜 单上。 一 参数文件说明 本程序需要借肋两个参数文件来实现: (1)、对菜单中每一项参数进行说明的文件(menu.def),它格式如下所述: !所属菜单代号!项顺序号!菜单项名称!外挂程序名称!下级菜单代号! 说明: 1、如菜单代号为"0",则表示此项属于主菜单; 2、如外挂程序名称为"0",则表示此菜单项对应的过程在菜单程序内部或对应于一个子菜单; 3、如下级菜单代号为"0",则表示此菜单项无下级子菜单; 4、项顺序号同时可作为菜单热键使用。 假如文件menu.def中有下面这一行: !0!3!格式化磁盘!format /dev/rfd0135ds18!0! 它表示主菜单的第三项为格式化磁盘,它对应的执行过程为 format /dev/rfd0135ds18,本项无子菜单。 如果用户想把自己编的实现查询功能程序XXX挂到本程序主菜单第4项上,则可在menu.def中增加下面 这一行: !0!4!查询!XXX!0! (2)、对各菜单参数进行说明文件(menu.conf),其格式如下所述: !菜单代号!上一级菜单代号!边框标志!菜单宽度!菜单行数!菜单列数!起始横坐标!起始纵坐标! 说明: 1、边框标志为"0"表示无框,为"1"表示有边框; 2、上级菜单代号为"-1",表示无上级菜单; 3、如菜单代号为"0",表示主菜单。 当用户对菜单显示样式不满意时,可通过调整此文件设计个性化的界面。
二 编程实现 本程序文件为menu.c,部分代码如下:
#include #define ESC 27 #define ENT 13 #define REFRESH 12 #define MAX_M 10 /* 菜单最大层数 */
void initial(),nomlastpos(),revcurpos(),disponepage(),dispprevline(); void dispnextline(),domenu(),getmenuconf(),keycont(); void getitem(), get_m_conf(), get_m_item(),clearwin(),execprog(); /* 标识每一菜单项的结构 */ struct menu { short menu_code; /* 所属菜单代号 */ short item_order; /* 项顺序号 */ char item[20]; /* 菜单项名称 */ char prog[80]; /* 本项菜单执行程序 */ short submenu_code; /* 下一级菜单编号 */ struct menu *next; /* 指向上一项的指针 */ struct menu *prev; /* 指向下一项的指针 */ } m_item,*head,*this,*new,*last,*scrpos,*lastscrpos,*begin,*lastbegin,*lastscr[MAX_M]; /* 标识每一菜单内容的结构 */ struct menuconf { short menu_code; /* 菜单代号 */ short last_code; /* 上一级菜单代号 */ short bord_flag; /* 边框标志 0--无边框 1--有边框 **/ short m_wight; /* 菜单显示宽度 */ short m_lengh; /* 每一行项数 */ short m_col; /* 菜单列数 */ short m_bx; /* 菜单起始横坐标 */ short m_by; /* 菜单起始纵坐标 */ } m_conf; WINDOW *menuwin, *boxwin, *curw, *lastw[MAX_M], *workwin; long curpos, lastcurpos, lastscrcurpos, lastmenucur[MAX_M]; short menu_no = 0, wno = 0;
/* 主函数 */ main() { initial(); getmenuconf(0); /* 取第0号菜单参数 */
/* 创建主窗口 */ menuwin=newwin(m_conf.m_lengh, m_conf.m_wight, m_conf.m_bx+1, m_conf.m_by+1); curw=menuwin; lastw[wno]=menuwin;
getitem(); /* 取当前菜单各项内容 */ domenu(head, 0); endwin(); }
/* 取菜单各项参数函数 */ void getitem() { FILE *fp; char buff[0x100];
/* 建边框窗口 */ boxwin=newwin(m_conf.m_lengh+2,m_conf.m_wight+2,m_conf.m_bx,m_conf.m_by); keypad(curw, TRUE); if (m_conf.bord_flag==1) { box(boxwin, 0,0 ); wrefresh(boxwin); }
head=NULL; if ((fp = fopen("./menu.def","r")) == NULL) { fprintf(stderr, "\n不能打开菜单定义文件\n"); return; } while( fgets(buff, 0x100, fp)!=NULL) { get_m_item(buff);
if (m_item.menu_code != menu_no) continue;
new=(struct menu*)malloc(sizeof(struct menu)); if (head == NULL) { last = head; head = new; } else { this->;next = new; last = this; } this = new; this->;menu_code=m_item.menu_code; this->;item_order=m_item.item_order; strcpy(this->;item,m_item.item); strcpy(this->;prog,m_item.prog); this->;submenu_code=m_item.submenu_code; this->;next=NULL; this->;prev = last; } fclose(fp); } /* 菜单处理函数 */ void domenu(curscrp, curp) struct menu *curscrp; int curp; { int i, x, y; struct menu *mpos;
this = head; disponepage(this); curpos = curp; scrpos = curscrp; lastcurpos = lastscrcurpos = 0; revcurpos(); for(;;) { switch (wgetch(curw)) { case ENT: /* 有下一级菜单 */ if ((!strcmp(scrpos->;prog, "0")) && (scrpos->;submenu_code != 0)) { lastbegin = begin->;next; getmenuconf(scrpos->;submenu_code); menu_no = scrpos->;submenu_code;
wno++; lastmenucur[wno]=curpos; lastscr[wno] = scrpos; lastw[wno]=curw;
workwin=newwin(m_conf.m_lengh,m_conf.m_wight,m_conf.m_bx+1,m_conf.m_by+1); curw=workwin; getitem(); domenu(head, 0); } /* 是内部函数 */ /* 是外部可执行程序 */ else { endwin(); execprog(); } break; case ESC: case 'q': case 'Q': case '0': /* 无上级菜单 */ if (m_conf.last_code == -1) { clearwin(); endwin(); exit(0); } /* 有上级菜单 */ else { menu_no = m_conf.last_code; clearwin(); getmenuconf(menu_no); getitem(); touchwin(lastw[wno]); curw=lastw[wno]; curpos = lastmenucur[wno]; scrpos = lastscr[wno]; wno--; wrefresh(curw); } break; case 'r': case 'R': case REFRESH: /* 重显屏幕 */ wrefresh(curscr); break; case KEY_RIGHT: /* 右光标键 */ if ( scrpos->;next != NULL ) { lastcurpos = curpos; lastscrpos = scrpos; scrpos=scrpos->;next; getyx(curw, x, y); if((x==m_conf.m_lengh-1)&&(curpos%m_conf.m_col==m_conf.m_col-1)){ curpos-=(m_conf.m_col-1); lastcurpos = curpos - 1; /* 实现向上卷屏 */ wmove(curw, 0, 0); wdeleteln(curw); dispnextline("R"); } else curpos++; if ((curpos%m_conf.m_col == 0) && (m_conf.m_lengh == 1)) { revcurpos(); break; } else { nomlastpos(); revcurpos(); } } break; case KEY_LEFT: /* 左光标键 */ if ( scrpos->;prev != NULL ) { lastcurpos = curpos; lastscrpos = scrpos; scrpos=scrpos->;prev; getyx(curw, x, y); if ((x==0) && (curpos%m_conf.m_col ==0)) { curpos+=m_conf.m_col-1; lastcurpos = curpos + 1; /* 实现向下卷屏 */ winsertln(curw); dispprevline("L"); } else curpos--; if ((curpos%m_conf.m_col==m_conf.m_col-1)&&(m_conf.m_lengh==1)) { revcurpos(); break; } else { nomlastpos(); revcurpos(); } } break; case KEY_UP: /* 上光标键 */ lastcurpos = curpos; lastscrpos = scrpos; mpos = scrpos; ; for(i=0; i/td>; if ( mpos->;prev != NULL ) mpos=mpos->;prev; else break; } if ( i==m_conf.m_col ) { getyx(curw, x, y); if (x==0) { lastcurpos += m_conf.m_col; /* 实现向下卷屏 */ winsertln(curw); dispprevline("U"); } else { curpos-=m_conf.m_col; } scrpos = mpos; if ( m_conf.m_lengh!=1) nomlastpos(); revcurpos(); } break; case KEY_DOWN: /* 下光标键 */ lastcurpos = curpos; lastscrpos = scrpos; mpos = scrpos; ; for(i=0; i/td>; if ( mpos->;next != NULL ) mpos=mpos->;next; else break; } if ( i==m_conf.m_col ) { getyx(curw, x, y); if (x==m_conf.m_lengh-1) { lastcurpos -= m_conf.m_col; /* 实现向上卷屏 */ wmove(curw, 0, 0); wdeleteln(curw); dispnextline("D"); } else curpos+=m_conf.m_col; scrpos = mpos; if ( m_conf.m_lengh!=1) nomlastpos(); revcurpos(); } break; default: beep(); break; } } } /* 反显当前项函数 */ void revcurpos() { wattrset(curw, A_STANDOUT); wmove(curw, curpos/m_conf.m_col, (curpos%m_conf.m_col)*m_conf.m_wight/m_conf.m_col+m_conf.m_col); wprintw(curw, "%s", scrpos->;item); wattrset(curw, A_NORMAL); wrefresh(boxwin); } /* 正常显示上一项函数 */ void nomlastpos() { wmove(curw, lastcurpos/m_conf.m_col, (lastcurpos%m_conf.m_col) *m_conf.m_wight/m_conf.m_col+m_conf.m_col); wprintw(curw, "%s", lastscrpos->;item); } /* 显示一页函数 */ void disponepage(first) struct menu *first; { short col, row;
begin=first; /* begin 为本页首指针 */ ; for(row=0; row/td>; ; for(col=0; col/td>; /* m_conf.m_wight/m_col为每一菜单项应占列数*/ wmove(curw,row,col*m_conf.m_wight/m_conf.m_col+m_conf.m_col); wprintw(curw, "%s", first->;item); wrefresh(curw); last = first; first = first->;next; if (first == NULL) { break; } } } } /* 显示上一行函数 */ void dispprevline(flag) char flag[2]; /* L-左光标引起 U-上光标引起 */ { struct menu *tmppos; int tmpcurpos;
tmpcurpos = curpos; tmppos = scrpos; if ( flag[0] == 'U') { while ( tmpcurpos % m_conf.m_col != 0) { tmppos = tmppos->;prev; tmpcurpos--; } tmppos = tmppos->;prev; } for (tmpcurpos = m_conf.m_col-1; tmpcurpos >;= 0; tmpcurpos--) { wmove(curw, 0, (tmpcurpos%m_conf.m_col) *m_conf.m_wight/m_conf.m_col+m_conf.m_col); wprintw(curw, "%s", tmppos->;item); begin = tmppos; /*begin 为本页首指针*/ last = tmppos; tmppos = tmppos->;prev; if (tmppos == NULL) break; } wrefresh(curw); } /* 显示下一行函数 */ void dispnextline(flag) char flag[2];/* R-右光标引起 D-下光标引起 */ { struct menu *tmppos; int tmpcurpos;
tmpcurpos = curpos; tmppos = scrpos; if ( flag[0] == 'D') { while ( tmpcurpos % m_conf.m_col != m_conf.m_col-1) { tmppos = tmppos->;next; tmpcurpos++; } tmppos = tmppos->;next; }
for (tmpcurpos = 0; tmpcurpos < m_conf.m_col; tmpcurpos++) { wmove(curw, m_conf.m_lengh-1, (tmpcurpos%m_conf.m_col) *m_conf.m_wight/m_conf.m_col+m_conf.m_col); wprintw(curw, "%s", tmppos->;item); last=tmppos;/* last 为本页最后一个结点指针 */ begin=tmppos; tmppos = tmppos->;next; if (tmppos == NULL) break; } } /* 取指定菜单参数函数 */ void getmenuconf(menu_code) short menu_code; { FILE *fp; char menu_buff[0x100];
if ((fp = fopen("menu.conf", "r"))==NULL) { fprintf(stderr, "can not open menu config file"); return; } while( fgets(menu_buff, 0x100, fp)!=NULL ) { get_m_conf(menu_buff); if (m_conf.menu_code == menu_code) break; } return ; } /* 取指定菜单参数处理函数 */ void get_m_conf(menu_conf) char *menu_conf; { register i, j, k; char buff[20];
j = k = 0; for (i = 0; i < strlen(menu_conf); i++) { if ( menu_conf[i] == '!' ) { j++; if ( j == 1) { k = i+1; continue; } switch(j) { case 2: memcpy(buff, &menu_conf[k], i-k); buff[i-k]=0; m_conf.menu_code = atoi(buff); k=i+1; break; case 3: memcpy(buff, &menu_conf[k], i-k); buff[i-k]=0; m_conf.last_code = atoi(buff); k=i+1; break; case 4: memcpy(buff, &menu_conf[k], i-k); buff[i-k]=0; m_conf.bord_flag = atoi(buff); k=i+1; break; case 5: memcpy(buff, &menu_conf[k], i-k); buff[i-k]=0; m_conf.m_wight = atoi(buff); k=i+1; break; case 6: memcpy(buff, &menu_conf[k], i-k); buff[i-k]=0; m_conf.m_lengh = atoi(buff); k=i+1; break; case 7: memcpy(buff, &menu_conf[k], i-k); buff[i-k]=0; m_conf.m_col = atoi(buff); k=i+1; break; case 8: memcpy(buff, &menu_conf[k], i-k); buff[i-k]=0; m_conf.m_bx = atoi(buff); k=i+1; break; case 9: memcpy(buff, &menu_conf[k], i-k); buff[i-k]=0; m_conf.m_by = atoi(buff); k=i+1; break; default: break; } } } } /* 取指定项参数处理函数 */ void get_m_item(menu_item) char *menu_item; { register i, j, k; char buff[80];
j = k = 0; for (i = 0; i < strlen(menu_item); i++) { if ( menu_item[i] == '!' ) { j++; if ( j == 1) { k = i+1; continue; } switch(j) { case 2: memcpy(buff, &menu_item[k], i-k); buff[i-k] = 0; m_item.menu_code = atoi(buff); k=i+1; break; case 3: memcpy(buff, &menu_item[k], i-k); buff[i-k] = 0; m_item.item_order = atoi(buff); k=i+1; break; case 4: memcpy(buff, &menu_item[k], i-k); buff[i-k] = 0; strcpy(m_item.item,buff); k=i+1; break; case 5: memcpy(buff, &menu_item[k], i-k); buff[i-k] = 0; strcpy(m_item.prog,buff); k=i+1; break; case 6: memcpy(buff, &menu_item[k], i-k); buff[i-k] = 0; m_item.submenu_code = atoi(buff); k=i+1; break; default: break; } } } } void initial() /* 自定开启 curses 函式 */ { initscr(); cbreak(); nonl(); noecho(); intrflush(stdscr,FALSE); keypad(stdscr,TRUE); refresh(); } /* 按键等待函数 */ void keycont() { fprintf(stderr, "按键继续..."); getchar(); } /* 运行可执行程序函数 */ void execprog() { system("clear"); fprintf(stderr, "%s: \n", scrpos->;item); system(scrpos->;prog); keycont(); initial(); touchwin(boxwin); touchwin(curw); keypad(curw, TRUE); wrefresh(boxwin); wrefresh(curw); } /* 清除窗口函数 */ void clearwin() { wmove(boxwin, 0, 0); wclrtobot(boxwin); wrefresh(boxwin); delwin(curw); delwin(boxwin); } 三 编译说明 该程序可用如下命令编译(在SCO OpenServer 5.05下编译通过): cc -o menu menu.c -lcurses |