在使用表单工作时,有时不希望使用new_field()来一个域一个域地建立表单,这样做不仅麻烦也不便与修改,每次画面变更之后还要重新编译。这里我们采用一个更加通用的方法--建立一个表单格式定义文件,使程序代码在每次调用表单时都读取这个文件,搜索指定表单,不用重新编译,就可以马上改变画面。 这个表单定义文件的格式是: 1001*0*联机签到 1001|0|000|柜 员:|12|24||...... 1001|0|001||12|32|8|...... 1001|0|002|密 码:|14|24||...... 1001|0|003||14|32|8|...... 1002*0*修改柜员密码 ...... 每个表单在此文件中有一个唯一的主画面号。比如1001等,几个副画面号如0 1 等,在定义时用*分隔;而表单中的每一个域都在此文件中定义,表单域的各特征项如所属主画面号、副画面号、序号、名称、位置、长度、是否标签域、是否密码域、是否返回、所用缓冲区号、对齐、类型等等都用|分隔。这些时建域时使用的。 要利用此文本文件建域,就需要一些工作: 首先以画面号、副画面号来读取文件中的域定义:
CLFIELDNODE **cl_readfields(hm, fhm) char *hm; char *fhm; {
int i; int cnt;
CLFIELDNODE **fnode;
huamian = (CLHMFORM *)malloc(sizeof(CLHMFORM)); memset(huamian, 0, sizeof(CLHMFORM));
if((cnt = cl_readhuamian(hm, fhm)) <= 0) return((CLFIELDNODE **)0); fnode = (CLFIELDNODE **)calloc(cnt + 1, sizeof(CLFIELDNODE)); for(i = 0; i < cnt; i++) { fnode[i] = cl_convdefine(huamian->slavelist[i]); }
fnode[cnt] = (CLFIELDNODE *)0; return(fnode);
} 注意,这里的huamian是一个全局量,定义为CLHMFORM类型指针,此类型在头文件中定义为: typedef struct _master { char hmlevel[1]; char hmname[20]; char hmtype[1]; CLSLAVE slavelist[MAX_FIELDS]; } CLHMFORM; CLSLAVE的定义是与画面定义文件中的域的每一行的项目一一对应,含有各域的各种特性。 cl_convdefine()函数转换从表单定义文件读取的域特性信息,他将CLSLAVE类型转换成可用于域属性设置及可用于set_field()和new_field()等函数的类型。CLFIELDNODE结构成员与CLSLAVE的成员一一对应,只不过成员的类型已经从字符数组变成了可用于建域的各种类型。 CLFIELDNODE *cl_convdefine(slave) CLSLAVE slave; {
static char *t = NULL;
CLFIELDNODE *data; data = (CLFIELDNODE *)malloc(sizeof(CLFIELDNODE)); memset(data, 0, sizeof(CLFIELDNODE));
t = strstr(slave.length, "."); data->pad = atoi(++t); t = strtok(slave.length, "."); data->length = atoi(t); data->id = atoi(slave.seri); data->y = atoi(slave.y); data->x = atoi(slave.x);
/* strncpy(data->name, ut_stripstring(slave.name, " "), 32); */ strncpy(data->name, slave.name, LABEL_MAX_LEN); data->buffnum = atoi(slave.bufnum);
switch(CONV_TYPE(slave.just)) { case 'C' : data->just = JUSTIFY_CENTER; break; case 'L' : data->just = JUSTIFY_LEFT; break; case 'R' : data->just = JUSTIFY_RIGHT; break; case 'N' : data->just = NO_JUSTIFICATION; break; default : data->just = NO_JUSTIFICATION; break;
}
data->ftype = _caseyn(slave.ftype); data->selectable = _caseyn(slave.selectable); data->inputflag = _caseyn(slave.inputflag); data->visible = _caseyn(slave.visible); data->passflag = _caseyn(slave.passflag); data->enable = _caseyn(slave.enable); data->autoskip = 0; data->fullflag = _caseyn(slave.fullflag); data->pageflag = _caseyn(slave.pageflag); data->recvflag = _caseyn(slave.recvflag); data->retuflag = _caseyn(slave.retuflag); data->retubuff = atoi(slave.retubuf);
strncpy(data->ddefault, slave.ddefault, LABEL_MAX_LEN); /* strncpy(data->ddefault, ut_stripstring(slave.ddefault, " "), 32); */
switch(CONV_TYPE(slave.dtype)) { case 'A' : switch(slave.dtype[1]) { case 'N' : data->dtype = 0; break; case 'L' : data->dtype = 2; break; case 'M' : data->dtype = 8; break; default : data->dtype = 0; break; } break;
case 'E' : data->dtype = 3; break; case 'P' : data->dtype = 4; break; case 'D' : if(slave.dtype[1] == 'I') data->dtype = 1; else data->dtype = 5; break; case 'R' : data->dtype = 6; break; case 'S' : data->dtype = 7; break; default : data->dtype = 0; break; }
return(data); }
cl_readhuamian()函数打开表单定义文件,用正规式和fgets()读取表单主、副画面号和域定义,他以主、副画面号为参数,利用读取带分隔符字符串的函数ut_readtext()将此画面的域定义读到全局量huamian。返回此表单画面的域数量。 int cl_readhuamian(h, f) char *h, *f; {
int p = 0; int cnt = 0; int *t_ps = &cnt;
static char hm[80]; static char tmpstr[8]; static char tstr[200];
FILE *pfp;
if(strcmp(f, " ")) sprintf(hm, FMT2, h, '*', f); else sprintf(hm, "grep \"^%s\\%c\\*\" %s | tail -1", h, '*', EFPATH);
memset(tstr, 0, 200); if(strcmp(f, " ")) sprintf(hm, FMT1, h, f); else sprintf(hm, FMT1, h, "|");
pfp = popen(hm, "r");
while(fgets(tstr, 180, pfp) != NULL) { p = 0; strcpy(tmpstr, ut_readtext(tstr, "|", &p)); strcpy(tmpstr, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].seri, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].name, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].y, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].x, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].length, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].ftype, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].bufnum, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].just, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].selectable, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].inputflag, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].fullflag, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].enable, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].visible, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].passflag, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].dtype, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].ddefault, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].pageflag, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].recvflag, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].retuflag, ut_readtext(tstr, "|", &p)); strcpy(huamian->slavelist[*t_ps].retubuf, ut_readtext(tstr, "|", &p)); (*t_ps)++; } pclose(pfp);
return((*t_ps));
}
读取了表单画面文件中的域定义内容之后,就经过转换就可以利用new_field()建立各域,利用前面讲的cl_dispform()和cl_inputdata()就可以显示表单、进入输入循环了。
cl_dispform()调用了cl_createform()。他接收两个参数:主画面号、副画面号,返回一个FORM结构指针。
FORM *cl_createform(hm, fhm) char *hm, *fhm; { static FORM *form; static int fldcnt;
fldcnt = cl_countfldnum(hm, fhm); if(fldcnt > 0) { form = new_form(cl_createfields(cl_readfields(hm,fhm), fldcnt)); return(form); }
/* if(form) return(form); */ else return((FORM *)0); }
先说明一下cl_countfldnum(),他的主要作用是返回一个整形变量来表示本画面共有多少个域。借助一个自增的整型指针。 int cl_countfldnum(h, f) char *h, *f; {
int cnt = 0; int *t_ps = &cnt;
static char hm[80]; static char tstr[200];
FILE *pfp;
if(strcmp(f, " ")) sprintf(hm, FMT2, h, '*', f); else sprintf(hm, "grep \"^%s\\%c\\*\" %s | tail -1", h, '*', EFPATH);
memset(tstr, 0, 200); if(strcmp(f, " ")) sprintf(hm, FMT1, h, f); else sprintf(hm, FMT1, h, "|");
pfp = popen(hm, "r");
while(fgets(tstr, 180, pfp) != NULL) { (*t_ps)++; } pclose(pfp);
return((*t_ps));
}
再来说明一下函数cl_createfields(),他利用CLFIELDNODE指针列表和本画面域数量建立各域:确定域类型,是否是标签,是调用cl_createlabel()否则cl_createfield()。他遍历CLFIELDNODE指针列表,一个节点、一个节点地检查,分别建立各域,最后返回FIELD指针数组给cl_createfields()建立表单。 FIELD **cl_createfields(nodelist, fieldcnt) CLFIELDNODE **nodelist; int fieldcnt; {
int i;
FIELD **workfld;
if(fieldcnt <= 0) return((FIELD **)0);
workfld = (FIELD **)calloc(fieldcnt + 1, sizeof(FIELD));
for(i = 0; i < fieldcnt; i++) { if(nodelist[i]->ftype) workfld[i] = cl_createfield(nodelist[i]); else workfld[i] = cl_createlabel(nodelist[i]); }
workfld[fieldcnt] = (FIELD *)0; return(workfld); }
FIELD *cl_createfield(no) CLFIELDNODE *no; {
FIELD *f; if(!no) return((FIELD *)0);
f = new_field(1, no->length, no->y, no->x, 0, no->buffnum); set_field_userptr(f, (void *)0); set_field_just(f, no->just); if(no->inputflag) { field_opts_off(f, O_ACTIVE); } switch(no->dtype) { case 0: break; case 1: set_field_type(f, TYPE_INTEGER, no->pad, -10000000, 10000000); break; case 2: set_field_type(f, TYPE_ALNUM, no->pad); break; case 3: set_field_type(f, TYPE_ENUM, (char **)0, FALSE, FALSE); break; case 5: /* set_field_type(f, TYPE_REGEXP, "(19|20)\d\d[- /.](0[[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"); set_field_type(f, TYPE_REGEXP, "[1-2][0-9][0-9][0-9](0[1-9]\r1[012])[0-3][0-9]"); */ set_field_type(f, TYPE_REGEXP, "[1-2][0-9][0-9][0-9][0-1][0-9][0-3][0-9]"); break; case 6: set_field_type(f, TYPE_REGEXP, "[a-zA-Z,<>/\\~0-9]"); break; case 7: break; case 8: set_field_type(f, TYPE_NUMERIC, no->pad, -10000000, 10000000); break; default : break; } if(f) return(f); else return((FIELD *)0); } FIELD *cl_createlabel(no) CLFIELDNODE *no; { FIELD *f;
int len; if(!no) return((FIELD *)0);
if(!no->length) len = strlen(no->name); else len = no->length; f = new_field(1, len, no->y, no->x, 0, 0); if(f) { set_field_buffer(f, 0, no->name); set_field_just(f, no->just); field_opts_off(f, O_ACTIVE); return(f); } else return((FIELD *)0); }
cl_createfield()函数传递一个参数:CLFIELDNODE指针no,然后调用new_field()利用CLFIELDNODE的域信息建立新域,调用set_field_userptr()、set_field_just()、set_field_type()等调正域属性,返回域指针。 而cl_createlable()就比较简单,他的参数与返回值与cl_createfield()一样,他利用域的坐标建立一个域,然后调用 set_field_buffer(f, 0, no->name); set_field_just(f, no->just); set_opts_off(f, O_ACTIVE); 来使他显示标签名称,光标不在其上停留。
这几个函数之间的关系:
+-----------------------------------------+ |cl_dispform() | |->cl_createform() | | |->cl_countfldnum() | | |->cl_createfields() | | |->cl_readfields() | | |->cl_readhuamian() | | |->cl_convdefine() | | |->cl_createfield() | | |->cl_createlabel() | | |->new_field() | +-----------------------------------------+
顺便说以下数据输入函数cl_inputdata(),这个函数在显示表单后,等待用户输入数据,他和cl_dispform()一起构成了用户的主界面。他的返回值现在有两个:一个是F4一个是ESC。ESC取消F4提交。他的主要参数是一个WINDOW指针,以确定表单窗口,一个FORM指针,即用户表单,由cl_dispform()返回,一个数据缓冲区,保存用户数据,一个32位掩码,对应32个域,以确定那些域可以返回以填充数据缓冲区。 int cl_inputdata(win, form, imask, idata) WINDOW *win; FORM *form; unsigned long imask; char *idata; { int rc; int finish = 0; int fldidx; int fldcnt; int rows, cols, frow, fcol, nrow, nbuf;
FIELD **flist = (FIELD **)0;
fldcnt = field_count(form); cl_setcurrent(win);
cl_outputdata(win, form, imask, idata, 1); memset(idata, 0, DATA_DEFAU_LEN);
while(!finish) { switch(form_driver(form, rc = cl_virtualize(form, workwin))) { case E_OK: break; case E_UNKNOWN_COMMAND : switch(cl_getrequest(rc)) { case 9: finish = 9; break; case 4: finish = 4; form_driver(form, REQ_VALIDATION); flist = form_fields(form); for(fldidx = 0; fldidx < fldcnt; fldidx++) { if(ut_getbits(imask, fldidx)) { if(field_info(flist[fldidx], &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK && nbuf > 0) { strcat(idata, field_buffer(flist[fldidx], 1)); strcat(idata, "|"); } else { strcat(idata, field_buffer(flist[fldidx], 0)); strcat(idata, "|"); } } } break; default : finish = 0; break; } /* switch */ break; default : /* ------------------------if using this then form input no loop --------------- finish = -1; */ break; } /* switch */ } /* while */ return(rc); } 这里的cl_outputdata()用于显示用户数据缓冲区的原始内容,ut_getbit()是筛选掩码用的函数,看哪位是1,如该位为1,则使用 该位代表的域数据。然后看是否是密码域,是则使用1号域缓冲区,否则使用默认域缓冲区。
| |