Subject: Patch: Window group support

From: Matthias Clasen <[email-masked]>
Date: Wed, 6 Jan 1999 00:53:15 +0100

This patch adds some support for window groups.  

New functions:

  IconifyGroup [ value ]
	       Iconifies a window group if it is not already iconified
	       or de-iconifies it if it is already iconified.  If the 
	       optional argument value is positive only iconification 
	       will be allowed.  It the optional argument is negative only 
	       de-iconification will be allowed.
	       Iconifying a window group means unmapping all windows and 
	       icons of group members and map the group icon, that is the
	       icon of the group leader.  Technically, all group members
	       are iconified and except the group leader have their icon
	       suppressed.
	       De-iconifying a window group means restoring all group
	       members to their state before the group was iconified.
	       There is a technical difficulty here, since clients may
	       create a dedicated group leader, which is not intended
	       to be ever mapped.  In this case, fvwm restores the
	       group leader to iconic state with suppressed icon. To 
	       make sure that dedicated group leaders don't get mapped,
	       the iconify function has been forbidden on them.
	       If a client de-iconifies a window while its group is
	       iconified, fvwm de-iconifies the group first.

  All (conditions) command
	       Performs command on all windows satisfying all
	       conditions.  There are four additional conditions
	       which have to do with window groups. These are
	       "Group", "!Group", "Leader", "!Leader". 
	       "Group" is true if the window being operated on
	       is in the same group as the current window, 
	       "Leader" is true if the window being operated on
	       is the leader of the current window's group. 
	       Examples:
	       
	           # concentrate on one application	           
		   All (!Group) Iconify 1   
		   
		   # Take your 50 Gimp windows with you
		   All (Group) Stick 
		   
		   # This may be useful on a client which has
		   # its main window as group leader.
		   All (Group !Leader) Iconify 1

There are some shortcomings in the window group support implemented
in this patch: 

  * As it is now, dedicated group leaders are decorated with a
    complete FvwmWindow structure and put in the main list of
    windows. They are marked with a flag, so it would be possible
    to skip them if necessary, but it would be better to keep them
    completely separate from the real windows.

  * There is no convenient way to assign icon/icon_name  to a group
    if the client doesn't give its dedicated group leader a recognizable
    name. E.g. the group leader of the gimp has no name or icon_name
    and class Gimp. Maybe we need a GroupStyle command which would 
    be analogous to Style, but apply to the groups of the windows
    matched by the search pattern.

--- fvwm.h.orig	Fri Jan  1 21:50:52 1999
+++ fvwm.h	Tue Jan  5 21:56:21 1999
@@ -249,6 +249,12 @@
       unsigned ViewportMoved : 1; /* To prevent double move in MoveViewport. */
       unsigned IconifiedByParent : 1; /* To prevent iconified transients in a
 				       * parent icon from counting for Next */
+      unsigned OnlyGroupLeader:1; /* This is not a normal window
+				     that should ever be mapped */
+      unsigned GroupIconified :1; 	
+      unsigned IndividuallyIconified :1; /* To restore iconified flag 
+                                            when group is deiconified */   
+      unsigned IconSuppressed :1; /* To restore suppressicon flag */
       unsigned  : 1;
     } tmpflags;
 #endif /* GSFR */
@@ -291,6 +297,11 @@
   unsigned long onFlags;
   unsigned long offFlags;
   char *name;
+  Window leader;
+  Bool needsGroup;
+  Bool needsNotGroup;
+  Bool needsLeader;
+  Bool needsNotLeader;
 } WindowConditionMask;
 
 /***************************************************************************
@@ -406,6 +417,8 @@
 extern Atom _XA_OL_DECOR_RESIZE;
 extern Atom _XA_OL_DECOR_HEADER;
 extern Atom _XA_OL_DECOR_ICON_NAME;
+
+#define ICCCM2
 
 /* include this down here because FvwmWindows must be defined when including
  * this header file. */
--- misc.h.orig	Fri Jan  1 21:50:53 1999
+++ misc.h	Tue Jan  5 20:23:00 1999
@@ -295,6 +295,8 @@
 void SetClientProp(FvwmWindow *);
 void Iconify(FvwmWindow *, int, int);
 void DeIconify(FvwmWindow *);
+void IconifyGroup(FvwmWindow *, int, int);
+void DeIconifyGroup(FvwmWindow *);
 void KeepOnTop(void);
 void show_panner(void);
 void WaitForButtonsUp(void);
@@ -364,6 +366,7 @@
 void scroll(F_CMD_ARGS);
 void movecursor(F_CMD_ARGS);
 void iconify_function(F_CMD_ARGS);
+void iconify_group_function(F_CMD_ARGS);
 void raise_function(F_CMD_ARGS);
 void lower_function(F_CMD_ARGS);
 void destroy_function(F_CMD_ARGS);
@@ -457,6 +460,7 @@
 void PrevFunc(F_CMD_ARGS);
 void NoneFunc(F_CMD_ARGS);
 void CurrentFunc(F_CMD_ARGS);
+void AllFunc(F_CMD_ARGS);
 void DirectionFunc(F_CMD_ARGS);
 void WindowIdFunc(F_CMD_ARGS);
 void ReadFile(F_CMD_ARGS);
--- parse.h.orig	Fri Jan  1 21:50:53 1999
+++ parse.h	Tue Jan  5 21:05:16 1999
@@ -78,6 +78,7 @@
     F_EXEC_SETUP,
     F_CURSOR_STYLE,
     F_CURRENT,
+    F_ALL,
     F_SETENV,
     F_SET_ANIMATION,
     F_SET_MENUSTYLE,
@@ -119,6 +120,7 @@
     F_WINDOW_SHADE,
     F_COLOR_LIMIT,
     F_ANIMATED_MOVE,
+    F_ICONIFY_GROUP,
 
 /* Functions for use by modules only! */
     F_SEND_WINDOW_LIST = 1000
--- add_window.c.orig	Fri Jan  1 21:50:51 1999
+++ add_window.c	Mon Jan  4 22:04:39 1999
@@ -119,6 +119,7 @@
   tmp_win->flags = 0;
   tmp_win->tmpflags.ViewportMoved = 0;
   tmp_win->tmpflags.IconifiedByParent = 0;
+  tmp_win->tmpflags.GroupIconified = 0;
   tmp_win->w = w;
 
   tmp_win->cmap_windows = (Window *)NULL;
@@ -780,6 +781,7 @@
       resize_window(&Event , tmp_win->w, tmp_win, C_WINDOW, "", 0);
     }
   InstallWindowColormaps(colormap_win);
+
   return (tmp_win);
 }
 
--- builtins.c.orig	Fri Jan  1 21:50:51 1999
+++ builtins.c	Tue Jan  5 23:34:18 1999
@@ -39,6 +39,17 @@
 #endif
 };
 
+/* This is enabled during operation on multiple windows at one
+  (the "All" command), since it is annoying to get 5 beeps from
+  5 windows not allowing to be iconized. 
+*/
+static int fail_silently = 0;
+
+void FunctionNotAllowed(void)
+{
+   if (!fail_silently) XBell(dpy, 0);
+}
+
 /***********************************************************************
  *
  *  Procedure:
@@ -332,7 +343,7 @@
 #endif
      )
   {
-    XBell(dpy, 0);
+    FunctionNotAllowed();
     return;
   }
   n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);
@@ -398,8 +409,8 @@
 	return;
 
     if (!(tmp_win->flags & TITLE) || (tmp_win->flags & MAXIMIZED)) {
-	XBell(dpy, 0);
-	return;
+      FunctionNotAllowed();
+      return;
     }
     while (isspace(*action))++action;
     if (isdigit(*action))
@@ -691,20 +702,53 @@
 
   GetIntegerArguments(action, NULL, &val, 1);
 
-  if (tmp_win->flags & ICONIFIED)
+  /* Moved the check to cover both cases. We want to disallow
+     deiconification of artificial group leader icons. */
+  if(check_allowed_function2(F_ICONIFY,tmp_win)==0) 
+    {
+      FunctionNotAllowed();
+      return;
+    }
+  else
+    {
+      if (tmp_win->flags & ICONIFIED)
+	{
+	  if(val <=0)
+	    DeIconify(tmp_win);
+	}
+      else
+	{
+	  if(val >=0)
+	    Iconify(tmp_win,eventp->xbutton.x_root-5,eventp->xbutton.y_root-5);
+	}
+    }
+}
+
+void iconify_group_function(XEvent *eventp,Window w,FvwmWindow *tmp_win,
+		            unsigned long context,char *action, int *Module)
+
+{
+  int val = 0;
+
+  if (DeferExecution(eventp,&w,&tmp_win,&context, SELECT, ButtonRelease))
+    return;
+
+  GetIntegerArguments(action, NULL, &val, 1);
+
+  if (tmp_win->tmpflags.GroupIconified)
   {
     if(val <=0)
-      DeIconify(tmp_win);
+      DeIconifyGroup(tmp_win);
   }
   else
   {
-    if(check_allowed_function2(F_ICONIFY,tmp_win) == 0)
+    if(check_allowed_function2(F_ICONIFY_GROUP,tmp_win) == 0)
     {
-      XBell(dpy, 0);
+      FunctionNotAllowed();
       return;
     }
     if(val >=0)
-      Iconify(tmp_win,eventp->xbutton.x_root-5,eventp->xbutton.y_root-5);
+      IconifyGroup(tmp_win,eventp->xbutton.x_root-5,eventp->xbutton.y_root-5);
   }
 }
 
@@ -745,7 +789,7 @@
 
   if(check_allowed_function2(F_DESTROY,tmp_win) == 0)
   {
-    XBell(dpy, 0);
+    FunctionNotAllowed();
     return;
   }
 
@@ -765,7 +809,7 @@
 
   if(check_allowed_function2(F_DELETE,tmp_win) == 0)
   {
-    XBell(dpy, 0);
+    FunctionNotAllowed();
     return;
   }
 
@@ -775,7 +819,7 @@
     return;
   }
   else
-    XBell (dpy, 0);
+    FunctionNotAllowed();
   XSync(dpy,0);
 }
 
@@ -787,7 +831,7 @@
 
   if(check_allowed_function2(F_CLOSE,tmp_win) == 0)
   {
-    XBell(dpy, 0);
+    FunctionNotAllowed();
     return;
   }
 
@@ -4123,6 +4167,11 @@
   mask->useCirculateHitIcon = 0;
   mask->onFlags = 0;
   mask->offFlags = 0;
+  mask->leader = None;
+  mask->needsGroup = 0;
+  mask->needsNotGroup = 0;
+  mask->needsLeader = 0;
+  mask->needsNotLeader = 0;
 }
 
 /**********************************************************************
@@ -4144,7 +4193,15 @@
 
   while (condition)
   {
-    if (StrEquals(condition,"Iconic"))
+    if (StrEquals(condition,"Group"))
+      mask->needsGroup = 1;
+    else if (StrEquals(condition,"!Group"))
+      mask->needsNotGroup = 1;
+    else if (StrEquals(condition,"Leader"))
+      mask->needsLeader = 1;
+    else if (StrEquals(condition,"!Leader"))
+      mask->needsNotLeader = 1;
+    else if (StrEquals(condition,"Iconic"))
       mask->onFlags |= ICONIFIED;
     else if(StrEquals(condition,"!Iconic"))
       mask->offFlags |= ICONIFIED;
@@ -4196,6 +4253,21 @@
 	mask->needsName = 1;
     }
 
+    if (mask->needsGroup || mask->needsNotGroup ||
+        mask->needsLeader || mask->needsNotLeader) 
+      {
+	if ((Scr.Focus != NULL) &&
+	    (Scr.Focus->wmhints != NULL) &&
+	    (Scr.Focus->wmhints->flags & WindowGroupHint))
+	  {
+	    mask->leader = Scr.Focus->wmhints->window_group;
+	    if (mask->leader == Scr.Root) 
+	      {
+		mask->leader = None;
+	      }
+	  }
+      }
+    
     if (prev_condition)
       free(prev_condition);
 
@@ -4218,6 +4290,7 @@
   Bool fMatchesClass;
   Bool fMatchesResource;
   Bool fMatches;
+  Window fleader;
 
   if ((mask->onFlags & fw->flags) != mask->onFlags)
     return 0;
@@ -4263,6 +4336,28 @@
   if (mask->needsNotName && fMatches)
     return 0;
 
+  fleader = None;
+  if (fw->wmhints && (fw->wmhints->flags & WindowGroupHint))
+    {
+      fleader = fw->wmhints->window_group;
+      if (fleader == Scr.Root) 
+	{
+	  fleader = None;
+	}
+    }
+
+  if (mask->needsLeader && (mask->leader != fw->w))
+    return 0;
+
+  if (mask->needsNotLeader && (mask->leader == fw->w) && (fw->w != None))
+    return 0;
+
+  if (mask->needsGroup && (mask->leader != fleader))
+    return 0;
+
+  if (mask->needsNotGroup && (mask->leader == fleader) && (fleader != None))
+    return 0;
+      
   return 1;
 }
 
@@ -4403,6 +4498,33 @@
   }
 }
 
+void AllFunc(XEvent *eventp, Window junk, FvwmWindow *tmp_win,
+              unsigned long context, char *action,int* Module)
+{
+  FvwmWindow *t;
+  char *restofline;
+  WindowConditionMask mask;
+  char *flags;
+
+  flags = CreateFlagString(action, &restofline);
+  DefaultConditionMask(&mask);
+  CreateConditionMask(flags, &mask);
+  mask.useCirculateHit = 1;
+  mask.useCirculateHitIcon = 1;
+
+  fail_silently = 1;
+  for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) 
+    {
+      if (MatchesConditionMask(t, &mask)) 
+	{
+	  ExecuteFunction(restofline,t,eventp,C_WINDOW,*Module);
+	}
+    }  
+  fail_silently = 0;
+
+  FreeConditionMask(&mask);
+}
+	       
 static void GetDirectionReference(FvwmWindow *w, int *x, int *y)
 {
   if ((w->flags & ICONIFIED) != 0)
--- decorations.c.orig	Fri Jan  1 21:50:51 1999
+++ decorations.c	Tue Jan  5 14:29:43 1999
@@ -495,10 +495,23 @@
         if (!(t->functions & MWM_FUNC_RESIZE))
           return 0;
         break;
+      case F_ICONIFY_GROUP:
+	if (t->wmhints == NULL)
+	  return 0;
+	if (!(t->wmhints->flags & WindowGroupHint))
+	  return 0;
+	if ((t->wmhints->window_group == None) || 
+            (t->wmhints->window_group == Scr.Root))
+	  return 0;
+        break;
       case F_ICONIFY:
-        if ((!(t->flags & ICONIFIED))&&
-            (!(t->functions & MWM_FUNC_MINIMIZE)))
+        if (!(t->flags & ICONIFIED) &&
+	    (!(t->functions & MWM_FUNC_MINIMIZE)))
           return 0;
+	/* To avoid having our artificial group leaders pop up 
+	   accidentially, we disable F_ICONIFY on them */
+	if (t->tmpflags.OnlyGroupLeader)
+	  return 0;
         break;
       case F_MAXIMIZE:
         if (!(t->functions & MWM_FUNC_MAXIMIZE))
--- functions.c.orig	Fri Jan  1 21:50:52 1999
+++ functions.c	Tue Jan  5 21:19:26 1999
@@ -68,6 +68,7 @@
 #endif /* USEDECOR */
   {"AddToFunc",    add_item_to_func, F_ADDFUNC,             FUNC_NO_WINDOW},
   {"AddToMenu",    add_item_to_menu, F_ADDMENU,             FUNC_NO_WINDOW},
+  {"All",          AllFunc,          F_ALL,                 FUNC_NO_WINDOW},
   {"AnimatedMove", animated_move_window,F_ANIMATED_MOVE,    FUNC_NEEDS_WINDOW},
   {"Beep",         Bell,             F_BEEP,                FUNC_NO_WINDOW},
 #ifdef BORDERSTYLE
@@ -114,6 +115,7 @@
   {"HilightColor", SetHiColor,       F_HICOLOR,             FUNC_NO_WINDOW},
   {"IconFont",     LoadIconFont,     F_ICONFONT,            FUNC_NO_WINDOW},
   {"Iconify",      iconify_function, F_ICONIFY,             FUNC_NEEDS_WINDOW},
+  {"IconifyGroup", iconify_group_function, F_ICONIFY_GROUP, FUNC_NEEDS_WINDOW},
   {"IconPath",     setIconPath,      F_ICON_PATH,           FUNC_NO_WINDOW},
   {"Key",          ParseKeyEntry,    F_KEY,                 FUNC_NO_WINDOW},
   {"KillModule",   module_zapper,    F_ZAP,                 FUNC_NO_WINDOW},
--- icons.c.orig	Fri Jan  1 21:50:52 1999
+++ icons.c	Wed Jan  6 00:13:43 1999
@@ -800,8 +800,6 @@
 #endif
 }
 
-
-
 /***********************************************************************
  *
  *  Procedure:
@@ -815,6 +813,10 @@
   if(!tmp_win)
     return;
 
+  if (tmp_win->tmpflags.GroupIconified) {
+    DeIconifyGroup(tmp_win);
+  }
+
   /* AS dje  RaiseWindow(tmp_win); */
   /* now de-iconify transients */
   for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
@@ -965,11 +967,12 @@
   BroadcastConfig(M_CONFIGURE_WINDOW,tmp_win);
 
   LowerWindow(tmp_win);
-  if(tmp_win->Desk == Scr.CurrentDesk)
-    {
+  if (tmp_win->flags & SUPPRESSICON) {
+    tmp_win->flags |= ICON_UNMAPPED;    
+  } else if (tmp_win->Desk == Scr.CurrentDesk) {
       if (tmp_win->icon_w != None)
-        XMapWindow(dpy, tmp_win->icon_w);
-
+	XMapWindow(dpy, tmp_win->icon_w);
+      
       if(tmp_win->icon_pixmap_w != None)
 	XMapWindow(dpy, tmp_win->icon_pixmap_w);
       KeepOnTop();
@@ -1011,4 +1014,136 @@
   XChangeProperty (dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32,
 		   PropModeReplace, (unsigned char *) data, 2);
   return;
+}
+
+#include <X11/Xproto.h>
+#include <X11/Xatom.h>
+
+void IconifyGroup(FvwmWindow *tmp_win, int def_x, int def_y)
+{
+  FvwmWindow *t, *group;
+
+  if (!tmp_win || 
+      tmp_win->tmpflags.GroupIconified ||
+      !tmp_win->wmhints ||
+      !(tmp_win->wmhints->flags & WindowGroupHint) ||
+      (tmp_win->wmhints->window_group == None) ||
+      (tmp_win->wmhints->window_group == Scr.Root)) {
+    return;
+  }
+
+  group = NULL;
+
+  for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) {
+    if (t->w == tmp_win->wmhints->window_group) {
+      group = t;
+      fvwm_msg(DBG, "IconifyGroup", "group leader %x found\n", t->w);
+    } else if ((t->wmhints) &&
+               (t->wmhints->flags & WindowGroupHint)&&
+               (t->wmhints->window_group == tmp_win->wmhints->window_group)) {
+
+      t->tmpflags.GroupIconified = 1;
+      t->tmpflags.IndividuallyIconified = (t->flags & ICONIFIED) ? 1 : 0;
+      t->tmpflags.IconSuppressed = (t->flags & SUPPRESSICON) ? 1 : 0; 
+
+      fvwm_msg(DBG, "IconifyGroup", "%s group member %x found\n",
+	     (t->tmpflags.IndividuallyIconified ? "iconic" : "normal"),
+	     t->w); 
+ 
+      t->flags |= SUPPRESSICON;
+
+      Iconify(t, t->icon_x_loc, t->icon_y_loc); 
+    }    
+  }
+
+  if (group == NULL) {
+    /* The client has specified a group leader which he didn't
+       map. We'll assume that it is a window created specifically
+       for this purpose. We really shouldn't map it, but it is too 
+       complicated to create a FvwmWindow structure for it without 
+       mapping it. The FvwmWindow is really too much overhead only
+       for its icon, too bad. It will also have unwanted effects
+       in the FvwmWindow list structures, I guess. 
+       We can at least move it out of sight. */
+
+
+    /* Set window_group so that this window is recognizable as 
+       group leader */
+    XWMHints *wmh = XGetWMHints (dpy, tmp_win->wmhints->window_group);
+    
+    if (wmh == NULL)
+      wmh = XAllocWMHints ();
+    
+    if (!(wmh->flags & WindowGroupHint)) {
+      wmh->flags |= WindowGroupHint;
+      wmh->window_group = tmp_win->wmhints->window_group;
+      XSetWMHints (dpy, tmp_win->wmhints->window_group, wmh);
+    }
+    XFree (wmh);
+      
+    group = AddWindow (tmp_win->wmhints->window_group);
+    /* And we keep it out of sight */
+    group->flags |= WINDOWLISTSKIP|CirculateSkip;
+
+    /* These settings make sure that the group leader will not
+       show up on screen when you deiconify the group. It will
+       still show up if you deiconify only the group leader window.
+       But I have changed check_if_function_allowed to disable
+       iconify on windows with the OnlyGroupLeader flag set...
+    */
+    group->flags |= SUPPRESSICON;
+    group->flags |= ICONIFIED;
+    group->tmpflags.OnlyGroupLeader = 1; 
+
+    fvwm_msg(DBG, "IconifyGroup", "artificial group leader %x created\n", 
+	     group->w);
+  }
+  
+  group->tmpflags.GroupIconified = 1;
+  group->tmpflags.IndividuallyIconified = (group->flags & ICONIFIED) ? 1 : 0;
+  group->tmpflags.IconSuppressed = (group->flags & SUPPRESSICON) ? 1 : 0; 
+  
+  group->flags &= ~SUPPRESSICON;
+  
+  Iconify(group, def_x, def_y); 
+      
+}
+
+void DeIconifyGroup(FvwmWindow *tmp_win)
+{
+  FvwmWindow *t;
+
+  if (!tmp_win || 
+      !tmp_win->tmpflags.GroupIconified ||
+      !tmp_win->wmhints ||
+      !(tmp_win->wmhints->flags & WindowGroupHint) ||
+      (tmp_win->wmhints->window_group == None) ||
+      (tmp_win->wmhints->window_group == Scr.Root)) {
+    return;
+  }
+
+  for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) {
+    if ((t->w == tmp_win->wmhints->window_group) ||
+	((t->wmhints) &&
+	 (t->wmhints->flags & WindowGroupHint) &&
+	 (t->wmhints->window_group == tmp_win->wmhints->window_group))) {
+      t->tmpflags.GroupIconified = 0;
+      if (t->tmpflags.IconSuppressed) {
+        t->flags |= SUPPRESSICON;
+      } else {
+        t->flags &= ~SUPPRESSICON;
+      }
+      if (t->tmpflags.IndividuallyIconified) {
+        fvwm_msg(DBG, "DeIconifyGroup", "restore group %s %x to iconic", 
+	         (t->w == tmp_win->wmhints->window_group) ? "leader" : "member",
+	         t->w);       
+        Iconify (t, t->icon_x_loc, t->icon_y_loc);
+      } else {
+        fvwm_msg(DBG, "DeIconifyGroup", "restore group %s %x to normal", 
+	         (t->w == tmp_win->wmhints->window_group) ? "leader" : "member",
+	         t->w);       
+        DeIconify (t);
+      }
+    }    
+  }
 }
--- fvwm.h.orig	Fri Jan  1 21:50:52 1999
+++ fvwm.h	Tue Jan  5 21:56:21 1999
@@ -249,6 +249,12 @@
       unsigned ViewportMoved : 1; /* To prevent double move in MoveViewport. */
       unsigned IconifiedByParent : 1; /* To prevent iconified transients in a
 				       * parent icon from counting for Next */
+      unsigned OnlyGroupLeader:1; /* This is not a normal window
+				     that should ever be mapped */
+      unsigned GroupIconified :1; 	
+      unsigned IndividuallyIconified :1; /* To restore iconified flag 
+                                            when group is deiconified */   
+      unsigned IconSuppressed :1; /* To restore suppressicon flag */
       unsigned  : 1;
     } tmpflags;
 #endif /* GSFR */
@@ -291,6 +297,11 @@
   unsigned long onFlags;
   unsigned long offFlags;
   char *name;
+  Window leader;
+  Bool needsGroup;
+  Bool needsNotGroup;
+  Bool needsLeader;
+  Bool needsNotLeader;
 } WindowConditionMask;
 
 /***************************************************************************
@@ -406,6 +417,8 @@
 extern Atom _XA_OL_DECOR_RESIZE;
 extern Atom _XA_OL_DECOR_HEADER;
 extern Atom _XA_OL_DECOR_ICON_NAME;
+
+#define ICCCM2
 
 /* include this down here because FvwmWindows must be defined when including
  * this header file. */
--- events.c.orig	Fri Jan  1 21:50:52 1999
+++ events.c	Wed Jan  6 00:29:42 1999
@@ -721,6 +721,18 @@
       if (Tmp_win == NULL)
 	return;
     }
+
+  if (Tmp_win->tmpflags.OnlyGroupLeader) {
+    /* Window group support. Fvwm creates an FvwmWindow with the
+       OnlyGroupLeader flag set for unmapped group leaders. This
+       prevents deiconifying them. If they are mapped by the
+       client later on, we should remove the flag to enable
+       deiconification.
+    */
+    Tmp_win->tmpflags.OnlyGroupLeader = 0;
+    Tmp_win->flags &= ~SUPPRESSICON;
+  }
+
   /*
       Make sure at least part of window is on this page
       before giving it focus...

-- 
Matthias Clasen, 
Tel. 0761/203-5606
Email: [email-masked]
Mathematisches Institut, Albert-Ludwigs-Universitaet Freiburg
--
Visit the official FVWM web page at <URL:http://www.hpc.uh.edu/fvwm/>.
To unsubscribe from the list, send "unsubscribe fvwm-workers" in the
body of a message to [email-masked].
To report problems, send mail to [email-masked].

---

From: Tim Phipps <[email-masked]>
Date: Thu, 07 Jan 1999 14:40:11 +0000

>                "Group", "!Group", "Leader", "!Leader".

Are these extra conditions available to the Next/Prev commands as well?

> +         ExecuteFunction(restofline,t,eventp,C_WINDOW,*Module);

What happens if the action manipulates the windowlist (e.g. Focus,
Destroy)? I think this might cause Fvwm to go off into freed memory or
to loop forever.  It might be better to: run down the windowlist and
count the number of windows; malloc an array for the WindowIDs; fill the
array with all the WindowIDs; run through the array testing the
condition; execute the action using the WindowID command with the
WindowID and finally free the array. Something similar to this already
happens in the WindowList command.

Cheers,
Tim.

---

From: Dominik Vogt <[email-masked]>
Date: Thu, 7 Jan 1999 16:44:45 +0100

On Thu, Jan 07, 1999 at 02:40:11PM +0000, Tim Phipps wrote:
> Destroy)?

This is easy to answer: it will coredump (we already have tried this
with the purify script).

> I think this might cause Fvwm to go off into freed memory or

I imagine we could do some more enhancements in this direction
that should be coordinated somehow:

1) Have some mechanism to select multiple windows to apply
   functions to, e.g. draw a rectangle on the root window
   that selects all windows/icons at least partially within
   or select a group of windows/icons by clicking on each
   (GroupRectangle / GroupSelect). This group would be used
   to execute the next function/builtin or until deselected.

2) Have a 'Repeat' function that allows you to execute the
   same action multiple times without having to invoke it
   again and again. E.g. 'Repeat Move' would allow you to
   move random windows on the desk until you're satisfied
   (and press escape).

I think in this approach it would make sense to have a
dedicated 'Group' and perhaps a 'GroupLeader' command.

Bye

Dominik

-- 
Dominik Vogt, Hewlett-Packard GmbH, Dept. BVS
Herrenberger Str.130, 71034 Boeblingen, Germany
phone: 07031/14-4596, fax: 07031/14-3883, [email-masked]
--
Visit the official FVWM web page at <URL:http://www.hpc.uh.edu/fvwm/>.
To unsubscribe from the list, send "unsubscribe fvwm-workers" in the
body of a message to [email-masked].
To report problems, send mail to [email-masked].