diff options
Diffstat (limited to 'list_window.c')
-rw-r--r-- | list_window.c | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/list_window.c b/list_window.c new file mode 100644 index 0000000..72caad0 --- /dev/null +++ b/list_window.c @@ -0,0 +1,708 @@ +/* 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 + * + **************************************************************** + */ + +/* Deals with the list of windows */ + +/* NOTE: A 'struct win *' is used as the 'data' for each row. It might make more sense + * to use 'struct win* ->w_number' as the 'data', instead, because that way, we can + * verify that the window does exist (by looking at wtab[]). + */ + +#include "config.h" +#include "screen.h" +#include "layer.h" +#include "extern.h" +#include "list_generic.h" + +extern struct layer *flayer; +extern struct display *display, *displays; + +extern char *wlisttit; +extern char *wliststr; + +extern struct mchar mchar_blank, mchar_so; +extern int renditions[]; + +extern struct win **wtab, *windows, *fore; +extern int maxwin; + +extern char *noargs[]; + +static char ListID[] = "window"; + +struct gl_Window_Data +{ + struct win *group; /* Set only for a W_TYPE_GROUP window */ + int order; /* MRU? NUM? */ + int onblank; + int nested; + struct win *fore; /* The foreground window we had. */ +}; + +/* Is this wdata for a group window? */ +#define WLIST_FOR_GROUP(wdate) ((wdata)->group && !(wdata)->onblank && Layer2Window(flayer) && Layer2Window(flayer)->w_type == W_TYPE_GROUP) + +/* This macro should not be used if 'fn' is expected to update the window list */ +#define FOR_EACH_WINDOW(_wdata, _w, fn) do { \ + if ((_wdata)->order == WLIST_MRU) \ + { \ + struct win *_ww; \ + for (_ww = windows; _ww; _ww = _ww->w_next) \ + { \ + _w = _ww; \ + fn \ + } \ + } \ + else \ + { \ + struct win **_ww, *_witer; \ + for (_ww = wtab, _witer = windows; _witer && _ww - wtab < maxwin; _ww++) \ + { \ + if (!(_w = *_ww)) continue; \ + fn \ + _witer = _witer->w_next; \ + } \ + } \ + } while (0) + +/* Is 'a' an ancestor of 'd'? */ +static int +window_ancestor(struct win *a, struct win *d) +{ + if (!a) + return 1; /* Every window is a descendant of the 'null' group */ + for (; d; d = d->w_group) + if (d->w_group == a) + return 1; + return 0; +} + +static void +window_kill_confirm(char *buf, int len, char *data) +{ + struct win *w = windows; + struct action act; + + if (len || (*buf != 'y' && *buf != 'Y')) + { + memset(buf, 0, len); + return; + } + + /* Loop over the windows to make sure that the window actually still exists. */ + for (; w; w = w->w_next) + if (w == (struct win *)data) + break; + + if (!w) + return; + + /* Pretend the selected window is the foreground window. Then trigger a non-interactive 'kill' */ + fore = w; + act.nr = RC_KILL; + act.args = noargs; + act.argl = 0; + act.quiet = 0; + DoAction(&act, -1); +} + +static struct ListRow * +gl_Window_add_group(struct ListData *ldata, struct ListRow *row) +{ + /* Right now, 'row' doesn't have any child. */ + struct gl_Window_Data *wdata = ldata->data; + struct win *group = row->data, *w; + struct ListRow *cur = row; + + ASSERT(wdata->nested); + + FOR_EACH_WINDOW(wdata, w, + if (w->w_group != group) + continue; + + cur = glist_add_row(ldata, w, cur); + if (w == wdata->fore) + ldata->selected = cur; + + if (w->w_type == W_TYPE_GROUP) + cur = gl_Window_add_group(ldata, cur); + ); + + return cur; +} + +static void +gl_Window_rebuild(struct ListData *ldata) +{ + struct ListRow *row = NULL; + struct gl_Window_Data *wdata = ldata->data; + struct win *w; + + FOR_EACH_WINDOW(wdata, w, + if (w->w_group != wdata->group) + continue; + row = glist_add_row(ldata, w, row); + if (w == wdata->fore) + ldata->selected = row; + if (w->w_type == W_TYPE_GROUP && wdata->nested) + row = gl_Window_add_group(ldata, row); + ); + glist_display_all(ldata); +} + +static struct ListRow * +gl_Window_findrow(struct ListData *ldata, struct win *p) +{ + struct ListRow *row = ldata->root; + for (; row; row = row->next) + { + if (row->data == p) + break; + } + return row; +} + +static int +gl_Window_remove(struct ListData *ldata, struct win *p) +{ + struct ListRow *row = gl_Window_findrow(ldata, p); + if (!row) + return 0; + + /* Remove 'row'. Update 'selected', 'top', 'root' if necessary. */ + if (row->next) + row->next->prev = row->prev; + if (row->prev) + row->prev->next = row->next; + + if (ldata->selected == row) + ldata->selected = row->prev ? row->prev : row->next; + if (ldata->top == row) + ldata->top = row->prev ? row->prev : row->next; + if (ldata->root == row) + ldata->root = row->next; + + ldata->list_fn->gl_freerow(ldata, row); + free(row); + + return 1; +} + +static int +gl_Window_header(struct ListData *ldata) +{ + char *str; + struct gl_Window_Data *wdata = ldata->data; + int g; + + if ((g = (wdata->group != NULL))) + { + LPutWinMsg(flayer, "Group: ", 7, &mchar_blank, 0, 0); + LPutWinMsg(flayer, wdata->group->w_title, strlen(wdata->group->w_title), &mchar_blank, 7, 0); + } + + str = MakeWinMsgEv(wlisttit, (struct win *)0, '%', flayer->l_width, (struct event *)0, 0); + + LPutWinMsg(flayer, str, strlen(str), &mchar_blank, 0, g); + return 2 + g; +} + +static int +gl_Window_footer(struct ListData *ldata) +{ + return 0; +} + +static int +gl_Window_row(struct ListData *ldata, struct ListRow *lrow) +{ + char *str; + struct win *w, *g; + int xoff; + struct mchar *mchar; + struct mchar mchar_rend = mchar_blank; + struct gl_Window_Data *wdata = ldata->data; + + w = lrow->data; + + /* First, make sure we want to display this window in the list. + * If we are showing a list for a group, and not on blank, then we must + * only show the windows directly belonging to that group. + * Otherwise, do some more checks. */ + + for (xoff = 0, g = w->w_group; g != wdata->group; g = g->w_group) + xoff += 2; + str = MakeWinMsgEv(wliststr, w, '%', flayer->l_width - xoff, NULL, 0); + if (ldata->selected == lrow) + mchar = &mchar_so; + else if (w->w_monitor == MON_DONE && renditions[REND_MONITOR] != -1) + { + mchar = &mchar_rend; + ApplyAttrColor(renditions[REND_MONITOR], mchar); + } + else if ((w->w_bell == BELL_DONE || w->w_bell == BELL_FOUND) && renditions[REND_BELL] != -1) + { + mchar = &mchar_rend; + ApplyAttrColor(renditions[REND_BELL], mchar); + } + else if ((w->w_silence == SILENCE_FOUND || w->w_silence == SILENCE_DONE) && renditions[REND_SILENCE] != -1) + { + mchar = &mchar_rend; + ApplyAttrColor(renditions[REND_SILENCE], mchar); + } + else + mchar = &mchar_blank; + + LPutWinMsg(flayer, str, flayer->l_width, mchar, xoff, lrow->y); + if (xoff) + LPutWinMsg(flayer, "", xoff, mchar, 0, lrow->y); + + return 1; +} + +static int +gl_Window_input(struct ListData *ldata, char **inp, int *len) +{ + struct win *win; + unsigned char ch; + struct display *cd = display; + struct gl_Window_Data *wdata = ldata->data; + + if (!ldata->selected) + return 0; + + ch = (unsigned char) **inp; + ++*inp; + --*len; + + win = ldata->selected->data; + switch (ch) + { + case ' ': + case '\n': + case '\r': + if (!win) + break; +#ifdef MULTIUSER + if (display && AclCheckPermWin(D_user, ACL_READ, win)) + return 0; /* Not allowed to switch to this window. */ +#endif + if (WLIST_FOR_GROUP(wdata)) + SwitchWindow(win->w_number); + else + { + /* Abort list only when not in a group window. */ + glist_abort(); + display = cd; + if (D_fore != win) + SwitchWindow(win->w_number); + } + *len = 0; + break; + + case 'm': + /* Toggle MRU-ness */ + wdata->order = wdata->order == WLIST_MRU ? WLIST_NUM : WLIST_MRU; + glist_remove_rows(ldata); + gl_Window_rebuild(ldata); + break; + + case 'g': + /* Toggle nestedness */ + wdata->nested = !wdata->nested; + glist_remove_rows(ldata); + gl_Window_rebuild(ldata); + break; + + case 'a': + /* All-window view */ + if (wdata->group) + { + int order = wdata->order | (wdata->nested ? WLIST_NESTED : 0); + glist_abort(); + display = cd; + display_windows(1, order, NULL); + *len = 0; + } + else if (!wdata->nested) + { + wdata->nested = 1; + glist_remove_rows(ldata); + gl_Window_rebuild(ldata); + } + break; + + case 010: /* ^H */ + case 0177: /* Backspace */ + if (!wdata->group) + break; + if (wdata->group->w_group) + { + /* The parent is another group window. So switch to that window. */ + struct win *g = wdata->group->w_group; + glist_abort(); + display = cd; + SetForeWindow(g); + *len = 0; + } + else + { + /* We were in a group view. Now we are moving to an all-window view. + * So treat it as 'windowlist on blank'. */ + int order = wdata->order | (wdata->nested ? WLIST_NESTED : 0); + glist_abort(); + display = cd; + display_windows(1, order, NULL); + *len = 0; + } + break; + + case ',': /* Switch numbers with the previous window. */ + if (wdata->order == WLIST_NUM && ldata->selected->prev) + { + struct win *pw = ldata->selected->prev->data; + if (win->w_group != pw->w_group) + break; /* Do not allow switching with the parent group */ + + /* When a windows's number is successfully changed, it triggers a WListUpdatecv + * with NULL window. So that causes a redraw of the entire list. So reset the + * 'selected' after that. */ + wdata->fore = win; + WindowChangeNumber(win->w_number, pw->w_number); + } + break; + + case '.': /* Switch numbers with the next window. */ + if (wdata->order == WLIST_NUM && ldata->selected->next) + { + struct win *nw = ldata->selected->next->data; + if (win->w_group != nw->w_group) + break; /* Do not allow switching with the parent group */ + + wdata->fore = win; + WindowChangeNumber(win->w_number, nw->w_number); + } + break; + + case 'K': /* Kill a window */ + { + char str[MAXSTR]; + snprintf(str, sizeof(str) - 1, "Really kill window %d (%s) [y/n]", + win->w_number, win->w_title); + Input(str, 1, INP_RAW, window_kill_confirm, (char *)win, 0); + } + break; + + case 033: /* escape */ + case 007: /* ^G */ + if (!WLIST_FOR_GROUP(wdata)) + { + int fnumber = wdata->onblank ? wdata->fore->w_number : -1; + glist_abort(); + display = cd; + if (fnumber >= 0) + SwitchWindow(fnumber); + *len = 0; + } + break; + default: + if (ch >= '0' && ch <= '9') + { + struct ListRow *row = ldata->root; + for (; row; row = row->next) + { + struct win *w = row->data; + if (w->w_number == ch - '0') + { + struct ListRow *old = ldata->selected; + if (old == row) + break; + ldata->selected = row; + 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. */ + ldata->top = row; + 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(); + } + break; + } + } + break; + } + --*inp; + ++*len; + return 0; + } + return 1; +} + +static int +gl_Window_freerow(struct ListData *ldata, struct ListRow *row) +{ + return 0; +} + +static int +gl_Window_free(struct ListData *ldata) +{ + Free(ldata->data); + return 0; +} + +static int +gl_Window_match(struct ListData *ldata, struct ListRow *row, const char *needle) +{ + struct win *w = row->data; + if (InStr(w->w_title, needle)) + return 1; + return 0; +} + +static struct GenericList gl_Window = +{ + gl_Window_header, + gl_Window_footer, + gl_Window_row, + gl_Window_input, + gl_Window_freerow, + gl_Window_free, + gl_Window_match +}; + +void +display_windows(int onblank, int order, struct win *group) +{ + struct win *p; + struct ListData *ldata; + struct gl_Window_Data *wdata; + + if (flayer->l_width < 10 || flayer->l_height < 6) + { + LMsg(0, "Window size too small for window list page"); + return; + } + + if (group) + onblank = 0; /* When drawing a group window, ignore 'onblank' */ + + if (onblank) + { + debug3("flayer %x %d %x\n", flayer, flayer->l_width, flayer->l_height); + if (!display) + { + LMsg(0, "windowlist -b: display required"); + return; + } + p = D_fore; + if (p) + { + SetForeWindow((struct win *)0); + if (p->w_group) + { + D_fore = p->w_group; + flayer->l_data = (char *)p->w_group; + } + Activate(0); + } + if (flayer->l_width < 10 || flayer->l_height < 6) + { + LMsg(0, "Window size too small for window list page"); + return; + } + } + else + p = Layer2Window(flayer); + if (!group && p) + group = p->w_group; + + ldata = glist_display(&gl_Window, ListID); + if (!ldata) + { + if (onblank && p) + { + /* Could not display the list. So restore the window. */ + SetForeWindow(p); + Activate(1); + } + return; + } + + wdata = calloc(1, sizeof(struct gl_Window_Data)); + wdata->group = group; + wdata->order = (order & ~WLIST_NESTED); + wdata->nested = !!(order & WLIST_NESTED); + wdata->onblank = onblank; + + /* Set the most recent window as selected. */ + wdata->fore = windows; + while (wdata->fore && wdata->fore->w_group != group) + wdata->fore = wdata->fore->w_next; + + ldata->data = wdata; + + gl_Window_rebuild(ldata); +} + +static void +WListUpdate(struct win *p, struct ListData *ldata) +{ + struct gl_Window_Data *wdata = ldata->data; + struct ListRow *row, *rbefore; + struct win *before; + int d = 0, sel = 0; + + if (!p) + { + if (ldata->selected) + wdata->fore = ldata->selected->data; /* Try to retain the current selection */ + glist_remove_rows(ldata); + gl_Window_rebuild(ldata); + return; + } + + /* First decide if this window should be displayed at all. */ + d = 1; + if (wdata->order == WLIST_NUM || wdata->order == WLIST_MRU) + { + if (p->w_group != wdata->group) + { + if (!wdata->nested) + d = 0; + else + d = window_ancestor(wdata->group, p); + } + } + + if (!d) + { + if (gl_Window_remove(ldata, p)) + glist_display_all(ldata); + return; + } + + /* OK, so we keep the window in the list. Update the ordering. + * First, find the row where this window should go to. Then, either create + * a new row for that window, or move the exising row for the window to the + * correct place. */ + before = NULL; + if (wdata->order == WLIST_MRU) + { + if (windows != p) + for (before = windows; before; before = before->w_next) + if (before->w_next == p) + break; + } + else if (wdata->order == WLIST_NUM) + { + if (p->w_number != 0) + { + struct win **w = wtab + p->w_number - 1; + for (; w >= wtab; w--) + { + if (*w && (*w)->w_group == wdata->group) + { + before = *w; + break; + } + } + } + } + + /* Now, find the row belonging to 'before' */ + if (before) + rbefore = gl_Window_findrow(ldata, before); + else if (wdata->nested && p->w_group) /* There's no 'before'. So find the group window */ + rbefore = gl_Window_findrow(ldata, p->w_group); + else + rbefore = NULL; + + /* For now, just remove the row containing 'p' if it is not already in the right place . */ + row = gl_Window_findrow(ldata, p); + if (row) + { + if (row->prev != rbefore) + { + sel = ldata->selected->data == p; + gl_Window_remove(ldata, p); + } + else + p = NULL; /* the window is in the correct place */ + } + if (p) + { + row = glist_add_row(ldata, p, rbefore); + if (sel) + ldata->selected = row; + } + glist_display_all(ldata); +} + +void +WListUpdatecv(cv, p) +struct canvas *cv; +struct win *p; +{ + struct ListData *ldata; + struct gl_Window_Data *wdata; + + if (cv->c_layer->l_layfn != &ListLf) + return; + ldata = cv->c_layer->l_data; + if (ldata->name != ListID) + return; + wdata = ldata->data; + CV_CALL(cv, WListUpdate(p, ldata)); +} + +void +WListLinkChanged() +{ + struct display *olddisplay = display; + struct canvas *cv; + struct ListData *ldata; + struct gl_Window_Data *wdata; + + for (display = displays; display; display = display->d_next) + for (cv = D_cvlist; cv; cv = cv->c_next) + { + if (!cv->c_layer || cv->c_layer->l_layfn != &ListLf) + continue; + ldata = cv->c_layer->l_data; + if (ldata->name != ListID) + continue; + wdata = ldata->data; + if (!(wdata->order & WLIST_MRU)) + continue; + CV_CALL(cv, WListUpdate(0, ldata)); + } + display = olddisplay; +} + |