/* Modern implementation of UIFC (user interface) library based on uifc.c */ // vi: tabstop=4 /* $Id: uifc32.c,v 1.268 2020/08/16 20:37:08 rswindell Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * * Copyright Rob Swindell - http://www.synchro.net/copyright.html * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * See the GNU Lesser General Public License for more details: lgpl.txt or * * http://www.fsf.org/copyleft/lesser.html * * * * Anonymous FTP access to the most recent released source is available at * * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * * * * Anonymous CVS access to the development source and modification history * * is available at cvs.synchro.net:/cvsroot/sbbs, example: * * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * * (just hit return, no password is necessary) * * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * * You are encouraged to submit any modifications (preferably in Unix diff * * format) via e-mail to mods@synchro.net * * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ #ifdef __unix__ #include #include #ifdef __QNX__ #include #endif #define mswait(x) delay(x) #elif defined(_WIN32) #include #include #define mswait(x) Sleep(x) #endif #include // for alloca() #include // localtime_r() #include "xpprintf.h" #include "ciolib.h" #include "uifc.h" #define MAX_GETSTR 5120 #define BLINK 128 static int cursor; static char* helpfile=0; static uint helpline=0; static size_t blk_scrn_len; static struct vmem_cell *blk_scrn; static struct vmem_cell *tmp_buffer; static struct vmem_cell *tmp_buffer2; static win_t sav[MAX_BUFS]; static uifcapi_t* api; /* Prototypes */ static int uprintf(int x, int y, unsigned attr, char *fmt,...); static void bottomline(int line); static char *utimestr(time_t *intime); static void help(void); static int ugetstr(int left, int top, int width, char *outstr, int max, long mode, int *lastkey); static void timedisplay(BOOL force); /* API routines */ static void uifcbail(void); static int uscrn(const char *str); static int ulist(int mode, int left, int top, int width, int *dflt, int *bar ,const char *title, char **option); static int uinput(int imode, int left, int top, const char *prompt, char *str ,int len ,int kmode); static int umsg(const char *str); static int umsgf(char *fmt, ...); static BOOL confirm(char *fmt, ...); static BOOL deny(char *fmt, ...); static void upop(const char *str); static void sethelp(int line, char* file); static void showbuf(int mode, int left, int top, int width, int height, const char *title , const char *hbuf, int *curp, int *barp); /* Dynamic menu support */ static int *last_menu_cur=NULL; static int *last_menu_bar=NULL; static int save_menu_cur=-1; static int save_menu_bar=-1; static int save_menu_opts=-1; char* uifcYesNoOpts[]={"Yes","No",NULL}; static void reset_dynamic(void) { last_menu_cur=NULL; last_menu_bar=NULL; save_menu_cur=-1; save_menu_bar=-1; save_menu_opts=-1; } static uifc_graphics_t cp437_chars = { .background=0xb0, .help_char='?', .close_char=0xfe, .up_arrow=30, .down_arrow=31, .button_left='[', .button_right=']', .list_top_left=0xc9, .list_top=0xcd, .list_top_right=0xbb, .list_separator_left=0xcc, .list_separator_right=0xb9, .list_horizontal_separator=0xcd, .list_left=0xba, .list_right=0xba, .list_bottom_left=0xc8, .list_bottom_right=0xbc, .list_bottom=0xcd, .list_scrollbar_separator=0xb3, .input_top_left=0xc9, .input_top=0xcd, .input_top_right=0xbb, .input_left=0xba, .input_right=0xba, .input_bottom_left=0xc8, .input_bottom_right=0xbc, .input_bottom=0xcd, .popup_top_left=0xda, .popup_top=0xc4, .popup_top_right=0xbf, .popup_left=0xb3, .popup_right=0xb3, .popup_bottom_left=0xc0, .popup_bottom_right=0xd9, .popup_bottom=0xc4, .help_top_left=0xda, .help_top=0xc4, .help_top_right=0xbf, .help_left=0xb3, .help_right=0xb3, .help_bottom_left=0xc0, .help_bottom_right=0xd9, .help_bottom=0xc4, .help_titlebreak_left=0xb4, .help_titlebreak_right=0xc3, .help_hitanykey_left=0xb4, .help_hitanykey_right=0xc3, }; /****************************************************************************/ /* Initialization function, see uifc.h for details. */ /* Returns 0 on success. */ /****************************************************************************/ void uifc_mouse_enable(void) { ciomouse_setevents(0); ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_START); ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_MOVE); ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_END); ciomouse_addevent(CIOLIB_BUTTON_1_CLICK); ciomouse_addevent(CIOLIB_BUTTON_2_CLICK); ciomouse_addevent(CIOLIB_BUTTON_3_CLICK); ciomouse_addevent(CIOLIB_BUTTON_4_PRESS); ciomouse_addevent(CIOLIB_BUTTON_5_PRESS); mousepointer(CIOLIB_MOUSEPTR_BAR); showmouse(); } void uifc_mouse_disable(void) { ciomouse_setevents(0); hidemouse(); } int kbwait(void) { int timeout=0; while(timeout++<50) { if(kbhit()) return(TRUE); mswait(1); } return(FALSE); } int inkey(void) { int c; c=getch(); if(!c || c==0xe0) c|=(getch()<<8); return(c); } int uifcini32(uifcapi_t* uifcapi) { unsigned i; struct text_info txtinfo; if(uifcapi==NULL || uifcapi->size!=sizeof(uifcapi_t)) return(-1); api=uifcapi; if (api->chars == NULL) api->chars = &cp437_chars; if (api->yesNoOpts == NULL) api->yesNoOpts = uifcYesNoOpts; /* install function handlers */ api->bail=uifcbail; api->scrn=uscrn; api->msg=umsg; api->msgf=umsgf; api->confirm=confirm; api->deny=deny; api->pop=upop; api->list=ulist; api->input=uinput; api->sethelp=sethelp; api->showhelp=help; api->showbuf=showbuf; api->timedisplay=timedisplay; api->bottomline=bottomline; api->getstrxy=ugetstr; api->printf=uprintf; if(api->scrn_len!=0) { switch(api->scrn_len) { case 14: textmode(C80X14); break; case 21: textmode(C80X21); break; case 25: textmode(C80); break; case 28: textmode(C80X28); break; case 43: textmode(C80X43); break; case 50: textmode(C80X50); break; case 60: textmode(C80X60); break; default: textmode(C4350); break; } } #if 0 clrscr(); #endif gettextinfo(&txtinfo); /* unsupported mode? */ if(txtinfo.screenheightMAX_LINES */ || txtinfo.screenwidth<40) { textmode(C80); /* set mode to 80x25*/ gettextinfo(&txtinfo); } window(1,1,txtinfo.screenwidth,txtinfo.screenheight); api->scrn_len=txtinfo.screenheight; if(api->scrn_lenscrn_len,MIN_LINES); return(-2); } api->scrn_len--; /* account for status line */ if(txtinfo.screenwidth<40) { uifcbail(); printf("\r\nUIFC: Screen width (%u) must be at least 40 characters\r\n" ,txtinfo.screenwidth); return(-3); } api->scrn_width=txtinfo.screenwidth; if(!(api->mode&UIFC_COLOR) && (api->mode&UIFC_MONO || txtinfo.currmode==MONO || txtinfo.currmode==BW40 || txtinfo.currmode==BW80 || txtinfo.currmode==MONO14 || txtinfo.currmode==BW40X14 || txtinfo.currmode==BW80X14 || txtinfo.currmode==MONO21 || txtinfo.currmode==BW40X21 || txtinfo.currmode==BW80X21 || txtinfo.currmode==MONO28 || txtinfo.currmode==BW40X28 || txtinfo.currmode==BW80X28 || txtinfo.currmode==MONO43 || txtinfo.currmode==BW40X43 || txtinfo.currmode==BW80X43 || txtinfo.currmode==MONO50 || txtinfo.currmode==BW40X50 || txtinfo.currmode==BW80X50 || txtinfo.currmode==MONO60 || txtinfo.currmode==BW40X60 || txtinfo.currmode==BW80X60 || txtinfo.currmode==ATARI_40X24 || txtinfo.currmode==ATARI_80X25)) { api->bclr=BLACK; api->hclr=WHITE; api->lclr=LIGHTGRAY; api->cclr=LIGHTGRAY; api->lbclr=BLACK|(LIGHTGRAY<<4); /* lightbar color */ } else { api->bclr=BLUE; api->hclr=YELLOW; api->lclr=WHITE; api->cclr=CYAN; api->lbclr=BLUE|(LIGHTGRAY<<4); /* lightbar color */ } blk_scrn_len=api->scrn_width*api->scrn_len; if((blk_scrn=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL) { cprintf("UIFC line %d: error allocating %u bytes." ,__LINE__,blk_scrn_len * sizeof(*blk_scrn)); return(-1); } if((tmp_buffer=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL) { cprintf("UIFC line %d: error allocating %u bytes." ,__LINE__,blk_scrn_len * sizeof(*blk_scrn)); return(-1); } if((tmp_buffer2=(struct vmem_cell *)malloc(blk_scrn_len * sizeof(*blk_scrn)))==NULL) { cprintf("UIFC line %d: error allocating %u bytes." ,__LINE__,blk_scrn_len * sizeof(*blk_scrn)); return(-1); } for(i=0;icclr|(api->bclr<<4); blk_scrn[i].ch = api->chars->background; blk_scrn[i].font = 0; attr2palette(blk_scrn[i].legacy_attr, &blk_scrn[i].fg, &blk_scrn[i].bg); } cursor=_NOCURSOR; _setcursortype(cursor); if(cio_api.mouse && !(api->mode&UIFC_NOMOUSE)) { api->mode|=UIFC_MOUSE; uifc_mouse_enable(); } /* A esc_delay of less than 10 is stupid... silently override */ if(api->esc_delay < 10) api->esc_delay=25; if(cio_api.escdelay) *(cio_api.escdelay)=api->esc_delay; for(i=0; isavnum=0; api->initialized=TRUE; return(0); } void docopy(void) { int key; struct mouse_event mevent; struct ciolib_screen *screen; struct ciolib_screen *sbuffer; int sbufsize; int x,y,startx,starty,endx,endy,lines; int outpos; char *copybuf; uint32_t bg0; uint32_t bg1; uint32_t bg6; uint32_t fg8; attr2palette(0x08, &fg8, &bg0); attr2palette(0x10, NULL, &bg1); attr2palette(0x60, NULL, &bg6); screen=ciolib_savescreen(); sbuffer=ciolib_savescreen(); freepixels(sbuffer->pixels); sbuffer->pixels = NULL; sbufsize=screen->text_info.screenwidth * screen->text_info.screenheight * sizeof(screen->vmem[0]); while(1) { key=getch(); if(key==0 || key==0xe0) key|=getch()<<8; switch(key) { case CIO_KEY_MOUSE: getmouse(&mevent); if(mevent.startxvmem,screen->vmem,sbufsize); for(y=starty-1;yscrn_width+x; if (sbuffer->vmem[pos].bg != bg1) sbuffer->vmem[pos].bg = bg1; else sbuffer->vmem[pos].bg = bg6; if (sbuffer->vmem[pos].bg == sbuffer->vmem[pos].fg) attr2palette(sbuffer->vmem[pos].legacy_attr | 0x08, &sbuffer->vmem[pos].fg, &sbuffer->vmem[pos].bg); if ((sbuffer->vmem[pos].legacy_attr & 0x70) != 0x10) sbuffer->vmem[pos].legacy_attr = (sbuffer->vmem[pos].legacy_attr & 0x8f) | 0x10; else sbuffer->vmem[pos].legacy_attr = (sbuffer->vmem[pos].legacy_attr & 0x8f) | 0x60; if (((sbuffer->vmem[pos].legacy_attr & 0x70) >> 4) == (sbuffer->vmem[pos].legacy_attr & 0x08)) sbuffer->vmem[pos].legacy_attr |= 0x08; } } restorescreen(sbuffer); break; case CIOLIB_BUTTON_1_DRAG_END: lines=abs(mevent.endy-mevent.starty)+1; copybuf=malloc(((endy-starty+1)*(endx-startx+1)+1+lines*2)*4); outpos=0; for(y=starty-1;yvmem[(y*api->scrn_width+x)].ch ? screen->vmem[(y*api->scrn_width+x)].ch : ' '; utf8str = cp_to_utf8(conio_fontdata[screen->vmem[(y*api->scrn_width+x)].font].cp, &ch, 1, &outlen); if (utf8str == NULL) continue; memcpy(copybuf + outpos, utf8str, outlen); outpos += outlen; } #ifdef _WIN32 copybuf[outpos++]='\r'; #endif copybuf[outpos++]='\n'; } copybuf[outpos]=0; copytext(copybuf, strlen(copybuf)); free(copybuf); restorescreen(screen); freescreen(screen); freescreen(sbuffer); return; } break; default: restorescreen(screen); freescreen(screen); freescreen(sbuffer); ungetch(key); return; } } } static int uifc_getmouse(struct mouse_event *mevent) { mevent->startx=0; mevent->starty=0; mevent->event=0; if(api->mode&UIFC_MOUSE) { getmouse(mevent); if(mevent->event==CIOLIB_BUTTON_3_CLICK) return(ESC); if(mevent->event==CIOLIB_BUTTON_1_DRAG_START) { docopy(); return(0); } if(mevent->starty==api->buttony) { if(mevent->startx>=api->exitstart && mevent->startx<=api->exitend && mevent->event==CIOLIB_BUTTON_1_CLICK) { return(ESC); } if(mevent->startx>=api->helpstart && mevent->startx<=api->helpend && mevent->event==CIOLIB_BUTTON_1_CLICK) { return(CIO_KEY_F(1)); } } if (mevent->event == CIOLIB_BUTTON_4_PRESS) return(CIO_KEY_UP); if (mevent->event == CIOLIB_BUTTON_5_PRESS) return(CIO_KEY_DOWN); return(0); } return(-1); } void uifcbail(void) { int i; _setcursortype(_NORMALCURSOR); textattr(LIGHTGRAY); uifc_mouse_disable(); suspendciolib(); FREE_AND_NULL(blk_scrn); FREE_AND_NULL(tmp_buffer); FREE_AND_NULL(tmp_buffer2); api->initialized=FALSE; for(i=0; i< MAX_BUFS; i++) FREE_AND_NULL(sav[i].buf); } /****************************************************************************/ /* Clear screen, fill with background attribute, display application title. */ /* Returns 0 on success. */ /****************************************************************************/ int uscrn(const char *str) { textattr(api->bclr|(api->cclr<<4)); gotoxy(1,1); clreol(); gotoxy(3,1); cputs(str); if(!vmem_puttext(1,2,api->scrn_width,api->scrn_len,blk_scrn)) return(-1); gotoxy(1,api->scrn_len+1); clreol(); reset_dynamic(); setname(str); return(0); } /****************************************************************************/ /****************************************************************************/ static void scroll_text(int x1, int y1, int x2, int y2, int down) { vmem_gettext(x1,y1,x2,y2,tmp_buffer2); if(down) vmem_puttext(x1,y1+1,x2,y2,tmp_buffer2); else vmem_puttext(x1,y1,x2,y2-1,tmp_buffer2+(((x2-x1)+1))); } /****************************************************************************/ /* Updates time in upper left corner of screen with current time in ASCII/ */ /* Unix format */ /****************************************************************************/ static void timedisplay(BOOL force) { static time_t savetime; static int savemin; time_t now; struct tm gm; int old_hold; now=time(NULL); localtime_r(&now, &gm); if(force || savemin != gm.tm_min || difftime(now,savetime)>=60) { old_hold=hold_update; hold_update=FALSE; uprintf(api->scrn_width-25,1,api->bclr|(api->cclr<<4),utimestr(&now)); hold_update=old_hold; savetime=now; savemin = gm.tm_min; } } /****************************************************************************/ /* Truncates white-space chars off end of 'str' */ /****************************************************************************/ static void truncspctrl(char *str) { uint c; c=strlen(str); while(c && (uchar)str[c-1]<=' ') c--; if(str[c]!=0) /* don't write to string constants */ str[c]=0; } static void inactive_win(struct vmem_cell *buf, int left, int top, int right, int bottom, int y, int hbrdrsize, uchar cclr, uchar lclr, uchar hclr, int btop) { int width = right - left + 1; int height = bottom - top + 1; int i, j; vmem_gettext(left, top, right, bottom, buf); for (i=0; i < (width * height); i++) set_vmem_attr(&buf[i], lclr | (cclr<<4)); j=(((y-btop)*width))+3+((width-hbrdrsize-2)); for(i=(((y-btop)*width))+3;iscrn_len-3; int hbrdrsize=2; int lbrdrwidth=1; int rbrdrwidth=1; int vbrdrsize=4; int tbrdrwidth=3; int bbrdrwidth=1; int title_len; int tmpcur=0; struct mouse_event mevnt; char *title=NULL; int a,b,c,longopt; int optheight=0; int gotkey; uchar hclr,lclr,bclr,cclr,lbclr; if(cur==NULL) cur=&tmpcur; api->exit_flags = 0; hclr=api->hclr; lclr=api->lclr; bclr=api->bclr; cclr=api->cclr; lbclr=api->lbclr; if(mode & WIN_INACT) { bclr=api->cclr; hclr=api->lclr; lclr=api->lclr; cclr=api->cclr; lbclr=(api->cclr<<4)|api->hclr; } title=strdup(initial_title==NULL?"":initial_title); if(!(api->mode&UIFC_NHM)) uifc_mouse_disable(); title_len=strlen(title); if(mode&WIN_FAT) { s_top=1; s_left=2; s_right=api->scrn_width-3; /* Leave space for the shadow */ s_bottom=api->scrn_len-1; /* Leave one for the shadow */ } if(mode&WIN_NOBRDR) { hbrdrsize=0; vbrdrsize=0; lbrdrwidth=0; rbrdrwidth=0; tbrdrwidth=0; bbrdrwidth=0; } /* Count the options */ while (option != NULL && opts < MAX_OPTS) { if (option[opts] == NULL || option[opts][0] == 0) break; else opts++; } if (mode&WIN_XTR && optssavnum>=MAX_BUFS-1) putch(7); api->help_available = (api->helpbuf!=NULL || api->helpixbfile[0]!=0); /* Create the status bar/bottom-line */ int bline = mode; if (api->bottomline != NULL) { if ((mode&(WIN_XTR | WIN_PASTEXTR)) == WIN_XTR && (*cur) == opts - 1) api->bottomline(bline & ~WIN_PASTE); else api->bottomline(bline); } optheight=opts+vbrdrsize; height=optheight; if(mode&WIN_FIXEDHEIGHT) { height=api->list_height; } if(top+height>s_bottom) height=(s_bottom)-top; if(optheight>height) optheight=height; if(!width || widthwidth) width=j; } } } /* Determine minimum widths here to accommodate mouse "icons" in border */ if(!(mode&WIN_NOBRDR) && api->mode&UIFC_MOUSE) { if(api->help_available && width<8) width=8; else if(width<5) width=5; } if(width>(s_right+1)-s_left) { width=(s_right+1)-s_left; if(title_len>(width-hbrdrsize-2)) { *(title+width-hbrdrsize-2-3)='.'; *(title+width-hbrdrsize-2-2)='.'; *(title+width-hbrdrsize-2-1)='.'; *(title+width-hbrdrsize-2)=0; title_len=strlen(title); } } if(mode&WIN_L2R) left=(s_right-s_left-width+1)/2; else if(mode&WIN_RHT) left=s_right-(width+hbrdrsize+2+left); if(mode&WIN_T2B) top=(api->scrn_len-height+1)/2-2; else if(mode&WIN_BOT) top=s_bottom-height-top; if(left<0) left=0; if(top<0) top=0; /* Dynamic Menus */ if(mode&WIN_DYN && cur != NULL && bar != NULL && last_menu_cur==cur && last_menu_bar==bar && save_menu_cur==*cur && save_menu_bar==*bar && save_menu_opts==opts) { is_redraw=1; } if(mode&WIN_DYN && mode&WIN_REDRAW) is_redraw=1; if(mode&WIN_DYN && mode&WIN_NODRAW) is_redraw=0; if(mode&WIN_ORG && !(mode&WIN_SAV)) { /* Clear all save buffers on WIN_ORG */ for(i=0; i< MAX_BUFS; i++) FREE_AND_NULL(sav[i].buf); api->savnum=0; } if(mode&WIN_SAV) { /* Check if this screen (by cur/bar) is already saved */ for(i=0; isavnum-1; j>i; j--) { /* Restore old screens */ vmem_puttext(sav[j].left,sav[j].top,sav[j].right,sav[j].bot ,(void *)sav[j].buf); /* put original window back */ FREE_AND_NULL(sav[j].buf); } api->savnum=i; } } } /* savnum not the next one - must be a dynamic window or we popped back up the stack */ if(sav[api->savnum].buf != NULL) { /* Is this even the right window? */ if(sav[api->savnum].cur==cur && sav[api->savnum].bar==bar) { if((sav[api->savnum].left!=s_left+left || sav[api->savnum].top!=s_top+top || sav[api->savnum].right!=s_left+left+width+1 || sav[api->savnum].bot!=s_top+top+height)) { /* dimensions have changed */ vmem_puttext(sav[api->savnum].left,sav[api->savnum].top,sav[api->savnum].right,sav[api->savnum].bot ,(void *)sav[api->savnum].buf); /* put original window back */ FREE_AND_NULL(sav[api->savnum].buf); if ((sav[api->savnum].buf = malloc((width + 3) * (height + 2) * sizeof(struct vmem_cell)))==NULL) { cprintf("UIFC line %d: error allocating %u bytes." ,__LINE__,(width+3)*(height+2)*sizeof(struct vmem_cell)); free(title); if(!(api->mode&UIFC_NHM)) uifc_mouse_enable(); return(-1); } vmem_gettext(s_left+left,s_top+top,s_left+left+width+1 ,s_top+top+height,(void *)sav[api->savnum].buf); /* save again */ sav[api->savnum].left=s_left+left; sav[api->savnum].top=s_top+top; sav[api->savnum].right=s_left+left+width+1; sav[api->savnum].bot=s_top+top+height; sav[api->savnum].cur=cur; sav[api->savnum].bar=bar; } } else { /* Find something available... */ while(sav[api->savnum].buf!=NULL) api->savnum++; } } else { if((sav[api->savnum].buf=malloc((width+3)*(height+2)*sizeof(struct vmem_cell)))==NULL) { cprintf("UIFC line %d: error allocating %u bytes." ,__LINE__,(width+3)*(height+2)*sizeof(struct vmem_cell)); free(title); if(!(api->mode&UIFC_NHM)) uifc_mouse_enable(); return(-1); } vmem_gettext(s_left+left,s_top+top,s_left+left+width+1 ,s_top+top+height,(void *)sav[api->savnum].buf); sav[api->savnum].left=s_left+left; sav[api->savnum].top=s_top+top; sav[api->savnum].right=s_left+left+width+1; sav[api->savnum].bot=s_top+top+height; sav[api->savnum].cur=cur; sav[api->savnum].bar=bar; } } if(!is_redraw) { if(mode&WIN_ORG) { /* Clear around menu */ if(top) vmem_puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn); if((unsigned)(s_top+height+top)<=api->scrn_len) vmem_puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn); if(left) vmem_puttext(1,s_top+top,s_left+left-1,s_top+height+top ,blk_scrn); if(s_left+left+width<=s_right) vmem_puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width ,s_top+height+top,blk_scrn); } ptr=tmp_buffer; if(!(mode&WIN_NOBRDR)) { set_vmem(ptr++, api->chars->list_top_left, hclr|(bclr<<4), 0); if(api->mode&UIFC_MOUSE) { set_vmem(ptr++, api->chars->button_left, hclr|(bclr<<4), 0); /* *(ptr++)='þ'; */ set_vmem(ptr++, api->chars->close_char, lclr|(bclr<<4), 0); set_vmem(ptr++, api->chars->button_right, hclr|(bclr<<4), 0); i=3; if(api->help_available) { set_vmem(ptr++, api->chars->button_left, hclr|(bclr<<4), 0); set_vmem(ptr++, api->chars->help_char, lclr|(bclr<<4), 0); set_vmem(ptr++, api->chars->button_right, hclr|(bclr<<4), 0); i+=3; } api->buttony=s_top+top; api->exitstart=s_left+left+1; api->exitend=s_left+left+3; api->helpstart=s_left+left+4; api->helpend=s_left+left+6; } else i=0; for(;ichars->list_top, hclr|(bclr<<4), 0); set_vmem(ptr++, api->chars->list_top_right, hclr|(bclr<<4), 0); set_vmem(ptr++, api->chars->list_left, hclr|(bclr<<4), 0); a=title_len; b=(width-a-1)/2; for(i=0;ichars->list_right, hclr|(bclr<<4), 0); set_vmem(ptr++, api->chars->list_separator_left, hclr|(bclr<<4), 0); for(i=0;ichars->list_horizontal_separator, hclr|(bclr<<4), 0); set_vmem(ptr++, api->chars->list_separator_right, hclr|(bclr<<4), 0); } if((*cur)>=opts) (*cur)=opts-1; /* returned after scrolled */ if(!bar) { if((*cur)>height-vbrdrsize-1) (*cur)=height-vbrdrsize-1; if((*cur)>opts-1) (*cur)=opts-1; i=0; } else { if((*bar)>=opts) (*bar)=opts-1; if((*bar)>height-vbrdrsize-1) (*bar)=height-vbrdrsize-1; if((*cur)==opts-1) (*bar)=height-vbrdrsize-1; if((*bar)>opts-1) (*bar)=opts-1; if((*bar)<0) (*bar)=0; i=(*cur)-(*bar); if (i < 0) { *bar += i; i = 0; } if(i+(height-vbrdrsize-1)>=opts) { (*bar)=(height-vbrdrsize); if (*bar > *cur) *bar = *cur; i=(*cur)-(*bar)+1; } } if((*cur)<0) (*cur)=0; j=0; if(i<0) i=0; longopt=0; while(jchars->list_left, hclr|(bclr<<4), 0); set_vmem(ptr++, ' ', hclr|(bclr<<4), 0); set_vmem(ptr++, api->chars->list_scrollbar_separator, lclr|(bclr<<4), 0); if(i==(*cur)) a=lbclr; else a=lclr|(bclr<<4); if(ilongopt) longopt=b; if(b+hbrdrsize+2>width) b=width-hbrdrsize-2; for(c=0;cchars->list_right, hclr|(bclr<<4), 0); i++; j++; } if(!(mode&WIN_NOBRDR)) { set_vmem(ptr++, api->chars->list_bottom_left, hclr|(bclr<<4), 0); for(i=0;ichars->list_bottom, hclr|(bclr<<4), 0); set_vmem(ptr, api->chars->list_bottom_right, hclr|(bclr<<4), 0); /* Not incremented to shut up BCC */ } vmem_puttext(s_left+left,s_top+top,s_left+left+width-1 ,s_top+top+height-1,tmp_buffer); if(bar) y=top+tbrdrwidth+(*bar); else y=top+tbrdrwidth+(*cur); if(opts+vbrdrsize>height && ((!bar && (*cur)!=opts-1) || (bar && ((*cur)-(*bar))+(height-vbrdrsize)chars->down_arrow); /* put down arrow */ textattr(hclr|(bclr<<4)); } if(bar && (*bar)!=(*cur)) { gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth); textattr(lclr|(bclr<<4)); putch(api->chars->up_arrow); /* put the up arrow */ textattr(hclr|(bclr<<4)); } if(!(mode&WIN_NOBRDR)) { /* Shadow */ if(api->bclr==BLUE) { vmem_gettext(s_left+left+width,s_top+top+1,s_left+left+width+1 ,s_top+top+height-1,shade); for(i=0;ilongopt) longopt=b; if(b+hbrdrsize+2>width) b=width-hbrdrsize-2; for(c=0;cmode&UIFC_NHM)) uifc_mouse_enable(); if(mode&WIN_IMM) { return(-2); } if(mode&WIN_ORG) { if(api->timedisplay != NULL) api->timedisplay(/* force? */TRUE); } while(1) { #if 0 /* debug */ struct text_info txtinfo; gettextinfo(&txtinfo); gotoxy(30,1); cprintf("y=%2d h=%2d c=%2d b=%2d s=%2d o=%2d w=%d/%d h=%d/%d" ,y,height,*cur,bar ? *bar :0xff,api->savnum,opts,txtinfo.screenwidth, api->scrn_width, txtinfo.screenheight, api->scrn_len); #endif if(api->timedisplay != NULL) api->timedisplay(/* force? */FALSE); gotkey=0; textattr(((api->lbclr)&0x0f)|((api->lbclr >> 4)&0x0f)); gotoxy(s_left+lbrdrwidth+2+left, s_top+y); if((api->exit_flags & UIFC_XF_QUIT) || kbwait() || (mode&(WIN_POP|WIN_SEL))) { if(api->exit_flags & UIFC_XF_QUIT) gotkey = CIO_KEY_QUIT; else if(mode&WIN_POP) gotkey=ESC; else if(mode&WIN_SEL) gotkey=CR; else gotkey=inkey(); if(gotkey==CIO_KEY_MOUSE) { if((gotkey=uifc_getmouse(&mevnt))==0) { /* Clicked in menu */ if(mevnt.startx>=s_left+left+lbrdrwidth+2 && mevnt.startx<=s_left+left+width-rbrdrwidth-1 && mevnt.starty>=s_top+top+tbrdrwidth && mevnt.starty<=(s_top+top+optheight)-bbrdrwidth-1 && mevnt.event==CIOLIB_BUTTON_1_CLICK) { (*cur)=((mevnt.starty)-(s_top+top+tbrdrwidth))+(*cur+(top+tbrdrwidth-y)); if(bar) (*bar)=(*cur); y=top+tbrdrwidth+((mevnt.starty)-(s_top+top+tbrdrwidth)); if(!opts) continue; if(mode&WIN_SAV) api->savnum++; if(mode&WIN_ACT) { if(!(api->mode&UIFC_NHM)) uifc_mouse_disable(); if((win=malloc((width+3)*(height+2)*sizeof(*win)))==NULL) { cprintf("UIFC line %d: error allocating %u bytes." ,__LINE__,(width+3)*(height+2)*sizeof(*win)); if(!(api->mode&UIFC_NHM)) uifc_mouse_enable(); return(-1); } inactive_win(win, s_left+left, s_top+top, s_left+left+width-1, s_top+top+height-1, y, hbrdrsize, cclr, lclr, hclr, top); free(win); if(!(api->mode&UIFC_NHM)) uifc_mouse_enable(); } else if(mode&WIN_SAV) { api->savnum--; if(!(api->mode&UIFC_NHM)) uifc_mouse_disable(); if(sav[api->savnum].buf != NULL) vmem_puttext(sav[api->savnum].left,sav[api->savnum].top ,sav[api->savnum].right,sav[api->savnum].bot ,(void *)sav[api->savnum].buf); if(!(api->mode&UIFC_NHM)) uifc_mouse_enable(); FREE_AND_NULL(sav[api->savnum].buf); } if(mode&WIN_XTR && (*cur)==opts-1) return(MSK_INS|*cur); return(*cur); } /* Clicked Scroll Up */ else if(mevnt.startx==s_left+left+lbrdrwidth && mevnt.starty==s_top+top+tbrdrwidth && mevnt.event==CIOLIB_BUTTON_1_CLICK) { gotkey=CIO_KEY_PPAGE; } /* Clicked Scroll Down */ else if(mevnt.startx==s_left+left+lbrdrwidth && mevnt.starty==(s_top+top+height)-bbrdrwidth-1 && mevnt.event==CIOLIB_BUTTON_1_CLICK) { gotkey=CIO_KEY_NPAGE; } /* Clicked Outside of Window */ else if((mevnt.startxs_left+left+width-1 || mevnt.startys_top+top+height-1) && (mevnt.event==CIOLIB_BUTTON_1_CLICK || mevnt.event==CIOLIB_BUTTON_3_CLICK)) { if(mode&WIN_UNGETMOUSE) { ungetmouse(&mevnt); gotkey=CIO_KEY_MOUSE; } else { gotkey=ESC; } } } } /* For compatibility with terminals lacking special keys */ switch(gotkey) { case '\b': gotkey=ESC; break; case '+': gotkey=CIO_KEY_IC; /* insert */ break; case '-': case DEL: gotkey=CIO_KEY_DC; /* delete */ break; case CTRL_B: if(!(api->mode&UIFC_NOCTRL)) gotkey=CIO_KEY_HOME; break; case CTRL_C: if(!(api->mode&UIFC_NOCTRL)) gotkey=CIO_KEY_F(5); /* copy */ break; case CTRL_D: if(!(api->mode&UIFC_NOCTRL)) gotkey=CIO_KEY_NPAGE; break; case CTRL_E: if(!(api->mode&UIFC_NOCTRL)) gotkey=CIO_KEY_END; break; case CTRL_U: if(!(api->mode&UIFC_NOCTRL)) gotkey=CIO_KEY_PPAGE; break; case CTRL_V: if(!(api->mode&UIFC_NOCTRL)) gotkey=CIO_KEY_F(6); /* paste */ break; case CTRL_X: if(!(api->mode&UIFC_NOCTRL)) gotkey=CIO_KEY_SHIFT_DC; /* cut */ break; case CTRL_Z: if(!(api->mode&UIFC_NOCTRL)) gotkey=CIO_KEY_F(1); /* help */ break; case CIO_KEY_ABORTED: gotkey=ESC; break; case CIO_KEY_QUIT: api->exit_flags |= UIFC_XF_QUIT; gotkey=ESC; break; } if(gotkey>255) { s=0; switch(gotkey) { /* ToDo extended keys */ case CIO_KEY_HOME: /* home */ if(!opts) break; if(opts+vbrdrsize>optheight) { gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth); textattr(lclr|(bclr<<4)); putch(' '); /* Delete the up arrow */ gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1); putch(api->chars->down_arrow); /* put the down arrow */ uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth ,lbclr ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[0]); for(i=1;ioptheight) { gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth); /* like end */ textattr(lclr|(bclr<<4)); putch(api->chars->up_arrow); /* put the up arrow */ gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1); putch(' '); /* delete the down arrow */ for(i=(opts+vbrdrsize)-optheight,j=0;ichars->down_arrow); /* put the dn arrow */ } y++; scroll_text(s_left+left+lbrdrwidth+1,s_top+top+tbrdrwidth ,s_left+left+width-rbrdrwidth-1,s_top+top+height-bbrdrwidth-1,1); uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth ,lbclr ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[*cur]); } else { vmem_gettext(s_left+lbrdrwidth+2+left,s_top+y ,s_left+left+width-rbrdrwidth-1,s_top+y,line); for(i=0;ichars->up_arrow); /* put the up arrow */ else putch(' '); /* delete the up arrow */ gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1); if(opts >= height-tbrdrwidth && *cur + height - vbrdrsize < opts) putch(api->chars->down_arrow); /* put the down arrow */ else putch(' '); /* delete the down arrow */ for(i=*cur,j=0;i<=*cur-vbrdrsize-1+optheight;i++,j++) uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth+j ,i==*cur ? lbclr : lclr|(bclr<<4) ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[i]); break; case CIO_KEY_NPAGE: /* PgDn */ if(!opts) break; *cur += (height-vbrdrsize-1); if(*cur>opts-1) *cur = opts-1; if(bar) *bar = optheight-vbrdrsize-1; y=top+optheight-bbrdrwidth-1; gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth); textattr(lclr|(bclr<<4)); if(*cur>height-vbrdrsize-1) /* Scroll mode */ putch(api->chars->up_arrow); /* put the up arrow */ else putch(' '); /* delete the up arrow */ gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1); if(*cur < opts-1) putch(api->chars->down_arrow); /* put the down arrow */ else putch(' '); /* delete the down arrow */ for(i=*cur+vbrdrsize+1-optheight,j=0;i<=*cur;i++,j++) uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth+j ,i==*cur ? lbclr : lclr|(bclr<<4) ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[i]); break; case CIO_KEY_END: /* end */ if(!opts) break; if(opts+vbrdrsize>height) { /* Scroll mode */ gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth); textattr(lclr|(bclr<<4)); putch(api->chars->up_arrow); /* put the up arrow */ gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1); putch(' '); /* delete the down arrow */ for(i=(opts+vbrdrsize)-height,j=0;iheight) { /* like home */ gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth); textattr(lclr|(bclr<<4)); putch(' '); /* Delete the up arrow */ gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1); putch(api->chars->down_arrow); /* put the down arrow */ uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth ,lbclr ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[0]); for(i=1;ichars->up_arrow); /* put the up arrow */ } y--; /* gotoxy(1,1); cprintf("\rdebug: %4d ",__LINE__); */ scroll_text(s_left+left+lbrdrwidth+1,s_top+top+tbrdrwidth ,s_left+left+width-rbrdrwidth-1,s_top+top+height-bbrdrwidth-1,0); /* gotoxy(1,1); cprintf("\rdebug: %4d ",__LINE__); */ uprintf(s_left+left+lbrdrwidth+2,s_top+top+height-bbrdrwidth-1 ,lbclr ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[*cur]); } else { vmem_gettext(s_left+lbrdrwidth+2+left,s_top+y ,s_left+left+width-rbrdrwidth-1,s_top+y ,line); for(i=0; i < width; i++) set_vmem_attr(&line[i], lbclr); vmem_puttext(s_left+lbrdrwidth+2+left,s_top+y ,s_left+left+width-rbrdrwidth-1,s_top+y ,line); } break; case CIO_KEY_F(1): /* F1 - Help */ { struct vmem_cell *save = malloc(width*height*sizeof(*save)); if(save != NULL) { vmem_gettext(s_left+left,s_top+top,s_left +left+width-1,s_top+top+height-1,save); struct vmem_cell *copy = malloc(width*height*sizeof(*copy)); if(copy != NULL) { inactive_win(copy, s_left+left, s_top+top, s_left+left+width-1, s_top+top+height-1, y, hbrdrsize, cclr, lclr, hclr, top); free(copy); } } api->showhelp(); if(save != NULL) { vmem_puttext(s_left+left,s_top+top,s_left +left+width-1,s_top+top+height-1,save); free(save); } break; } case CIO_KEY_F(2): /* F2 - Edit */ if(mode&WIN_XTR && (*cur)==opts-1) /* can't edit */ break; /* extra line */ if(mode&WIN_EDIT) { if(mode&WIN_SAV) api->savnum++; if(mode&WIN_ACT) inactive_win(tmp_buffer, s_left+left, s_top+top, s_left+left+width-1, s_top+top+height-1, y, hbrdrsize, cclr, lclr, hclr, top); else if(mode&WIN_SAV) { api->savnum--; if(sav[api->savnum].buf != NULL) vmem_puttext(sav[api->savnum].left,sav[api->savnum].top ,sav[api->savnum].right,sav[api->savnum].bot ,(void *)sav[api->savnum].buf); FREE_AND_NULL(sav[api->savnum].buf); } return((*cur)|MSK_EDIT); } break; case CIO_KEY_F(5): /* F5 - Copy */ case CIO_KEY_CTRL_IC: /* Ctrl-Insert */ if(mode&WIN_COPY && !(mode&WIN_XTR && (*cur)==opts-1)) return((*cur) | MSK_COPY); break; case CIO_KEY_SHIFT_DC: /* Shift-Del: Cut */ if(mode&WIN_CUT && !(mode&WIN_XTR && (*cur) == opts - 1)) return((*cur) | MSK_CUT); break; case CIO_KEY_SHIFT_IC: /* Shift-Insert: Paste */ case CIO_KEY_F(6): /* F6 - Paste */ if(mode&WIN_PASTE && (mode&WIN_PASTEXTR || !(mode&WIN_XTR && (*cur)==opts-1))) return((*cur)|MSK_PASTE); break; case CIO_KEY_IC: /* insert */ if(mode&WIN_INS) { if(mode&WIN_SAV) api->savnum++; if(mode&WIN_INSACT) inactive_win(tmp_buffer, s_left+left,s_top+top,s_left+left+width-1,s_top+top+height-1,y, hbrdrsize, cclr, lclr, hclr, top); else if(mode&WIN_SAV) { api->savnum--; if(sav[api->savnum].buf != NULL) vmem_puttext(sav[api->savnum].left,sav[api->savnum].top ,sav[api->savnum].right,sav[api->savnum].bot ,(void *)sav[api->savnum].buf); FREE_AND_NULL(sav[api->savnum].buf); } if(!opts) { return(MSK_INS); } return((*cur)|MSK_INS); } break; case CIO_KEY_DC: /* delete */ if(mode&WIN_XTR && (*cur)==opts-1) /* can't delete */ break; /* extra line */ if(mode&WIN_DEL) { if(mode&WIN_SAV) api->savnum++; if(mode&WIN_DELACT) inactive_win(tmp_buffer, s_left+left, s_top+top, s_left+left+width-1, s_top+top+height-1, y, hbrdrsize, cclr, lclr, hclr, top); else if(mode&WIN_SAV) { api->savnum--; if (sav[api->savnum].buf != NULL) vmem_puttext(sav[api->savnum].left,sav[api->savnum].top ,sav[api->savnum].right,sav[api->savnum].bot ,(void *)sav[api->savnum].buf); FREE_AND_NULL(sav[api->savnum].buf); } return((*cur)|MSK_DEL); } break; default: if(mode&WIN_EXTKEYS) return(-2-gotkey); break; } } else { gotkey&=0xff; if(isalnum(gotkey) && opts>1 && option[0][0]) { search[s]=gotkey; search[s+1]=0; for(j=(*cur)+1,a=b=0;a<2;j++) { /* a = search count */ if(j==opts) { /* j = option count */ j=-1; /* b = letter count */ continue; } if(j==(*cur)) { b++; continue; } if(b>=longopt) { b=0; a++; } if(a==1 && !s) break; if(option[j]!=NULL && strlen(option[j])>(size_t)b && ((!a && s && !strnicmp(option[j]+b,search,s+1)) || ((a || !s) && toupper(option[j][b])==toupper(gotkey)))) { if(a) s=0; else s++; if(y+(j-(*cur))+2>height+top) { (*cur)=j; gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth); textattr(lclr|(bclr<<4)); putch(api->chars->up_arrow); /* put the up arrow */ if((*cur)==opts-1) { gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1); putch(' '); /* delete the down arrow */ } for(i=((*cur)+vbrdrsize+1)-height,j=0;i<(*cur)+1;i++,j++) uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth+j ,i==(*cur) ? lbclr : lclr|(bclr<<4) ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[i]); y=top+height-bbrdrwidth-1; if(bar) (*bar)=optheight-vbrdrsize-1; break; } if(y-((*cur)-j)chars->down_arrow); /* put the down arrow */ uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth ,lbclr ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[(*cur)]); for(i=1;ij) y-=(*cur)-j; else y+=j-(*cur); if(bar) { if((*cur)>j) (*bar)-=(*cur)-j; else (*bar)+=j-(*cur); } (*cur)=j; vmem_gettext(s_left+lbrdrwidth+2+left,s_top+y ,s_left+left+width-rbrdrwidth-1,s_top+y,line); for(i=0; i < width; i++) set_vmem_attr(&line[i], lbclr); vmem_puttext(s_left+lbrdrwidth+2+left,s_top+y ,s_left+left+width-rbrdrwidth-1,s_top+y,line); break; } } if(a==2) s=0; } else switch(gotkey) { case CR: if(!opts) break; if(mode&WIN_SAV) api->savnum++; if(mode&WIN_ACT) inactive_win(tmp_buffer, s_left+left, s_top+top, s_left+left+width-1, s_top+top+height-1, y, hbrdrsize, cclr, lclr, hclr, top); else if(mode&WIN_SAV) { api->savnum--; if (sav[api->savnum].buf != NULL) vmem_puttext(sav[api->savnum].left,sav[api->savnum].top ,sav[api->savnum].right,sav[api->savnum].bot ,(void *)sav[api->savnum].buf); FREE_AND_NULL(sav[api->savnum].buf); } if(mode&WIN_XTR && (*cur)==opts-1) return(MSK_INS|*cur); return(*cur); case 3: case ESC: if(mode&WIN_SAV) api->savnum++; if(mode&WIN_ESC || (mode&WIN_CHE && api->changes)) { vmem_gettext(s_left+left,s_top+top,s_left +left+width-1,s_top+top+height-1,tmp_buffer); for(i=0; i < (width * height);i++) set_vmem_attr(&tmp_buffer[i], lclr|(cclr<<4)); vmem_puttext(s_left+left,s_top+top,s_left +left+width-1,s_top+top+height-1,tmp_buffer); } else if(mode&WIN_SAV) { api->savnum--; if (sav[api->savnum].buf != NULL) vmem_puttext(sav[api->savnum].left,sav[api->savnum].top ,sav[api->savnum].right,sav[api->savnum].bot ,(void *)sav[api->savnum].buf); FREE_AND_NULL(sav[api->savnum].buf); } return(-1); case CTRL_F: /* find */ case CTRL_G: if(/*!(api->mode&UIFC_NOCTRL)*/1) { // No no, *this* control key is fine! if (gotkey == CTRL_G || api->input(WIN_MID|WIN_SAV, 0, 0, "Find", search, sizeof(search), K_EDIT|K_FIND) > 0) { for (j = (*cur) + 1; j != *cur; j++, j = (j >= opts) ? 0 : j) { if (option[j] == NULL || j >= opts) continue; if (strcasestr(option[j], search) != NULL) { // Copy/pasted from search above. if(y+(j-(*cur))+2>height+top) { (*cur)=j; gotoxy(s_left+left+lbrdrwidth,s_top+top+tbrdrwidth); textattr(lclr|(bclr<<4)); putch(api->chars->up_arrow); /* put the up arrow */ if((*cur)==opts-1) { gotoxy(s_left+left+lbrdrwidth,s_top+top+height-bbrdrwidth-1); putch(' '); /* delete the down arrow */ } for(i=((*cur)+vbrdrsize+1)-height,j=0;i<(*cur)+1;i++,j++) uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth+j ,i==(*cur) ? lbclr : lclr|(bclr<<4) ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[i]); y=top+height-bbrdrwidth-1; if(bar) (*bar)=optheight-vbrdrsize-1; break; } if(y-((*cur)-j)chars->down_arrow); /* put the down arrow */ uprintf(s_left+left+lbrdrwidth+2,s_top+top+tbrdrwidth ,lbclr ,"%-*.*s",width-hbrdrsize-2,width-hbrdrsize-2,option[(*cur)]); for(i=1;ij) y-=(*cur)-j; else y+=j-(*cur); if(bar) { if((*cur)>j) (*bar)-=(*cur)-j; else (*bar)+=j-(*cur); } (*cur)=j; vmem_gettext(s_left+lbrdrwidth+2+left,s_top+y ,s_left+left+width-rbrdrwidth-1,s_top+y,line); for(i=0; i < width; i++) set_vmem_attr(&line[i], lbclr); vmem_puttext(s_left+lbrdrwidth+2+left,s_top+y ,s_left+left+width-rbrdrwidth-1,s_top+y,line); break; } } } } break; default: if(mode&WIN_EXTKEYS) return(-2-gotkey); } } /* Update the status bar to reflect the Put/Paste option applicability */ if (bline&WIN_PASTE && api->bottomline != NULL) { if ((mode&(WIN_XTR | WIN_PASTEXTR)) == WIN_XTR && (*cur) == opts - 1) api->bottomline(bline & ~WIN_PASTE); else api->bottomline(bline); } } else mswait(1); if(mode&WIN_DYN) { save_menu_cur=*cur; save_menu_bar=*bar; save_menu_opts=opts; return(-2-gotkey); } } } /*************************************************************************/ /* This function is a windowed input string input routine. */ /*************************************************************************/ int uinput(int mode, int left, int top, const char *inprompt, char *str, int max, int kmode) { struct vmem_cell shade[MAX_COLS], save_buf[MAX_COLS*4], in_win[MAX_COLS*3]; int width; int height=3; int i,plen,slen,j; int iwidth; int l; char *prompt; int s_top=SCRN_TOP; int s_left=SCRN_LEFT; int s_right=SCRN_RIGHT; int s_bottom=api->scrn_len-3; int hbrdrsize=2; int tbrdrwidth=1; reset_dynamic(); if(mode&WIN_FAT) { s_top=1; s_left=2; s_right=api->scrn_width-3; /* Leave space for the shadow */ } if(mode&WIN_NOBRDR) { hbrdrsize=0; tbrdrwidth=0; height=1; } prompt=strdup(inprompt==NULL ? "":inprompt); plen=strlen(prompt); if(!plen) slen=2+hbrdrsize; else slen=4+hbrdrsize; width=plen+slen+max; if(width>(s_right-s_left+1)) width=(s_right-s_left+1); if(mode&WIN_T2B) top=(api->scrn_len-height+1)/2-2; else if(mode&WIN_BOT) top=s_bottom-height-top; if(mode&WIN_L2R) left=(s_right-s_left-width+1)/2; if(left<=-(s_left)) left=-(s_left)+1; if(top<0) top=0; if(mode&WIN_SAV) vmem_gettext(s_left+left,s_top+top,s_left+left+width+1 ,s_top+top+height,save_buf); if(mode&WIN_ORG) { /* Clear around menu */ if(top) vmem_puttext(1,2,api->scrn_width,s_top+top-1,blk_scrn); if((unsigned)(s_top+height+top)<=api->scrn_len) vmem_puttext(1,s_top+height+top,api->scrn_width,api->scrn_len,blk_scrn); if(left) vmem_puttext(1,s_top+top,s_left+left-1,s_top+height+top ,blk_scrn); if(s_left+left+width<=s_right) vmem_puttext(s_left+left+width,s_top+top,/* s_right+2 */api->scrn_width ,s_top+height+top,blk_scrn); } iwidth=width-plen-slen; while(iwidth<1 && plen>4) { plen=strlen(prompt); prompt[plen-1]=0; prompt[plen-2]='.'; prompt[plen-3]='.'; prompt[plen-4]='.'; plen--; iwidth=width-plen-slen; } i=0; if(!(mode&WIN_NOBRDR)) { set_vmem(&in_win[i++], api->chars->input_top_left, api->hclr|(api->bclr<<4), 0); for(j=1;jchars->input_top, api->hclr|(api->bclr<<4), 0); if(api->mode&UIFC_MOUSE && width>6) { j=1; set_vmem(&in_win[j++], api->chars->button_left, api->hclr|(api->bclr<<4), 0); /* in_win[4]='þ'; */ set_vmem(&in_win[j++], api->chars->close_char, api->lclr|(api->bclr<<4), 0); set_vmem(&in_win[j++], api->chars->button_right, api->hclr|(api->bclr<<4), 0); l=3; if(api->helpbuf!=NULL || api->helpixbfile[0]!=0) { set_vmem(&in_win[j++], api->chars->button_left, api->hclr|(api->bclr<<4), 0); set_vmem(&in_win[j++], api->chars->help_char, api->lclr|(api->bclr<<4), 0); set_vmem(&in_win[j++], api->chars->button_right, api->hclr|(api->bclr<<4), 0); l+=3; } api->buttony=s_top+top; api->exitstart=s_left+left+1; api->exitend=s_left+left+3; api->helpstart=s_left+left+4; api->helpend=s_left+left+l; } set_vmem(&in_win[i++], api->chars->input_top_right, api->hclr|(api->bclr<<4), 0); set_vmem(&in_win[i++], api->chars->input_right, api->hclr|(api->bclr<<4), 0); } if(plen) set_vmem(&in_win[i++], ' ', api->lclr|(api->bclr<<4), 0); for(j=0;prompt[j];j++) set_vmem(&in_win[i++], prompt[j], api->lclr|(api->bclr<<4), 0); if(plen) set_vmem(&in_win[i++], ':', api->lclr|(api->bclr<<4), 0); for(j=0;jlclr|(api->bclr<<4), 0); } if(!(mode&WIN_NOBRDR)) { set_vmem(&in_win[i++], api->chars->input_right, api->hclr|(api->bclr<<4), 0); set_vmem(&in_win[i++], api->chars->input_bottom_left, api->hclr|(api->bclr<<4), 0); for(j=1;jchars->input_bottom, api->hclr|(api->bclr<<4), 0); set_vmem(&in_win[i], api->chars->input_bottom_right, api->hclr|(api->bclr<<4), 0); /* I is not incremented to shut up BCC */ } vmem_puttext(s_left+left,s_top+top,s_left+left+width-1 ,s_top+top+height-1,in_win); if(!(mode&WIN_NOBRDR)) { /* Shadow */ if(api->bclr==BLUE) { vmem_gettext(s_left+left+width,s_top+top+1,s_left+left+width+1 ,s_top+top+(height-1),shade); for(j=0;j<6;j++) set_vmem_attr(&shade[j], DARKGRAY); vmem_puttext(s_left+left+width,s_top+top+1,s_left+left+width+1 ,s_top+top+(height-1),shade); vmem_gettext(s_left+left+2,s_top+top+3,s_left+left+width+1 ,s_top+top+height,shade); for(j=0;jbottomline != NULL) api->bottomline(WIN_COPY|WIN_CUT|WIN_PASTE); textattr(api->lclr|(api->bclr<<4)); if(!plen) i=ugetstr(s_left+left+2,s_top+top+tbrdrwidth,iwidth,str,max,kmode,NULL); else i=ugetstr(s_left+left+plen+4,s_top+top+tbrdrwidth,iwidth,str,max,kmode,NULL); if(mode&WIN_SAV) vmem_puttext(s_left+left,s_top+top,s_left+left+width+1 ,s_top+top+height,save_buf); free(prompt); return(i); } /****************************************************************************/ /* Displays the message 'str' and waits for the user to select "OK" */ /****************************************************************************/ int umsg(const char *str) { int i=0; char *ok[2]={"OK",""}; if(api->mode&UIFC_INMSG) /* non-cursive */ return -1; api->mode|=UIFC_INMSG; i = ulist(WIN_SAV|WIN_MID,0,0,0,&i,0,str,ok); api->mode&=~UIFC_INMSG; return i; } /* Same as above, using printf-style varargs */ int umsgf(char* fmt, ...) { int retval = -1; va_list va; char* buf = NULL; va_start(va, fmt); vasprintf(&buf, fmt, va); va_end(va); if(buf != NULL) { retval = umsg(buf); free(buf); } return retval; } static int yesno(int dflt, char* fmt, va_list va) { int retval; char* buf = NULL; vasprintf(&buf, fmt, va); if(buf == NULL) return dflt; retval = ulist(WIN_SAV|WIN_MID,0,0,0,&dflt,0,buf,api->yesNoOpts); free(buf); return retval; } static BOOL confirm(char* fmt, ...) { int retval; va_list va; va_start(va, fmt); retval = yesno(0, fmt, va); va_end(va); return retval == 0; } static BOOL deny(char* fmt, ...) { int retval; va_list va; va_start(va, fmt); retval = yesno(1, fmt, va); va_end(va); return retval != 0; } /***************************************/ /* Private sub - updates a ugetstr box */ /***************************************/ void getstrupd(int left, int top, int width, char *outstr, int cursoffset, int *scrnoffset, int mode) { _setcursortype(_NOCURSOR); if(cursoffset<*scrnoffset) *scrnoffset=cursoffset; if(*scrnoffset+width < cursoffset) *scrnoffset=cursoffset-width; gotoxy(left,top); if(mode&K_PASSWORD) // This typecast is to suppress a clang warning "adding 'unsigned long' to a string does not append to the string [-Wstring-plus-int]" cprintf("%-*.*s",width,width,((char *)"********************************************************************************")+(80-strlen(outstr+*scrnoffset))); else cprintf("%-*.*s",width,width,outstr+*scrnoffset); gotoxy(left+(cursoffset-*scrnoffset),top); _setcursortype(cursor); } /****************************************************************************/ /* Gets a string of characters from the user. Turns cursor on. Allows */ /* Different modes - K_* macros. ESC aborts input. */ /****************************************************************************/ int ugetstr(int left, int top, int width, char *outstr, int max, long mode, int *lastkey) { char *str; int ch; int i,j,k,f=0; /* i=offset, j=length */ BOOL gotdecimal=FALSE; int soffset=0; struct mouse_event mevnt; char *pastebuf=NULL; unsigned char *pb=NULL; api->exit_flags = 0; if((str=alloca(max+1))==NULL) { cprintf("UIFC line %d: error allocating %u bytes\r\n" ,__LINE__,(max+1)); _setcursortype(cursor); return(-1); } gotoxy(left,top); _setcursortype(cursor = api->insert_mode ? _SOLIDCURSOR : _NORMALCURSOR); str[0]=0; if(mode&K_EDIT && outstr[0]) { /*** truncspctrl(outstr); ***/ outstr[max]=0; i=j=strlen(outstr); textattr(api->lbclr); getstrupd(left, top, width, outstr, i, &soffset, mode); textattr(api->lclr|(api->bclr<<4)); if(strlen(outstr)<(size_t)width) { k=wherex(); f=wherey(); cprintf("%*s",width-strlen(outstr),""); gotoxy(k,f); } strcpy(str,outstr); #if 0 while(kbwait()==0) { mswait(1); } #endif f=inkey(); if(f==CIO_KEY_QUIT) { api->exit_flags |= UIFC_XF_QUIT; return -1; } if(f==CIO_KEY_MOUSE) { f=uifc_getmouse(&mevnt); if(f==0 || (f==ESC && mevnt.event==CIOLIB_BUTTON_3_CLICK)) { if(mode & K_MOUSEEXIT && (mevnt.starty != top || mevnt.startx > left+width || mevnt.startx < left) && mevnt.event==CIOLIB_BUTTON_1_CLICK) { if(lastkey) *lastkey=CIO_KEY_MOUSE; ungetmouse(&mevnt); return(j); } if(mevnt.startx>=left && mevnt.startx<=left+width && mevnt.event==CIOLIB_BUTTON_1_CLICK) { i=mevnt.startx-left+soffset; if(i>j) i=j; } if(mevnt.starty == top && mevnt.startx>=left && mevnt.startx<=left+width && (mevnt.event==CIOLIB_BUTTON_2_CLICK || mevnt.event==CIOLIB_BUTTON_3_CLICK)) { i=mevnt.startx-left+soffset; if(i>j) i=j; pb=(uint8_t *)getcliptext(); if (pb) { pastebuf = utf8_to_cp(CIOLIB_CP437, pb, '?', strlen((char *)pb), NULL); free(pb); pb=(unsigned char *)pastebuf; } else { free(pastebuf); pastebuf = NULL; } f=0; } } } if(f == CR || (f >= 0xff && f != CIO_KEY_DC) || (f == 3840 && mode&K_TABEXIT) // Backtab || (f == '\t' && mode&K_TABEXIT) || (f == '%' && mode&K_SCANNING) || f==CTRL_B || f==CTRL_E || f==CTRL_Z || f==CTRL_X /* Cut */ || f==CTRL_C /* Copy */ || f==0) { getstrupd(left, top, width, str, i, &soffset, mode); } else { getstrupd(left, top, width, str, i, &soffset, mode); i=j=0; } } else i=j=0; ch=0; while(ch!=CR) { if(i>j) j=i; str[j]=0; getstrupd(left, top, width, str, i, &soffset, mode); if(f || pb!=NULL || (ch=inkey())!=0) { if(f) { ch=f; f=0; } else if(pb!=NULL) { ch=*(pb++); if(!*pb) { free(pastebuf); pastebuf=NULL; pb=NULL; } } if(ch==CIO_KEY_MOUSE) { ch=uifc_getmouse(&mevnt); if(ch==0 || (ch==ESC && mevnt.event==CIOLIB_BUTTON_3_CLICK)) { if(mode & K_MOUSEEXIT && (mevnt.starty != top || mevnt.startx > left+width || mevnt.startx < left) && mevnt.event==CIOLIB_BUTTON_1_CLICK) { if(lastkey) *lastkey=CIO_KEY_MOUSE; ungetmouse(&mevnt); ch=CR; continue; } if(mevnt.starty == top && mevnt.startx>=left && mevnt.startx<=left+width && mevnt.event==CIOLIB_BUTTON_1_CLICK) { i=mevnt.startx-left+soffset; if(i>j) i=j; } if(mevnt.starty == top && mevnt.startx>=left && mevnt.startx<=left+width && (mevnt.event==CIOLIB_BUTTON_2_CLICK || mevnt.event==CIOLIB_BUTTON_3_CLICK)) { i=mevnt.startx-left+soffset; if(i>j) i=j; pb=(uint8_t *)getcliptext(); if (pb) { pastebuf = utf8_to_cp(CIOLIB_CP437, pb, '?', strlen((char *)pb), NULL); free(pb); pb=(unsigned char *)pastebuf; } else { free(pastebuf); pastebuf = NULL; } ch=0; } } } if(lastkey != NULL) *lastkey=ch; switch(ch) { case CTRL_Z: case CIO_KEY_F(1): /* F1 Help */ api->showhelp(); if(api->exit_flags & UIFC_XF_QUIT) f = CIO_KEY_QUIT; continue; case CIO_KEY_LEFT: /* left arrow */ if(i) { i--; } continue; case CIO_KEY_RIGHT: /* right arrow */ if(iinsert_mode = !api->insert_mode; _setcursortype(cursor = api->insert_mode ? _SOLIDCURSOR : _NORMALCURSOR); continue; case BS: if(i) { if(i==j) { j--; i--; } else { i--; j--; if(str[i]=='.') gotdecimal=FALSE; for(k=i;k<=j;k++) str[k]=str[k+1]; } if (soffset > 0) soffset--; continue; } break; case CIO_KEY_DC: /* delete */ case DEL: /* sdl_getch() is returning 127 when keypad "Del" is hit */ if(iexit_flags |= UIFC_XF_QUIT; /* Fall-through */ case CIO_KEY_ABORTED: case ESC: { cursor=_NOCURSOR; _setcursortype(cursor); if(pastebuf!=NULL) { free(pastebuf); pastebuf = NULL; } return(-1); } case CR: break; case 3840: /* Backtab */ case '\t': if(mode&K_TABEXIT) ch=CR; break; case '%': /* '%' indicates that a UPC is coming next */ if(mode&K_SCANNING) ch=CR; break; case CIO_KEY_F(2): case CIO_KEY_UP: case CIO_KEY_DOWN: if(mode&K_DEUCEEXIT) { ch=CR; break; } continue; case CTRL_C: case CIO_KEY_CTRL_IC: /* Ctrl-Insert */ { size_t sz; uint8_t *utf8 = cp_to_utf8(CIOLIB_CP437, str, j, &sz); copytext((char *)utf8, sz); free(utf8); continue; } case CTRL_X: case CIO_KEY_SHIFT_DC: if(j) { size_t sz; uint8_t *utf8 = cp_to_utf8(CIOLIB_CP437, str, j, &sz); copytext((char *)utf8, sz); free(utf8); i=j=0; } continue; case CTRL_Y: if(i=' ' || (ch==1 && mode&K_MSG)) && iinsert_mode || jinsert_mode) { for(k=++j;k>i;k--) str[k]=str[k-1]; } str[i++]=ch; } } } str[j]=0; if(mode&K_EDIT) { truncspctrl(str); if(!(mode&K_FIND) && strcmp(outstr,str)) api->changes=1; } else { if(!(mode&K_FIND) && j) api->changes=1; } strcpy(outstr,str); cursor=_NOCURSOR; _setcursortype(cursor); if(pastebuf!=NULL) free(pastebuf); return(j); } /****************************************************************************/ /* Performs printf() through puttext() routine */ /****************************************************************************/ static int uprintf(int x, int y, unsigned attr, char *fmat, ...) { va_list argptr; char str[MAX_COLS+1]; struct vmem_cell buf[MAX_COLS]; int i; va_start(argptr,fmat); vsprintf(str,fmat,argptr); va_end(argptr); for(i = 0; str[i]; i++) set_vmem(&buf[i], str[i], attr, 0); vmem_puttext(x,y,x+(i-1),y,buf); return(i); } /****************************************************************************/ /* Display bottom line of screen in inverse */ /****************************************************************************/ void bottomline(int mode) { int i=1; i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4)," "); if(api->help_available) { i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"F1 "); i += uprintf(i,api->scrn_len+1,BLACK|(api->cclr<<4),"Help "); } if(mode&WIN_EDIT) { i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"F2 "); i += uprintf(i,api->scrn_len+1,BLACK|(api->cclr<<4),"Edit Item "); } if(mode&WIN_COPY) { if(api->mode&UIFC_NOCTRL) { i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"F5 "); i += uprintf(i,api->scrn_len+1,BLACK|(api->cclr<<4),"Copy "); } else { i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"^C"); i += uprintf(i,api->scrn_len+1,BLACK|(api->cclr<<4),"opy "); } } if(mode&WIN_CUT) { if(api->mode&UIFC_NOCTRL) i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"Shift-DEL "); else i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"^X "); i += uprintf(i,api->scrn_len+1,BLACK|(api->cclr<<4),"Cut "); } if(mode&WIN_PASTE) { if(api->mode&UIFC_NOCTRL) i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"F6 "); else i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"^V "); i += uprintf(i,api->scrn_len+1,BLACK|(api->cclr<<4),"Paste "); } if(mode&WIN_INS) { #ifdef __DARWIN__ i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"+/"); #endif i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"INS"); i += uprintf(i,api->scrn_len+1,BLACK|(api->cclr<<4),"ert Item "); } if(mode&WIN_DEL) { #ifdef __DARWIN__ i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"fn-"); #endif i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"DEL"); i += uprintf(i,api->scrn_len+1,BLACK|(api->cclr<<4),"ete Item "); } i += uprintf(i,api->scrn_len+1,api->bclr|(api->cclr<<4),"ESC "); /* Backspace is no good no way to abort editing */ i += uprintf(i,api->scrn_len+1,BLACK|(api->cclr<<4),"Exit"); gotoxy(i,api->scrn_len+1); if (wherex() == i && wherey() == api->scrn_len+1) { textattr(BLACK|(api->cclr<<4)); clreol(); } } /*****************************************************************************/ /* Generates a 24 character ASCII string that represents the time_t pointer */ /* Used as a replacement for ctime() */ /*****************************************************************************/ char *utimestr(time_t *intime) { static char str[25]; const char* wday=""; const char* mon=""; const char* mer= ""; int hour; struct tm *gm; gm=localtime(intime); switch(gm->tm_wday) { case 0: wday = "Sun"; break; case 1: wday = "Mon"; break; case 2: wday = "Tue"; break; case 3: wday = "Wed"; break; case 4: wday = "Thu"; break; case 5: wday = "Fri"; break; case 6: wday = "Sat"; break; } switch(gm->tm_mon) { case 0: mon = "Jan"; break; case 1: mon = "Feb"; break; case 2: mon = "Mar"; break; case 3: mon = "Apr"; break; case 4: mon = "May"; break; case 5: mon = "Jun"; break; case 6: mon = "Jul"; break; case 7: mon = "Aug"; break; case 8: mon = "Sep"; break; case 9: mon = "Oct"; break; case 10: mon = "Nov"; break; case 11: mon = "Dec"; break; } if(gm->tm_hour>=12) { mer = "pm"; hour=gm->tm_hour; if (hour > 12) hour-=12; } else { if(!gm->tm_hour) hour=12; else hour=gm->tm_hour; mer = "am"; } safe_snprintf(str, sizeof(str), "%s %s %02d %4d %02d:%02d %s",wday,mon,gm->tm_mday,1900+gm->tm_year ,hour,gm->tm_min,mer); return(str); } /****************************************************************************/ /* Status popup/down function, see uifc.h for details. */ /****************************************************************************/ void upop(const char *str) { static struct vmem_cell sav[80*3], buf[80*3]; int i,j,k; static int width; if(str == NULL) { vmem_puttext((api->scrn_width-width+1)/2+1,(api->scrn_len-3+1)/2+1 ,(api->scrn_width+width-1)/2+1,(api->scrn_len+3-1)/2+1,sav); return; } width = strlen(str); if(!width) return; width += 7; vmem_gettext((api->scrn_width-width+1)/2+1,(api->scrn_len-3+1)/2+1 ,(api->scrn_width+width-1)/2+1,(api->scrn_len+3-1)/2+1,sav); for(i=0; i < width*3; i++) set_vmem(&buf[i], ' ', api->hclr|(api->bclr<<4), 0); set_vmem(&buf[0], api->chars->popup_top_left, api->hclr|(api->bclr<<4), 0); for(i=1;i<(width-1);i++) set_vmem(&buf[i], api->chars->popup_top, api->hclr|(api->bclr<<4), 0); set_vmem(&buf[i++], api->chars->popup_top_right, api->hclr|(api->bclr<<4), 0); set_vmem(&buf[i++], api->chars->popup_left, api->hclr|(api->bclr<<4), 0); k=strlen(str); i+=((((width-3)-k)/2)); for(j=0;jhclr|(api->bclr<<4)|BLINK, 0); i=(((width-1)*2)+1); set_vmem(&buf[i++], api->chars->popup_right, api->hclr|(api->bclr<<4), 0); set_vmem(&buf[i++], api->chars->popup_bottom_left, api->hclr|(api->bclr<<4), 0); for(;i<((width*3)-1);i++) set_vmem(&buf[i], api->chars->popup_bottom, api->hclr|(api->bclr<<4), 0); set_vmem(&buf[i], api->chars->popup_bottom_right, api->hclr|(api->bclr<<4), 0); vmem_puttext((api->scrn_width-width+1)/2+1,(api->scrn_len-3+1)/2+1 ,(api->scrn_width+width-1)/2+1,(api->scrn_len+3-1)/2+1,buf); } /****************************************************************************/ /* Sets the current help index by source code file and line number. */ /****************************************************************************/ void sethelp(int line, char* file) { helpline=line; helpfile=file; } /****************************************************************************/ /* Shows a scrollable text buffer - optionally parsing "help markup codes" */ /****************************************************************************/ void showbuf(int mode, int left, int top, int width, int height, const char *title, const char *hbuf, int *curp, int *barp) { char inverse=0,high=0; struct vmem_cell *textbuf; struct vmem_cell *p; const char *cpc; struct vmem_cell *oldp=NULL; int i,j,k,len; int lines; int pad=1; int is_redraw=0; uint title_len=0; struct mouse_event mevnt; api->exit_flags = 0; _setcursortype(_NOCURSOR); title_len=strlen(title); if(api->mode&UIFC_MOUSE) title_len+=6; if((unsigned)(top+height)>=api->scrn_len) height = api->scrn_len - top; if(!width || (unsigned)width api->scrn_width) width = api->scrn_width - left + 1; if(mode&WIN_L2R) left=(api->scrn_width-width+2)/2; else if(mode&WIN_RHT) left=SCRN_RIGHT-(width+4+left); if(mode&WIN_T2B) top=(api->scrn_len-height+1)/2; else if(mode&WIN_BOT) top=api->scrn_len-height-3-top; if(left<0) left=0; if(top<0) top=0; if(mode&WIN_PACK) pad=0; /* Dynamic Menus */ if(mode&WIN_DYN && curp != NULL && barp != NULL && last_menu_cur==curp && last_menu_bar==barp && save_menu_cur==*curp && save_menu_bar==*barp) is_redraw=1; if(mode&WIN_DYN && mode&WIN_REDRAW) is_redraw=1; if(mode&WIN_DYN && mode&WIN_NODRAW) is_redraw=0; vmem_gettext(1,1,api->scrn_width,api->scrn_len,tmp_buffer); if(!is_redraw) { for(i=0; ihclr|(api->bclr<<4), 0); tmp_buffer2[0].ch=api->chars->help_top_left; j=title_len; if(j>width-6) { j=width-6; } for(i=1;i<(width-j)/2;i++) tmp_buffer2[i].ch=api->chars->help_top; if((api->mode&UIFC_MOUSE) && (!(mode&WIN_DYN))) { set_vmem(&tmp_buffer2[1], api->chars->button_left, api->hclr|(api->bclr<<4), 0); /* tmp_buffer2[4]='þ'; */ set_vmem(&tmp_buffer2[2], api->chars->close_char, api->lclr|(api->bclr<<4), 0); set_vmem(&tmp_buffer2[3], api->chars->button_right, api->hclr|(api->bclr<<4), 0); /* Buttons are ignored - leave it this way to not confuse stuff from help() */ } tmp_buffer2[i].ch = api->chars->help_titlebreak_left; i+=2; for(cpc=title;*cpc && cpc < &title[j];cpc++) { tmp_buffer2[i].ch=*cpc; i++; } i++; tmp_buffer2[i].ch=api->chars->help_titlebreak_right; i++; for(;i<((width-1));i++) tmp_buffer2[i].ch=api->chars->help_top; tmp_buffer2[i].ch=api->chars->help_top_right; i++; j=i; /* leave i alone */ for(k=0;k<(height-2);k++) { /* the sides of the box */ tmp_buffer2[j].ch=api->chars->help_left; j++; j+=((width-2)); tmp_buffer2[j].ch=api->chars->help_right; j++; } tmp_buffer2[j].ch = api->chars->help_bottom_left; j++; if(!(mode&WIN_DYN) && (width>31)) { for(k=j;kchars->help_bottom; tmp_buffer2[k].ch = api->chars->help_hitanykey_left; k+=2; tmp_buffer2[k].ch='H'; k++; tmp_buffer2[k].ch='i'; k++; tmp_buffer2[k].ch='t'; k+=2; tmp_buffer2[k].ch='a'; k++; tmp_buffer2[k].ch='n'; k++; tmp_buffer2[k].ch='y'; k+=2; tmp_buffer2[k].ch='k'; k++; tmp_buffer2[k].ch='e'; k++; tmp_buffer2[k].ch='y'; k+=2; tmp_buffer2[k].ch='t'; k++; tmp_buffer2[k].ch='o'; k+=2; tmp_buffer2[k].ch='c'; k++; tmp_buffer2[k].ch='o'; k++; tmp_buffer2[k].ch='n'; k++; tmp_buffer2[k].ch='t'; k++; tmp_buffer2[k].ch='i'; k++; tmp_buffer2[k].ch='n'; k++; tmp_buffer2[k].ch='u'; k++; tmp_buffer2[k].ch='e'; k+=2; tmp_buffer2[k].ch=api->chars->help_hitanykey_right; k++; for(j=k;jchars->help_bottom; } else { for(k=j;kchars->help_bottom; j=k; } tmp_buffer2[j].ch=api->chars->help_bottom_right; if(!(mode&WIN_DYN)) { tmp_buffer2[j-1].ch=api->chars->button_right; tmp_buffer2[j-2].ch=' '; tmp_buffer2[j-3].ch=' '; tmp_buffer2[j-4].ch=api->chars->button_left; #define SCROLL_UP_BUTTON_X left + (width - 4) #define SCROLL_UP_BUTTON_Y top + height #define SCROLL_DN_BUTTON_X left + (width - 3) #define SCROLL_DN_BUTTON_Y top + height } vmem_puttext(left,top+1,left+width-1,top+height,tmp_buffer2); } len=strlen(hbuf); lines=0; k=0; for(j=0;j=width-2-pad-pad && (hbuf[j+1]!='\n' && hbuf[j+1]!='\r'))) { k=0; lines++; } } if(k) lines++; if(lines < height-2-pad-pad) lines=height-2-pad-pad; if((textbuf=(struct vmem_cell *)malloc((width-2-pad-pad)*lines*sizeof(*textbuf)))==NULL) { cprintf("UIFC line %d: error allocating %u bytes\r\n" ,__LINE__,(width-2-pad-pad)*lines*sizeof(*textbuf)); _setcursortype(cursor); return; } for(i=0; i < (width - 2 - pad - pad) * lines;i++) set_vmem(&textbuf[i], ' ', (api->hclr|(api->bclr<<4)), 0); for(j=i=0; j < len; j++, i++) { if(hbuf[j]==LF) { i++; while(i%((width-2-pad-pad))) i++; i--; } else if(mode&WIN_HLP && (hbuf[j]==2 || hbuf[j]=='~')) { /* Ctrl-b toggles inverse */ inverse=!inverse; i--; } else if(mode&WIN_HLP && (hbuf[j]==1 || hbuf[j]=='`')) { /* Ctrl-a toggles high intensity */ high=!high; i--; } else if(hbuf[j]!=CR) { set_vmem(&textbuf[i], hbuf[j], inverse ? (api->bclr|(api->cclr<<4)) : high ? (api->hclr|(api->bclr<<4)) : (api->lclr|(api->bclr<<4)), 0); if(((i+1)%((width-2-pad-pad))==0 && (hbuf[j+1]==LF)) || (hbuf[j+1]==CR && hbuf[j+2]==LF)) i--; } else i--; } i=0; p=textbuf; struct vmem_cell * textend = textbuf + (lines-(height-2-pad-pad))*(width-2-pad-pad); if(mode&WIN_DYN) { vmem_puttext(left+1+pad,top+2+pad,left+width-2-pad,top+height-1-pad,p); } else { while(i==0) { if(p!=oldp) { if(p > textend) p=textend; if(plclr|(api->bclr<<4)); putch(p > textbuf ? api->chars->up_arrow : ' '); putch(p < textend ? api->chars->down_arrow : ' '); } if(kbwait()) { j=inkey(); if(j==CIO_KEY_MOUSE) { /* Ignores return value to avoid hitting help/exit hotspots */ if(uifc_getmouse(&mevnt)>=0) { /* Clicked Scroll Up */ if(mevnt.startx>=left+pad && mevnt.startx<=left+pad+width-3 && mevnt.starty>=top+pad+1 && mevnt.starty<=top+pad+(height/2)-2 && mevnt.event==CIOLIB_BUTTON_1_CLICK) { p -= ((width-2-pad-pad)*(height-5)); continue; } if(mevnt.startx == SCROLL_UP_BUTTON_X && mevnt.starty == SCROLL_UP_BUTTON_Y && mevnt.event==CIOLIB_BUTTON_1_CLICK) { p -= ((width-2-pad-pad)); continue; } /* Clicked Scroll Down */ if(mevnt.startx>=left+pad && mevnt.startx<=left+pad+width && mevnt.starty<=top+pad+height-2 && mevnt.starty>=top+pad+height-(height/2+1)-2 && mevnt.event==CIOLIB_BUTTON_1_CLICK) { p += (width-2-pad-pad)*(height-5); continue; } if(mevnt.startx == SCROLL_DN_BUTTON_X && mevnt.starty == SCROLL_DN_BUTTON_Y && mevnt.event==CIOLIB_BUTTON_1_CLICK) { p += ((width-2-pad-pad)); continue; } /* Scroll up */ if (mevnt.event == CIOLIB_BUTTON_4_PRESS) { p = p-((width-2-pad-pad)); continue; } /* Scroll down */ if (mevnt.event == CIOLIB_BUTTON_5_PRESS) { p += ((width-2-pad-pad)); continue; } /* Non-click events (drag, move, multiclick, etc) */ if(mevnt.event!=CIOLIB_BUTTON_CLICK(CIOLIB_BUTTON_NUMBER(mevnt.event))) continue; i=1; } continue; } switch(j) { case CIO_KEY_HOME: /* home */ p = textbuf; break; case CIO_KEY_UP: /* up arrow */ p = p-((width-2-pad-pad)); break; case CIO_KEY_PPAGE: /* PgUp */ p = p-((width-2-pad-pad)*(height-5)); break; case CIO_KEY_NPAGE: /* PgDn */ p += (width-2-pad-pad)*(height-5); break; case CIO_KEY_END: /* end */ p = textend; break; case CIO_KEY_DOWN: /* dn arrow */ p += ((width-2-pad-pad)); break; case CIO_KEY_QUIT: api->exit_flags |= UIFC_XF_QUIT; // Fall-through default: i=1; } } mswait(1); } vmem_puttext(1,1,api->scrn_width,api->scrn_len,tmp_buffer); } free(textbuf); if(is_redraw) /* Force redraw of menu also. */ reset_dynamic(); _setcursortype(cursor); } /************************************************************/ /* Help (F1) key function. Uses helpbuf as the help input. */ /************************************************************/ static void help(void) { char hbuf[HELPBUF_SIZE],str[256]; char *p; unsigned short line; /* This must be 16-bits */ long l; FILE *fp; api->exit_flags = 0; if(api->helpbuf==NULL && api->helpixbfile[0]==0) return; _setcursortype(_NOCURSOR); if(!api->helpbuf) { if((fp=fopen(api->helpixbfile,"rb"))==NULL) SAFEPRINTF(hbuf,"ERROR: Cannot open help index: %s" ,api->helpixbfile); else { p=strrchr(helpfile,'/'); if(p==NULL) p=strrchr(helpfile,'\\'); if(p==NULL) p=helpfile; else p++; l=-1L; while(!feof(fp)) { if(fread(str,12,1,fp)!=1) break; str[12]=0; if(fread(&line,2,1,fp)!=1) break; if(stricmp(str,p) || line!=helpline) { if(fseek(fp,4,SEEK_CUR)==0) break; continue; } if(fread(&l,4,1,fp)!=1) l=-1L; break; } fclose(fp); if(l==-1L) SAFEPRINTF3(hbuf,"ERROR: Cannot locate help key (%s:%u) in: %s" ,p,helpline,api->helpixbfile); else { if((fp=fopen(api->helpdatfile,"rb"))==NULL) SAFEPRINTF(hbuf,"ERROR: Cannot open help file: %s" ,api->helpdatfile); else { if(fseek(fp,l,SEEK_SET)!=0) { SAFEPRINTF4(hbuf,"ERROR: Cannot seek to help key (%s:%u) at %ld in: %s" ,p,helpline,l,api->helpixbfile); } else { if(fread(hbuf,1,HELPBUF_SIZE,fp)<1) { SAFEPRINTF4(hbuf,"ERROR: Cannot read help key (%s:%u) at %ld in: %s" ,p,helpline,l,api->helpixbfile); } } fclose(fp); } } } showbuf(WIN_MID|WIN_HLP, 0, 0, 76, api->scrn_len, "Online Help", hbuf, NULL, NULL); } else { showbuf(WIN_MID|WIN_HLP, 0, 0, 76, api->scrn_len, "Online Help", api->helpbuf, NULL, NULL); } }