diff options
Diffstat (limited to 'input.c')
-rw-r--r-- | input.c | 528 |
1 files changed, 528 insertions, 0 deletions
@@ -0,0 +1,528 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include <sys/types.h> +#include "config.h" +#include "screen.h" +#include "extern.h" + +#define INPUTLINE (flayer->l_height - 1) + +static void InpProcess __P((char **, int *)); +static void InpAbort __P((void)); +static void InpRedisplayLine __P((int, int, int, int)); + +extern struct layer *flayer; +extern struct display *display; +extern struct mchar mchar_blank, mchar_so; + +struct inpline +{ + char buf[MAXSTR+1]; /* text buffer */ + int len; /* length of the editible string */ + int pos; /* cursor position in editable string */ + struct inpline *next, *prev; +}; + +/* 'inphist' is used to store the current input when scrolling through history. + * inpline->prev == history-prev + * inpline->next == history-next + */ +static struct inpline inphist; + +struct inpdata +{ + struct inpline inp; + int inpmaxlen; /* MAXSTR, or less, if caller has shorter buffer */ + char *inpstring; /* the prompt */ + int inpstringlen; /* length of the prompt */ + int inpmode; /* INP_NOECHO, INP_RAW, INP_EVERY */ + void (*inpfinfunc) __P((char *buf, int len, char *priv)); + char *priv; /* private data for finfunc */ + int privdata; /* private data space */ + char *search; /* the search string */ +}; + +static struct LayFuncs InpLf = +{ + InpProcess, + InpAbort, + InpRedisplayLine, + DefClearLine, + DefRewrite, + DefResize, + DefRestore, + 0 +}; + +/* +** Here is the input routine +*/ + +/* called once, after InitOverlayPage in Input() or Isearch() */ +void +inp_setprompt(p, s) +char *p, *s; +{ + struct inpdata *inpdata; + + inpdata = (struct inpdata *)flayer->l_data; + if (p) + { + inpdata->inpstringlen = strlen(p); + inpdata->inpstring = p; + } + if (s) + { + if (s != inpdata->inp.buf) + strncpy(inpdata->inp.buf, s, sizeof(inpdata->inp.buf) - 1); + inpdata->inp.buf[sizeof(inpdata->inp.buf) - 1] = 0; + inpdata->inp.pos = inpdata->inp.len = strlen(inpdata->inp.buf); + } + InpRedisplayLine(INPUTLINE, 0, flayer->l_width - 1, 0); + flayer->l_x = inpdata->inpstringlen + (inpdata->inpmode & INP_NOECHO ? 0 : inpdata->inp.pos); + flayer->l_y = INPUTLINE; +} + +/* + * We dont use HS status line with Input(). + * If we would use it, then we should check e_tgetflag("es") if + * we are allowed to use esc sequences there. + * + * mode is an OR of + * INP_NOECHO == suppress echoing of characters. + * INP_RAW == raw mode. call finfunc after each character typed. + * INP_EVERY == digraph mode. + */ +void +Input(istr, len, mode, finfunc, priv, data) +char *istr; +int len; +int mode; +void (*finfunc) __P((char *buf, int len, char *priv)); +char *priv; +int data; +{ + int maxlen; + struct inpdata *inpdata; + + if (!flayer) + return; + + if (len > MAXSTR) + len = MAXSTR; + if (!(mode & INP_NOECHO)) + { + maxlen = flayer->l_width - 1 - strlen(istr); + if (len > maxlen) + len = maxlen; + } + if (len < 0) + { + LMsg(0, "Width %d chars too small", -len); + return; + } + if (InitOverlayPage(sizeof(*inpdata), &InpLf, 1)) + return; + flayer->l_mode = 1; + inpdata = (struct inpdata *)flayer->l_data; + inpdata->inpmaxlen = len; + inpdata->inpfinfunc = finfunc; + inpdata->inp.pos = inpdata->inp.len = 0; + inpdata->inp.prev = inphist.prev; + inpdata->inpmode = mode; + inpdata->privdata = data; + if (!priv) + priv = (char*)&inpdata->privdata; + inpdata->priv = priv; + inpdata->inpstringlen = 0; + inpdata->inpstring = NULL; + inpdata->search = NULL; + if (istr) + inp_setprompt(istr, (char *)NULL); +} + +static void +erase_chars(inpdata, from, to, x, mv) +struct inpdata *inpdata; +char *from; +char *to; +int x; +int mv; +{ + int chng; + ASSERT(from < to); + if (inpdata->inp.len > to - inpdata->inp.buf) + bcopy(to, from, inpdata->inp.len - (to - inpdata->inp.buf)); + chng = to - from; + if (mv) + { + x -= chng; + inpdata->inp.pos -= chng; + } + inpdata->inp.len -= chng; + if (!(inpdata->inpmode & INP_NOECHO)) + { + struct mchar mc; + char *s = from < to ? from : to; + mc = mchar_so; + while (s < inpdata->inp.buf+inpdata->inp.len) + { + mc.image = *s++; + LPutChar(flayer, &mc, x++, INPUTLINE); + } + while (chng--) + LPutChar(flayer, &mchar_blank, x++, INPUTLINE); + x = inpdata->inpstringlen + inpdata->inp.pos; + LGotoPos(flayer, x, INPUTLINE); + } +} + +static void +InpProcess(ppbuf, plen) +char **ppbuf; +int *plen; +{ + int len, x; + char *pbuf; + char ch; + struct inpdata *inpdata; + struct display *inpdisplay; + int prev, next, search = 0; + + inpdata = (struct inpdata *)flayer->l_data; + inpdisplay = display; + +#define RESET_SEARCH do { if (inpdata->search) Free(inpdata->search); } while (0) + + LGotoPos(flayer, inpdata->inpstringlen + (inpdata->inpmode & INP_NOECHO ? 0 : inpdata->inp.pos), INPUTLINE); + if (ppbuf == 0) + { + InpAbort(); + return; + } + x = inpdata->inpstringlen + inpdata->inp.pos; + len = *plen; + pbuf = *ppbuf; + while (len) + { + char *p = inpdata->inp.buf + inpdata->inp.pos; + + ch = *pbuf++; + len--; + if (inpdata->inpmode & INP_EVERY) + { + inpdata->inp.buf[inpdata->inp.len] = ch; + if (ch) + { + display = inpdisplay; + (*inpdata->inpfinfunc)(inpdata->inp.buf, inpdata->inp.len, inpdata->priv); + ch = inpdata->inp.buf[inpdata->inp.len]; + } + } + else if (inpdata->inpmode & INP_RAW) + { + display = inpdisplay; + (*inpdata->inpfinfunc)(&ch, 1, inpdata->priv); /* raw */ + if (ch) + continue; + } + if (((unsigned char)ch & 0177) >= ' ' && ch != 0177 && inpdata->inp.len < inpdata->inpmaxlen) + { + if (inpdata->inp.len > inpdata->inp.pos) + bcopy(p, p+1, inpdata->inp.len - inpdata->inp.pos); + inpdata->inp.buf[inpdata->inp.pos++] = ch; + inpdata->inp.len++; + + if (!(inpdata->inpmode & INP_NOECHO)) + { + struct mchar mc; + mc = mchar_so; + mc.image = *p++; + LPutChar(flayer, &mc, x, INPUTLINE); + x++; + if (p < inpdata->inp.buf+inpdata->inp.len) + { + while (p < inpdata->inp.buf+inpdata->inp.len) + { + mc.image = *p++; + LPutChar(flayer, &mc, x++, INPUTLINE); + } + x = inpdata->inpstringlen + inpdata->inp.pos; + LGotoPos(flayer, x, INPUTLINE); + } + } + RESET_SEARCH; + } + else if ((ch == '\b' || ch == 0177) && inpdata->inp.pos > 0) + { + erase_chars(inpdata, p-1, p, x, 1); + RESET_SEARCH; + } + else if (ch == '\025') /* CTRL-U */ + { + x = inpdata->inpstringlen; + if (inpdata->inp.len && !(inpdata->inpmode & INP_NOECHO)) + { + LClearArea(flayer, x, INPUTLINE, x + inpdata->inp.len - 1, INPUTLINE, 0, 0); + LGotoPos(flayer, x, INPUTLINE); + } + inpdata->inp.len = inpdata->inp.pos = 0; + } + else if (ch == '\013') /* CTRL-K */ + { + x = inpdata->inpstringlen + inpdata->inp.pos; + if (inpdata->inp.len > inpdata->inp.pos && !(inpdata->inpmode & INP_NOECHO)) + { + LClearArea(flayer, x, INPUTLINE, x + inpdata->inp.len - inpdata->inp.pos - 1, INPUTLINE, 0, 0); + LGotoPos(flayer, x, INPUTLINE); + } + inpdata->inp.len = inpdata->inp.pos; + } + else if (ch == '\027' && inpdata->inp.pos > 0) /* CTRL-W */ + { + char *oldp = p--; + while (p > inpdata->inp.buf && *p == ' ') + p--; + while (p > inpdata->inp.buf && *(p - 1) != ' ') + p--; + erase_chars(inpdata, p, oldp, x, 1); + RESET_SEARCH; + } + else if (ch == '\004' && inpdata->inp.pos < inpdata->inp.len) /* CTRL-D */ + { + erase_chars(inpdata, p, p+1, x, 0); + RESET_SEARCH; + } + else if (ch == '\001' || (unsigned char)ch == 0201) /* CTRL-A */ + { + LGotoPos(flayer, x -= inpdata->inp.pos, INPUTLINE); + inpdata->inp.pos = 0; + } + else if ((ch == '\002' || (unsigned char)ch == 0202) && inpdata->inp.pos > 0) /* CTRL-B */ + { + LGotoPos(flayer, --x, INPUTLINE); + inpdata->inp.pos--; + } + else if (ch == '\005' || (unsigned char)ch == 0205) /* CTRL-E */ + { + LGotoPos(flayer, x += inpdata->inp.len - inpdata->inp.pos, INPUTLINE); + inpdata->inp.pos = inpdata->inp.len; + } + else if ((ch == '\006' || (unsigned char)ch == 0206) && inpdata->inp.pos < inpdata->inp.len) /* CTRL-F */ + { + LGotoPos(flayer, ++x, INPUTLINE); + inpdata->inp.pos++; + } + else if ((prev = ((ch == '\020' || (unsigned char)ch == 0220) && /* CTRL-P */ + inpdata->inp.prev)) || + (next = ((ch == '\016' || (unsigned char)ch == 0216) && /* CTRL-N */ + inpdata->inp.next)) || + (search = ((ch == '\022' || (unsigned char)ch == 0222) && inpdata->inp.prev))) + { + struct mchar mc; + struct inpline *sel; + int pos = -1; + + mc = mchar_so; + + if (prev) + sel = inpdata->inp.prev; + else if (next) + sel = inpdata->inp.next; + else + { + /* search */ + inpdata->inp.buf[inpdata->inp.len] = 0; /* Remove the ctrl-r from the end */ + if (!inpdata->search) + inpdata->search = SaveStr(inpdata->inp.buf); + for (sel = inpdata->inp.prev; sel; sel = sel->prev) + { + char *f; + if ((f = strstr(sel->buf, inpdata->search))) + { + pos = f - sel->buf; + break; + } + } + if (!sel) + continue; /* Did not find a match. Process the next input. */ + } + + if (inpdata->inp.len && !(inpdata->inpmode & INP_NOECHO)) + LClearArea(flayer, inpdata->inpstringlen, INPUTLINE, inpdata->inpstringlen + inpdata->inp.len - 1, INPUTLINE, 0, 0); + + if ((prev || search) && !inpdata->inp.next) + inphist = inpdata->inp; + memcpy(&inpdata->inp, sel, sizeof(struct inpline)); + if (pos != -1) + inpdata->inp.pos = pos; + if (inpdata->inp.len > inpdata->inpmaxlen) + inpdata->inp.len = inpdata->inpmaxlen; + if (inpdata->inp.pos > inpdata->inp.len) + inpdata->inp.pos = inpdata->inp.len; + + x = inpdata->inpstringlen; + p = inpdata->inp.buf; + + if (!(inpdata->inpmode & INP_NOECHO)) + { + while (p < inpdata->inp.buf+inpdata->inp.len) + { + mc.image = *p++; + LPutChar(flayer, &mc, x++, INPUTLINE); + } + } + x = inpdata->inpstringlen + inpdata->inp.pos; + LGotoPos(flayer, x, INPUTLINE); + } + + else if (ch == '\003' || ch == '\007' || ch == '\033' || + ch == '\000' || ch == '\n' || ch == '\r') + { + if (ch != '\n' && ch != '\r') + inpdata->inp.len = 0; + inpdata->inp.buf[inpdata->inp.len] = 0; + + if (inpdata->inp.len && !(inpdata->inpmode & (INP_NOECHO | INP_RAW))) + { + struct inpline *store; + + /* Look for a duplicate first */ + for (store = inphist.prev; store; store = store->prev) + { + if (strcmp(store->buf, inpdata->inp.buf) == 0) + { + if (store->next) + store->next->prev = store->prev; + if (store->prev) + store->prev->next = store->next; + store->pos = inpdata->inp.pos; + break; + } + } + + if (!store) + { + store = malloc(sizeof(struct inpline)); + memcpy(store, &inpdata->inp, sizeof(struct inpline)); + } + store->next = &inphist; + store->prev = inphist.prev; + if (inphist.prev) + inphist.prev->next = store; + inphist.prev = store; + } + + flayer->l_data = 0; /* so inpdata does not get freed */ + InpAbort(); /* redisplays... */ + *ppbuf = pbuf; + *plen = len; + display = inpdisplay; + if ((inpdata->inpmode & INP_RAW) == 0) + (*inpdata->inpfinfunc)(inpdata->inp.buf, inpdata->inp.len, inpdata->priv); + else + (*inpdata->inpfinfunc)(pbuf - 1, 0, inpdata->priv); + if (inpdata->search) + free(inpdata->search); + free(inpdata); + return; + } + else + { + /* The user was searching, and then pressed some non-control input. So reset + * the search string. */ + RESET_SEARCH; + } + } + if (!(inpdata->inpmode & INP_RAW)) + { + flayer->l_x = inpdata->inpstringlen + (inpdata->inpmode & INP_NOECHO ? 0 : inpdata->inp.pos); + flayer->l_y = INPUTLINE; + } + *ppbuf = pbuf; + *plen = len; +} + +static void +InpAbort() +{ + LAY_CALL_UP(LayRedisplayLine(INPUTLINE, 0, flayer->l_width - 1, 0)); + ExitOverlayPage(); +} + +static void +InpRedisplayLine(y, xs, xe, isblank) +int y, xs, xe, isblank; +{ + int q, r, s, l, v; + struct inpdata *inpdata; + + inpdata = (struct inpdata *)flayer->l_data; + if (y != INPUTLINE) + { + LAY_CALL_UP(LayRedisplayLine(y, xs, xe, isblank)); + return; + } + inpdata->inp.buf[inpdata->inp.len] = 0; + q = xs; + v = xe - xs + 1; + s = 0; + r = inpdata->inpstringlen; + if (v > 0 && q < r) + { + l = v; + if (l > r - q) + l = r - q; + LPutStr(flayer, inpdata->inpstring + q - s, l, &mchar_so, q, y); + q += l; + v -= l; + } + s = r; + r += inpdata->inp.len; + if (!(inpdata->inpmode & INP_NOECHO) && v > 0 && q < r) + { + l = v; + if (l > r - q) + l = r - q; + LPutStr(flayer, inpdata->inp.buf + q - s, l, &mchar_so, q, y); + q += l; + v -= l; + } + s = r; + r = flayer->l_width; + if (!isblank && v > 0 && q < r) + { + l = v; + if (l > r - q) + l = r - q; + LClearArea(flayer, q, y, q + l - 1, y, 0, 0); + q += l; + } +} + |