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)
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[]).
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
;
48 extern char *noargs
[];
50 static char ListID
[] = "window";
54 struct win
*group
; /* Set only for a W_TYPE_GROUP window */
55 int order
; /* MRU? NUM? */
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) \
69 for (_ww = windows; _ww; _ww = _ww->w_next) \
77 struct win **_ww, *_witer; \
78 for (_ww = wtab, _witer = windows; _witer && _ww - wtab < maxwin; _ww++) \
80 if (!(_w = *_ww)) continue; \
82 _witer = _witer->w_next; \
87 /* Is 'a' an ancestor of 'd'? */
89 window_ancestor(struct win
*a
, struct win
*d
)
92 return 1; /* Every window is a descendant of the 'null' group */
93 for (; d
; d
= d
->w_group
)
100 window_kill_confirm(char *buf
, int len
, char *data
)
102 struct win
*w
= windows
;
105 if (len
|| (*buf
!= 'y' && *buf
!= 'Y'))
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
)
119 /* Pretend the selected window is the foreground window. Then trigger a non-interactive 'kill' */
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
)
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
);
154 gl_Window_rebuild(struct ListData
*ldata
)
156 struct ListRow
*row
= NULL
;
157 struct gl_Window_Data
*wdata
= ldata
->data
;
160 FOR_EACH_WINDOW(wdata
, w
,
161 if (w
->w_group
!= wdata
->group
)
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
)
185 gl_Window_remove(struct ListData
*ldata
, struct win
*p
)
187 struct ListRow
*row
= gl_Window_findrow(ldata
, p
);
191 /* Remove 'row'. Update 'selected', 'top', 'root' if necessary. */
193 row
->next
->prev
= 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
);
211 gl_Window_header(struct ListData
*ldata
)
214 struct gl_Window_Data
*wdata
= ldata
->data
;
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);
224 str
= MakeWinMsgEv(wlisttit
, (struct win
*)0, '%', flayer
->l_width
, (struct event
*)0, 0);
226 LPutWinMsg(flayer
, str
, strlen(str
), &mchar_blank
, 0, g
);
231 gl_Window_footer(struct ListData
*ldata
)
237 gl_Window_row(struct ListData
*ldata
, struct ListRow
*lrow
)
243 struct mchar mchar_rend
= mchar_blank
;
244 struct gl_Window_Data
*wdata
= ldata
->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
)
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
)
259 else if (w
->w_monitor
== MON_DONE
&& renditions
[REND_MONITOR
] != -1)
262 ApplyAttrColor(renditions
[REND_MONITOR
], mchar
);
264 else if ((w
->w_bell
== BELL_DONE
|| w
->w_bell
== BELL_FOUND
) && renditions
[REND_BELL
] != -1)
267 ApplyAttrColor(renditions
[REND_BELL
], mchar
);
270 mchar
= &mchar_blank
;
272 LPutWinMsg(flayer
, str
, flayer
->l_width
, mchar
, xoff
, lrow
->y
);
274 LPutWinMsg(flayer
, "", xoff
, mchar
, 0, lrow
->y
);
280 gl_Window_input(struct ListData
*ldata
, char **inp
, int *len
)
284 struct display
*cd
= display
;
285 struct gl_Window_Data
*wdata
= ldata
->data
;
287 if (!ldata
->selected
)
290 ch
= (unsigned char) **inp
;
294 win
= ldata
->selected
->data
;
303 if (display
&& AclCheckPermWin(D_user
, ACL_READ
, win
))
304 return; /* Not allowed to switch to this window. */
306 if (WLIST_FOR_GROUP(wdata
))
307 SwitchWindow(win
->w_number
);
310 /* Abort list only when not in a group window. */
314 SwitchWindow(win
->w_number
);
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
);
327 /* Toggle nestedness */
328 wdata
->nested
= !wdata
->nested
;
329 glist_remove_rows(ldata
);
330 gl_Window_rebuild(ldata
);
334 /* All-window view */
337 int order
= wdata
->order
| (wdata
->nested
? WLIST_NESTED
: 0);
340 display_windows(1, order
, NULL
);
343 else if (!wdata
->nested
)
346 glist_remove_rows(ldata
);
347 gl_Window_rebuild(ldata
);
352 case 0177: /* Backspace */
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
;
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);
371 display_windows(1, order
, NULL
);
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. */
387 WindowChangeNumber(win
, pw
->w_number
);
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 */
399 WindowChangeNumber(win
, nw
->w_number
);
403 case 'K': /* Kill a window */
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);
412 case 033: /* escape */
414 if (!WLIST_FOR_GROUP(wdata
))
416 int fnumber
= wdata
->onblank
? wdata
->fore
->w_number
: -1;
420 SwitchWindow(fnumber
);
433 gl_Window_freerow(struct ListData
*ldata
, struct ListRow
*row
)
439 gl_Window_free(struct ListData
*ldata
)
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
))
454 static struct GenericList gl_Window
=
466 display_windows(int onblank
, int order
, struct win
*group
)
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");
479 onblank
= 0; /* When drawing a group window, ignore 'onblank' */
483 debug3("flayer %x %d %x\n", flayer
, flayer
->l_width
, flayer
->l_height
);
486 LMsg(0, "windowlist -b: display required");
492 SetForeWindow((struct win
*)0);
496 flayer
->l_data
= (char *)p
->w_group
;
500 if (flayer
->l_width
< 10 || flayer
->l_height
< 6)
502 LMsg(0, "Window size too small for window list page");
507 p
= Layer2Window(flayer
);
511 ldata
= glist_display(&gl_Window
, ListID
);
516 /* Could not display the list. So restore the window. */
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
;
536 gl_Window_rebuild(ldata
);
540 WListUpdate(struct win
*p
, struct ListData
*ldata
)
542 struct gl_Window_Data
*wdata
= ldata
->data
;
543 struct ListRow
*row
, *rbefore
;
550 wdata
->fore
= ldata
->selected
->data
; /* Try to retain the current selection */
551 glist_remove_rows(ldata
);
552 gl_Window_rebuild(ldata
);
556 /* First decide if this window should be displayed at all. */
558 if (wdata
->order
== WLIST_NUM
|| wdata
->order
== WLIST_MRU
)
560 if (p
->w_group
!= wdata
->group
)
565 d
= window_ancestor(wdata
->group
, p
);
571 if (gl_Window_remove(ldata
, p
))
572 glist_display_all(ldata
);
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
581 if (wdata
->order
== WLIST_MRU
)
584 for (before
= windows
; before
; before
= before
->w_next
)
585 if (before
->w_next
== p
)
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
)
604 /* Now, find the row belonging to '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
);
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
);
616 if (row
->prev
!= rbefore
)
618 sel
= ldata
->selected
->data
== p
;
619 gl_Window_remove(ldata
, p
);
622 p
= NULL
; /* the window is in the correct place */
626 row
= glist_add_row(ldata
, p
, rbefore
);
628 ldata
->selected
= row
;
630 glist_display_all(ldata
);
638 struct ListData
*ldata
;
639 struct gl_Window_Data
*wdata
;
641 if (cv
->c_layer
->l_layfn
!= &ListLf
)
643 ldata
= cv
->c_layer
->l_data
;
644 if (ldata
->name
!= ListID
)
647 CV_CALL(cv
, WListUpdate(p
, ldata
));
653 struct display
*olddisplay
= display
;
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
)
663 ldata
= cv
->c_layer
->l_data
;
664 if (ldata
->name
!= ListID
)
667 if (!(wdata
->order
& WLIST_MRU
))
669 CV_CALL(cv
, WListUpdate(0, ldata
));
671 display
= olddisplay
;