/* 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; }