Select window by pressing # in window number.
[screen-lua.git] / src / list_window.c
blob4fd65d00578f87dd3490dc3e5d8e56f08a34e81e
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 if ((w->w_silence == SILENCE_FOUND || w->w_silence == SILENCE_DONE) && renditions[REND_SILENCE] != -1)
271 mchar = &mchar_rend;
272 ApplyAttrColor(renditions[REND_SILENCE], mchar);
274 else
275 mchar = &mchar_blank;
277 LPutWinMsg(flayer, str, flayer->l_width, mchar, xoff, lrow->y);
278 if (xoff)
279 LPutWinMsg(flayer, "", xoff, mchar, 0, lrow->y);
281 return 1;
284 static int
285 gl_Window_input(struct ListData *ldata, char **inp, int *len)
287 struct win *win;
288 unsigned char ch;
289 struct display *cd = display;
290 struct gl_Window_Data *wdata = ldata->data;
292 if (!ldata->selected)
293 return 0;
295 ch = (unsigned char) **inp;
296 ++*inp;
297 --*len;
299 win = ldata->selected->data;
300 switch (ch)
302 case ' ':
303 case '\n':
304 case '\r':
305 if (!win)
306 break;
307 #ifdef MULTIUSER
308 if (display && AclCheckPermWin(D_user, ACL_READ, win))
309 return; /* Not allowed to switch to this window. */
310 #endif
311 if (WLIST_FOR_GROUP(wdata))
312 SwitchWindow(win->w_number);
313 else
315 /* Abort list only when not in a group window. */
316 glist_abort();
317 display = cd;
318 if (D_fore != win)
319 SwitchWindow(win->w_number);
321 *len = 0;
322 break;
324 case 'm':
325 /* Toggle MRU-ness */
326 wdata->order = wdata->order == WLIST_MRU ? WLIST_NUM : WLIST_MRU;
327 glist_remove_rows(ldata);
328 gl_Window_rebuild(ldata);
329 break;
331 case 'g':
332 /* Toggle nestedness */
333 wdata->nested = !wdata->nested;
334 glist_remove_rows(ldata);
335 gl_Window_rebuild(ldata);
336 break;
338 case 'a':
339 /* All-window view */
340 if (wdata->group)
342 int order = wdata->order | (wdata->nested ? WLIST_NESTED : 0);
343 glist_abort();
344 display = cd;
345 display_windows(1, order, NULL);
346 *len = 0;
348 else if (!wdata->nested)
350 wdata->nested = 1;
351 glist_remove_rows(ldata);
352 gl_Window_rebuild(ldata);
354 break;
356 case 010: /* ^H */
357 case 0177: /* Backspace */
358 if (!wdata->group)
359 break;
360 if (wdata->group->w_group)
362 /* The parent is another group window. So switch to that window. */
363 struct win *g = wdata->group->w_group;
364 glist_abort();
365 display = cd;
366 SetForeWindow(g);
367 *len = 0;
369 else
371 /* We were in a group view. Now we are moving to an all-window view.
372 * So treat it as 'windowlist on blank'. */
373 int order = wdata->order | (wdata->nested ? WLIST_NESTED : 0);
374 glist_abort();
375 display = cd;
376 display_windows(1, order, NULL);
377 *len = 0;
379 break;
381 case ',': /* Switch numbers with the previous window. */
382 if (wdata->order == WLIST_NUM && ldata->selected->prev)
384 struct win *pw = ldata->selected->prev->data;
385 if (win->w_group != pw->w_group)
386 break; /* Do not allow switching with the parent group */
388 /* When a windows's number is successfully changed, it triggers a WListUpdatecv
389 * with NULL window. So that causes a redraw of the entire list. So reset the
390 * 'selected' after that. */
391 wdata->fore = win;
392 WindowChangeNumber(win, pw->w_number);
394 break;
396 case '.': /* Switch numbers with the next window. */
397 if (wdata->order == WLIST_NUM && ldata->selected->next)
399 struct win *nw = ldata->selected->next->data;
400 if (win->w_group != nw->w_group)
401 break; /* Do not allow switching with the parent group */
403 wdata->fore = win;
404 WindowChangeNumber(win, nw->w_number);
406 break;
408 case 'K': /* Kill a window */
410 char str[MAXSTR];
411 snprintf(str, sizeof(str) - 1, "Really kill window %d (%s) [y/n]",
412 win->w_number, win->w_title);
413 Input(str, 1, INP_RAW, window_kill_confirm, (char *)win, 0);
415 break;
417 case 033: /* escape */
418 case 007: /* ^G */
419 if (!WLIST_FOR_GROUP(wdata))
421 int fnumber = wdata->onblank ? wdata->fore->w_number : -1;
422 glist_abort();
423 display = cd;
424 if (fnumber >= 0)
425 SwitchWindow(fnumber);
426 *len = 0;
428 break;
429 default:
430 if (ch >= '0' && ch <= '9')
432 struct ListRow *row = ldata->root;
433 for (; row; row = row->next)
435 struct win *w = row->data;
436 if (w->w_number == ch - '0')
438 struct ListRow *old = ldata->selected;
439 if (old == row)
440 break;
441 ldata->selected = row;
442 if (ldata->selected->y == -1)
444 /* We need to list all the rows, since we are scrolling down. But first,
445 * find the top of the visible list. */
446 ldata->top = row;
447 glist_display_all(ldata);
449 else
451 /* just redisplay the two lines. */
452 ldata->list_fn->gl_printrow(ldata, old);
453 ldata->list_fn->gl_printrow(ldata, ldata->selected);
454 flayer->l_y = ldata->selected->y;
455 LaySetCursor();
457 break;
460 break;
462 --*inp;
463 ++*len;
464 return 0;
466 return 1;
469 static int
470 gl_Window_freerow(struct ListData *ldata, struct ListRow *row)
472 return 0;
475 static int
476 gl_Window_free(struct ListData *ldata)
478 Free(ldata->data);
479 return 0;
482 static int
483 gl_Window_match(struct ListData *ldata, struct ListRow *row, const char *needle)
485 struct win *w = row->data;
486 if (InStr(w->w_title, needle))
487 return 1;
488 return 0;
491 static struct GenericList gl_Window =
493 gl_Window_header,
494 gl_Window_footer,
495 gl_Window_row,
496 gl_Window_input,
497 gl_Window_freerow,
498 gl_Window_free,
499 gl_Window_match
502 void
503 display_windows(int onblank, int order, struct win *group)
505 struct win *p;
506 struct ListData *ldata;
507 struct gl_Window_Data *wdata;
509 if (flayer->l_width < 10 || flayer->l_height < 6)
511 LMsg(0, "Window size too small for window list page");
512 return;
515 if (group)
516 onblank = 0; /* When drawing a group window, ignore 'onblank' */
518 if (onblank)
520 debug3("flayer %x %d %x\n", flayer, flayer->l_width, flayer->l_height);
521 if (!display)
523 LMsg(0, "windowlist -b: display required");
524 return;
526 p = D_fore;
527 if (p)
529 SetForeWindow((struct win *)0);
530 if (p->w_group)
532 D_fore = p->w_group;
533 flayer->l_data = (char *)p->w_group;
535 Activate(0);
537 if (flayer->l_width < 10 || flayer->l_height < 6)
539 LMsg(0, "Window size too small for window list page");
540 return;
543 else
544 p = Layer2Window(flayer);
545 if (!group && p)
546 group = p->w_group;
548 ldata = glist_display(&gl_Window, ListID);
549 if (!ldata)
551 if (onblank && p)
553 /* Could not display the list. So restore the window. */
554 SetForeWindow(p);
555 Activate(1);
557 return;
560 wdata = calloc(1, sizeof(struct gl_Window_Data));
561 wdata->group = group;
562 wdata->order = (order & ~WLIST_NESTED);
563 wdata->nested = !!(order & WLIST_NESTED);
564 wdata->onblank = onblank;
566 /* Set the most recent window as selected. */
567 wdata->fore = windows;
568 while (wdata->fore && wdata->fore->w_group != group)
569 wdata->fore = wdata->fore->w_next;
571 ldata->data = wdata;
573 gl_Window_rebuild(ldata);
576 static void
577 WListUpdate(struct win *p, struct ListData *ldata)
579 struct gl_Window_Data *wdata = ldata->data;
580 struct ListRow *row, *rbefore;
581 struct win *before;
582 int d = 0, sel = 0;
584 if (!p)
586 if (ldata->selected)
587 wdata->fore = ldata->selected->data; /* Try to retain the current selection */
588 glist_remove_rows(ldata);
589 gl_Window_rebuild(ldata);
590 return;
593 /* First decide if this window should be displayed at all. */
594 d = 1;
595 if (wdata->order == WLIST_NUM || wdata->order == WLIST_MRU)
597 if (p->w_group != wdata->group)
599 if (!wdata->nested)
600 d = 0;
601 else
602 d = window_ancestor(wdata->group, p);
606 if (!d)
608 if (gl_Window_remove(ldata, p))
609 glist_display_all(ldata);
610 return;
613 /* OK, so we keep the window in the list. Update the ordering.
614 * First, find the row where this window should go to. Then, either create
615 * a new row for that window, or move the exising row for the window to the
616 * correct place. */
617 before = NULL;
618 if (wdata->order == WLIST_MRU)
620 if (windows != p)
621 for (before = windows; before; before = before->w_next)
622 if (before->w_next == p)
623 break;
625 else if (wdata->order == WLIST_NUM)
627 if (p->w_number != 0)
629 struct win **w = wtab + p->w_number - 1;
630 for (; w >= wtab; w--)
632 if (*w && (*w)->w_group == wdata->group)
634 before = *w;
635 break;
641 /* Now, find the row belonging to 'before' */
642 if (before)
643 rbefore = gl_Window_findrow(ldata, before);
644 else if (wdata->nested && p->w_group) /* There's no 'before'. So find the group window */
645 rbefore = gl_Window_findrow(ldata, p->w_group);
646 else
647 rbefore = NULL;
649 /* For now, just remove the row containing 'p' if it is not already in the right place . */
650 row = gl_Window_findrow(ldata, p);
651 if (row)
653 if (row->prev != rbefore)
655 sel = ldata->selected->data == p;
656 gl_Window_remove(ldata, p);
658 else
659 p = NULL; /* the window is in the correct place */
661 if (p)
663 row = glist_add_row(ldata, p, rbefore);
664 if (sel)
665 ldata->selected = row;
667 glist_display_all(ldata);
670 void
671 WListUpdatecv(cv, p)
672 struct canvas *cv;
673 struct win *p;
675 struct ListData *ldata;
676 struct gl_Window_Data *wdata;
678 if (cv->c_layer->l_layfn != &ListLf)
679 return;
680 ldata = cv->c_layer->l_data;
681 if (ldata->name != ListID)
682 return;
683 wdata = ldata->data;
684 CV_CALL(cv, WListUpdate(p, ldata));
687 void
688 WListLinkChanged()
690 struct display *olddisplay = display;
691 struct canvas *cv;
692 struct ListData *ldata;
693 struct gl_Window_Data *wdata;
695 for (display = displays; display; display = display->d_next)
696 for (cv = D_cvlist; cv; cv = cv->c_next)
698 if (!cv->c_layer || cv->c_layer->l_layfn != &ListLf)
699 continue;
700 ldata = cv->c_layer->l_data;
701 if (ldata->name != ListID)
702 continue;
703 wdata = ldata->data;
704 if (!(wdata->order & WLIST_MRU))
705 continue;
706 CV_CALL(cv, WListUpdate(0, ldata));
708 display = olddisplay;