Add NEWS bug entry for previous FvwmForm commit.
[fvwm.git] / fvwm / windowlist.c
blob23bc889c44b051091e1db68684c003c5508ba9d9
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * This module is all new
19 * by Rob Nation
20 * A little of it is borrowed from ctwm.
21 * Copyright 1993 Robert Nation. No restrictions are placed on this code,
22 * as long as the copyright notice is preserved
26 * fvwm window-list popup code
30 #include "config.h"
32 #include <stdio.h>
33 #include <limits.h>
35 #include "libs/fvwmlib.h"
36 #include "libs/FScreen.h"
37 #include "libs/FGettext.h"
38 #include "libs/Parse.h"
39 #include "libs/Strings.h"
40 #include "fvwm.h"
41 #include "externs.h"
42 #include "functions.h"
43 #include "misc.h"
44 #include "screen.h"
45 #include "menudim.h"
46 #include "menuitem.h"
47 #include "menuroot.h"
48 #include "menustyle.h"
49 #include "menus.h"
50 #include "menuparameters.h"
51 #include "conditional.h"
52 #include "stack.h"
53 #include "focus.h"
54 #include "virtual.h"
55 #include "geometry.h"
57 #define SHOW_GEOMETRY (1<<0)
58 #define SHOW_ALLDESKS (1<<1)
59 #define SHOW_NORMAL (1<<2)
60 #define SHOW_ICONIC (1<<3)
61 #define SHOW_STICKY_ACROSS_PAGES (1<<4)
62 #define SHOW_STICKY_ACROSS_DESKS (1<<5)
63 #define NO_DESK_SORT (1<<6)
64 #define SHOW_ICONNAME (1<<7)
65 #define SHOW_ALPHABETIC (1<<8)
66 #define SORT_BYCLASS (1<<9)
67 #define SORT_BYRESOURCE (1<<10)
68 #define SORT_REVERSE (1<<11)
69 #define SHOW_INFONOTGEO (1<<12)
70 #define NO_DESK_NUM (1<<13)
71 #define NO_CURRENT_DESK_TITLE (1<<14)
72 #define TITLE_FOR_ALL_DESKS (1<<15)
73 #define NO_NUM_IN_DESK_TITLE (1<<16)
74 #define SHOW_PAGE_X (1<<17)
75 #define SHOW_PAGE_Y (1<<18)
76 #define NO_LAYER (1<<19)
77 #define SHOW_SCREEN (1<<20)
78 #define SHOW_DEFAULT (SHOW_GEOMETRY | SHOW_ALLDESKS | SHOW_NORMAL | \
79 SHOW_ICONIC | SHOW_STICKY_ACROSS_PAGES | SHOW_STICKY_ACROSS_DESKS)
81 static char *get_desk_title(int desk, unsigned long flags, Bool is_top_title)
83 char *desk_name;
84 char *tlabel;
86 desk_name = GetDesktopName(desk);
87 if (desk_name != NULL)
89 tlabel = (char *)safemalloc(strlen(desk_name)+50);
91 else
93 tlabel = (char *)safemalloc(50);
96 if (desk_name != NULL)
98 if (flags & NO_NUM_IN_DESK_TITLE)
100 sprintf(tlabel, "%s%s", desk_name,
101 (is_top_title && (flags & SHOW_GEOMETRY)) ?
102 _("\tGeometry") : "");
104 else
106 sprintf(tlabel,"%d: %s%s", desk, desk_name,
107 (is_top_title && (flags & SHOW_GEOMETRY)) ?
108 _("\tGeometry") : "");
111 else
113 sprintf(tlabel,_("Desk: %d%s"),desk,
114 (is_top_title && (flags & SHOW_GEOMETRY)) ?
115 _("\tGeometry") : "");
118 return tlabel;
121 /* Function to compare window title names */
122 static int visibleCompare(const FvwmWindow **a, const FvwmWindow **b)
124 return strcasecmp((*a)->visible_name, (*b)->visible_name);
127 static int iconCompare(const FvwmWindow **a, const FvwmWindow **b)
129 return strcasecmp((*a)->visible_icon_name, (*b)->visible_icon_name);
132 /* Which of the compare functions to sort on. */
133 static int (*compare)(const FvwmWindow **a, const FvwmWindow **b);
135 static int classCompare(const FvwmWindow **a, const FvwmWindow **b)
137 int result = strcasecmp((*a)->class.res_class, (*b)->class.res_class);
138 if (result)
140 return result;
143 return strcasecmp((*a)->visible_name, (*b)->visible_name);
146 static int resourceCompare(const FvwmWindow **a, const FvwmWindow **b)
148 int result = strcasecmp((*a)->class.res_class, (*b)->class.res_class);
149 if (result)
151 return result;
154 result = strcasecmp((*a)->class.res_name, (*b)->class.res_name);
155 if (result)
157 return result;
160 return strcasecmp((*a)->visible_name, (*b)->visible_name);
163 static int classIconCompare(const FvwmWindow **a, const FvwmWindow **b)
165 int result = strcasecmp((*a)->class.res_class, (*b)->class.res_class);
166 if (result)
168 return result;
171 return strcasecmp((*a)->visible_icon_name, (*b)->visible_icon_name);
174 static int resourceIconCompare(const FvwmWindow **a, const FvwmWindow **b)
176 int result = strcasecmp((*a)->class.res_class, (*b)->class.res_class);
177 if (result)
179 return result;
182 result = strcasecmp((*a)->class.res_name, (*b)->class.res_name);
183 if (result)
185 return result;
188 return strcasecmp((*a)->visible_icon_name, (*b)->visible_icon_name);
191 static int compareReverse(const FvwmWindow **a, const FvwmWindow **b)
193 return -compare(a, b);
197 * Change by PRB (pete@tecc.co.uk), 31/10/93. Prepend a hot key
198 * specifier to each item in the list. This means allocating the
199 * memory for each item (& freeing it) rather than just using the window
200 * title directly. */
201 void CMD_WindowList(F_CMD_ARGS)
203 struct MenuRoot *mr;
204 struct MenuParameters mp;
205 char* ret_action = NULL;
206 FvwmWindow *t;
207 FvwmWindow **windowList;
208 FvwmWindow **iconifiedList = NULL;
209 int numWindows;
210 int ii;
211 char tname[128];
212 char loc[64];
213 char *name=NULL;
214 Bool free_name = False;
215 int dwidth;
216 int dheight;
217 char *tlabel;
218 int last_desk_done = INT_MIN;
219 int last_desk_displayed = INT_MIN;
220 int next_desk = 0;
221 char *t_hot=NULL; /* Menu label with hotkey added */
222 char scut = '0'; /* Current short cut key */
223 char *opts=NULL;
224 char *tok=NULL;
225 int desk = Scr.CurrentDesk;
226 unsigned long flags = SHOW_DEFAULT;
227 char *func = NULL;
228 char *ffunc = NULL;
229 char *tfunc = NULL;
230 char *default_action = NULL;
231 MenuReturn mret;
232 MenuOptions mops;
233 int low_layer = 0; /* show all layers by default */
234 int high_layer = INT_MAX;
235 int max_label_width = 0;
236 int skiplist_mode = 0; /* do not show skiplist by default */
237 Bool use_hotkey = True;
238 KeyCode old_sor_keycode;
239 char sor_default_keyname[8] = { 'M', 'e', 't', 'a', '_', 'L' };
240 char *sor_keyname = sor_default_keyname;
241 /* Condition vars. */
242 Bool use_condition = False;
243 Bool current_at_end = False;
244 Bool iconified_at_end = False;
245 int ic = 0;
246 int ij;
247 WindowConditionMask mask;
248 char *cond_flags;
249 Bool first_desk = True;
250 Bool empty_menu = True;
251 Bool was_get_menu_opts_called = False;
252 FvwmWindow * const fw = exc->w.fw;
253 const Window w = exc->w.w;
254 const exec_context_t *exc2;
256 memset(&mops, 0, sizeof(mops));
257 memset(&mret, 0, sizeof(MenuReturn));
258 /* parse postitioning args - must call this even if no action is given
259 * because it sets the xinerama screen origin */
260 if (action && *action)
262 /* Look for condition - CreateFlagString returns NULL if no '('
263 * or '[' */
264 cond_flags = CreateFlagString(action, &action);
265 if (cond_flags)
267 /* Create window mask */
268 use_condition = True;
269 DefaultConditionMask(&mask);
271 /* override for Current [] */
272 mask.my_flags.use_circulate_hit = 1;
273 mask.my_flags.use_circulate_hit_icon = 1;
275 CreateConditionMask(cond_flags, &mask);
276 free(cond_flags);
278 opts = get_menu_options(
279 action, w, fw, NULL, NULL, NULL, &mops);
280 was_get_menu_opts_called = True;
282 /* parse options */
283 while (opts && *opts)
285 opts = GetNextSimpleOption(opts, &tok);
286 if (!tok)
288 break;
291 if (StrEquals(tok,"NoHotkeys"))
293 use_hotkey = False;
295 else if (StrEquals(tok,"Function"))
297 opts = GetNextSimpleOption(opts, &func);
299 else if (StrEquals(tok,"Desk"))
301 free(tok);
302 opts = GetNextSimpleOption(opts, &tok);
303 if (tok)
305 desk = atoi(tok);
306 flags &= ~SHOW_ALLDESKS;
309 else if (StrEquals(tok,"CurrentDesk"))
311 desk = Scr.CurrentDesk;
312 flags &= ~SHOW_ALLDESKS;
314 else if (StrEquals(tok,"NotAlphabetic"))
316 flags &= ~SHOW_ALPHABETIC;
318 else if (StrEquals(tok,"Alphabetic"))
320 flags |= SHOW_ALPHABETIC;
322 else if (StrEquals(tok,"SortByClass"))
324 flags |= SORT_BYCLASS;
326 else if (StrEquals(tok,"SortByResource"))
328 flags |= SORT_BYRESOURCE;
330 else if (StrEquals(tok,"ReverseOrder"))
332 flags |= SORT_REVERSE;
334 else if (StrEquals(tok,"CurrentAtEnd"))
336 current_at_end = True;
338 else if (StrEquals(tok,"IconifiedAtEnd"))
340 iconified_at_end = True;
342 else if (StrEquals(tok,"NoDeskSort"))
344 flags |= NO_DESK_SORT;
346 else if (StrEquals(tok,"ShowPage"))
348 flags |= SHOW_PAGE_X | SHOW_PAGE_Y;
350 else if (StrEquals(tok,"ShowPageX"))
352 flags |= SHOW_PAGE_X;
354 else if (StrEquals(tok,"ShowPageY"))
356 flags |= SHOW_PAGE_Y;
358 else if (StrEquals(tok,"ShowScreen"))
360 flags |= SHOW_SCREEN;
362 else if (StrEquals(tok,"UseIconName"))
364 flags |= SHOW_ICONNAME;
366 else if (StrEquals(tok,"NoGeometry"))
368 flags &= ~SHOW_GEOMETRY;
369 flags &= ~SHOW_INFONOTGEO;
371 else if (StrEquals(tok,"NoGeometryWithInfo"))
373 flags &= ~SHOW_GEOMETRY;
374 flags |= SHOW_INFONOTGEO;
376 else if (StrEquals(tok,"Geometry"))
378 flags |= SHOW_GEOMETRY;
379 flags &= ~SHOW_INFONOTGEO;
381 else if (StrEquals(tok,"NoIcons"))
383 flags &= ~SHOW_ICONIC;
385 else if (StrEquals(tok,"Icons"))
387 flags |= SHOW_ICONIC;
389 else if (StrEquals(tok,"OnlyIcons"))
391 flags = SHOW_ICONIC;
393 else if (StrEquals(tok,"NoNormal"))
395 flags &= ~SHOW_NORMAL;
397 else if (StrEquals(tok,"Normal"))
399 flags |= SHOW_NORMAL;
401 else if (StrEquals(tok,"OnlyNormal"))
403 flags = SHOW_NORMAL;
405 else if (StrEquals(tok,"NoSticky"))
407 flags &= ~(SHOW_STICKY_ACROSS_PAGES);
408 flags &= ~(SHOW_STICKY_ACROSS_DESKS);
410 else if (StrEquals(tok,"NoStickyPage"))
412 flags &= ~(SHOW_STICKY_ACROSS_PAGES);
414 else if (StrEquals(tok,"NoStickyDesk"))
416 flags &= ~(SHOW_STICKY_ACROSS_DESKS);
418 else if (StrEquals(tok,"Sticky"))
420 flags |= SHOW_STICKY_ACROSS_PAGES;
421 flags |= SHOW_STICKY_ACROSS_DESKS;
423 else if (StrEquals(tok,"StickyPage"))
425 flags |= SHOW_STICKY_ACROSS_PAGES;
427 else if (StrEquals(tok,"StickyDesk"))
429 flags |= SHOW_STICKY_ACROSS_DESKS;
431 else if (StrEquals(tok,"OnlySticky"))
433 flags = SHOW_STICKY_ACROSS_PAGES;
434 flags = SHOW_STICKY_ACROSS_DESKS;
436 else if (StrEquals(tok,"OnlyStickyPage"))
438 flags = SHOW_STICKY_ACROSS_PAGES;
440 else if (StrEquals(tok,"OnlyStickyDesk"))
442 flags = SHOW_STICKY_ACROSS_DESKS;
444 else if (StrEquals(tok,"UseListSkip"))
446 /* deprecated as of 02-May-2007 (SS) */
447 fprintf(stderr, "UseListSkip is deprecated. Please use \"UseSkipList\".\n");
448 skiplist_mode = 1;
450 else if (StrEquals(tok,"UseSkipList"))
452 skiplist_mode = 1;
454 else if (StrEquals(tok,"OnlyListSkip"))
456 /* deprecated as of 02-May-2007 (SS) */
457 fprintf(stderr, "OnlyListSkip is deprecated. Please use \"OnlySkipList\".\n");
458 skiplist_mode = 2;
460 else if (StrEquals(tok,"OnlySkipList"))
462 skiplist_mode = 2;
464 else if (StrEquals(tok,"NoDeskNum"))
466 flags |= NO_DESK_NUM;
468 else if (StrEquals(tok,"NoLayer"))
470 flags |= NO_LAYER;
472 else if (StrEquals(tok,"NoCurrentDeskTitle"))
474 flags |= NO_CURRENT_DESK_TITLE;
476 else if (StrEquals(tok,"TitleForAllDesks"))
478 flags |= TITLE_FOR_ALL_DESKS;
480 else if (StrEquals(tok,"NoNumInDeskTitle"))
482 flags |= NO_NUM_IN_DESK_TITLE;
484 /* these are a bit dubious, but we should keep the
485 * OnTop options for compatibility */
486 else if (StrEquals(tok, "NoOnTop"))
488 if (high_layer >= Scr.TopLayer)
490 high_layer = Scr.TopLayer - 1;
493 else if (StrEquals(tok, "OnTop"))
495 if (high_layer < Scr.TopLayer)
497 high_layer = Scr.TopLayer;
500 else if (StrEquals(tok, "OnlyOnTop"))
502 high_layer = low_layer = Scr.TopLayer;
504 else if (StrEquals(tok, "NoOnBottom"))
506 if (low_layer <= Scr.BottomLayer)
508 low_layer = Scr.BottomLayer - 1;
511 else if (StrEquals(tok, "OnBottom"))
513 if (low_layer > Scr.BottomLayer)
515 low_layer = Scr.BottomLayer;
518 else if (StrEquals(tok, "OnlyOnBottom"))
520 high_layer = low_layer = Scr.BottomLayer;
522 else if (StrEquals(tok, "Layer"))
524 free(tok);
525 opts = GetNextSimpleOption(opts, &tok);
526 if (tok)
528 low_layer = high_layer = atoi(tok);
529 free(tok);
530 opts = GetNextSimpleOption(opts, &tok);
531 if (tok)
533 high_layer = atoi(tok);
537 else if (StrEquals(tok, "SelectOnRelease"))
539 if (sor_keyname != sor_default_keyname)
541 free(sor_keyname);
543 sor_keyname = NULL;
544 opts = GetNextSimpleOption(opts, &sor_keyname);
546 else if (StrEquals(tok, "MaxLabelWidth"))
548 char *wid;
550 opts = GetNextSimpleOption(opts, &wid);
551 if (wid)
553 max_label_width = atoi(wid);
554 if (max_label_width < 1)
556 max_label_width = 1;
558 free(wid);
561 else if (!opts || !*opts)
563 default_action = safestrdup(tok);
565 else
567 fvwm_msg(
568 ERR, "WindowList","Unknown option '%s'",
569 tok);
571 if (tok)
573 free(tok);
577 if (was_get_menu_opts_called == False)
579 opts = get_menu_options(
580 action, w, fw, NULL, NULL, NULL, &mops);
583 tlabel = get_desk_title(desk, flags, True);
584 mr = NewMenuRoot(tlabel);
585 if (!(flags & NO_CURRENT_DESK_TITLE))
587 AddToMenu(mr, tlabel, "TITLE", False, False, False);
588 empty_menu = False;
590 free(tlabel);
592 numWindows = 0;
593 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
595 numWindows++;
597 windowList = malloc(numWindows*sizeof(t));
598 if (windowList == NULL)
600 return;
602 if (iconified_at_end)
604 iconifiedList = malloc(numWindows*sizeof(t));
605 if (iconifiedList == NULL)
607 return;
610 /* get the windowlist starting from the current window (if any)*/
611 t = get_focus_window();
612 if (t == NULL)
614 t = Scr.FvwmRoot.next;
616 else if (current_at_end)
618 if (t->next)
620 t = t->next;
622 else
624 t = Scr.FvwmRoot.next;
627 for (ii = 0; ii < numWindows; ii++)
629 if (flags & SORT_REVERSE)
631 windowList[numWindows - ii - 1] = t;
633 else if (iconified_at_end && IS_ICONIFIED(t))
635 iconifiedList[ic++] = t;
637 else
639 windowList[ii - ic] = t;
641 if (t->next)
643 t = t->next;
645 else
647 t = Scr.FvwmRoot.next;
650 if (iconified_at_end && ic > 0)
652 if (current_at_end && ii > ic)
654 windowList[numWindows - 1] = windowList[--ii - ic];
656 for (ij = 0; ij < ic; ij++)
658 windowList[ij + (ii - ic)] = iconifiedList[ij];
662 /* Do alphabetic sort */
663 if (flags & (SHOW_ALPHABETIC | SORT_BYCLASS | SORT_BYRESOURCE))
665 /* This will be compare or compareReverse if a reverse order
666 * is selected. */
667 int (*sort)(const FvwmWindow **a, const FvwmWindow **b);
669 switch (flags & (SHOW_ALPHABETIC | SHOW_ICONNAME | \
670 SORT_BYCLASS | SORT_BYRESOURCE))
672 case SHOW_ALPHABETIC:
673 compare = visibleCompare;
674 break;
675 case SHOW_ALPHABETIC | SHOW_ICONNAME:
676 compare = iconCompare;
677 break;
678 /* Sorting based on class name produces an alphabetic
679 * order so the keyword alphabetic is redundant. */
680 case SORT_BYCLASS:
681 case SORT_BYCLASS | SHOW_ALPHABETIC:
682 compare = classCompare;
683 break;
684 case SORT_BYCLASS | SHOW_ICONNAME:
685 case SORT_BYCLASS | SHOW_ICONNAME | SHOW_ALPHABETIC:
686 compare = classIconCompare;
687 break;
688 case SORT_BYRESOURCE:
689 case SORT_BYRESOURCE | SORT_BYCLASS:
690 case SORT_BYRESOURCE | SORT_BYCLASS | SHOW_ALPHABETIC:
691 compare = resourceCompare;
692 break;
693 case SORT_BYRESOURCE | SHOW_ICONNAME:
694 case SORT_BYRESOURCE | SHOW_ICONNAME | SORT_BYCLASS:
695 case SORT_BYRESOURCE | SHOW_ICONNAME | SORT_BYCLASS | \
696 SHOW_ALPHABETIC:
697 compare = resourceIconCompare;
698 break;
700 /* All current cases are covered, but if something
701 * changes in the future we leave compare valid even if
702 * it isn't what is expected. */
703 default:
704 compare = visibleCompare;
705 break;
708 if ( flags & SORT_REVERSE )
710 sort = compareReverse;
712 else
714 sort = compare;
716 qsort(windowList, numWindows, sizeof(t),
717 (int(*)(const void*, const void*))sort);
720 while(next_desk != INT_MAX)
722 /* Sort window list by desktop number */
723 if ((flags & SHOW_ALLDESKS) && !(flags & NO_DESK_SORT))
725 /* run through the windowlist finding the first desk
726 * not already processed */
727 next_desk = INT_MAX;
728 for (ii = 0; ii < numWindows; ii++)
730 t = windowList[ii];
731 if (t->Desk >last_desk_done &&
732 t->Desk < next_desk)
734 next_desk = t->Desk;
738 if (!(flags & SHOW_ALLDESKS))
740 /* if only doing one desk and it hasn't been done */
741 if (last_desk_done == INT_MIN)
742 next_desk = desk; /* select the desk */
743 else
744 next_desk = INT_MAX; /* flag completion */
746 if (flags & NO_DESK_SORT)
747 next_desk = INT_MAX; /* only go through loop once */
749 last_desk_done = next_desk;
750 for (ii = 0; ii < numWindows; ii++)
752 t = windowList[ii];
753 if (t->Desk != next_desk && !(flags & NO_DESK_SORT))
755 continue;
757 if (skiplist_mode == 0 && DO_SKIP_WINDOW_LIST(t))
759 /* don't want skiplist windows - skip */
760 continue;
762 if (skiplist_mode == 2 && !DO_SKIP_WINDOW_LIST(t))
764 /* don't want no skiplist one - skip */
765 continue;
767 if (use_condition && !MatchesConditionMask(t, &mask))
769 /* doesn't match specified condition */
770 continue;
772 if (!(flags & SHOW_ICONIC) && (IS_ICONIFIED(t)))
774 /* don't want icons - skip */
775 continue;
777 if (!(flags & SHOW_STICKY_ACROSS_PAGES) &&
778 (IS_STICKY_ACROSS_PAGES(t)))
780 /* don't want sticky ones - skip */
781 continue;
783 if (!(flags & SHOW_STICKY_ACROSS_DESKS) &&
784 (IS_STICKY_ACROSS_DESKS(t)))
786 /* don't want sticky ones - skip */
787 continue;
789 if (!(flags & SHOW_NORMAL) &&
790 !(IS_ICONIFIED(t) ||
791 IS_STICKY_ACROSS_PAGES(t) ||
792 IS_STICKY_ACROSS_DESKS(t)))
794 /* don't want "normal" ones - skip */
795 continue;
797 if (get_layer(t) < low_layer ||
798 get_layer(t) > high_layer)
800 /* don't want this layer */
801 continue;
804 empty_menu = False;
805 /* add separator between desks when geometry
806 * shown but not at the top*/
807 if (t->Desk != last_desk_displayed)
809 if (last_desk_displayed != INT_MIN)
811 if (((flags & SHOW_GEOMETRY) ||
812 (flags & SHOW_INFONOTGEO)) &&
813 !(flags & TITLE_FOR_ALL_DESKS))
815 AddToMenu(
816 mr, NULL, NULL, False,
817 False, False);
819 if (flags & TITLE_FOR_ALL_DESKS)
821 tlabel = get_desk_title(
822 t->Desk, flags, False);
823 AddToMenu(
824 mr, tlabel, "TITLE",
825 False, False, False);
826 free(tlabel);
829 last_desk_displayed = t->Desk;
831 if (first_desk && flags & TITLE_FOR_ALL_DESKS)
833 tlabel = get_desk_title(t->Desk, flags, False);
834 AddToMenu(
835 mr, tlabel, "TITLE", False, False,
836 False);
837 free(tlabel);
839 first_desk = False;
841 if (flags & SHOW_ICONNAME)
843 name = t->visible_icon_name;
845 else
847 name = t->visible_name;
850 free_name = False;
851 if (!name)
853 name = "NULL_NAME";
855 else if (max_label_width > 0 &&
856 strlen(name) > max_label_width)
858 name = strdup(name);
859 name[max_label_width] = '\0';
860 free_name = True;
863 t_hot = safemalloc(strlen(name) + 80);
864 if (use_hotkey)
866 /* Generate label */
867 sprintf(t_hot, "&%c. ", scut);
869 else
871 *t_hot = 0;
873 if (!(flags & SHOW_INFONOTGEO))
875 strcat(t_hot, name);
877 if (*t_hot == 0)
879 strcpy(t_hot, " ");
882 /* Next shortcut key */
883 if (scut == '9')
885 scut = 'A';
887 else if (scut == 'Z')
889 scut = '0';
891 else
893 scut++;
896 if (flags & SHOW_INFONOTGEO)
898 tname[0]=0;
899 if (!IS_ICONIFIED(t) &&
900 !(flags & NO_DESK_NUM))
902 sprintf(loc,"%d:", t->Desk);
903 strcat(tname,loc);
905 if (IS_ICONIFIED(t))
907 strcat(tname, "(");
909 strcat(t_hot,"\t");
910 strcat(t_hot,tname);
911 strcat(t_hot, name);
912 if (IS_ICONIFIED(t))
914 strcat(t_hot, ")");
917 else if (flags & SHOW_GEOMETRY)
919 size_borders b;
921 tname[0]=0;
922 if (IS_ICONIFIED(t))
924 strcpy(tname, "(");
926 if (!(flags & NO_DESK_NUM))
928 sprintf(loc, "%d", t->Desk);
929 strcat(tname, loc);
931 if (flags & SHOW_SCREEN)
933 fscreen_scr_arg fscr;
934 int scr;
936 fscr.xypos.x =
937 Scr.Vx + t->g.frame.x +
938 t->g.frame.width / 2;
939 fscr.xypos.y =
940 Scr.Vy + t->g.frame.y +
941 t->g.frame.height / 2;
942 scr = FScreenGetScrId(
943 &fscr, FSCREEN_XYPOS);
944 sprintf(loc, "@%d", scr);
945 strcat(tname, loc);
947 if (flags & SHOW_PAGE_X)
949 sprintf(loc, "+%d",
950 (Scr.Vx + t->g.frame.x +
951 t->g.frame.width / 2) /
952 Scr.MyDisplayWidth);
953 strcat(tname, loc);
955 if (flags & SHOW_PAGE_Y)
957 sprintf(loc, "+%d",
958 (Scr.Vy + t->g.frame.y +
959 t->g.frame.height/2) /
960 Scr.MyDisplayHeight);
961 strcat(tname, loc);
963 if (!(flags & NO_LAYER))
965 sprintf(loc, "(%d)",
966 (get_layer(t)));
967 strcat(tname, loc);
969 strcat(tname, ":");
970 get_window_borders(t, &b);
971 dheight = t->g.frame.height -
972 b.total_size.height;
973 dwidth = t->g.frame.width -
974 b.total_size.width;
975 dwidth -= t->hints.base_width;
976 dheight -= t->hints.base_height;
977 dwidth /= t->hints.width_inc;
978 dheight /= t->hints.height_inc;
980 sprintf(loc,"%d",dwidth);
981 strcat(tname, loc);
982 sprintf(loc,"x%d",dheight);
983 strcat(tname, loc);
984 if (t->g.frame.x >=0)
986 sprintf(loc,"+%d",t->g.frame.x);
988 else
990 sprintf(loc,"%d",t->g.frame.x);
992 strcat(tname, loc);
993 if (t->g.frame.y >=0)
995 sprintf(loc,"+%d",t->g.frame.y);
997 else
999 sprintf(loc,"%d",t->g.frame.y);
1001 strcat(tname, loc);
1003 if (IS_STICKY_ACROSS_PAGES(t) ||
1004 IS_STICKY_ACROSS_DESKS(t))
1006 strcat(tname, " S");
1008 if (IS_ICONIFIED(t))
1010 strcat(tname, ")");
1012 strcat(t_hot,"\t");
1013 strcat(t_hot,tname);
1015 ffunc = func ? func : "WindowListFunc";
1016 tfunc = safemalloc(strlen(ffunc) + 36);
1017 /* support two ways for now: window context
1018 * (new) and window id param (old) */
1019 sprintf(tfunc, "WindowId %lu %s %lu",
1020 FW_W(t), ffunc, FW_W(t));
1021 AddToMenu(
1022 mr, t_hot, tfunc, False, False, False);
1023 free(tfunc);
1024 /* Add the title pixmap */
1025 if (FMiniIconsSupported && t->mini_icon)
1027 MI_MINI_ICON(MR_LAST_ITEM(mr))[0] =
1028 t->mini_icon;
1029 /* increase the cache count. Otherwise the
1030 * pixmap will be eventually removed from the
1031 * cache by DestroyMenu */
1032 t->mini_icon->count++;
1034 if (t_hot)
1036 free(t_hot);
1038 if (free_name)
1040 free(name);
1045 if (empty_menu)
1047 /* force current desk title */
1048 tlabel = get_desk_title(desk, flags, True);
1049 AddToMenu(mr, tlabel, "TITLE", False, False, False);
1050 free(tlabel);
1053 if (func)
1055 free(func);
1057 free(windowList);
1058 if (iconified_at_end)
1060 free(iconifiedList);
1062 /* Use the WindowList menu style if there is one */
1063 change_mr_menu_style(mr, "WindowList");
1065 /* Activate select_on_release style */
1066 old_sor_keycode = MST_SELECT_ON_RELEASE_KEY(mr);
1067 if (sor_keyname &&
1068 (!MST_SELECT_ON_RELEASE_KEY(mr) ||
1069 sor_keyname != sor_default_keyname))
1071 MST_SELECT_ON_RELEASE_KEY(mr) =
1072 XKeysymToKeycode(
1073 dpy, FvwmStringToKeysym(dpy, sor_keyname));
1076 memset(&mp, 0, sizeof(mp));
1077 mp.menu = mr;
1078 exc2 = exc_clone_context(exc, NULL, 0);
1079 mp.pexc = &exc2;
1080 mp.flags.has_default_action = (default_action && *default_action != 0);
1081 mp.flags.is_sticky = 1;
1082 mp.flags.is_submenu = 0;
1083 mp.flags.is_already_mapped = 0;
1084 mp.flags.is_triggered_by_keypress =
1085 (!default_action && exc->x.etrigger->type == KeyPress);
1086 mp.pops = &mops;
1087 mp.ret_paction = &ret_action;
1088 do_menu(&mp, &mret);
1089 /* Restore old menu style */
1090 MST_SELECT_ON_RELEASE_KEY(mr) = old_sor_keycode;
1091 if (ret_action)
1093 free(ret_action);
1095 DestroyMenu(mr, False, False);
1096 if (mret.rc == MENU_DOUBLE_CLICKED &&
1097 default_action && *default_action)
1099 execute_function(cond_rc, exc2, default_action, 0);
1101 if (default_action != NULL)
1103 free(default_action);
1105 if (use_condition)
1107 FreeConditionMask(&mask);
1109 if (sor_keyname && sor_keyname != sor_default_keyname)
1111 free(sor_keyname);
1113 exc_destroy_context(exc2);
1115 return;