summaryrefslogtreecommitdiffstats
path: root/list_window.c
diff options
context:
space:
mode:
Diffstat (limited to 'list_window.c')
-rw-r--r--list_window.c708
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;
+}
+