diff options
Diffstat (limited to 'list_generic.c')
-rw-r--r-- | list_generic.c | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/list_generic.c b/list_generic.c new file mode 100644 index 0000000..e32acf7 --- /dev/null +++ b/list_generic.c @@ -0,0 +1,486 @@ +/* Copyright (c) 2010 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * + * 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 "config.h" +#include "screen.h" +#include "list_generic.h" +#include "layer.h" +#include "extern.h" + +/* Deals with a generic list display */ + +extern struct layer *flayer; + +static void ListProcess __P((char **, int *)); +static void ListAbort __P((void)); +static void ListRedisplayLine __P((int, int, int, int)); +static void ListClearLine __P((int, int, int, int)); +static int ListRewrite __P((int, int, int, struct mchar *, int)); +static int ListResize __P((int, int)); +static void ListRestore __P((void)); +static void ListFree __P((void *)); + +struct LayFuncs ListLf = +{ + ListProcess, + ListAbort, + ListRedisplayLine, + ListClearLine, + ListRewrite, + ListResize, + ListRestore, + ListFree +}; + +/** Returns non-zero on success. */ +struct ListData * +glist_display(struct GenericList *list, const char *name) +{ + struct ListData *ldata; + + if (InitOverlayPage(sizeof(struct ListData), &ListLf, 0)) + return NULL; + ldata = flayer->l_data; + + ldata->name = name; /* We do not SaveStr, since the strings should be all static literals */ + ldata->list_fn = list; + + flayer->l_mode = 1; + flayer->l_x = 0; + flayer->l_y = flayer->l_height - 1; + + return ldata; +} + +static void +glist_decide_top(struct ListData *ldata) +{ + int count = flayer->l_height - 5; /* 2 for header, 1 for footer */ + struct ListRow *top = ldata->selected; + for (; count && top != ldata->root; top = top->prev, count--) + ; + ldata->top = top; +} + +static struct ListRow * +glist_search_dir(struct ListData *ldata, struct ListRow *start, int dir) +{ + struct ListRow *row = (dir == 1) ? start->next : start->prev; + for (; row; row = (dir == 1) ? row->next : row->prev) + if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search)) + return row; + + if (dir == 1) + row = ldata->root; + else + { + /* First, go to the end */ + if (!start->next) + row = start; + else + for (row = start->next; row->next; row = row->next) + ; + } + + for (; row != start; row = (dir == 1) ? row->next : row->prev) + if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search)) + break; + + return row; +} + +static void +glist_search(char *buf, int len, char *data) +{ + struct ListData *ldata = (struct ListData *)data; + struct ListRow *row; + + if (ldata->search) + Free(ldata->search); + if (len > 0) + ldata->search = SaveStr(buf); + else + return; + + for (row = ldata->selected; row; row = row->next) + if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search)) + break; + + if (!row) + for (row = ldata->root; row != ldata->selected; row = row->next) + if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search)) + break; + + if (row == ldata->selected) + return; + + ldata->selected = row; + if (ldata->selected->y == -1) + glist_decide_top(ldata); + glist_display_all(ldata); +} + +static void ListProcess(char **ppbuf, int *plen) +{ + struct ListData *ldata = flayer->l_data; + int count = 0; + + while (*plen > 0) + { + struct ListRow *old; + unsigned char ch; + + if (!flayer->l_mouseevent.start && ldata->list_fn->gl_pinput && + ldata->list_fn->gl_pinput(ldata, ppbuf, plen)) + continue; + + ch = **ppbuf; + ++*ppbuf; + --*plen; + + if (flayer->l_mouseevent.start) + { + int r = LayProcessMouse(flayer, ch); + if (r == -1) + { + LayProcessMouseSwitch(flayer, 0); + continue; + } + else + { + if (r) + ch = 0222; + else + continue; + } + } + + if (!ldata->selected) + { + *plen = 0; + break; + } + + old = ldata->selected; + +processchar: + switch (ch) + { + case ' ': + break; + + case '\r': + case '\n': + break; + + case 0220: /* up */ + case 16: /* ^P */ + case 'k': + if (!ldata->selected->prev) /* There's no where to go */ + break; + ldata->selected = old->prev; + break; + + case 0216: /* down */ + case 14: /* ^N like emacs */ + case 'j': + if (!ldata->selected->next) /* Nothing to do */ + break; + ldata->selected = old->next; + break; + + case 033: /* escape */ + case 007: /* ^G */ + ListAbort(); + *plen = 0; + return; + + case 0201: /* home */ + case 0001: /* ^A */ + ldata->selected = ldata->root; + break; + + case 0205: /* end */ + case 0005: /* ^E */ + while (ldata->selected->next) + ldata->selected = ldata->selected->next; + if (ldata->selected->y != -1) + { + /* Both old and current selections are on the screen. So we can just + * redraw these two affected rows. */ + } + break; + + case 0004: /* ^D (half-page down) */ + case 0006: /* page-down, ^F */ + count = (flayer->l_height - 4) >> (ch == 0004); + for (; ldata->selected->next && --count; + ldata->selected = ldata->selected->next) + ; + break; + + case 0025: /* ^U (half-page up) */ + case 0002: /* page-up, ^B */ + count = (flayer->l_height - 4) >> (ch == 0025); + for (; ldata->selected->prev && --count; + ldata->selected = ldata->selected->prev) + ; + break; + + case '/': /* start searching */ + if (ldata->list_fn->gl_matchrow) + { + char *s; + Input("Search: ", 80, INP_COOKED, glist_search, (char *)ldata, 0); + if ((s = ldata->search)) + { + for (; *s; s++) + { + char *ss = s; + int n = 1; + LayProcess(&ss, &n); + } + } + } + break; + + /* The following deal with searching. */ + + case 'n': /* search next */ + if (ldata->list_fn->gl_matchrow && ldata->search) + ldata->selected = glist_search_dir(ldata, ldata->selected, 1); + break; + + case 'N': /* search prev */ + if (ldata->list_fn->gl_matchrow && ldata->search) + ldata->selected = glist_search_dir(ldata, ldata->selected, -1); + break; + + /* Now, mouse events. */ + case 0222: + if (flayer->l_mouseevent.start) + { + int button = flayer->l_mouseevent.buffer[0]; + if (button == 'a') /* Scroll down */ + ch = 'j'; + else if (button == '`') /* Scroll up */ + ch = 'k'; + else if (button == ' ') /* Left click */ + { + int y = flayer->l_mouseevent.buffer[2]; + struct ListRow *r = ldata->top; + for (r = ldata->top; r && r->y != -1 && r->y != y; r = r->next) + ; + if (r && r->y == y) + ldata->selected = r; + ch = 0; + } + else + ch = 0; + LayProcessMouseSwitch(flayer, 0); + if (ch) + goto processchar; + } + else + LayProcessMouseSwitch(flayer, 1); + break; + } + + if (old == ldata->selected) /* The selection didn't change */ + continue; + + if (ldata->selected->y == -1) + { + /* We need to list all the rows, since we are scrolling down. But first, + * find the top of the visible list. */ + glist_decide_top(ldata); + glist_display_all(ldata); + } + else + { + /* just redisplay the two lines. */ + ldata->list_fn->gl_printrow(ldata, old); + ldata->list_fn->gl_printrow(ldata, ldata->selected); + flayer->l_y = ldata->selected->y; + LaySetCursor(); + } + } +} + +static void ListAbort(void) +{ + LAY_CALL_UP(LRefreshAll(flayer, 0)); + ExitOverlayPage(); +} + +static void ListFree(void *d) +{ + struct ListData *ldata = d; + glist_remove_rows(ldata); + if (ldata->list_fn->gl_free) + ldata->list_fn->gl_free(ldata); + if (ldata->search) + Free(ldata->search); +} + +static void ListRedisplayLine(int y, int xs, int xe, int isblank) +{ + struct ListData *ldata; + ASSERT(flayer); + + ldata = flayer->l_data; + if (y < 0) + { + glist_display_all(ldata); + return; + } + + if (!isblank) + LClearArea(flayer, xs, y, xe, y, 0, 0); + + if (ldata->top && y < ldata->top->y) + ldata->list_fn->gl_printheader(ldata); + else if (y + 1 == flayer->l_height) + ldata->list_fn->gl_printfooter(ldata); + else + { + struct ListRow *row; + for (row = ldata->top; row && row->y != -1; row = row->next) + if (row->y == y) + { + ldata->list_fn->gl_printrow(ldata, row); + break; + } + } +} + +static void ListClearLine(int y, int xs, int xe, int bce) +{ + DefClearLine(y, xs, xe, bce); +} + +static int ListRewrite(int y, int xs, int xe, struct mchar *rend, int doit) +{ + return EXPENSIVE; +} + +static int ListResize (int wi, int he) +{ + if (wi < 10 || he < 5) + return -1; + + flayer->l_width = wi; + flayer->l_height = he; + flayer->l_y = he - 1; + + return 0; +} + +static void ListRestore (void) +{ + DefRestore(); +} + +struct ListRow * +glist_add_row(struct ListData *ldata, void *data, struct ListRow *after) +{ + struct ListRow *r = calloc(1, sizeof(struct ListRow)); + r->data = data; + + if (after) + { + r->next = after->next; + r->prev = after; + after->next = r; + if (r->next) + r->next->prev = r; + } + else + { + r->next = ldata->root; + if (ldata->root) + ldata->root->prev = r; + ldata->root = r; + } + + return r; +} + +void +glist_remove_rows(struct ListData *ldata) +{ + struct ListRow *row; + for (row = ldata->root; row; ) + { + struct ListRow *r = row; + row = row->next; + ldata->list_fn->gl_freerow(ldata, r); + free(r); + } + ldata->root = ldata->selected = ldata->top = NULL; +} + +void +glist_display_all(struct ListData *list) +{ + int y; + struct ListRow *row; + + LClearAll(flayer, 0); + + y = list->list_fn->gl_printheader(list); + + if (!list->top) + list->top = list->root; + if (!list->selected) + list->selected = list->root; + + for (row = list->root; row != list->top; row = row->next) + row->y = -1; + + for (row = list->top; row; row = row->next) + { + row->y = y++; + if (!list->list_fn->gl_printrow(list, row)) + { + row->y = -1; + y--; + } + if (y + 1 == flayer->l_height) + break; + } + for (; row; row = row->next) + row->y = -1; + + list->list_fn->gl_printfooter(list); + if (list->selected && list->selected->y != -1) + flayer->l_y = list->selected->y; + else + flayer->l_y = flayer->l_height - 1; + LaySetCursor(); +} + +void glist_abort(void) +{ + ListAbort(); +} + |