Fix terminating an on-window list.
[screen-lua.git] / src / list_window.c
blob21ca83792a7385d60537b7da9dfe08da2ce73714
1 /* Copyright (c) 2010
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program (see the file COPYING); if not, see
17 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
20 ****************************************************************
23 /* Deals with the list of windows */
25 /* NOTE: A 'struct win *' is used as the 'data' for each row. It might make more sense
26 * to use 'struct win* ->w_number' as the 'data', instead, because that way, we can
27 * verify that the window does exist (by looking at wtab[]).
30 #include "config.h"
31 #include "screen.h"
32 #include "layer.h"
33 #include "extern.h"
34 #include "list_generic.h"
36 extern struct layer *flayer;
37 extern struct display *display, *displays;
39 extern char *wlisttit;
40 extern char *wliststr;
42 extern struct mchar mchar_blank, mchar_so;
43 extern int renditions[];
45 extern struct win **wtab, *windows, *fore;
46 extern int maxwin;
48 extern char *noargs[];
50 static char ListID[] = "window";
52 struct gl_Window_Data
54 struct win *group; /* Set only for a W_TYPE_GROUP window */
55 int order; /* MRU? NUM? */
56 int onblank;
57 int nested;
58 struct win *fore; /* The foreground window we had. */
61 /* This macro should not be used if 'fn' is expected to update the window list */
62 #define FOR_EACH_WINDOW(_wdata, _w, fn) do { \
63 if ((_wdata)->order == WLIST_MRU) \
64 { \
65 struct win *_ww; \
66 for (_ww = windows; _ww; _ww = _ww->w_next) \
67 { \
68 _w = _ww; \
69 fn \
70 } \
71 } \
72 else \
73 { \
74 struct win **_ww, *_witer; \
75 for (_ww = wtab, _witer = windows; _witer && _ww - wtab < maxwin; _ww++) \
76 { \
77 if (!(_w = *_ww)) continue; \
78 fn \
79 _witer = _witer->w_next; \
80 } \
81 } \
82 } while (0)
84 /* Is 'a' an ancestor of 'd'? */
85 static int
86 window_ancestor(struct win *a, struct win *d)
88 if (!a)
89 return 1; /* Every window is a descendant of the 'null' group */
90 for (; d; d = d->w_group)
91 if (d->w_group == a)
92 return 1;
93 return 0;
96 static void
97 window_kill_confirm(char *buf, int len, char *data)
99 struct win *w = windows;
100 struct action act;
102 if (len || (*buf != 'y' && *buf != 'Y'))
104 *buf = 0;
105 return;
108 /* Loop over the windows to make sure that the window actually still exists. */
109 for (; w; w = w->w_next)
110 if (w == (struct win *)data)
111 break;
113 if (!w)
114 return;
116 /* Pretend the selected window is the foreground window. Then trigger a non-interactive 'kill' */
117 fore = w;
118 act.nr = RC_KILL;
119 act.args = noargs;
120 act.argl = 0;
121 act.quiet = 0;
122 DoAction(&act, -1);
125 static struct ListRow *
126 gl_Window_add_group(struct ListData *ldata, struct ListRow *row)
128 /* Right now, 'row' doesn't have any child. */
129 struct gl_Window_Data *wdata = ldata->data;
130 struct win *group = row->data, *w;
131 struct ListRow *cur = row;
133 ASSERT(wdata->nested);
135 FOR_EACH_WINDOW(wdata, w,
136 if (w->w_group != group)
137 continue;
139 cur = glist_add_row(ldata, w, cur);
140 if (w == wdata->fore)
141 ldata->selected = cur;
143 if (w->w_type == W_TYPE_GROUP)
144 cur = gl_Window_add_group(ldata, cur);
147 return cur;
150 static void
151 gl_Window_rebuild(struct ListData *ldata)
153 struct ListRow *row = NULL;
154 struct gl_Window_Data *wdata = ldata->data;
155 struct win *w;
157 FOR_EACH_WINDOW(wdata, w,
158 if (w->w_group != wdata->group)
159 continue;
160 row = glist_add_row(ldata, w, row);
161 if (w == wdata->fore)
162 ldata->selected = row;
163 if (w->w_type == W_TYPE_GROUP && wdata->nested)
164 row = gl_Window_add_group(ldata, row);
166 glist_display_all(ldata);
169 static struct ListRow *
170 gl_Window_findrow(struct ListData *ldata, struct win *p)
172 struct ListRow *row = ldata->root;
173 for (; row; row = row->next)
175 if (row->data == p)
176 break;
178 return row;
181 static int
182 gl_Window_remove(struct ListData *ldata, struct win *p)
184 struct ListRow *row = gl_Window_findrow(ldata, p);
185 if (!row)
186 return 0;
188 /* Remove 'row'. Update 'selected', 'top', 'root' if necessary. */
189 if (row->next)
190 row->next->prev = row->prev;
191 if (row->prev)
192 row->prev->next = row->next;
194 if (ldata->selected == row)
195 ldata->selected = row->prev ? row->prev : row->next;
196 if (ldata->top == row)
197 ldata->top = row->prev ? row->prev : row->next;
198 if (ldata->root == row)
199 ldata->root = row->next;
201 ldata->list_fn->gl_freerow(ldata, row);
202 free(row);
204 return 1;
207 static int
208 gl_Window_header(struct ListData *ldata)
210 char *str;
211 struct gl_Window_Data *wdata = ldata->data;
212 int g;
214 if ((g = (wdata->group != NULL)))
216 LPutWinMsg(flayer, "Group: ", 7, &mchar_blank, 0, 0);
217 LPutWinMsg(flayer, wdata->group->w_title, strlen(wdata->group->w_title), &mchar_blank, 7, 0);
220 display = 0;
221 str = MakeWinMsgEv(wlisttit, (struct win *)0, '%', flayer->l_width, (struct event *)0, 0);
223 LPutWinMsg(flayer, str, strlen(str), &mchar_blank, 0, g);
224 return 2 + g;
227 static int
228 gl_Window_footer(struct ListData *ldata)
230 return 0;
233 static int
234 gl_Window_row(struct ListData *ldata, struct ListRow *lrow)
236 char *str;
237 struct win *w, *g;
238 int xoff;
239 struct mchar *mchar;
240 struct mchar mchar_rend = mchar_blank;
241 struct gl_Window_Data *wdata = ldata->data;
243 w = lrow->data;
245 /* First, make sure we want to display this window in the list.
246 * If we are showing a list for a group, and not on blank, then we must
247 * only show the windows directly belonging to that group.
248 * Otherwise, do some more checks. */
250 for (xoff = 0, g = w->w_group; g != wdata->group; g = g->w_group)
251 xoff += 2;
252 display = Layer2Window(flayer) ? 0 : flayer->l_cvlist ? flayer->l_cvlist->c_display : 0;
253 str = MakeWinMsgEv(wliststr, w, '%', flayer->l_width - xoff, NULL, 0);
254 if (ldata->selected == lrow)
255 mchar = &mchar_so;
256 else if (w->w_monitor == MON_DONE && renditions[REND_MONITOR] != -1)
258 mchar = &mchar_rend;
259 ApplyAttrColor(renditions[REND_MONITOR], mchar);
261 else if ((w->w_bell == BELL_DONE || w->w_bell == BELL_FOUND) && renditions[REND_BELL] != -1)
263 mchar = &mchar_rend;
264 ApplyAttrColor(renditions[REND_BELL], mchar);
266 else
267 mchar = &mchar_blank;
269 LPutWinMsg(flayer, str, flayer->l_width, mchar, xoff, lrow->y);
270 if (xoff)
271 LPutWinMsg(flayer, "", xoff, mchar, 0, lrow->y);
273 return 1;
276 static int
277 gl_Window_input(struct ListData *ldata, char **inp, int *len)
279 struct win *win;
280 unsigned char ch;
281 struct display *cd = display;
282 struct gl_Window_Data *wdata = ldata->data;
284 if (!ldata->selected)
285 return 0;
287 ch = (unsigned char) **inp;
288 ++*inp;
289 --*len;
291 win = ldata->selected->data;
292 switch (ch)
294 case ' ':
295 case '\n':
296 case '\r':
297 if (!win)
298 break;
299 #ifdef MULTIUSER
300 if (display && AclCheckPermWin(D_user, ACL_READ, win))
301 return; /* Not allowed to switch to this window. */
302 #endif
303 if (wdata->onblank || (!wdata->onblank && wdata->group))
305 /* Do not abort the group window. */
306 glist_abort();
307 display = cd;
309 if (D_fore != win)
310 SwitchWindow(win->w_number);
311 *len = 0;
312 break;
314 case 'm':
315 /* Toggle MRU-ness */
316 wdata->order = wdata->order == WLIST_MRU ? WLIST_NUM : WLIST_MRU;
317 glist_remove_rows(ldata);
318 gl_Window_rebuild(ldata);
319 break;
321 case 'g':
322 /* Toggle nestedness */
323 wdata->nested = !wdata->nested;
324 glist_remove_rows(ldata);
325 gl_Window_rebuild(ldata);
326 break;
328 case 'a':
329 /* All-window view */
330 if (wdata->group)
332 int order = wdata->order | (wdata->nested ? WLIST_NESTED : 0);
333 glist_abort();
334 display = cd;
335 display_windows(1, order, NULL);
336 *len = 0;
338 else if (!wdata->nested)
340 wdata->nested = 1;
341 glist_remove_rows(ldata);
342 gl_Window_rebuild(ldata);
344 break;
346 case 010: /* ^H */
347 case 0177: /* Backspace */
348 if (!wdata->group)
349 break;
350 if (wdata->group->w_group)
352 /* The parent is another group window. So switch to that window. */
353 struct win *g = wdata->group->w_group;
354 glist_abort();
355 display = cd;
356 SetForeWindow(g);
357 *len = 0;
359 else
361 /* We were in a group view. Now we are moving to an all-window view.
362 * So treat it as 'windowlist on blank'. */
363 int order = wdata->order | (wdata->nested ? WLIST_NESTED : 0);
364 glist_abort();
365 display = cd;
366 display_windows(1, order, NULL);
367 *len = 0;
369 break;
371 case ',': /* Switch numbers with the previous window. */
372 if (wdata->order == WLIST_NUM && ldata->selected->prev)
374 struct win *pw = ldata->selected->prev->data;
375 if (win->w_group != pw->w_group)
376 break; /* Do not allow switching with the parent group */
378 /* When a windows's number is successfully changed, it triggers a WListUpdatecv
379 * with NULL window. So that causes a redraw of the entire list. So reset the
380 * 'selected' after that. */
381 wdata->fore = win;
382 WindowChangeNumber(win, pw->w_number);
384 break;
386 case '.': /* Switch numbers with the next window. */
387 if (wdata->order == WLIST_NUM && ldata->selected->next)
389 struct win *nw = ldata->selected->next->data;
390 if (win->w_group != nw->w_group)
391 break; /* Do not allow switching with the parent group */
393 wdata->fore = win;
394 WindowChangeNumber(win, nw->w_number);
396 break;
398 case 'K': /* Kill a window */
400 char str[MAXSTR];
401 snprintf(str, sizeof(str) - 1, "Really kill window %d (%s) [y/n]",
402 win->w_number, win->w_title);
403 Input(str, 1, INP_RAW, window_kill_confirm, (char *)win, 0);
405 break;
407 case 033: /* escape */
408 case 007: /* ^G */
409 if (wdata->onblank || (!wdata->onblank && wdata->group))
411 int fnumber = wdata->fore->w_number;
412 glist_abort();
413 display = cd;
414 if (wdata->onblank)
415 SwitchWindow(fnumber);
416 *len = 0;
417 break;
419 /* else FALLTHROUGH */
420 default:
421 --*inp;
422 ++*len;
423 return 0;
425 return 1;
428 static int
429 gl_Window_freerow(struct ListData *ldata, struct ListRow *row)
431 return 0;
434 static int
435 gl_Window_free(struct ListData *ldata)
437 Free(ldata->data);
438 return 0;
441 static int
442 gl_Window_match(struct ListData *ldata, struct ListRow *row, const char *needle)
444 struct win *w = row->data;
445 if (InStr(w->w_title, needle))
446 return 1;
447 return 0;
450 static struct GenericList gl_Window =
452 gl_Window_header,
453 gl_Window_footer,
454 gl_Window_row,
455 gl_Window_input,
456 gl_Window_freerow,
457 gl_Window_free,
458 gl_Window_match
461 void
462 display_windows(int onblank, int order, struct win *group)
464 struct win *p;
465 struct ListData *ldata;
466 struct gl_Window_Data *wdata;
468 if (flayer->l_width < 10 || flayer->l_height < 6)
470 LMsg(0, "Window size too small for window list page");
471 return;
474 if (group)
475 onblank = 0; /* When drawing a group window, ignore 'onblank' */
477 if (onblank)
479 debug3("flayer %x %d %x\n", flayer, flayer->l_width, flayer->l_height);
480 if (!display)
482 LMsg(0, "windowlist -b: display required");
483 return;
485 p = D_fore;
486 if (p)
488 SetForeWindow((struct win *)0);
489 if (p->w_group)
491 D_fore = p->w_group;
492 flayer->l_data = (char *)p->w_group;
494 Activate(0);
496 if (flayer->l_width < 10 || flayer->l_height < 6)
498 LMsg(0, "Window size too small for window list page");
499 return;
502 else
503 p = Layer2Window(flayer);
504 if (!group && p)
505 group = p->w_group;
507 ldata = glist_display(&gl_Window, ListID);
508 if (!ldata)
510 if (onblank && p)
512 /* Could not display the list. So restore the window. */
513 SetForeWindow(p);
514 Activate(1);
516 return;
519 wdata = calloc(1, sizeof(struct gl_Window_Data));
520 wdata->group = group;
521 wdata->order = (order & ~WLIST_NESTED);
522 wdata->nested = !!(order & WLIST_NESTED);
523 wdata->onblank = onblank;
525 /* Set the most recent window as selected. */
526 wdata->fore = windows;
527 while (wdata->fore && wdata->fore->w_group != group)
528 wdata->fore = wdata->fore->w_next;
530 ldata->data = wdata;
532 gl_Window_rebuild(ldata);
535 static void
536 WListUpdate(struct win *p, struct ListData *ldata)
538 struct gl_Window_Data *wdata = ldata->data;
539 struct ListRow *row, *rbefore;
540 struct win *before;
541 int d = 0, sel = 0;
543 if (!p)
545 if (ldata->selected)
546 wdata->fore = ldata->selected->data; /* Try to retain the current selection */
547 glist_remove_rows(ldata);
548 gl_Window_rebuild(ldata);
549 return;
552 /* First decide if this window should be displayed at all. */
553 d = 1;
554 if (wdata->order == WLIST_NUM || wdata->order == WLIST_MRU)
556 if (p->w_group != wdata->group)
558 if (!wdata->nested)
559 d = 0;
560 else
561 d = window_ancestor(wdata->group, p);
565 if (!d)
567 if (gl_Window_remove(ldata, p))
568 glist_display_all(ldata);
569 return;
572 /* OK, so we keep the window in the list. Update the ordering.
573 * First, find the row where this window should go to. Then, either create
574 * a new row for that window, or move the exising row for the window to the
575 * correct place. */
576 before = NULL;
577 if (wdata->order == WLIST_MRU)
579 if (windows != p)
580 for (before = windows; before; before = before->w_next)
581 if (before->w_next == p)
582 break;
584 else if (wdata->order == WLIST_NUM)
586 if (p->w_number != 0)
588 struct win **w = wtab + p->w_number - 1;
589 for (; w >= wtab; w--)
591 if (*w && (*w)->w_group == wdata->group)
593 before = *w;
594 break;
600 /* Now, find the row belonging to 'before' */
601 if (before)
602 rbefore = gl_Window_findrow(ldata, before);
603 else if (wdata->nested && p->w_group) /* There's no 'before'. So find the group window */
604 rbefore = gl_Window_findrow(ldata, p->w_group);
605 else
606 rbefore = NULL;
608 /* For now, just remove the row containing 'p' if it is not already in the right place . */
609 row = gl_Window_findrow(ldata, p);
610 if (row)
612 if (row->prev != rbefore)
614 sel = ldata->selected->data == p;
615 gl_Window_remove(ldata, p);
617 else
618 p = NULL; /* the window is in the correct place */
620 if (p)
622 row = glist_add_row(ldata, p, rbefore);
623 if (sel)
624 ldata->selected = row;
626 glist_display_all(ldata);
629 void
630 WListUpdatecv(cv, p)
631 struct canvas *cv;
632 struct win *p;
634 struct ListData *ldata;
635 struct gl_Window_Data *wdata;
637 if (cv->c_layer->l_layfn != &ListLf)
638 return;
639 ldata = cv->c_layer->l_data;
640 if (ldata->name != ListID)
641 return;
642 wdata = ldata->data;
643 CV_CALL(cv, WListUpdate(p, ldata));
646 void
647 WListLinkChanged()
649 struct display *olddisplay = display;
650 struct canvas *cv;
651 struct ListData *ldata;
652 struct gl_Window_Data *wdata;
654 for (display = displays; display; display = display->d_next)
655 for (cv = D_cvlist; cv; cv = cv->c_next)
657 if (!cv->c_layer || cv->c_layer->l_layfn != &ListLf)
658 continue;
659 ldata = cv->c_layer->l_data;
660 if (ldata->name != ListID)
661 continue;
662 wdata = ldata->data;
663 if (!(wdata->order & WLIST_MRU))
664 continue;
665 CV_CALL(cv, WListUpdate(0, ldata));
667 display = olddisplay;