close_delete_destroy upgrade
[fvwm.git] / fvwm / windowlist.c
blobd3b8821fc3c8dad95ce715c3cfddf7534572763c
1 /* This program is free software; you can redistribute it and/or modify
2 * it under the terms of the GNU General Public License as published by
3 * the Free Software Foundation; either version 2 of the License, or
4 * (at your option) any later version.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 /****************************************************************************
17 * This module is all new
18 * by Rob Nation
19 * A little of it is borrowed from ctwm.
20 * Copyright 1993 Robert Nation. No restrictions are placed on this code,
21 * as long as the copyright notice is preserved
22 ****************************************************************************/
23 /***********************************************************************
25 * fvwm window-list popup code
27 ***********************************************************************/
29 #include "config.h"
31 #include <stdio.h>
32 #include <limits.h>
34 #include "libs/fvwmlib.h"
35 #include "fvwm.h"
36 #include "externs.h"
37 #include "cursor.h"
38 #include "functions.h"
39 #include "bindings.h"
40 #include "misc.h"
41 #include "screen.h"
42 #include "fvwmlib.h"
43 #include "menus.h"
44 #include "conditional.h"
45 #include "stack.h"
47 extern FvwmWindow *Tmp_win;
48 extern FvwmWindow *ButtonWindow;
50 #define SHOW_GEOMETRY (1<<0)
51 #define SHOW_ALLDESKS (1<<1)
52 #define SHOW_NORMAL (1<<2)
53 #define SHOW_ICONIC (1<<3)
54 #define SHOW_STICKY (1<<4)
55 #define NO_DESK_SORT (1<<6)
56 #define SHOW_ICONNAME (1<<7)
57 #define SHOW_ALPHABETIC (1<<8)
58 #define SHOW_INFONOTGEO (1<<9)
59 #define SHOW_EVERYTHING (SHOW_GEOMETRY | SHOW_ALLDESKS | SHOW_NORMAL | SHOW_ICONIC | SHOW_STICKY)
61 /* Function to compare window title names
63 static int globalFlags;
64 static int winCompare(const FvwmWindow **a, const FvwmWindow **b)
66 if(globalFlags & SHOW_ICONNAME)
67 return strcasecmp((*a)->icon_name,(*b)->icon_name);
68 else
69 return strcasecmp((*a)->name,(*b)->name);
74 * Change by PRB (pete@tecc.co.uk), 31/10/93. Prepend a hot key
75 * specifier to each item in the list. This means allocating the
76 * memory for each item (& freeing it) rather than just using the window
77 * title directly. */
78 void do_windowList(XEvent *eventp,Window w,FvwmWindow *tmp_win,
79 unsigned long context, char *action,int *Module)
81 MenuRoot *mr;
82 MenuParameters mp;
83 char* ret_action = NULL;
84 FvwmWindow *t;
85 FvwmWindow **windowList;
86 int numWindows;
87 int ii;
88 char tname[80] = "";
89 char loc[40];
90 char *name=NULL;
91 int dwidth;
92 int dheight;
93 char tlabel[50]="";
94 int last_desk_done = INT_MIN;
95 int last_desk_displayed = INT_MIN;
96 int next_desk = 0;
97 char *t_hot=NULL; /* Menu label with hotkey added */
98 char scut = '0'; /* Current short cut key */
99 char *opts=NULL;
100 char *tok=NULL;
101 int desk = Scr.CurrentDesk;
102 int flags = SHOW_EVERYTHING;
103 char *func = NULL;
104 char *tfunc = NULL;
105 char *default_action = NULL;
106 MenuReturn mret;
107 XEvent *teventp;
108 MenuOptions mops;
109 int low_layer = 0; /* show all layers by default */
110 int high_layer = INT_MAX;
111 int tc;
112 int show_listskip = 0; /* do not show listskip by default */
113 Bool use_hotkey = True;
114 KeyCode old_sor_keycode;
115 char sor_default_keyname[8] = { 'M', 'e', 't', 'a', '_', 'L' };
116 char *sor_keyname = sor_default_keyname;
117 /* Condition vars. */
118 Bool use_condition = False;
119 WindowConditionMask mask;
120 char *cond_flags;
122 memset(&(mops.flags), 0, sizeof(mops.flags));
123 memset(&mret, 0, sizeof(MenuReturn));
124 if (action && *action)
126 /* Look for condition - CreateFlagString returns NULL if no '(' or '[' */
127 cond_flags = CreateFlagString(action, &action);
128 if (cond_flags)
130 /* Create window mask */
131 use_condition = True;
132 DefaultConditionMask(&mask);
134 /* override for Current [] */
135 mask.my_flags.use_circulate_hit = 1;
136 mask.my_flags.use_circulate_hit_icon = 1;
138 CreateConditionMask(cond_flags, &mask);
139 free(cond_flags);
142 if (action && *action)
144 /* parse postitioning args */
145 opts = GetMenuOptions(action, w, tmp_win, NULL, NULL, &mops);
148 /* parse options */
149 while (opts && *opts)
151 opts = GetNextSimpleOption(opts, &tok);
152 if (!tok)
153 break;
155 if (StrEquals(tok,"NoHotkeys"))
157 use_hotkey = False;
159 else if (StrEquals(tok,"Function"))
161 opts = GetNextSimpleOption(opts, &func);
163 else if (StrEquals(tok,"Desk"))
165 free(tok);
166 opts = GetNextSimpleOption(opts, &tok);
167 if (tok)
169 desk = atoi(tok);
170 flags &= ~SHOW_ALLDESKS;
173 else if (StrEquals(tok,"CurrentDesk"))
175 desk = Scr.CurrentDesk;
176 flags &= ~SHOW_ALLDESKS;
178 else if (StrEquals(tok,"NotAlphabetic"))
179 flags &= ~SHOW_ALPHABETIC;
180 else if (StrEquals(tok,"Alphabetic"))
181 flags |= SHOW_ALPHABETIC;
182 else if (StrEquals(tok,"NoDeskSort"))
183 flags |= NO_DESK_SORT;
184 else if (StrEquals(tok,"UseIconName"))
185 flags |= SHOW_ICONNAME;
186 else if (StrEquals(tok,"NoGeometry"))
188 flags &= ~SHOW_GEOMETRY;
189 flags &= ~SHOW_INFONOTGEO;
191 else if (StrEquals(tok,"NoGeometryWithInfo"))
193 flags &= ~SHOW_GEOMETRY;
194 flags |= SHOW_INFONOTGEO;
196 else if (StrEquals(tok,"Geometry"))
198 flags |= SHOW_GEOMETRY;
199 flags &= ~SHOW_INFONOTGEO;
201 else if (StrEquals(tok,"NoIcons"))
202 flags &= ~SHOW_ICONIC;
203 else if (StrEquals(tok,"Icons"))
204 flags |= SHOW_ICONIC;
205 else if (StrEquals(tok,"OnlyIcons"))
206 flags = SHOW_ICONIC;
207 else if (StrEquals(tok,"NoNormal"))
208 flags &= ~SHOW_NORMAL;
209 else if (StrEquals(tok,"Normal"))
210 flags |= SHOW_NORMAL;
211 else if (StrEquals(tok,"OnlyNormal"))
212 flags = SHOW_NORMAL;
213 else if (StrEquals(tok,"NoSticky"))
214 flags &= ~SHOW_STICKY;
215 else if (StrEquals(tok,"Sticky"))
216 flags |= SHOW_STICKY;
217 else if (StrEquals(tok,"OnlySticky"))
218 flags = SHOW_STICKY;
219 else if (StrEquals(tok,"UseListSkip"))
220 show_listskip = 1;
221 else if (StrEquals(tok,"OnlyListSkip"))
222 show_listskip = 2;
224 these are a bit dubious, but we
225 should keep the OnTop options
226 for compatibility
228 else if (StrEquals(tok, "NoOnTop"))
230 if (high_layer >= Scr.TopLayer)
231 high_layer = Scr.TopLayer - 1;
233 else if (StrEquals(tok, "OnTop"))
235 if (high_layer < Scr.TopLayer)
236 high_layer = Scr.TopLayer;
238 else if (StrEquals(tok, "OnlyOnTop"))
240 high_layer = low_layer = Scr.TopLayer;
242 else if (StrEquals(tok, "NoOnBottom"))
244 if (low_layer <= Scr.BottomLayer)
245 low_layer = Scr.BottomLayer - 1;
247 else if (StrEquals(tok, "OnBottom"))
249 if (low_layer > Scr.BottomLayer)
250 low_layer = Scr.BottomLayer;
252 else if (StrEquals(tok, "OnlyOnBottom"))
254 high_layer = low_layer = Scr.BottomLayer;
256 else if (StrEquals(tok, "Layer"))
258 free(tok);
259 opts = GetNextSimpleOption(opts, &tok);
260 if (tok)
262 low_layer = high_layer = atoi(tok);
263 free(tok);
264 opts = GetNextSimpleOption(opts, &tok);
265 if (tok)
267 high_layer = atoi(tok);
271 else if (StrEquals(tok, "SelectOnRelease"))
273 if (sor_keyname != sor_default_keyname)
274 free(sor_keyname);
275 sor_keyname = NULL;
276 opts = GetNextSimpleOption(opts, &sor_keyname);
278 else if (!opts || !*opts)
279 default_action = strdup(tok);
280 else
282 fvwm_msg(ERR,"WindowList","Unknown option '%s'",tok);
284 if (tok)
285 free(tok);
289 globalFlags = flags;
290 if (flags & SHOW_GEOMETRY)
292 sprintf(tlabel,"Desk: %d\tGeometry",desk);
294 else
296 sprintf(tlabel,"Desk: %d",desk);
298 mr = NewMenuRoot(tlabel);
299 AddToMenu(mr, tlabel, "TITLE", FALSE, FALSE);
301 numWindows = 0;
302 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
304 numWindows++;
306 windowList = malloc(numWindows*sizeof(t));
307 if (windowList == NULL)
309 return;
311 /* get the windowlist starting from the current window (if any)*/
312 if ((t = Scr.Focus) == NULL)
313 t = Scr.FvwmRoot.next;
314 for (ii = 0; ii < numWindows; ii++)
316 windowList[ii] = t;
317 if (t->next)
318 t = t->next;
319 else
320 t = Scr.FvwmRoot.next;
323 /* Do alphabetic sort */
324 if (flags & SHOW_ALPHABETIC)
325 qsort(windowList,numWindows,sizeof(t),
326 (int(*)(const void*,const void*))winCompare);
328 while(next_desk != INT_MAX)
330 /* Sort window list by desktop number */
331 if((flags & SHOW_ALLDESKS) && !(flags & NO_DESK_SORT))
333 /* run through the windowlist finding the first desk not already
334 * processed */
335 next_desk = INT_MAX;
336 for (ii = 0; ii < numWindows; ii++)
338 t = windowList[ii];
339 if((t->Desk >last_desk_done)&&(t->Desk < next_desk))
340 next_desk = t->Desk;
343 if(!(flags & SHOW_ALLDESKS))
345 /* if only doing one desk and it hasn't been done */
346 if(last_desk_done == INT_MIN)
347 next_desk = desk; /* select the desk */
348 else
349 next_desk = INT_MAX; /* flag completion */
351 if(flags & NO_DESK_SORT)
352 next_desk = INT_MAX; /* only go through loop once */
354 last_desk_done = next_desk;
355 for (ii = 0; ii < numWindows; ii++)
357 t = windowList[ii];
358 if(((t->Desk == next_desk) || (flags & NO_DESK_SORT)))
360 if (!show_listskip && DO_SKIP_WINDOW_LIST(t))
361 continue; /* don't want listskip windows - skip */
362 if (show_listskip == 2 && !DO_SKIP_WINDOW_LIST(t))
363 continue; /* don't want no listskip one - skip */
364 if (use_condition && !MatchesConditionMask(t, &mask))
365 continue; /* doesn't match specified condition */
366 if (!(flags & SHOW_ICONIC) && (IS_ICONIFIED(t)))
367 continue; /* don't want icons - skip */
368 if (!(flags & SHOW_STICKY) && (IS_STICKY(t)))
369 continue; /* don't want sticky ones - skip */
370 if (!(flags & SHOW_NORMAL) &&
371 !((IS_ICONIFIED(t)) || (IS_STICKY(t))))
372 continue; /* don't want "normal" ones - skip */
373 if ((get_layer(t) < low_layer) || (get_layer(t) > high_layer))
374 continue; /* don't want this layer */
376 /* add separator between desks when geometry shown but not at the top*/
377 if (t->Desk != last_desk_displayed)
379 if (last_desk_displayed != INT_MIN)
380 if ((flags & SHOW_GEOMETRY) || (flags & SHOW_INFONOTGEO))
381 AddToMenu(mr, NULL, NULL, FALSE, FALSE);
382 last_desk_displayed = t->Desk;
385 if(flags & SHOW_ICONNAME)
386 name = t->icon_name;
387 else
388 name = t->name;
390 t_hot = safemalloc(strlen(name) + 48);
391 if (use_hotkey)
392 sprintf(t_hot, "&%c. ", scut); /* Generate label */
393 else
394 *t_hot = 0;
395 if(!(flags & SHOW_INFONOTGEO))
396 strcat(t_hot, name);
397 if (*t_hot == 0)
398 strcpy(t_hot, " ");
400 /* Next shortcut key */
401 if (scut == '9')
402 scut = 'A';
403 else if (scut == 'Z')
404 scut = '0';
405 else
406 scut++;
408 if (flags & SHOW_INFONOTGEO)
410 tname[0]=0;
411 if(!IS_ICONIFIED(t))
413 sprintf(loc,"%d:", t->Desk);
414 strcat(tname,loc);
416 else
417 strcat(tname, "(");
418 strcat(t_hot,"\t");
419 strcat(t_hot,tname);
420 strcat(t_hot, name);
421 if(IS_ICONIFIED(t))
422 strcat(t_hot, ")");
424 else if (flags & SHOW_GEOMETRY)
426 tname[0]=0;
427 if(IS_ICONIFIED(t))
428 strcpy(tname, "(");
429 sprintf(loc,"%d(%d):",t->Desk, get_layer(t));
430 strcat(tname,loc);
432 dheight = t->frame_g.height - t->title_g.height -2*t->boundary_width;
433 dwidth = t->frame_g.width - 2*t->boundary_width;
435 dwidth -= t->hints.base_width;
436 dheight -= t->hints.base_height;
438 dwidth /= t->hints.width_inc;
439 dheight /= t->hints.height_inc;
441 sprintf(loc,"%d",dwidth);
442 strcat(tname, loc);
443 sprintf(loc,"x%d",dheight);
444 strcat(tname, loc);
445 if(t->frame_g.x >=0)
446 sprintf(loc,"+%d",t->frame_g.x);
447 else
448 sprintf(loc,"%d",t->frame_g.x);
449 strcat(tname, loc);
450 if(t->frame_g.y >=0)
451 sprintf(loc,"+%d",t->frame_g.y);
452 else
453 sprintf(loc,"%d",t->frame_g.y);
454 strcat(tname, loc);
456 if (IS_STICKY(t))
457 strcat(tname, " S");
458 if (IS_ICONIFIED(t))
459 strcat(tname, ")");
460 strcat(t_hot,"\t");
461 strcat(t_hot,tname);
463 if (!func)
465 tfunc = safemalloc(40);
466 sprintf(tfunc,"WindowListFunc %lu", t->w);
468 else
470 tfunc = safemalloc(strlen(func) + 32);
471 sprintf(tfunc,"%s %lu", func, t->w);
473 AddToMenu(mr, t_hot, tfunc, FALSE, FALSE);
474 free(tfunc);
475 #ifdef MINI_ICONS
476 /* Add the title pixmap */
477 if (t->mini_icon) {
478 MI_MINI_ICON(MR_LAST_ITEM(mr))[0] = t->mini_icon;
479 t->mini_icon->count++; /* increase the cache count!!
480 otherwise the pixmap will be
481 eventually removed from the
482 cache by DestroyMenu */
484 #endif
485 if (t_hot)
486 free(t_hot);
491 if (func)
492 free(func);
493 free(windowList);
494 if (!default_action && eventp && eventp->type == KeyPress)
495 teventp = (XEvent *)1;
496 else
497 teventp = eventp;
499 /* Use the WindowList menu style is there is one */
500 change_mr_menu_style(mr, "WindowList");
502 /* Activate select_on_release style */
503 old_sor_keycode = MST_SELECT_ON_RELEASE_KEY(mr);
504 if (sor_keyname &&
505 (!MST_SELECT_ON_RELEASE_KEY(mr) || sor_keyname != sor_default_keyname))
507 KeyCode keycode = 0;
509 if (sor_keyname)
511 keycode = XKeysymToKeycode(dpy, FvwmStringToKeysym(dpy, sor_keyname));
513 MST_SELECT_ON_RELEASE_KEY(mr) =
514 XKeysymToKeycode(dpy, FvwmStringToKeysym(dpy, sor_keyname));
517 mp.menu = mr;
518 mp.parent_menu = NULL;
519 mp.parent_item = NULL;
520 t = Tmp_win;
521 mp.pTmp_win = &t;
522 mp.button_window = ButtonWindow;
523 tc = context;
524 mp.pcontext = &tc;
525 mp.flags.has_default_action = (default_action && *default_action != 0);
526 mp.flags.is_menu_from_frame_or_window_or_titlebar = False;
527 mp.flags.is_sticky = True;
528 mp.flags.is_submenu = False;
529 mp.flags.is_already_mapped = False;
530 mp.eventp = teventp;
531 mp.pops = &mops;
532 mp.ret_paction = &ret_action;
534 do_menu(&mp, &mret);
535 /* Restore old menu style */
536 MST_SELECT_ON_RELEASE_KEY(mr) = old_sor_keycode;
537 if (ret_action)
538 free(ret_action);
539 DestroyMenu(mr, False);
540 if (mret.rc == MENU_DOUBLE_CLICKED && default_action && *default_action)
541 old_execute_function(
542 default_action, tmp_win, eventp, context, *Module, 0 , NULL);
543 if (default_action != NULL)
544 free(default_action);
545 if (use_condition)
546 FreeConditionMask(&mask);