/* 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 * https://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(); }