Fix an invalid memory read.
[screen-lua.git] / src / list_window.c
blobee71fdea33447068df815e227eddfa39a8caed65
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 /* Is this wdata for a group window? */
62 #define WLIST_FOR_GROUP(wdate) ((wdata)->group && !(wdata)->onblank && Layer2Window(flayer) && Layer2Window(flayer)->w_type == W_TYPE_GROUP)
64 /* This macro should not be used if 'fn' is expected to update the window list */
65 #define FOR_EACH_WINDOW(_wdata, _w, fn) do { \
66 if ((_wdata)->order == WLIST_MRU) \
67 { \
68 struct win *_ww; \
69 for (_ww = windows; _ww; _ww = _ww->w_next) \
70 { \
71 _w = _ww; \
72 fn \
73 } \
74 } \
75 else \
76 { \
77 struct win **_ww, *_witer; \
78 for (_ww = wtab, _witer = windows; _witer && _ww - wtab < maxwin; _ww++) \
79 { \
80 if (!(_w = *_ww)) continue; \
81 fn \
82 _witer = _witer->w_next; \
83 } \
84 } \
85 } while (0)
87 /* Is 'a' an ancestor of 'd'? */
88 static int
89 window_ancestor(struct win *a, struct win *d)
91 if (!a)
92 return 1; /* Every window is a descendant of the 'null' group */
93 for (; d; d = d->w_group)
94 if (d->w_group == a)
95 return 1;
96 return 0;
99 static void
100 window_kill_confirm(char *buf, int len, char *data)
102 struct win *w = windows;
103 struct action act;
105 if (len || (*buf != 'y' && *buf != 'Y'))
107 *buf = 0;
108 return;
111 /* Loop over the windows to make sure that the window actually still exists. */
112 for (; w; w = w->w_next)
113 if (w == (struct win *)data)
114 break;
116 if (!w)
117 return;
119 /* Pretend the selected window is the foreground window. Then trigger a non-interactive 'kill' */
120 fore = w;
121 act.nr = RC_KILL;
122 act.args = noargs;
123 act.argl = 0;
124 act.quiet = 0;
125 DoAction(&act, -1);
128 static struct ListRow *
129 gl_Window_add_group(struct ListData *ldata, struct ListRow *row)
131 /* Right now, 'row' doesn't have any child. */
132 struct gl_Window_Data *wdata = ldata->data;
133 struct win *group = row->data, *w;
134 struct ListRow *cur = row;
136 ASSERT(wdata->nested);
138 FOR_EACH_WINDOW(wdata, w,
139 if (w->w_group != group)
140 continue;
142 cur = glist_add_row(ldata, w, cur);
143 if (w == wdata->fore)
144 ldata->selected = cur;
146 if (w->w_type == W_TYPE_GROUP)
147 cur = gl_Window_add_group(ldata, cur);
150 return cur;
153 static void
154 gl_Window_rebuild(struct ListData *ldata)
156 struct ListRow *row = NULL;
157 struct gl_Window_Data *wdata = ldata->data;
158 struct win *w;
160 FOR_EACH_WINDOW(wdata, w,
161 if (w->w_group != wdata->group)
162 continue;
163 row = glist_add_row(ldata, w, row);
164 if (w == wdata->fore)
165 ldata->selected = row;
166 if (w->w_type == W_TYPE_GROUP && wdata->nested)
167 row = gl_Window_add_group(ldata, row);
169 glist_display_all(ldata);
172 static struct ListRow *
173 gl_Window_findrow(struct ListData *ldata, struct win *p)
175 struct ListRow *row = ldata->root;
176 for (; row; row = row->next)
178 if (row->data == p)
179 break;
181 return row;
184 static int
185 gl_Window_remove(struct ListData *ldata, struct win *p)
187 struct ListRow *row = gl_Window_findrow(ldata, p);
188 if (!row)
189 return 0;
191 /* Remove 'row'. Update 'selected', 'top', 'root' if necessary. */
192 if (row->next)
193 row->next->prev = row->prev;
194 if (row->prev)
195 row->prev->next = row->next;
197 if (ldata->selected == row)
198 ldata->selected = row->prev ? row->prev : row->next;
199 if (ldata->top == row)
200 ldata->top = row->prev ? row->prev : row->next;
201 if (ldata->root == row)
202 ldata->root = row->next;
204 ldata->list_fn->gl_freerow(ldata, row);
205 free(row);
207 return 1;
210 static int
211 gl_Window_header(struct ListData *ldata)
213 char *str;
214 struct gl_Window_Data *wdata = ldata->data;
215 int g;
217 if ((g = (wdata->group != NULL)))
219 LPutWinMsg(flayer, "Group: ", 7, &mchar_blank, 0, 0);
220 LPutWinMsg(flayer, wdata->group->w_title, strlen(wdata->group->w_title), &mchar_blank, 7, 0);
223 display = 0;
224 str = MakeWinMsgEv(wlisttit, (struct win *)0, '%', flayer->l_width, (struct event *)0, 0);
226 LPutWinMsg(flayer, str, strlen(str), &mchar_blank, 0, g);
227 return 2 + g;
230 static int
231 gl_Window_footer(struct ListData *ldata)
233 return 0;
236 static int
237 gl_Window_row(struct ListData *ldata, struct ListRow *lrow)
239 char *str;
240 struct win *w, *g;
241 int xoff;
242 struct mchar *mchar;
243 struct mchar mchar_rend = mchar_blank;
244 struct gl_Window_Data *wdata = ldata->data;
246 w = lrow->data;
248 /* First, make sure we want to display this window in the list.
249 * If we are showing a list for a group, and not on blank, then we must
250 * only show the windows directly belonging to that group.
251 * Otherwise, do some more checks. */
253 for (xoff = 0, g = w->w_group; g != wdata->group; g = g->w_group)
254 xoff += 2;
255 display = Layer2Window(flayer) ? 0 : flayer->l_cvlist ? flayer->l_cvlist->c_display : 0;
256 str = MakeWinMsgEv(wliststr, w, '%', flayer->l_width - xoff, NULL, 0);
257 if (ldata->selected == lrow)
258 mchar = &mchar_so;
259 else if (w->w_monitor == MON_DONE && renditions[REND_MONITOR] != -1)
261 mchar = &mchar_rend;
262 ApplyAttrColor(renditions[REND_MONITOR], mchar);
264 else if ((w->w_bell == BELL_DONE || w->w_bell == BELL_FOUND) && renditions[REND_BELL] != -1)
266 mchar = &mchar_rend;
267 ApplyAttrColor(renditions[REND_BELL], mchar);
269 else
270 mchar = &mchar_blank;
272 LPutWinMsg(flayer, str, flayer->l_width, mchar, xoff, lrow->y);
273 if (xoff)
274 LPutWinMsg(flayer, "", xoff, mchar, 0, lrow->y);
276 return 1;
279 static int
280 gl_Window_input(struct ListData *ldata, char **inp, int *len)
282 struct win *win;
283 unsigned char ch;
284 struct display *cd = display;
285 struct gl_Window_Data *wdata = ldata->data;
287 if (!ldata->selected)
288 return 0;
290 ch = (unsigned char) **inp;
291 ++*inp;
292 --*len;
294 win = ldata->selected->data;
295 switch (ch)
297 case ' ':
298 case '\n':
299 case '\r':
300 if (!win)
301 break;
302 #ifdef MULTIUSER
303 if (display && AclCheckPermWin(D_user, ACL_READ, win))
304 return; /* Not allowed to switch to this window. */
305 #endif
306 if (WLIST_FOR_GROUP(wdata))
307 SwitchWindow(win->w_number);
308 else
310 /* Abort list only when not in a group window. */
311 glist_abort();
312 display = cd;
313 if (D_fore != win)
314 SwitchWindow(win->w_number);
316 *len = 0;
317 break;
319 case 'm':
320 /* Toggle MRU-ness */
321 wdata->order = wdata->order == WLIST_MRU ? WLIST_NUM : WLIST_MRU;
322 glist_remove_rows(ldata);
323 gl_Window_rebuild(ldata);
324 break;
326 case 'g':
327 /* Toggle nestedness */
328 wdata->nested = !wdata->nested;
329 glist_remove_rows(ldata);
330 gl_Window_rebuild(ldata);
331 break;
333 case 'a':
334 /* All-window view */
335 if (wdata->group)
337 int order = wdata->order | (wdata->nested ? WLIST_NESTED : 0);
338 glist_abort();
339 display = cd;
340 display_windows(1, order, NULL);
341 *len = 0;
343 else if (!wdata->nested)
345 wdata->nested = 1;
346 glist_remove_rows(ldata);
347 gl_Window_rebuild(ldata);
349 break;
351 case 010: /* ^H */
352 case 0177: /* Backspace */
353 if (!wdata->group)
354 break;
355 if (wdata->group->w_group)
357 /* The parent is another group window. So switch to that window. */
358 struct win *g = wdata->group->w_group;
359 glist_abort();
360 display = cd;
361 SetForeWindow(g);
362 *len = 0;
364 else
366 /* We were in a group view. Now we are moving to an all-window view.
367 * So treat it as 'windowlist on blank'. */
368 int order = wdata->order | (wdata->nested ? WLIST_NESTED : 0);
369 glist_abort();
370 display = cd;
371 display_windows(1, order, NULL);
372 *len = 0;
374 break;
376 case ',': /* Switch numbers with the previous window. */
377 if (wdata->order == WLIST_NUM && ldata->selected->prev)
379 struct win *pw = ldata->selected->prev->data;
380 if (win->w_group != pw->w_group)
381 break; /* Do not allow switching with the parent group */
383 /* When a windows's number is successfully changed, it triggers a WListUpdatecv
384 * with NULL window. So that causes a redraw of the entire list. So reset the
385 * 'selected' after that. */
386 wdata->fore = win;
387 WindowChangeNumber(win, pw->w_number);
389 break;
391 case '.': /* Switch numbers with the next window. */
392 if (wdata->order == WLIST_NUM && ldata->selected->next)
394 struct win *nw = ldata->selected->next->data;
395 if (win->w_group != nw->w_group)
396 break; /* Do not allow switching with the parent group */
398 wdata->fore = win;
399 WindowChangeNumber(win, nw->w_number);
401 break;
403 case 'K': /* Kill a window */
405 char str[MAXSTR];
406 snprintf(str, sizeof(str) - 1, "Really kill window %d (%s) [y/n]",
407 win->w_number, win->w_title);
408 Input(str, 1, INP_RAW, window_kill_confirm, (char *)win, 0);
410 break;
412 case 033: /* escape */
413 case 007: /* ^G */
414 if (!WLIST_FOR_GROUP(wdata))
416 int fnumber = wdata->onblank ? wdata->fore->w_number : -1;
417 glist_abort();
418 display = cd;
419 if (fnumber >= 0)
420 SwitchWindow(fnumber);
421 *len = 0;
423 break;
424 default:
425 --*inp;
426 ++*len;
427 return 0;
429 return 1;
432 static int
433 gl_Window_freerow(struct ListData *ldata, struct ListRow *row)
435 return 0;
438 static int
439 gl_Window_free(struct ListData *ldata)
441 Free(ldata->data);
442 return 0;
445 static int
446 gl_Window_match(struct ListData *ldata, struct ListRow *row, const char *needle)
448 struct win *w = row->data;
449 if (InStr(w->w_title, needle))
450 return 1;
451 return 0;
454 static struct GenericList gl_Window =
456 gl_Window_header,
457 gl_Window_footer,
458 gl_Window_row,
459 gl_Window_input,
460 gl_Window_freerow,
461 gl_Window_free,
462 gl_Window_match
465 void
466 display_windows(int onblank, int order, struct win *group)
468 struct win *p;
469 struct ListData *ldata;
470 struct gl_Window_Data *wdata;
472 if (flayer->l_width < 10 || flayer->l_height < 6)
474 LMsg(0, "Window size too small for window list page");
475 return;
478 if (group)
479 onblank = 0; /* When drawing a group window, ignore 'onblank' */
481 if (onblank)
483 debug3("flayer %x %d %x\n", flayer, flayer->l_width, flayer->l_height);
484 if (!display)
486 LMsg(0, "windowlist -b: display required");
487 return;
489 p = D_fore;
490 if (p)
492 SetForeWindow((struct win *)0);
493 if (p->w_group)
495 D_fore = p->w_group;
496 flayer->l_data = (char *)p->w_group;
498 Activate(0);
500 if (flayer->l_width < 10 || flayer->l_height < 6)
502 LMsg(0, "Window size too small for window list page");
503 return;
506 else
507 p = Layer2Window(flayer);
508 if (!group && p)
509 group = p->w_group;
511 ldata = glist_display(&gl_Window, ListID);
512 if (!ldata)
514 if (onblank && p)
516 /* Could not display the list. So restore the window. */
517 SetForeWindow(p);
518 Activate(1);
520 return;
523 wdata = calloc(1, sizeof(struct gl_Window_Data));
524 wdata->group = group;
525 wdata->order = (order & ~WLIST_NESTED);
526 wdata->nested = !!(order & WLIST_NESTED);
527 wdata->onblank = onblank;
529 /* Set the most recent window as selected. */
530 wdata->fore = windows;
531 while (wdata->fore && wdata->fore->w_group != group)
532 wdata->fore = wdata->fore->w_next;
534 ldata->data = wdata;
536 gl_Window_rebuild(ldata);
539 static void
540 WListUpdate(struct win *p, struct ListData *ldata)
542 struct gl_Window_Data *wdata = ldata->data;
543 struct ListRow *row, *rbefore;
544 struct win *before;
545 int d = 0, sel = 0;
547 if (!p)
549 if (ldata->selected)
550 wdata->fore = ldata->selected->data; /* Try to retain the current selection */
551 glist_remove_rows(ldata);
552 gl_Window_rebuild(ldata);
553 return;
556 /* First decide if this window should be displayed at all. */
557 d = 1;
558 if (wdata->order == WLIST_NUM || wdata->order == WLIST_MRU)
560 if (p->w_group != wdata->group)
562 if (!wdata->nested)
563 d = 0;
564 else
565 d = window_ancestor(wdata->group, p);
569 if (!d)
571 if (gl_Window_remove(ldata, p))
572 glist_display_all(ldata);
573 return;
576 /* OK, so we keep the window in the list. Update the ordering.
577 * First, find the row where this window should go to. Then, either create
578 * a new row for that window, or move the exising row for the window to the
579 * correct place. */
580 before = NULL;
581 if (wdata->order == WLIST_MRU)
583 if (windows != p)
584 for (before = windows; before; before = before->w_next)
585 if (before->w_next == p)
586 break;
588 else if (wdata->order == WLIST_NUM)
590 if (p->w_number != 0)
592 struct win **w = wtab + p->w_number - 1;
593 for (; w >= wtab; w--)
595 if (*w && (*w)->w_group == wdata->group)
597 before = *w;
598 break;
604 /* Now, find the row belonging to 'before' */
605 if (before)
606 rbefore = gl_Window_findrow(ldata, before);
607 else if (wdata->nested && p->w_group) /* There's no 'before'. So find the group window */
608 rbefore = gl_Window_findrow(ldata, p->w_group);
609 else
610 rbefore = NULL;
612 /* For now, just remove the row containing 'p' if it is not already in the right place . */
613 row = gl_Window_findrow(ldata, p);
614 if (row)
616 if (row->prev != rbefore)
618 sel = ldata->selected->data == p;
619 gl_Window_remove(ldata, p);
621 else
622 p = NULL; /* the window is in the correct place */
624 if (p)
626 row = glist_add_row(ldata, p, rbefore);
627 if (sel)
628 ldata->selected = row;
630 glist_display_all(ldata);
633 void
634 WListUpdatecv(cv, p)
635 struct canvas *cv;
636 struct win *p;
638 struct ListData *ldata;
639 struct gl_Window_Data *wdata;
641 if (cv->c_layer->l_layfn != &ListLf)
642 return;
643 ldata = cv->c_layer->l_data;
644 if (ldata->name != ListID)
645 return;
646 wdata = ldata->data;
647 CV_CALL(cv, WListUpdate(p, ldata));
650 void
651 WListLinkChanged()
653 struct display *olddisplay = display;
654 struct canvas *cv;
655 struct ListData *ldata;
656 struct gl_Window_Data *wdata;
658 for (display = displays; display; display = display->d_next)
659 for (cv = D_cvlist; cv; cv = cv->c_next)
661 if (!cv->c_layer || cv->c_layer->l_layfn != &ListLf)
662 continue;
663 ldata = cv->c_layer->l_data;
664 if (ldata->name != ListID)
665 continue;
666 wdata = ldata->data;
667 if (!(wdata->order & WLIST_MRU))
668 continue;
669 CV_CALL(cv, WListUpdate(0, ldata));
671 display = olddisplay;