#include #include #include #include #include #ifdef ENABLE_UTF8 #include #else #include #endif #include #include #include #include #include #include #include extern char *strdup(const char* s); #include #include #ifdef NEED_BCOPY #define memmove(dst,src,len) bcopy(src,dst,len) #endif #ifdef OLD_REALLOC #define realloc(s,l) myrealloc(s,l) #endif #ifdef DMALLOC #include "dmalloc.h" #endif #include "complete.h" #include "default.h" #include "display.h" #include "eval.h" #include "main.h" #include "misc.h" #include "sheet.h" #include "utf8.h" static Key wgetc(void); /* redraw -- redraw whole screen */ static void redraw(void) { (void)touchwin(curscr); (void)wrefresh(curscr); } /* do_attribute -- set cell attributes */ static int do_attribute(Sheet *cursheet) { int c; Cell *cc = curcell(cursheet); Style sc = getstyle(cursheet, cursheet->cur); do { MarkState ms = getmarkstate(cursheet); const char* prompt = _("Block attribute"); if (ms == UNMARKED) prompt = _("Cell attribute"); const char *mainmenu[] = { _("jJ)ustify"), _("fF)loats"), _("tT)ypeface"), _("mM)isc"), _("lL)abel"), _("kLock)"), NULL }; c = line_menu(prompt, mainmenu, 0); /* There is a magic number "5" in the next line, presumably it represents the selection of the lock attribute in the list of items above, in which lock does indeed appear at index 5 */ if (ms == UNMARKED && c != 5 && locked(cc)) line_msg(_("Cell attribute:"), _("Cell is locked")); else { switch (c) { case -2: case -1: c = KEY_CANCEL; break; case 0: { const char *justifymenu[] = { _("lL)eft"), _("rR)ight"), _("cC)entered"), NULL }; switch (c = line_menu(prompt, justifymenu, sc.adjust)) { case -2: case -1: c = K_INVALID; break; case 0: c = ADJUST_LEFT; break; case 1: c = ADJUST_RIGHT; break; case 2: c = ADJUST_CENTER; break; default: assert(0); } break; } case 1: { const char *floatmenu[] = { _("dD)ecimal"), _("sS)cientific"), _("cC)ompact"), _("hH)exact"), _("pP)recision"), NULL }; switch (c = line_menu(prompt, floatmenu, sc.fform)) { case -2: case -1: c = K_INVALID; break; case 0: c = ADJUST_DECIMAL; break; case 1: c = ADJUST_SCIENTIFIC; break; case 2: c = ADJUST_COMPACT; break; case 3: c = ADJUST_HEXACT; break; case 4: c = ADJUST_PRECISION; break; default: assert(0); } break; } case 2: { const char *typemenu[] = { _("bB)old"), _("dD)im"), _("uU)nderline"), _("fF)oreground"), _("kBack)ground"), _("hH)ashing"), NULL }; switch (c = line_menu(prompt, typemenu, 0)) { case -2: case -1: c = K_INVALID; break; case 0: c = ADJUST_BOLD; break; case 1: c = ADJUST_DIM; break; case 2: c = ADJUST_UNDERLINE; break; case 3: c = ADJUST_FOREGROUND; break; case 4: c = ADJUST_BACKGROUND; break; case 5: c = ADJUST_HASHING; break; default: assert(0); } break; } case 3: { const char *miscmenu[] = { _("sS)hadow"), _("iI)gnore"), _("oO)utput special characters"), NULL }; switch (c = line_menu(prompt, miscmenu, 0)) { case -2: case -1: c = K_INVALID; break; case 0: c = ADJUST_SHADOW; break; case 1: c = ADJUST_IGNORE; break; case 2: c = ADJUST_TRANSPARENT; break; default: assert(0); } break; } case 4: c = ADJUST_LABEL; break; case 5: c = ADJUST_LOCK; break; default: assert(0); } } } while (c == K_INVALID); if (c == KEY_CANCEL) c = K_INVALID; return c; } /* do_file -- file menu */ static int do_file(Sheet *cursheet) { int c = 0; do { const char *menu[] = { _("lL)oad"), _("sS)ave"), _("nN)ame"), NULL }; switch (c = line_menu(_("File:"), menu, 0)) { case -2: case -1: c = KEY_CANCEL; break; case 0: c = K_LOADMENU; break; case 1: c = K_SAVEMENU; break; case 2: c = K_NAME; break; default: assert(0); } } while (c == K_INVALID); if (c == KEY_CANCEL) c = K_INVALID; return c; } /* do_shell -- spawn a shell */ static int do_shell(void) { pid_t pid; struct sigaction interrupt; refresh(); interrupt.sa_flags = 0; sigemptyset(&interrupt.sa_mask); interrupt.sa_handler = SIG_IGN; sigaction(SIGINT, &interrupt, (struct sigaction *)0); sigaction(SIGQUIT, &interrupt, (struct sigaction *)0); switch (pid=fork()) { /* -1 */ case -1: line_msg(_("Spawn sub shell"),strerror(errno)); break; /* 0 */ case 0: { const char *shell; if ((shell=getenv("SHELL"))==(const char*)0) { struct passwd *pwd; if ((pwd=getpwuid(getuid()))==(struct passwd*)0) { shell="/bin/sh"; } else { shell=pwd->pw_shell; } } line_msg((const char*)0,_("Sub shell started")); move(LINES-1,0); curs_set(1); refresh(); reset_shell_mode(); puts("\n"); interrupt.sa_handler=SIG_DFL; sigaction(SIGINT,&interrupt,(struct sigaction *)0); sigaction(SIGQUIT,&interrupt,(struct sigaction *)0); execl(shell,shell,(const char*)0); exit(127); break; } /* default */ default: { pid_t r; int status; while ((r=wait(&status))!=-1 && r!=pid); reset_prog_mode(); interrupt.sa_handler=SIG_DFL; sigaction(SIGINT,&interrupt,(struct sigaction *)0); sigaction(SIGQUIT,&interrupt,(struct sigaction *)0); clear(); refresh(); curs_set(0); redraw(); } } return -1; } /* do_block -- block menu */ static int do_block(Sheet *cursheet) { int c = 0; do { const char* block[] = { _("ecle)ar"), _("iI)nsert"), _("dD)elete"), _("mM)ove"), _("cC)opy"), _("fF)ill"), _("sS)ort"), _("rMir)ror"), NULL }; switch (c = line_menu(_("Block menu:"), block, 0)) { case -2: case -1: c = KEY_CANCEL; break; case 0: c = BLOCK_CLEAR; break; case 1: c = BLOCK_INSERT; break; case 2: c = BLOCK_DELETE; break; case 3: c = BLOCK_MOVE; break; case 4: c = BLOCK_COPY; break; case 5: c = BLOCK_FILL; break; case 6: c = BLOCK_SORT; break; case 7: c = BLOCK_MIRROR; break; } } while (c == K_INVALID); if (c == KEY_CANCEL) c = K_INVALID; return c; } Key show_menu(Sheet *cursheet) { int c = K_INVALID; do { const char* menu[] = { _("aA)ttributes"), _("wW)idth"), _("hH)eight"), _("bB)lock"), _("fF)ile"), _("gG)oto"), _("sS)hell"), _("vV)ersion"), _("qQ)uit"), NULL }; switch (c=line_menu(_("Main menu:"),menu,0)) { case -2: case -1: c = KEY_CANCEL; break; case 0: c = do_attribute(cursheet); break; case 1: c = K_COLWIDTH; break; case 2: c = K_ROWHEIGHT; break; case 3: c = do_block(cursheet); break; case 4: c = do_file(cursheet); break; case 5: c = K_GOTO; break; case 6: do_shell(); c = KEY_CANCEL; break; case 7: c = K_ABOUT; break; case 8: c = K_QUIT; break; default: assert(0); } } while (c == K_INVALID); if (c == KEY_CANCEL) c = K_INVALID; return c; } /* do_bg -- background teapot */ static void do_bg(void) { struct termios t; if (tcgetattr(0,&t)==0 && t.c_cc[VSUSP]!=_POSIX_VDISABLE) { line_msg((const char*)0,_("Teapot stopped")); move(LINES-1,0); curs_set(1); refresh(); reset_shell_mode(); puts("\n"); kill(getpid(),SIGSTOP); clear(); refresh(); reset_prog_mode(); curs_set(0); } else line_msg((const char*)0,_("The susp character is undefined")); } void display_main(Sheet *cursheet) { cursheet->maxx = (size_t)COLS; cursheet->maxy = (size_t)(LINES-1); Key k; do { update(cursheet); k = wgetc(); wmove(stdscr, LINES-1, 0); wclrtoeol(stdscr); switch ((int)k) { case KEY_SUSPEND: case '\032': do_bg(); k = K_INVALID; break; case '\014': redraw(); k = K_INVALID; break; case KEY_F(0): case KEY_F(10): k = show_menu(cursheet); break; } } while (k == K_INVALID || !do_sheetcmd(cursheet, k, 0) || !doanyway(cursheet,_("Sheet modified, leave anyway?"))); } #define CHANNEL_MAX 1000 typedef short CursesColor[3]; void display_init(Sheet *cursheet, bool imp_redraw) { initscr(); start_color(); init_color(DefaultCN[BACKGROUND], CHANNEL_MAX, CHANNEL_MAX, CHANNEL_MAX); assume_default_colors(COLOR_BLACK, DefaultCN[BACKGROUND]); if (debug_level > 1) printf("Terminal has colors: %d, #colors:%d, #pairs:%d", has_colors(), COLORS, COLOR_PAIRS); curs_set(0); noecho(); raw(); nonl(); keypad(stdscr,TRUE); clear(); refresh(); #ifdef HAVE_TYPEAHEAD if (imp_redraw) typeahead(-1); #endif /* allocate and initialize the palette */ assert(COLORS > 0); if ((size_t)COLORS < cursheet->max_colors) cursheet->max_colors = (size_t)COLORS; cursheet->palette = (void *)malloc(cursheet->max_colors*sizeof(CursesColor)); memset(cursheet->palette, '\0', cursheet->max_colors*sizeof(CursesColor)); CursesColor *palt = (CursesColor *)(cursheet->palette); for (ColorNum i = 0; i <= DefaultCN[BACKGROUND]; ++i) (void)color_content(i, &(palt[i][0]), &(palt[i][1]), &(palt[i][2])); } void display_end(Sheet* sheet) { curs_set(1); echo(); noraw(); refresh(); endwin(); free(sheet->palette); } void redraw_cell(Sheet *sheet, const Location at ) { update(sheet); } static RowHgtT eff_height(Sheet *sheet, CoordT y) { RowHgtT eff = rowheight(sheet, y, sheet->cur[Z]) / ROWHEIGHT_DENOMINATOR; if (eff == 0) return 1; return eff; } /* redraw_sheet -- draw a sheet with cell cursor */ void redraw_sheet(Sheet *sheet) { char pbuf[80]; char *buf=malloc(128); size_t bufsz=128; const char *label; Location tmp; Cell *cell; MarkState ms; char *err; char moveonly; assert(sheet != (Sheet*)0); assert(IN_OCTANT(sheet->cur)); assert(sheet->offx >= 0); assert(sheet->offy >= 0); /* correct offsets to keep cursor visible */ while (shadowed(sheet, sheet->cur)) { --(sheet->cur[X]); assert(sheet->cur[X] >= 0); } /* Calculate the nearest Y offset that will show the current row */ if (sheet->cur[Y] < sheet->offy) sheet->offy = sheet->cur[Y]; else { size_t available = sheet->maxy - 2 - (header ? 1 : 0); CoordT newoffy = sheet->cur[Y]; size_t needed = eff_height(sheet, newoffy); while (needed < available && newoffy > 0) { RowHgtT prevhgt = eff_height(sheet, newoffy - 1); if (needed + prevhgt > available) break; needed += prevhgt; --newoffy; } if (newoffy > sheet->offy) sheet->offy = newoffy; } /* Calculate the nearest X offset that will show the current column */ if (sheet->cur[X] < sheet->offx) sheet->offx = sheet->cur[X]; else { size_t width = header ? 4 : 0; bool again; size_t col; do { again = false; col = 0; for (CoordT x = sheet->offx; width <= (size_t)(sheet->maxx); width += columnwidth(sheet, x, sheet->cur[Z]), ++x, ++col); --col; sheet->width = col; if (sheet->cur[X] != sheet->offx) { if (col==0) { ++sheet->offx; again = true; } else if ((size_t)(sheet->cur[X] - sheet->offx) >= col) { sheet->offx++; again = true; } } } while (again); } short curcp = 1; init_pair(curcp, DefaultCN[FOREGROUND], COLOR_YELLOW); if (header) { (void)wattron(stdscr,DEF_NUMBER); /* draw x numbers */ for (size_t width=4; width < (size_t)(sheet->maxx); ++width) mvwaddch(stdscr, (int)(sheet->oriy), (int)(sheet->orix + width), (chtype)(unsigned char)' '); ColWidT width = 4; for (CoordT x = sheet->offx; width < sheet->maxx; ++x) { short usecp = 0; if (x == sheet->cur[X]) usecp = curcp; ColWidT col = columnwidth(sheet, x, sheet->cur[Z]); if (bufsz<(size_t)(col*UTF8SZ+1)) buf=realloc(buf,bufsz=(size_t)(col*UTF8SZ+1)); snprintf(buf,bufsz,"%d",x); if (mbslen(buf)>col) { buf[col-1]='$'; buf[col]='\0'; } adjust(CENTER,buf,(size_t)col); assert((size_t)sheet->maxx >= width); if ((size_t)(sheet->maxx)-width < col) buf[(size_t)(sheet->maxx)-width] = '\0'; wcolor_set(stdscr, usecp, NULL); mvwaddstr(stdscr, (int)(sheet->oriy), (int)(sheet->orix+width), buf); ColWidT used = strlen(buf); while (used++ < col) waddch(stdscr, (chtype)(unsigned char)' '); wcolor_set(stdscr, 0, NULL); width += col; } /* draw y numbers */ RowHgtT height = 1; for (CoordT y = sheet->offy; height + 1 < sheet->maxy; ++y) { short usecp = 0; if (y == sheet->cur[Y]) usecp = curcp; wcolor_set(stdscr, usecp, NULL); (void)mvwprintw(stdscr, (int)(height+sheet->oriy), (int)sheet->orix, "%-4d", y); RowHgtT rows = eff_height(sheet, y); for (RowHgtT extra = 1; extra < rows; ++extra) { mvwaddstr(stdscr, (int)(height+extra+sheet->oriy), (int)sheet->orix, " "); } wcolor_set(stdscr, 0, NULL); height += eff_height(sheet, y); } (void)wattroff(stdscr,DEF_NUMBER); /* draw z number */ (void)mvwprintw(stdscr, (CoordT)(sheet->oriy), (CoordT)(sheet->orix), "%3d", sheet->cur[Z]); } ++curcp; /* draw elements */ RowHgtT height = header ? 1 : 0; for (CoordT y = sheet->offy; height + 1 < sheet->maxy; ++y) { RowHgtT rows = eff_height(sheet, y); ColWidT width = header ? 4 : 0; for (CoordT x = sheet->offx; width < sheet->maxx; width += columnwidth(sheet, x, sheet->cur[Z]), ++x) { size_t size,realsize,fill, cutoff = 0; int realx = x; if (x == sheet->offx) { Location fil; fil[X] = realx; fil[Y] = y - (header ? 1 : 0) + sheet->offy; fil[Z] = sheet->cur[Z]; while (shadowed(sheet, fil)) { --realx; fil[X] = realx; cutoff += columnwidth(sheet, realx, sheet->cur[Z]); } } tmp[X] = realx; tmp[Y] = y; tmp[Z] = sheet->cur[Z]; cell = safe_cell_at(sheet, tmp); Style sc = getstyle(sheet, tmp); if ((size = cellwidth(sheet, tmp))) { if (bufsz < (size*UTF8SZ+1)) buf = realloc(buf, bufsz=(size*UTF8SZ+1)); size_t esize = size; char* ebuf = buf; if (sc.italic) { esize -= 2; *buf = '*'; ebuf += 1; } size_t spot = printvalue(ebuf, (esize*UTF8SZ + 1), esize, quote, sc.fform, sc.precision, sheet, tmp); if (sc.italic) { ebuf[spot++] = '*'; ebuf[spot] = '\0'; } adjust(sc.adjust, buf, size); assert(size >= cutoff); if (width + size - cutoff >= (size_t)(sheet->maxx)) { *(buf + cutoff + (size_t)sheet->maxx - width) = '\0'; realsize = (size_t)(sheet->maxx) - width + cutoff; } else realsize = size; ms = getmarkstate(sheet); short usecp = -1; short hashcp = -1; ColorNum fg = sc.aspect[FOREGROUND]; if (fg == NO_COLOR_SET) fg = DefaultCN[FOREGROUND]; ColorNum bg = sc.aspect[BACKGROUND]; if (bg == NO_COLOR_SET) bg = DefaultCN[BACKGROUND]; ColorNum hg = sc.aspect[HASHING]; if (hg == NO_COLOR_SET) hg = bg; for (short trycp = 0; trycp < curcp; ++trycp) { short pfg, pbg; pair_content(trycp, &pfg, &pbg); if (fg != pfg) continue; if (bg == pbg) usecp = trycp; if (hg == pbg) hashcp = trycp; if (usecp >= 0 && hashcp >= 0) break; } if (usecp < 0) { usecp = curcp; init_pair(curcp++, fg, bg); } if (hashcp < 0) { if (bg == hg) hashcp = usecp; else { hashcp = curcp; init_pair(curcp++, fg, hg); } } wcolor_set(stdscr, usecp, NULL); bool invert = (ms != UNMARKED) && loc_in_box(tmp, sheet->mark1, sheet->mark2); if (x == sheet->cur[X] && y == sheet->cur[Y]) invert = (ms == MARKING) ? true : !invert; if (invert) (void)wattron(stdscr,DEF_CELLCURSOR); if (sc.dim) wattron(stdscr, A_DIM); if (sc.bold) wattron(stdscr, A_BOLD); if (sc.underline) wattron(stdscr, A_UNDERLINE); short nextcp = hashcp; chtype blank = (chtype)' '; if (hashcp == usecp && fg != INVISIBLE_COLOR) { (void)mvwaddstr(stdscr, (int)(height + sheet->oriy), (int)(width + sheet->orix), buf+cutoff); } else { // Alternate colors to simulate hash chtype next_char = (chtype)buf[cutoff]; if (fg == INVISIBLE_COLOR) next_char = blank; (void)mvwaddch(stdscr, (int)(height + sheet->oriy), (int)(width + sheet->orix), next_char); for (size_t ix = cutoff+1; buf[ix]; ++ix) { wcolor_set(stdscr, nextcp, NULL); if (nextcp == hashcp) nextcp = usecp; else nextcp = hashcp; next_char = fg == INVISIBLE_COLOR ? blank : (chtype)buf[ix]; (void)waddch(stdscr, next_char); } } for (fill=mbslen(buf+cutoff); filloriy), (int)(width + sheet->orix), " "); for (fill = 1; fill < realsize; ++fill) (void)waddch(stdscr, (chtype)(unsigned char)' '); } wcolor_set(stdscr, 0, NULL); wstandend(stdscr); } } height += rows; } /* draw contents of current element */ if (bufsz < (size_t)(sheet->maxx)*UTF8SZ + 1) buf = realloc(buf, bufsz = (size_t)(sheet->maxx)*UTF8SZ + 1); label = getlabel(curcell(sheet)); assert(label != (const char*)0); moveonly = sheet->moveonly ? *_("V") : *_("E"); if (*label=='\0') sprintf(pbuf, "%c @(%d,%d,%d)=", moveonly, sheet->cur[X], sheet->cur[Y], sheet->cur[Z]); else sprintf(pbuf, "%c @(%s)=", moveonly, label); (void)strncpy(buf,pbuf,bufsz); buf[bufsz-1] = 0; if ((err=geterror(sheet,sheet->cur)) != (const char*)0) { (void)strncpy(buf, err, bufsz); free(err); } else { cell = curcell(sheet); Token bc = gettok(cell, BASE_CONT); printtok(buf+strlen(buf), bufsz-strlen(buf), 0, QUOTE_STRING, FLT_COMPACT, -1, TRUNCATED_ERROR, &bc); Token ic = gettok(cell, ITER_CONT); if (ic.type != EMPTY && mbslen(buf) < (size_t)(sheet->maxx + 1 - 4)) { strcat(buf," -> "); printtok(buf+strlen(buf), bufsz-strlen(buf), 0, QUOTE_STRING, FLT_COMPACT, -1, TRUNCATED_ERROR, &ic); } } *mbspos(buf, (int)sheet->maxx) = 0; (void)mvwaddstr(stdscr, (int)(sheet->oriy + sheet->maxy) - 1, (int)(sheet->orix), buf); for (size_t col = mbslen(buf); col < sheet->maxx; ++col) (void)waddch(stdscr, ' '); } /* line_file -- line editor function for file name entry */ const char *line_file(const char *file, const char *pattern, const char *title, int create) { static char buf[PATH_MAX] = ""; int rc; size_t dummy1 = 0, dummy2 = 0; if (file) strncpy(buf, file, sizeof(buf)); buf[sizeof(buf)-1] = 0; rc = line_edit((Sheet*)0, buf, sizeof(buf), title, &dummy1, &dummy2); if (rc < 0) return NULL; return buf; } /* line_edit -- line editor function */ int line_edit(Sheet *sheet, char *buf, size_t size, const char *prompt, size_t *x, size_t *offx) { char *src, *dest; assert(buf != NULL); assert(prompt != NULL); assert(x != (size_t*)0); assert(offx != (size_t*)0); (void)curs_set(1); bool insert = true; int mx = COLS; CoordT promptlen = (CoordT)(mbslen(prompt)+1); (void)mvwaddstr(stdscr,LINES-1,0,prompt); (void)waddch(stdscr,(chtype)(unsigned char)' '); int c; do { /* correct offx so cursor stays visible */ if (*x < *offx) *offx = *x; if ((CoordT)(*x - *offx) > (mx-promptlen-1)) *offx = *x - (size_t)mx + (size_t)promptlen + 1; /* display buffer */ (void)wmove(stdscr, LINES-1, promptlen); src = mbspos(buf, (int)(*offx)); dest = mbspos(buf, (int)(*offx) + COLS - promptlen); int i = 0; for (; *src && src < dest; ++i, ++src) (void)waddch(stdscr, (chtype)(unsigned char)(*src)); if (i != mx) (void)wclrtoeol(stdscr); /* show cursor */ (void)wmove(stdscr, LINES-1, (CoordT)(*x-*offx) + promptlen); src = dest = mbspos(buf, (int)(*x)); c = wgetc(); if (sheet != (Sheet*)0 && sheet->moveonly) switch (c) { /* ^o -- switch back to line editor */ case '\t': case '\017': sheet->moveonly=0; break; /* v -- insert value of current cell */ case 'v': { char valbuf[1024]; printvalue(valbuf, sizeof(valbuf), 0, QUOTE_STRING, FLT_COMPACT, 0, sheet, sheet->cur); if (strlen(buf)+strlen(valbuf) >= (size-1)) break; (void)memmove(src+strlen(valbuf), src, strlen(src)); (void)memcpy(src, valbuf, strlen(valbuf)); (*x) += mbslen(valbuf); break; } /* p -- insert position of current cell */ case 'p': { char valbuf[1024]; sprintf(valbuf, "(%d,%d,%d)", sheet->cur[X], sheet->cur[Y], sheet->cur[Z]); if (strlen(buf)+strlen(valbuf) >= (size-1)) break; (void)memmove(src+strlen(valbuf), src, strlen(src)); (void)memcpy(src, valbuf, strlen(valbuf)); (*x) += mbslen(valbuf); break; } /* default -- move around in sheet */ default: (void)do_sheetcmd(sheet,c,1); redraw_sheet(sheet); break; } else switch (c) { /* UP */ case K_UP: break; /* LEFT */ case K_LEFT: if (*x > 0) (*x)--; break; /* RIGHT */ case K_RIGHT: if (*x < mbslen(buf)) (*x)++; break; /* BACKSPACE */ case K_BACKSPACE: if (*x > 0) { memmove(mbspos(src, -1), src, strlen(src)+1); (*x)--; } break; /* C-i -- file name completion */ case '\t': completefile(buf, src, size); break; /* DC */ case K_DC: src = mbspos(dest, 1); if (*x < strlen(buf)) memmove(dest, src, strlen(src)+1); break; /* HOME */ case K_HOME: *x = 0; break; /* END */ case K_END: *x = mbslen(buf); break; /* IC */ case KEY_IC: insert = !insert; break; /* EIC */ case KEY_EIC: insert = false; break; /* control t */ case '\024': if (*x > 0) { dest = mbspos(src, -1); if (*x == mbslen(buf)) { src = dest; dest = mbspos(src, -1); (*x)--; } char *end = mbspos(src, 1); while (src != end) { char k = *src; memmove(dest+1, dest, (size_t)(src-dest)); *dest = k; src++; dest++; } (*x)++; } break; /* control backslash */ case '\034': { int level; char open = 0, close = 0, dir = 1; switch (*dest) { case ')': dir = -1; /* FALL THROUGH */ case '(': open = '('; close = ')'; break; case '}': dir = -1; /* FALL THROUGH */ case '{': open = '{'; close = '}'; break; case ']': dir = -1; /* FALL THROUGH */ case '[': open = '['; close = ']'; break; default: break; } level = dir; while (*dest && level) { dest += dir; if (*dest == open) level--; else if (*dest == close) level++; } if (!level) *x = mbslen(buf)-mbslen(dest); break; } /* DL */ case KEY_DL: *src = '\0'; break; /* control o */ case '\017': if (sheet!=(Sheet*)0) sheet->moveonly=1; break; /* default */ default: { if (((unsigned int)c) < ' ' || ((unsigned int)c) >= 256) break; bool iscont = is_mbcont((unsigned char)c); if (strlen(buf) >= (size-1)) { if (iscont) { dest = mbspos(src, -1); memmove(dest, src, strlen(src)+1); } break; } if (insert || iscont) memmove(src+1, src, strlen(src)+1); else { if (is_mbchar((unsigned char)(*src))) memmove(src+1, mbspos(src, 1), strlen(mbspos(src, 1))+1); if (!*src) *(src+1) = '\0'; } *src = (char)c; if (!iscont) (*x)++; break; } } } while (c != K_ENTER && c != KEY_CANCEL && (c != K_UP || (sheet!=(Sheet*)0 && sheet->moveonly))); if (sheet) sheet->moveonly = false; (void)curs_set(0); (void)wmove(stdscr, LINES-1, 0); (void)wclrtoeol(stdscr); switch (c) { case KEY_CANCEL: return -1; case K_UP: return -2; default: return 0; } } /* line_ok -- one line yes/no menu */ int line_ok(const char *prompt, int curx) { assert(curx == 0 || curx == 1); const char* menu[] = { _("nN)o"), _("yY)es"), NULL }; return line_menu(prompt, menu, curx); } /* line_binary -- two choices with cancel */ int line_binary(const char *prompt, const char* op1, const char* op2, int curx) { assert(curx == 0 || curx == 1); const char* menu[] = { _("cC)ancel"), op1, op2, NULL }; return line_menu(prompt, menu, curx+1) - 1; } /* line_menu -- one line menu */ /* Notes */ /* The choices are terminated by the last element having a (const char*)str field. Each item can be chosen by tolower(*str) or by the key stored in the c field. Use a space as first character of str, if you only want the function key to work. */ int line_menu(const char *prompt, const char **choice, int curx) { assert(prompt != NULL); assert(choice != (const char **)0); assert(curx >= 0); mvwaddstr(stdscr,LINES-1,0,prompt); size_t promptlen = mbslen(prompt); int maxx = 0; while (choice[maxx] != NULL) ++maxx; int offx = 0; int c; do { int x; (void)wmove(stdscr, LINES-1, (int)promptlen); /* correct offset so choice is visible */ if (curx <= offx) offx = curx; else do { size_t width = promptlen; x = offx; while (x < maxx && width + mbslen(choice[x]+1) + 1 <= (size_t)COLS) { width += mbslen(choice[x]+1) + 1; ++x; } --x; if (x < curx) ++offx; } while (x < curx); /* show visible choices */ size_t width = promptlen; for (x = offx; x < maxx && width + mbslen(choice[x]+1) + 1 <= (size_t)COLS; width += mbslen(choice[x]+1) + 1, ++x) { (void)waddch(stdscr, (chtype)(unsigned char)' '); if (x == curx) (void)wattron(stdscr, DEF_MENU); (void)waddstr(stdscr, (char*)(choice[x]+1)); if (x == curx) (void)wattroff(stdscr,DEF_MENU); } if (width < (size_t)COLS) (void)wclrtoeol(stdscr); switch (c = wgetc()) { /* KEY_LEFT -- move to previous item */ case K_BACKSPACE: case K_LEFT: if (curx > 0) --curx; else curx = maxx-1; break; /* Space, Tab, KEY_RIGHT -- move to next item */ case ' ': case '\t': case K_RIGHT: if (curx < (maxx-1)) ++curx; else curx = 0; break; /* default -- search choice keys */ default: { for (int i = 0; i < maxx; ++i) if (c < 256 && tolower(c) == choice[i][0]) { c = K_ENTER; curx = i; } } } } while (c != K_ENTER && c != K_DOWN && c != KEY_CANCEL && c != K_UP); (void)wmove(stdscr, LINES-1, 0); (void)wclrtoeol(stdscr); switch (c) { case KEY_CANCEL: return -1; case K_UP: return -2; default: return curx; } } /* line_msg -- one line message which will be cleared by someone else */ void line_msg(const char *prompt, const char *msg) { int width; assert(msg!=(const char*)0); if (!*msg) msg = _("Use F0, F10 or / for menu"); if (!batch) { width=1; mvwaddch(stdscr,LINES-1,0,(chtype)(unsigned char)'['); if (prompt!=(const char*)0) { for (; width -- FPAGE */ case KEY_NPAGE: case '>': return K_FPAGE; /* C-x C-c -- QUIT */ case '\03': return K_QUIT; /* C-x C-s -- SAVE */ case '\023': return K_SAVE; /* C-x C-r -- LOAD */ case '\022': return K_LOAD; /* default -- INVALID, general invalid value */ default: return K_INVALID; } } /* ESC, get one more key */ case '\033': { switch (wgetch(stdscr)) { /* M-v -- PPAGE */ case 'v': return K_PPAGE; /* M-Enter -- MENTER */ case KEY_ENTER: case '\r': case '\n': return K_MENTER; /* M-s -- edit style expression */ case 's': return K_EDIT_STYLE_EXPR; /* M-z -- SAVEQUIT */ case 'z': return K_SAVEQUIT; /* default -- INVALID, general invalid value */ default: return K_INVALID; } } /* _("Load sheet file format:") */ case KEY_F(2): return K_LOADMENU; /* _("Save sheet file format:") */ case KEY_F(3): return K_SAVEMENU; /* default */ default: return c; } } void find_helpfile(char *buf, size_t size, const char *argv0) { strncpy(buf, HELPFILE, size); buf[size-1] = 0; }