Subject: Patch to allow multiple window names in conditions

From: Norman Yarvin <[email-masked]>
Date: Mon, 31 May 2004 21:53:51 -0400

This is a small patch that allows multiple window names to be specified
in conditions.  I've tried to write it with the same style used in the
rest of the code, although some variable names may be a bit terse by your
standards.  The patch keeps the window names in a linked list.  It
includes updates to the man page and ChangeLog.  The patch is against the
current CVS code.  What exactly it does is best described by the modified
portion of the man page:

--------

       In addition, the conditions may include one or  more  window  names  to
       match to.  The window names may include the wildcards '*' and '?'.  The
       window name,  icon  name,  class,  and  resource  are  considered  when
       attempting to find a match.  If more than one window name is given, the
       implicit operator is a boolean OR -- that is,  the  condition  will  be
       satisfied  if  the window has any of those names.  Each window name can
       begin with '!' which prevents command if any of the window  name,  icon
       name, class or resource match.  Mixtures of window names beginning with
       '!' and window names without '!'  are allowed.  The general rule is  as
       follows:  if  any  of the window names with '!' match, the condition is
       not satisfied; otherwise if any of the names without '!' match,  or  if
       there are no names without '!', then the condition is satisfied.

       Examples:

                 Next (Netscape konqueror Mozilla*) WarpToWindow 99 90

       This  goes to the next web browser window, no matter which of the three
       named web browsers is being used.

                 Next (XTerm !xterm3) WarpToWindow 99 90

       This goes to the next xterm window, except that the one named  "xterm3"
       (with the -name option to xterm) is excluded.

                 Next (!XTerm !rxvt !emacs !Netscape !konqueror !Mozilla* \
                      !xdvi !xpdf) WarpToWindow 99 90

       This goes to the next window which doesn't have one of the above names.

--------

I'm not subscribed to the fvwm-workers mailing list, so please forward
any comments to me as well as to the list (or yell at me to subscribe for
a while, if that would be appropriate).

Index: ChangeLog
===================================================================
RCS file: /home/cvs/fvwm/fvwm/ChangeLog,v
retrieving revision 1.2496
diff -u -u -r1.2496 ChangeLog
--- ChangeLog	31 May 2004 20:19:23 -0000	1.2496
+++ ChangeLog	1 Jun 2004 01:44:44 -0000
@@ -1,3 +1,10 @@
+2004-05-31  Norman Yarvin  <[email-masked]>
+
+	* fvwm/fvwm.1.in
+	* fvmw/fvwm.h
+	* fvwm/conditional.c: modified the code for conditions so that
+	multiple window names may be specified in each condition
+
 2004-05-31  Dan Espen  <[email-masked]>
 
 	* fvwm/fvwm.1.in (COLORSETS): Moved FvwmTheme description of colorsets
Index: fvwm/conditional.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/conditional.c,v
retrieving revision 1.93
diff -u -u -r1.93 conditional.c
--- fvwm/conditional.c	9 May 2004 12:59:55 -0000	1.93
+++ fvwm/conditional.c	1 Jun 2004 01:44:46 -0000
@@ -382,13 +382,14 @@
  */
 void FreeConditionMask(WindowConditionMask *mask)
 {
-	if (mask->my_flags.needs_name)
-	{
-		free(mask->name);
-	}
-	else if (mask->my_flags.needs_not_name)
-	{
-		free(mask->name - 1);
+	struct namelist *p,*p2;
+
+	for (p=mask->namelist; p;)
+	{ 
+		free(p->name);
+		p2=p->next;
+		free(p);
+		p=p2;
 	}
 
 	return;
@@ -614,21 +615,16 @@
 			}
 			mask->my_flags.needs_same_layer = on;
 		}
-		else if (!mask->my_flags.needs_name &&
-			 !mask->my_flags.needs_not_name)
-		{
-			/* only 1st name to avoid mem leak */
-			mask->name = condition;
-			condition = NULL;
-			if (on == 0)
-			{
-				mask->my_flags.needs_not_name = 1;
-				mask->name++;
-			}
-			else
-			{
-				mask->my_flags.needs_name = 1;
-			}
+		else
+		{ 
+			struct namelist *p;
+
+			p = (struct namelist *)
+				safemalloc(sizeof(struct namelist));
+			p->name = condition;
+			p->next = mask->namelist;
+			mask->namelist = p;
+			condition = NULL; 
 		}
 
 		if (prev_condition)
@@ -663,6 +659,9 @@
 	int is_on_page;
 	int is_on_global_page;
 	FvwmWindow *sf = get_focus_window();
+	struct namelist *p;
+	char *name;
+	Bool invert,tried,matched;
 
 	/* match FixedSize conditional */
 	/* special treatment for FixedSize, because more than just
@@ -822,27 +821,52 @@
 		}
 	}
 
-	/* Yes, I know this could be shorter, but it's hard to understand then
-	 */
-	does_name_match = matchWildcards(mask->name, fw->name.name);
-	does_icon_name_match = matchWildcards(mask->name, fw->icon_name.name);
-	does_class_match =
-		(fw->class.res_class && matchWildcards(
-			mask->name,fw->class.res_class));
-	does_resource_match =
-		(fw->class.res_name && matchWildcards(
-			mask->name, fw->class.res_name));
-	does_match =
-		(does_name_match || does_icon_name_match || does_class_match ||
-		 does_resource_match);
-	if (mask->my_flags.needs_name && !does_match)
-	{
-		return False;
-	}
-	if (mask->my_flags.needs_not_name && does_match)
-	{
-		return False;
-	}
+	tried = False;
+	matched = False;
+
+	for(p=mask->namelist; p; p=p->next)
+	{ 
+		name = p->name;
+		invert = False;
+		if(*name == '!')
+		{
+			name++;
+			invert = True;
+		}
+
+		/* Yes, I know this could be shorter, but it's hard to
+		 * understand then
+		 */
+		does_name_match = matchWildcards(name, fw->name.name);
+		does_icon_name_match = 
+				  matchWildcards(name, fw->icon_name.name);
+		does_class_match =                    (fw->class.res_class && 
+				  matchWildcards(name, fw->class.res_class));
+		does_resource_match =                 (fw->class.res_name &&
+				  matchWildcards(name, fw->class.res_name));
+		does_match =
+			(does_name_match || does_icon_name_match ||
+			 does_class_match || does_resource_match);
+
+		if(invert && does_match)
+		{
+			return False;		/* "!window" takes priority */
+		}
+		if(!invert && does_match)
+		{
+			matched = True;		/* this condition satisfied */
+		}
+		if(!invert)
+		{
+			tried = True;
+		}
+	} 
+
+	if(tried && !matched)
+	{ 
+		return False; 
+	}	
+
 	if (mask->layer == -1 && sf)
 	{
 		int is_same_layer;
Index: fvwm/fvwm.1.in
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/fvwm.1.in,v
retrieving revision 1.136
diff -u -u -r1.136 fvwm.1.in
--- fvwm/fvwm.1.in	31 May 2004 20:19:23 -0000	1.136
+++ fvwm/fvwm.1.in	1 Jun 2004 01:45:00 -0000
@@ -10324,12 +10324,37 @@
 
 In addition, the
 .I conditions
-may include one window name to match to.  The window name may
-include the wildcards '*' and '?'.  The window name, icon name,
-class, and resource are considered when attempting to find a
-match.  The window name can begin with '!' which prevents
+may include one or more window names to match to.  The window
+names may include the wildcards '*' and '?'.  The window name,
+icon name, class, and resource are considered when attempting to
+find a match.  If more than one window name is given, the
+implicit operator is a boolean OR -- that is, the condition will
+be satisfied if the window has any of those names.  Each window
+name can begin with '!' which prevents
 .I command
 if any of the window name, icon name, class or resource match.
+Mixtures of window names beginning with '!' and window names
+without '!'  are allowed.  The general rule is as follows: if any
+of the window names with '!' match, the condition is not
+satisfied; otherwise if any of the names without '!' match, or if
+there are no names without '!', then the condition is satisfied.
+
+Examples:
+.EX
+	Next (Netscape konqueror Mozilla*) WarpToWindow 99 90
+.EE
+This goes to the next web browser window, no matter which of the three
+named web browsers is being used.
+.EX
+	Next (XTerm !xterm3) WarpToWindow 99 90
+.EE
+This goes to the next xterm window, except that the one named
+"xterm3" (with the -name option to xterm) is excluded.
+.EX
+	Next (!XTerm !rxvt !emacs !Netscape !konqueror !Mozilla* \\
+		!xdvi !xpdf) WarpToWindow 99 90
+.EE
+This goes to the next window which doesn't have one of the above names.
 
 Any condition can be negated by using a an exclamation mark ('!')
 directly in front of its name.
Index: fvwm/fvwm.h
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/fvwm.h,v
retrieving revision 1.231
diff -u -u -r1.231 fvwm.h
--- fvwm/fvwm.h	30 May 2004 14:05:48 -0000	1.231
+++ fvwm/fvwm.h	1 Jun 2004 01:45:01 -0000
@@ -400,6 +400,12 @@
 } window_flags;
 
 /* Window mask for Circulate and Direction functions */
+struct namelist
+{
+	char *name;
+	struct namelist *next;
+};
+
 typedef struct WindowConditionMask
 {
 	struct
@@ -419,8 +425,6 @@
 #define NEEDS_TRUE  1
 #define NEEDS_FALSE 2
 		unsigned needs_focus : 2;
-		unsigned needs_name : 1;
-		unsigned needs_not_name : 1;
 		unsigned needs_pointer : 2;
 		unsigned needs_same_layer : 1;
 		unsigned use_circulate_hit : 1;
@@ -430,7 +434,7 @@
 	} my_flags;
 	window_flags flags;
 	window_flags flag_mask;
-	char *name;
+	struct namelist *namelist;
 	int layer;
 } WindowConditionMask;
 
--
Visit the official FVWM web page at <URL:http://www.fvwm.org/>.
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: [email-masked]
Date: Tue, 1 Jun 2004 03:37:51 GMT

>     Next (Netscape konqueror Mozilla*) WarpToWindow 99 90

This is a nice implementation of an "OR" rule, is there any way of specifying
and "AND" rule? For example, a rule to match a window with Class "Netscape" 
and Title "local*". Now that multiple window names can be read, why not
include one.

For example, i would want these to this expression
All (Netscape) ThisWindow (local*) ...
to be reduced to 
All (Netscape local*)

But this is exactly the syntax used by this patch, hence it means "OR".
By default, all other conditions must be true, so i propose an alternate
syntax for window name/class/resource "OR" conditions:
All (Netscape |konqueror |Mozilla*) ...

Perhaps it's not worth the effort?
I just thought it was a little counter-intuitive.

This also works well with "not" commands:
All (Netscape |konqueror |Mozilla* !Opera) ...
will match all browsers except opera if "!Opera" is read as an "AND NOT"
(which is subtly different to the patch syntax's implied "OR NOT")

I guess one could implement an "OR NOT" rule, but i strongly doubt the 
usefulness of it.

I'd imagine a _full_ implementation of this would require a linked list
of linked lists so one can specify multiple OR conditions eg.:
All (Netscape |konqueror  http* |file* !http://www.google*)
would match all netscape and konqueror windows with titles that start with 
either http or file but not showing google. Note the "not google" rule needn't
be applied to a specific list as its evaluation to true anywhere negates the
rule.

But of course a simpler (perhaps better too) method would just implement:
All (Netscape |konqueror |http |file !opera)

Of course, these are all just suggestions :)

Great patch though,

- Craig

--
[email-masked]