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
);
269 else if ((w
->w_silence
== SILENCE_FOUND
|| w
->w_silence
== SILENCE_DONE
) && renditions
[REND_SILENCE
] != -1)
272 ApplyAttrColor(renditions
[REND_SILENCE
], mchar
);
275 mchar
= &mchar_blank
;
277 LPutWinMsg(flayer
, str
, flayer
->l_width
, mchar
, xoff
, lrow
->y
);
279 LPutWinMsg(flayer
, "", xoff
, mchar
, 0, lrow
->y
);
285 gl_Window_input(struct ListData
*ldata
, char **inp
, int *len
)
289 struct display
*cd
= display
;
290 struct gl_Window_Data
*wdata
= ldata
->data
;
292 if (!ldata
->selected
)
295 ch
= (unsigned char) **inp
;
299 win
= ldata
->selected
->data
;
308 if (display
&& AclCheckPermWin(D_user
, ACL_READ
, win
))
309 return; /* Not allowed to switch to this window. */
311 if (WLIST_FOR_GROUP(wdata
))
312 SwitchWindow(win
->w_number
);
315 /* Abort list only when not in a group window. */
319 SwitchWindow(win
->w_number
);
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
);
332 /* Toggle nestedness */
333 wdata
->nested
= !wdata
->nested
;
334 glist_remove_rows(ldata
);
335 gl_Window_rebuild(ldata
);
339 /* All-window view */
342 int order
= wdata
->order
| (wdata
->nested
? WLIST_NESTED
: 0);
345 display_windows(1, order
, NULL
);
348 else if (!wdata
->nested
)
351 glist_remove_rows(ldata
);
352 gl_Window_rebuild(ldata
);
357 case 0177: /* Backspace */
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
;
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);
376 display_windows(1, order
, NULL
);
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. */
392 WindowChangeNumber(win
, pw
->w_number
);
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 */
404 WindowChangeNumber(win
, nw
->w_number
);
408 case 'K': /* Kill a window */
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);
417 case 033: /* escape */
419 if (!WLIST_FOR_GROUP(wdata
))
421 int fnumber
= wdata
->onblank
? wdata
->fore
->w_number
: -1;
425 SwitchWindow(fnumber
);
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
;
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. */
447 glist_display_all(ldata
);
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
;
470 gl_Window_freerow(struct ListData
*ldata
, struct ListRow
*row
)
476 gl_Window_free(struct ListData
*ldata
)
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
))
491 static struct GenericList gl_Window
=
503 display_windows(int onblank
, int order
, struct win
*group
)
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");
516 onblank
= 0; /* When drawing a group window, ignore 'onblank' */
520 debug3("flayer %x %d %x\n", flayer
, flayer
->l_width
, flayer
->l_height
);
523 LMsg(0, "windowlist -b: display required");
529 SetForeWindow((struct win
*)0);
533 flayer
->l_data
= (char *)p
->w_group
;
537 if (flayer
->l_width
< 10 || flayer
->l_height
< 6)
539 LMsg(0, "Window size too small for window list page");
544 p
= Layer2Window(flayer
);
548 ldata
= glist_display(&gl_Window
, ListID
);
553 /* Could not display the list. So restore the window. */
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
;
573 gl_Window_rebuild(ldata
);
577 WListUpdate(struct win
*p
, struct ListData
*ldata
)
579 struct gl_Window_Data
*wdata
= ldata
->data
;
580 struct ListRow
*row
, *rbefore
;
587 wdata
->fore
= ldata
->selected
->data
; /* Try to retain the current selection */
588 glist_remove_rows(ldata
);
589 gl_Window_rebuild(ldata
);
593 /* First decide if this window should be displayed at all. */
595 if (wdata
->order
== WLIST_NUM
|| wdata
->order
== WLIST_MRU
)
597 if (p
->w_group
!= wdata
->group
)
602 d
= window_ancestor(wdata
->group
, p
);
608 if (gl_Window_remove(ldata
, p
))
609 glist_display_all(ldata
);
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
618 if (wdata
->order
== WLIST_MRU
)
621 for (before
= windows
; before
; before
= before
->w_next
)
622 if (before
->w_next
== p
)
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
)
641 /* Now, find the row belonging to '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
);
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
);
653 if (row
->prev
!= rbefore
)
655 sel
= ldata
->selected
->data
== p
;
656 gl_Window_remove(ldata
, p
);
659 p
= NULL
; /* the window is in the correct place */
663 row
= glist_add_row(ldata
, p
, rbefore
);
665 ldata
->selected
= row
;
667 glist_display_all(ldata
);
675 struct ListData
*ldata
;
676 struct gl_Window_Data
*wdata
;
678 if (cv
->c_layer
->l_layfn
!= &ListLf
)
680 ldata
= cv
->c_layer
->l_data
;
681 if (ldata
->name
!= ListID
)
684 CV_CALL(cv
, WListUpdate(p
, ldata
));
690 struct display
*olddisplay
= display
;
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
)
700 ldata
= cv
->c_layer
->l_data
;
701 if (ldata
->name
!= ListID
)
704 if (!(wdata
->order
& WLIST_MRU
))
706 CV_CALL(cv
, WListUpdate(0, ldata
));
708 display
= olddisplay
;