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 /* 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) \
66 for (_ww = windows; _ww; _ww = _ww->w_next) \
74 struct win **_ww, *_witer; \
75 for (_ww = wtab, _witer = windows; _witer && _ww - wtab < maxwin; _ww++) \
77 if (!(_w = *_ww)) continue; \
79 _witer = _witer->w_next; \
84 /* Is 'a' an ancestor of 'd'? */
86 window_ancestor(struct win
*a
, struct win
*d
)
89 return 1; /* Every window is a descendant of the 'null' group */
90 for (; d
; d
= d
->w_group
)
97 window_kill_confirm(char *buf
, int len
, char *data
)
99 struct win
*w
= windows
;
102 if (len
|| (*buf
!= 'y' && *buf
!= 'Y'))
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
)
116 /* Pretend the selected window is the foreground window. Then trigger a non-interactive 'kill' */
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
)
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
);
151 gl_Window_rebuild(struct ListData
*ldata
)
153 struct ListRow
*row
= NULL
;
154 struct gl_Window_Data
*wdata
= ldata
->data
;
157 FOR_EACH_WINDOW(wdata
, w
,
158 if (w
->w_group
!= wdata
->group
)
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
)
182 gl_Window_remove(struct ListData
*ldata
, struct win
*p
)
184 struct ListRow
*row
= gl_Window_findrow(ldata
, p
);
188 /* Remove 'row'. Update 'selected', 'top', 'root' if necessary. */
190 row
->next
->prev
= 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
);
208 gl_Window_header(struct ListData
*ldata
)
211 struct gl_Window_Data
*wdata
= ldata
->data
;
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);
221 str
= MakeWinMsgEv(wlisttit
, (struct win
*)0, '%', flayer
->l_width
, (struct event
*)0, 0);
223 LPutWinMsg(flayer
, str
, strlen(str
), &mchar_blank
, 0, g
);
228 gl_Window_footer(struct ListData
*ldata
)
234 gl_Window_row(struct ListData
*ldata
, struct ListRow
*lrow
)
240 struct mchar mchar_rend
= mchar_blank
;
241 struct gl_Window_Data
*wdata
= ldata
->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
)
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
)
256 else if (w
->w_monitor
== MON_DONE
&& renditions
[REND_MONITOR
] != -1)
259 ApplyAttrColor(renditions
[REND_MONITOR
], mchar
);
261 else if ((w
->w_bell
== BELL_DONE
|| w
->w_bell
== BELL_FOUND
) && renditions
[REND_BELL
] != -1)
264 ApplyAttrColor(renditions
[REND_BELL
], mchar
);
267 mchar
= &mchar_blank
;
269 LPutWinMsg(flayer
, str
, flayer
->l_width
, mchar
, xoff
, lrow
->y
);
271 LPutWinMsg(flayer
, "", xoff
, mchar
, 0, lrow
->y
);
277 gl_Window_input(struct ListData
*ldata
, char **inp
, int *len
)
281 struct display
*cd
= display
;
282 struct gl_Window_Data
*wdata
= ldata
->data
;
284 if (!ldata
->selected
)
287 ch
= (unsigned char) **inp
;
291 win
= ldata
->selected
->data
;
300 if (display
&& AclCheckPermWin(D_user
, ACL_READ
, win
))
301 return; /* Not allowed to switch to this window. */
303 if (wdata
->onblank
|| (!wdata
->onblank
&& wdata
->group
))
305 /* Do not abort the group window. */
310 SwitchWindow(win
->w_number
);
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
);
322 /* Toggle nestedness */
323 wdata
->nested
= !wdata
->nested
;
324 glist_remove_rows(ldata
);
325 gl_Window_rebuild(ldata
);
329 /* All-window view */
332 int order
= wdata
->order
| (wdata
->nested
? WLIST_NESTED
: 0);
335 display_windows(1, order
, NULL
);
338 else if (!wdata
->nested
)
341 glist_remove_rows(ldata
);
342 gl_Window_rebuild(ldata
);
347 case 0177: /* Backspace */
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
;
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);
366 display_windows(1, order
, NULL
);
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. */
382 WindowChangeNumber(win
, pw
->w_number
);
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 */
394 WindowChangeNumber(win
, nw
->w_number
);
398 case 'K': /* Kill a window */
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);
407 case 033: /* escape */
409 if (wdata
->onblank
|| (!wdata
->onblank
&& wdata
->group
))
411 int fnumber
= wdata
->fore
->w_number
;
415 SwitchWindow(fnumber
);
419 /* else FALLTHROUGH */
429 gl_Window_freerow(struct ListData
*ldata
, struct ListRow
*row
)
435 gl_Window_free(struct ListData
*ldata
)
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
))
450 static struct GenericList gl_Window
=
462 display_windows(int onblank
, int order
, struct win
*group
)
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");
475 onblank
= 0; /* When drawing a group window, ignore 'onblank' */
479 debug3("flayer %x %d %x\n", flayer
, flayer
->l_width
, flayer
->l_height
);
482 LMsg(0, "windowlist -b: display required");
488 SetForeWindow((struct win
*)0);
492 flayer
->l_data
= (char *)p
->w_group
;
496 if (flayer
->l_width
< 10 || flayer
->l_height
< 6)
498 LMsg(0, "Window size too small for window list page");
503 p
= Layer2Window(flayer
);
507 ldata
= glist_display(&gl_Window
, ListID
);
512 /* Could not display the list. So restore the window. */
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
;
532 gl_Window_rebuild(ldata
);
536 WListUpdate(struct win
*p
, struct ListData
*ldata
)
538 struct gl_Window_Data
*wdata
= ldata
->data
;
539 struct ListRow
*row
, *rbefore
;
546 wdata
->fore
= ldata
->selected
->data
; /* Try to retain the current selection */
547 glist_remove_rows(ldata
);
548 gl_Window_rebuild(ldata
);
552 /* First decide if this window should be displayed at all. */
554 if (wdata
->order
== WLIST_NUM
|| wdata
->order
== WLIST_MRU
)
556 if (p
->w_group
!= wdata
->group
)
561 d
= window_ancestor(wdata
->group
, p
);
567 if (gl_Window_remove(ldata
, p
))
568 glist_display_all(ldata
);
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
577 if (wdata
->order
== WLIST_MRU
)
580 for (before
= windows
; before
; before
= before
->w_next
)
581 if (before
->w_next
== p
)
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
)
600 /* Now, find the row belonging to '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
);
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
);
612 if (row
->prev
!= rbefore
)
614 sel
= ldata
->selected
->data
== p
;
615 gl_Window_remove(ldata
, p
);
618 p
= NULL
; /* the window is in the correct place */
622 row
= glist_add_row(ldata
, p
, rbefore
);
624 ldata
->selected
= row
;
626 glist_display_all(ldata
);
634 struct ListData
*ldata
;
635 struct gl_Window_Data
*wdata
;
637 if (cv
->c_layer
->l_layfn
!= &ListLf
)
639 ldata
= cv
->c_layer
->l_data
;
640 if (ldata
->name
!= ListID
)
643 CV_CALL(cv
, WListUpdate(p
, ldata
));
649 struct display
*olddisplay
= display
;
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
)
659 ldata
= cv
->c_layer
->l_data
;
660 if (ldata
->name
!= ListID
)
663 if (!(wdata
->order
& WLIST_MRU
))
665 CV_CALL(cv
, WListUpdate(0, ldata
));
667 display
= olddisplay
;