Subject: Direction function

From: Teemu Hirsimaki <[email-masked]>
Date: Sun, 15 Nov 1998 12:03:29 +0200 (EET)

I have made a patch which provides a "Direction" function for finding
the closest window in a given direction and executing a function on
it.  I use it only for moving focus as I like mouseless operations,
but maybe it is better to keep the function general.

At the moment I have copied the flag test from the "Circulate"
function, so that I can use "!Sticky" and other useful flags, but of
course, having the same code in two different places is not the
correct solution.  Would it be a good idea if i tried to write a
general flag test function, which could be then used from "Circulate"
and "Direction"?  Or would it better to implement "Direction" features
inside the "Circulate" function?

Or is anyone interested in such feature at all? :)
I find it very handy for moving from window to another.

-- 
Teemu Hirsimäki
[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].

---

From: [email-masked]
Date: Mon, 16 Nov 1998 11:44:03 +0100

> I find it very handy for moving from window to another.

Let's have a look at the patch. Go ahead and post it.

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].

---

From: Teemu Hirsimaki <[email-masked]>
Date: Wed, 18 Nov 1998 02:51:25 +0200 (EET)

> Let's have a look at the patch. Go ahead and post it.

Ok.  Here it is.  

First I implemented a WindowMask struct which holds the flags used in
Circulate function.  This struct is handled by a few functions:

FreeMask	- frees the name field of mask
DefaultMask	- sets the default values of the mask
CreateMask	- sets the values of the mask from a string of flags
MatchesMask	- check whether a window matches the mask

There is also a function CreateFlagString, which is just to parse the
text between [ ] or ( ) characters and return it and the rest of the
line.

Then I modified the Circulate function to use these functions - hope
that I did not break anything - I do not normally use Circulate in my
configuration. :) Though, I checked it a little and it seemed to work
without hogging any memory.  I implemented also a new Direction
function which can be used like this, for example:

Direction North [!Sticky] Focus
Direction East [!Sticky] Focus
Direction South [!Sticky] Focus
Direction West [!Sticky] Focus

The score function for choosing the closet window is calculated based
on the position of the titlebar, which feeled most logical to me.

And here is the patch.  I hope that those revision numbers do not
cause any problems.

Index: fvwm/fvwm/builtins.c
diff -c fvwm/fvwm/builtins.c:1.1.1.1 fvwm/fvwm/builtins.c:1.2
*** fvwm/fvwm/builtins.c:1.1.1.1	Wed Nov 18 02:15:30 1998
--- fvwm/fvwm/builtins.c	Wed Nov 18 02:21:55 1998
***************
*** 3079,3207 ****
      free(szValue);
  }
  
! 
! /**************************************************************************
!  *
!  * Direction = 1 ==> "Next" operation
!  * Direction = -1 ==> "Previous" operation
!  * Direction = 0 ==> operation on current window (returns pass or fail)
!  *
!  **************************************************************************/
! FvwmWindow *Circulate(char *action, int Direction, char **restofline)
  {
!   int l,pass = 0;
!   FvwmWindow *fw, *found = NULL;
!   char *t,*tstart,*name = NULL, *expression, *condition, *prev_condition=NULL;
!   char *orig_expr;
!   Bool needsCurrentDesk = 0;
!   Bool needsCurrentPage = 0;
! 
!   Bool needsName = 0;
!   Bool needsNotName = 0;
!   Bool useCirculateHit = (Direction) ? 0 : 1; /* override for Current [] */
!   Bool useCirculateHitIcon = (Direction) ? 0 : 1;
!   unsigned long onFlags = 0;
!   unsigned long offFlags = 0;
    char closeopt;
! 
!   l=0;
  
!   if(action == NULL)
!     return NULL;
! 
!   t = action;
!   while(isspace(*t)&&(*t!= 0))
!     t++;
!   if(*t == '[' || *t == '(')
    {
!     if (*t == '[')
!       closeopt = ']';
!     else
        closeopt = ')';
!     t++;
!     tstart = t;
! 
!     while((*t !=0)&&(*t != closeopt))
!     {
!       t++;
!       l++;
!     }
!     if(*t == 0)
!     {
!       fvwm_msg(ERR,"Circulate","Conditionals require closing parenthesis");
!       return NULL;
      }
  
!     *restofline = t+1;
  
!     orig_expr = expression = safemalloc(l+1);
!     strncpy(expression,tstart,l);
!     expression[l] = 0;
!     expression = GetNextToken(expression,&condition);
!     while (condition)
      {
!       if     (StrEquals(condition,"Iconic"))
!         onFlags |= ICONIFIED;
!       else if(StrEquals(condition,"!Iconic"))
!         offFlags |= ICONIFIED;
!       else if(StrEquals(condition,"Visible"))
!         onFlags |= VISIBLE;
!       else if(StrEquals(condition,"!Visible"))
!         offFlags |= VISIBLE;
!       else if(StrEquals(condition,"Sticky"))
!         onFlags |= STICKY;
!       else if(StrEquals(condition,"!Sticky"))
!         offFlags |= STICKY;
!       else if(StrEquals(condition,"Maximized"))
!         onFlags |= MAXIMIZED;
!       else if(StrEquals(condition,"!Maximized"))
!         offFlags |= MAXIMIZED;
!       else if(StrEquals(condition,"Transient"))
!         onFlags |= TRANSIENT;
!       else if(StrEquals(condition,"!Transient"))
!         offFlags |= TRANSIENT;
!       else if(StrEquals(condition,"Raised"))
!         onFlags |= RAISED;
!       else if(StrEquals(condition,"!Raised"))
!         offFlags |= RAISED;
!       else if(StrEquals(condition,"CurrentDesk"))
!         needsCurrentDesk = 1;
!       else if(StrEquals(condition,"CurrentPage"))
!       {
!         needsCurrentDesk = 1;
!         needsCurrentPage = 1;
!       }
!       else if(StrEquals(condition,"CurrentPageAnyDesk") ||
!               StrEquals(condition,"CurrentScreen"))
!         needsCurrentPage = 1;
!       else if(StrEquals(condition,"CirculateHit"))
!         useCirculateHit = 1;
!       else if(StrEquals(condition,"CirculateHitIcon"))
!         useCirculateHitIcon = 1;
!       else if(!needsName && !needsNotName) /* only 1st name to avoid mem leak */
        {
!         name = condition;
!         condition = NULL;
!         if (name[0] == '!')
!         {
!           needsNotName = 1;
!           name++;
!         }
!         else
!           needsName = 1;
        }
!       if(prev_condition)free(prev_condition);
!       prev_condition = condition;
!       expression = GetNextToken(expression,&condition);
      }
!     if(prev_condition != NULL)
        free(prev_condition);
!     if(orig_expr != NULL)
!       free(orig_expr);
    }
-   else
-     *restofline = t;
  
    if(Scr.Focus != NULL)
    {
      if(Direction == 1)
--- 3079,3303 ----
      free(szValue);
  }
  
! /**********************************************************************
!  * Parses the flag string and returns the text between [ ] or ( ) 
!  * characters.  The start of the rest of the line is put in restptr.
!  * Note that the returned string is allocated here and it must be
!  * freed when it is not needed anymore.
!  **********************************************************************/
! char *CreateFlagString(char *string, char **restptr)
  {
!   char *retval;
!   char *c;
!   char *start;
    char closeopt;
!   int length;
!   
!   c = string;
!   while (isspace(*c) && (*c != 0))
!     c++;
  
!   if (*c == '[' || *c == '(')
    {
!     /* Get the text between [ ] or ( ) */
!     if (*c == '[') 
!       closeopt = ']'; 
!     else 
        closeopt = ')';
!     c++;
!     start = c;
!     length = 0;
!     while (*c != closeopt) {
!       if (*c == 0) {
! 	fvwm_msg(ERR, "CreateMask", "Conditionals require closing parenthesis");
! 	return NULL;
!       }
!       c++;
!       length++;
      }
+ 
+     /* We must allocate a new string because we null terminate the string
+      * between the [ ] or ( ) characters.
+      */
+     retval = safemalloc(length + 1);
+     strncpy(retval, start, length);
+     retval[length] = 0;
+ 
+     *restptr = c + 1;
+   }
+   else {
+     retval = NULL;
+     *restptr = c;
+   }
+ 
+   return retval;
+ }
  
! /**********************************************************************
!  * The name field of the mask is allocated in CreateMask.
!  * It must be freed.
!  **********************************************************************/
! void FreeMask(WindowMask *mask)
! {
!   if (mask->needsName)
!     free(mask->name);
!   else if (mask->needsNotName)
!     free(mask->name - 1);
! }
  
! /* Assign the default values for the window mask */
! void DefaultMask(WindowMask *mask)
! {
!   mask->name = NULL;
!   mask->needsCurrentDesk = 0;
!   mask->needsCurrentPage = 0;
!   mask->needsName = 0;
!   mask->needsNotName = 0;
!   mask->useCirculateHit = 0;
!   mask->useCirculateHitIcon = 0;
!   mask->onFlags = 0;
!   mask->offFlags = 0;
! }
! 
! /**********************************************************************
!  * Note that this function allocates the name field of the mask struct.
!  * FreeMask must be called for the mask when the mask is discarded.
!  **********************************************************************/
! void CreateMask(char *flags, WindowMask *mask)
! {
!   char *condition;
!   char *prev_condition = NULL;
!   char *tmp;
! 
!   if (flags == NULL)
!     return;
! 
!   /* Next parse the flags in the string. */
!   tmp = flags;
!   tmp = GetNextToken(tmp, &condition);
! 
!   while (condition)
!   {
!     if (StrEquals(condition,"Iconic"))
!       mask->onFlags |= ICONIFIED;
!     else if(StrEquals(condition,"!Iconic"))
!       mask->offFlags |= ICONIFIED;
!     else if(StrEquals(condition,"Visible"))
!       mask->onFlags |= VISIBLE;
!     else if(StrEquals(condition,"!Visible"))
!       mask->offFlags |= VISIBLE;
!     else if(StrEquals(condition,"Sticky"))
!       mask->onFlags |= STICKY;
!     else if(StrEquals(condition,"!Sticky"))
!       mask->offFlags |= STICKY;
!     else if(StrEquals(condition,"Maximized"))
!       mask->onFlags |= MAXIMIZED;
!     else if(StrEquals(condition,"!Maximized"))
!       mask->offFlags |= MAXIMIZED;
!     else if(StrEquals(condition,"Transient"))
!       mask->onFlags |= TRANSIENT;
!     else if(StrEquals(condition,"!Transient"))
!       mask->offFlags |= TRANSIENT;
!     else if(StrEquals(condition,"Raised"))
!       mask->onFlags |= RAISED;
!     else if(StrEquals(condition,"!Raised"))
!       mask->offFlags |= RAISED;
!     else if(StrEquals(condition,"CurrentDesk"))
!       mask->needsCurrentDesk = 1;
!     else if(StrEquals(condition,"CurrentPage"))
      {
!       mask->needsCurrentDesk = 1;
!       mask->needsCurrentPage = 1;
!     }
!     else if(StrEquals(condition,"CurrentPageAnyDesk") ||
! 	    StrEquals(condition,"CurrentScreen"))
!       mask->needsCurrentPage = 1;
!     else if(StrEquals(condition,"CirculateHit"))
!       mask->useCirculateHit = 1;
!     else if(StrEquals(condition,"CirculateHitIcon"))
!       mask->useCirculateHitIcon = 1;
!     else if(!mask->needsName && !mask->needsNotName) 
!     {
!       /* only 1st name to avoid mem leak */
!       mask->name = condition;
!       condition = NULL;
!       if (mask->name[0] == '!')
        {
! 	mask->needsNotName = 1;
! 	mask->name++;
        }
!       else
! 	mask->needsName = 1;
      }
! 
!     if (prev_condition) 
        free(prev_condition);
! 
!     prev_condition = condition;
!     tmp = GetNextToken(tmp, &condition);
    }
  
+   if(prev_condition != NULL)
+     free(prev_condition);
+ }
+ 
+ /**********************************************************************
+  * Checks whether the given window matches the mask created with
+  * CreateMask.
+  **********************************************************************/
+ Bool MatchesMask(FvwmWindow *fw, WindowMask *mask)
+ {
+   return 
+     ((mask->onFlags & fw->flags) == mask->onFlags) &&
+     ((mask->offFlags & fw->flags) == 0) &&
+     (mask->useCirculateHit || !(fw->flags & CirculateSkip)) &&
+     ((mask->useCirculateHitIcon && fw->flags & ICONIFIED) ||
+      !(fw->flags & CirculateSkipIcon && fw->flags & ICONIFIED)) &&
+     (!mask->needsCurrentDesk || fw->Desk == Scr.CurrentDesk) &&
+     (!mask->needsCurrentPage || (fw->frame_x < Scr.MyDisplayWidth &&
+ 				 fw->frame_y < Scr.MyDisplayHeight &&
+ 				 fw->frame_x + fw->frame_width > 0 &&
+ 				 fw->frame_y + fw->frame_height > 0)
+       ) &&
+     (!mask->needsName ||
+      matchWildcards(mask->name, fw->name) ||
+      matchWildcards(mask->name, fw->icon_name) ||
+      fw->class.res_class && matchWildcards(mask->name, fw->class.res_class) ||
+      fw->class.res_name && matchWildcards(mask->name, fw->class.res_name)
+       ) &&
+     (!mask->needsNotName ||
+      !(matchWildcards(mask->name, fw->name) ||
+        matchWildcards(mask->name, fw->icon_name) ||
+        fw->class.res_class && matchWildcards(mask->name, fw->class.res_class)||
+        fw->class.res_name && matchWildcards(mask->name, fw->class.res_name)
+        )
+       );
+ }
+ 
+ /**************************************************************************
+  *
+  * Direction = 1 ==> "Next" operation
+  * Direction = -1 ==> "Previous" operation
+  * Direction = 0 ==> operation on current window (returns pass or fail)
+  *
+  **************************************************************************/
+ FvwmWindow *Circulate(char *action, int Direction, char **restofline)
+ {
+   int pass = 0;
+   FvwmWindow *fw, *found = NULL;
+   WindowMask mask;
+   char *flags;
+ 
+   /* Create window mask */
+   flags = CreateFlagString(action, restofline);
+   DefaultMask(&mask);
+   if (Direction == 0) { /* override for Current [] */
+     mask.useCirculateHit = 1;
+     mask.useCirculateHitIcon = 1;
+   }
+   CreateMask(flags, &mask);
+   free(flags);
+ 
    if(Scr.Focus != NULL)
    {
      if(Direction == 1)
***************
*** 3219,3266 ****
      while((fw != NULL)&&(found==NULL)&&(fw != &Scr.FvwmRoot))
      {
  #ifdef FVWM_DEBUG_MSGS
! 	fvwm_msg(DBG,"Circulate","Trying %s",fw->name);
  #endif /* FVWM_DEBUG_MSGS */
        /* Make CirculateUp and CirculateDown take args. by Y.NOMURA */
!       if ((onFlags & fw->flags) == onFlags &&
!           (offFlags & fw->flags) == 0 &&
!           (useCirculateHit || !(fw->flags & CirculateSkip)) &&
!           ((useCirculateHitIcon && fw->flags & ICONIFIED) ||
!            !(fw->flags & CirculateSkipIcon && fw->flags & ICONIFIED)) &&
!           (!needsCurrentDesk || fw->Desk == Scr.CurrentDesk) &&
!           (!needsCurrentPage || (fw->frame_x < Scr.MyDisplayWidth &&
!                                  fw->frame_y < Scr.MyDisplayHeight &&
!                                  fw->frame_x + fw->frame_width > 0 &&
!                                  fw->frame_y + fw->frame_height > 0)
!             ) &&
!           (!needsName ||
!            matchWildcards(name, fw->name) ||
!            matchWildcards(name, fw->icon_name) ||
!            fw->class.res_class && matchWildcards(name, fw->class.res_class) ||
!            fw->class.res_name && matchWildcards(name, fw->class.res_name)
!             ) &&
!           (!needsNotName ||
!            !(matchWildcards(name, fw->name) ||
!              matchWildcards(name, fw->icon_name) ||
!              fw->class.res_class && matchWildcards(name, fw->class.res_class)||
!              fw->class.res_name && matchWildcards(name, fw->class.res_name)
!              )
!             )
!         )
!         found = fw;
        else
        {
!         if(Direction == 1)
!           fw = fw->prev;
!         else
!           fw = fw->next;
        }
        if (Direction == 0)
        {
!         if (needsName)
!           free(name);
!         else if (needsNotName)
!           free(name - 1);
          return found;
        }
      }
--- 3315,3335 ----
      while((fw != NULL)&&(found==NULL)&&(fw != &Scr.FvwmRoot))
      {
  #ifdef FVWM_DEBUG_MSGS
!       fvwm_msg(DBG,"Circulate","Trying %s",fw->name);
  #endif /* FVWM_DEBUG_MSGS */
        /* Make CirculateUp and CirculateDown take args. by Y.NOMURA */
!       if (MatchesMask(fw, &mask))
! 	found = fw;
        else
        {
! 	if(Direction == 1)
! 	  fw = fw->prev;
! 	else
! 	  fw = fw->next;
        }
        if (Direction == 0)
        {
! 	FreeMask(&mask);
          return found;
        }
      }
***************
*** 3283,3294 ****
      }
      pass++;
    }
!   if (needsName)
!     free(name);
!   else if (needsNotName)
!     free(name - 1);
    return found;
- 
  }
  
  void PrevFunc(XEvent *eventp,Window junk,FvwmWindow *tmp_win,
--- 3352,3359 ----
      }
      pass++;
    }
!   FreeMask(&mask);
    return found;
  }
  
  void PrevFunc(XEvent *eventp,Window junk,FvwmWindow *tmp_win,
***************
*** 3343,3348 ****
--- 3408,3530 ----
    {
      ExecuteFunction(restofline,found,eventp,C_WINDOW,*Module);
    }
+ }
+ 
+ /**********************************************************************
+  * Execute a function to the closest window in the given
+  * direction.
+  **********************************************************************/
+ void Direction(XEvent *eventp,Window junk,FvwmWindow *tmp_win, 
+ 	       unsigned long context, char *action, int *Module)
+ {
+   int my_x;
+   int my_y;
+   int his_x;
+   int his_y;
+   int score;
+   int offset;
+   int distance;
+   int best_score;
+   FvwmWindow *window;
+   FvwmWindow *best_window;
+   char dir;
+   char *flags;
+   char *restofline;
+   char *tmp;
+   WindowMask mask;
+ 
+   /* Parse the direction. */
+   action = GetNextToken(action, &tmp);
+   if (StrEquals("North", tmp))
+     dir = 0;
+   else if (StrEquals("East", tmp))
+     dir = 1;
+   else if (StrEquals("South", tmp))
+     dir = 2;
+   else if (StrEquals("West", tmp))
+     dir = 3;
+   else {
+     fvwm_msg(ERR, "Direction","No such direction %s", tmp);
+     free(tmp);
+     return;
+   }
+   free(tmp);
+ 
+   /* Create the mask for flags */
+   flags = CreateFlagString(action, &restofline);
+   DefaultMask(&mask);
+   CreateMask(flags, &mask);
+   free(flags);
+ 
+   /* If there is a focused window, use that as a starting point.
+    * Otherwise we use the pointer as a starting point. */
+   if (tmp_win != NULL) {
+     if ((tmp_win->flags & ICONIFIED) != 0)
+     {
+       my_x = tmp_win->icon_x_loc + tmp_win->icon_w_width / 2;
+       my_y = tmp_win->icon_y_loc + tmp_win->icon_w_height / 2;
+     }
+     else
+     {
+       my_x = tmp_win->frame_x + tmp_win->frame_width / 2;
+       my_y = tmp_win->frame_y;
+     }
+   }
+   else
+     XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, 
+ 		  &my_x, &my_y, &JunkX, &JunkY, &JunkMask);
+ 
+   /* Next we iterate through all windows and choose the closest one in
+    * the wanted direction. 
+    */
+   best_window = NULL;
+   best_score = -1;
+   for (window = Scr.FvwmRoot.next; window != NULL; window = window->next) {
+ 
+     /* Skip every window that does not match conditionals.
+      * Skip also currently focused window.  That would be too close. :) 
+      */
+     if (window == tmp_win) continue;
+     if (!MatchesMask(window, &mask)) continue;
+ 
+     /* Calculate relative location of the window. */
+     if ((window->flags & ICONIFIED) != 0) {
+       his_x = window->icon_x_loc + window->icon_w_width / 2;
+       his_y = window->icon_y_loc + window->icon_w_height / 2;
+     }
+     else {
+       his_x = window->frame_x + window->frame_width / 2;
+       his_y = window->frame_y;
+     }
+     his_x -= my_x;
+     his_y -= my_y;
+ 
+     /* Arrange so that distance and offset are positive in desired 
+      * direction. 
+      */
+     if (dir == 0 || dir == 2) {
+       offset = (his_x < 0) ? -his_x : his_x;
+       distance = (dir == 0) ? -his_y : his_y;
+     } else {
+       offset = (his_y < 0) ? -his_y : his_y;
+       distance = (dir == 3) ? -his_x : his_x;
+     }
+ 	
+     /* Target must be in given direction. */
+     if (distance <= 0) continue;
+ 
+     /* Calculate score for this window.  The smaller the better. */
+     score = 1000 * offset / distance + distance;
+     if (best_score == -1 || score < best_score) {
+       best_window = window;
+       best_score = score;
+     }
+   }
+ 
+   if (best_window != NULL)
+     ExecuteFunction(restofline, best_window, eventp, C_WINDOW, *Module);
+   
+   FreeMask(&mask);
  }
  
  void WindowIdFunc(XEvent *eventp,Window junk,FvwmWindow *tmp_win,
Index: fvwm/fvwm/functions.c
diff -c fvwm/fvwm/functions.c:1.1.1.1 fvwm/fvwm/functions.c:1.2
*** fvwm/fvwm/functions.c:1.1.1.1	Wed Nov 18 02:15:30 1998
--- fvwm/fvwm/functions.c	Wed Nov 18 02:21:57 1998
***************
*** 84,89 ****
--- 84,90 ----
    {"DestroyFunc",  destroy_menu,     F_DESTROY_MENU,        FUNC_NO_WINDOW},
    {"DestroyMenu",  destroy_menu,     F_DESTROY_MENU,        FUNC_NO_WINDOW},
    {"DestroyModuleConfig", DestroyModConfig, F_DESTROY_MOD,  FUNC_NO_WINDOW},
+   {"Direction",    Direction,        F_DIRECTION,           FUNC_NO_WINDOW},
    {"Echo",         echo_func,        F_ECHO,                FUNC_NO_WINDOW},
    {"EdgeResistance",SetEdgeResistance,F_EDGE_RES,           FUNC_NO_WINDOW},
    {"EdgeScroll",   SetEdgeScroll,    F_EDGE_SCROLL,         FUNC_NO_WINDOW},
Index: fvwm/fvwm/fvwm.h
diff -c fvwm/fvwm/fvwm.h:1.1.1.1 fvwm/fvwm/fvwm.h:1.2
*** fvwm/fvwm/fvwm.h:1.1.1.1	Wed Nov 18 02:15:30 1998
--- fvwm/fvwm/fvwm.h	Wed Nov 18 02:21:58 1998
***************
*** 236,241 ****
--- 236,256 ----
      unsigned      ViewportMoved        : 1;  /*  To prevent double move in MoveViewport.  */
  } FvwmWindow;
  
+ /* Window mask for Circulate and Direction functions */
+ typedef struct WindowMask {
+   Bool needsCurrentDesk;
+   Bool needsCurrentPage;
+   Bool needsName;
+   Bool needsNotName;
+   Bool useCirculateHit;
+   Bool useCirculateHitIcon;
+   Bool useCirculateSkip;
+   Bool useCirculateSkipIcon;
+   unsigned long onFlags;
+   unsigned long offFlags;
+   char *name;
+ } WindowMask;
+ 
  /***************************************************************************
   * window flags definitions 
   ***************************************************************************/
Index: fvwm/fvwm/misc.h
diff -c fvwm/fvwm/misc.h:1.1.1.1 fvwm/fvwm/misc.h:1.2
*** fvwm/fvwm/misc.h:1.1.1.1	Wed Nov 18 02:15:30 1998
--- fvwm/fvwm/misc.h	Wed Nov 18 02:21:59 1998
***************
*** 524,529 ****
--- 524,531 ----
  	      unsigned long context, char *action,int* Module);
  void CurrentFunc(XEvent *eventp,Window w,FvwmWindow *tmp_win,
   	      unsigned long context, char *action,int* Module);
+ void Direction(XEvent *eventp,Window junk,FvwmWindow *tmp_win, 
+ 	       unsigned long context, char *action, int *Module);
  void WindowIdFunc(XEvent *eventp,Window w,FvwmWindow *tmp_win,
                    unsigned long context, char *action,int* Module);
  void ReadFile(XEvent *eventp,Window junk,FvwmWindow *tmp_win,
Index: fvwm/fvwm/parse.h
diff -c fvwm/fvwm/parse.h:1.1.1.1 fvwm/fvwm/parse.h:1.2
*** fvwm/fvwm/parse.h:1.1.1.1	Wed Nov 18 02:15:30 1998
--- fvwm/fvwm/parse.h	Wed Nov 18 02:22:00 1998
***************
*** 63,68 ****
--- 63,69 ----
      F_BUTTON_STYLE,
      F_READ,
      F_ADDMENU2,
+     F_DIRECTION,
      F_NEXT,
      F_PREV,
      F_NONE,
--
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: Dominik Vogt <[email-masked]>
Date: Thu, 19 Nov 1998 11:48:09 +0100

> Ok.  Here it is.  

I had a little time to test your patch and I think it's great!
Comprehensible mouseless operation is possible at last. 8-)=)
I want that patch as soon as possible, even before the 2.2 release.
However a few things have to be done first :(

1) The manpage has to be updated.

2) Some names should be more specific:

     Direction()   -> DirectionFunc()
     ...Mask()     -> ...ConditionMask()
     WindowMask    -> WindowConditionMask

   No big deal, ther're just a bit meaningless.

3) The scoring algorithm is a little erratic and needs some more
   work:

   Example: (windows a, b and c)

       +---------------------+
       |                     |
       |                     |
       |           a         |
       |                     |
       |                     |
       |   +--------------+  |
       +---|              |--+
           |              |
           |       b      |
           |              |--+
           |              |  |
           +--------------+ c|
                        |    |
                        +----+

   Assume window a has the focus at first:
   (I used 'Direction ... [!Sticky] Focus')

     south -> b
     south -> c
     north -> a!! should probably be b, no matter how wide b is.

   Another one:

           +--------------+   
           |              |   
           |              |
           |       b      |
           |              |--+
           |              |  |
   +-------+--------------+ c|
   |     a     |        |    |
   +-----------+        +----+

   Note that a and b do not overlap.

   a: East -> c
   c: West -> a

   I think you should take the overall proximity of the windows into
   account. Window c is clearly more east of a than b, but b is much
   closer and still east of a. Big jumps can confuse the user.

Bye

Dominik

---

From: Austin Donnelly <[email-masked]>
Date: Thu, 19 Nov 1998 11:18:04 +0000

>    closer and still east of a. Big jumps can confuse the user.

Why not base it on the mid-points of the windows under consideration?

Austin

---

From: Brad Giaccio <[email-masked]>
Date: Thu, 19 Nov 1998 08:07:01 -0500 (EST)

I agree with you on all counts here.  I had been using this patch for
quite a while, the author had released it previouslly as FocusToDir.  But
some work deffintelly does need to be done on the scoring, but I wouldn't
let that stop us from putting it in right now.  Using Next/Prev is pretty
counterintuitive anyway so a few inconsistences with this method won't
make to much differenc to most anyway.  Just my 2 cents.

Brad

-- 
--- There are two kinds of knowledge, you either know the answer or
			you know where to find it
		      -Kane, Johnson, and anonymous

---

From: Dominik Vogt <[email-masked]>
Date: Thu, 19 Nov 1998 14:17:42 +0100

> make to much differenc to most anyway.  Just my 2 cents.

I'd love to put it in immediately. The only reason why I have not
committed it already is that Teemu said he's working with 2.1.1 and
I don't want to mix up CVS vs his patch. I can do the name changes
as well.

Proposal:

I commit the patch he posted on friday, we package 2.1.2 and all further
patches go against that. Is that allright with you, Teemu? You don't
necessarily need to do all the work on the scoring function
yourself.

Is anybody autoconf'ed well enough to make this release? I'd do
it myself but are still having problems with the distribution.
Dan? What about Steve (are you still alive)?

Bye

Dominik

---

From: Dan Espen <[email-masked]>
Date: Thu, 19 Nov 1998 08:37:09 -0500

> Dan? What about Steve (are you still alive)?

I'm not sure if I  have everything I need, if  I get a chance I'll try
it.  I don't think you want to rely on me  getting this done, at least
not today.

I think one  of  your lists  would be in  order.   As it stands,   the
AUTHORS file  has one  author, I  think  updating this file should  be
mandatory before creating 2.1.2.

We also really need to get the NEWS file correct.  It should list all
new features since 2.0.46.

I assume 2.1.2 will be an ANNOUNCED beta?

Does anyone else feel  that the announced  beta should be done at  the
same  time we install the  web  docs?  (I  know  Jason has machines to
install,  and other work to   do, but I   wouldn't mind waiting, if it
won't be too long.)

--
Dan Espen                                Phone: (732) 699-5570
444 Hoes Lane  Room RRC 1C-214           E-mail: [email-masked]
Piscataway, NJ 08854
--
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: Dominik Vogt <[email-masked]>
Date: Thu, 19 Nov 1998 14:53:37 +0100

> it.  I don't think you want to rely on me  getting this done, at least

No, not today, first I want to commit the direction patch.

> I think one  of  your lists  would be in  order.

Sorry, I have no clue what you are talking about. What lists?

> As it stands,   the

Oh well, I can fill in the things I have done or know of, but that's
limited to the recent past.

> new features since 2.0.46.

I'm willing to help here, but someone else should work on this too.

> I assume 2.1.2 will be an ANNOUNCED beta?

Hmm, sounds a bit risky with the latest purify log. On the other
hand these problems do not occur during a 'normal' fvwm session (?)
Two things to consider:

 - what impact will the announcement have? Will we have time to
   fix the problems we're aware of at the moment?
 - should we have the bug tracker installed first (I think yes).

Overall I'd be more comfortable if we make 2.1.2 unofficial, get
Jitterbug or whatever running and announce 2.1.3 as official beta
late next week.
 
> install,  and other work to   do, but I   wouldn't mind waiting, if it

It should, but if it takes a few days I don't mind.

Bye

Dominik

---

From: Teemu Hirsimaki <[email-masked]>
Date: Thu, 19 Nov 1998 16:19:17 +0200 (EET)

> Comprehensible mouseless operation is possible at last. 8-)=)

Thanks.  I have liked the feature very much too.  I have it bound on
vi-keys with Super-modifier (old caps-lock - who needs it anyway :)
And that super can be used for wm-function, without confusing other
applications.

> 1) The manpage has to be updated.

Yep.  If nobody insist doing it himself, I can do that.

>    Example: (windows a, b and c)

I thought that also.  But the following can also be confusing.

+---+ +---+
| a | | b |
+---+ +---+
      +---+
      | c |
      +---+
 
a: south -> definitely c
c: north -> now a might be quite confusing

But I am sure that some people would expect to end up in the same
window if they move north and south.  Maybe we should implement some
remember-the-last-window-in-the-direction-func-flag. :)

>    Another one:

I would expect a: east -> c, but that's beacuse I have used this patch
quite a lot myself. :)

>    closer and still east of a. Big jumps can confuse the user.

Yep.  It is hard to devide where to make the limit.  If we move b
upwards, when would it be more logical to jump in c instead of b?  And
always a malicious user can produce a weird window configuration in
which the DirectionFunc does not work "correctly". :)

Maybe we could implement a few (maybe 2 or 3) user configurable
coefficients in then score function, because no score function can be
logical to all of us.  The 3 coefficient could be for
- angle
- pythagoran distance
- distance in the given direction

---

From: Dan Espen <[email-masked]>
Date: Thu, 19 Nov 1998 09:18:52 -0500

> No, not today, first I want to commit the direction patch.
OK.
> Sorry, I have no clue what you are talking about. What lists?

I'm refering to the lists you generally send under the subject line
of "Status".  The things remaining to be done, and I suggested time line.

> limited to the recent past.

Just add yourself, Steve has a really good list of contributors, I hope
he will commit it soon.

> I'm willing to help here, but someone else should work on this too.

Just put in the things you have done, or are sure about.

>  - should we have the bug tracker installed first (I think yes).

I don't see any  risk.  I think  we  will get some  valuable debugging
done.  I'd like the web docs done, but I think  the bug tracker should
be on the nice to have, but not mandatory list.  At this point we have
a number of people with update access, who  know the code better every
day. I think handling bugs should be no problem.

> Jitterbug or whatever running and announce 2.1.3 as official beta

Since late next week was the time frame I had in mind, we agree.

---

From: Steve Robbins <[email-masked]>
Date: Thu, 19 Nov 1998 09:06:03 -0500 (EST)

> Dan? What about Steve (are you still alive)?

Aye, I'm alive, but only barely ;-)

I'm much too swamped with work & travel to do anything at all before the
new year.  Someone else will have to do the autoconfing stuff, at least
for now. 

--
Steve Robbins                              <[email-masked]>

Remember: they always let you down when you need 'em.

---

From: Teemu Hirsimaki <[email-masked]>
Date: Thu, 19 Nov 1998 16:32:39 +0200 (EET)

> as well.  I commit the patch he posted on friday, we package 2.1.2
> and all further patches go against that. Is that allright with you,
> Teemu?

Sure, go ahead.  The DirectionFunc is so small that it does not matter
even if I had to modify a few things manually.

> You don't necessarily need to do all the work on the scoring
> function yourself.

No problem, I am quite pleased to do that.  I think the most important
thing is to make the score function as configurable as possible to
satisfy all users.

However, my biggest worry is the condition flag system of Circulate
which I modified.  I must admit that I do not know that system too
well, because I have not used those functions myself.

---

From: Dominik Vogt <[email-masked]>
Date: Thu, 19 Nov 1998 15:47:32 +0100

> thing is to make the score function as configurable as possible to

Hmm, the part 'satisfy all users' is good, but I don't think we need
a configurable scoring function. I can't imagine anyone is willing
to experiment with such parameters if there is a reasonable default.
 
> well, because I have not used those functions myself.

Don't worry about that before problems occur. Most of it was
cut-and-paste and I think the bugs therein have been there before.

Bye

Dominik

---

From: Dominik Vogt <[email-masked]>
Date: Thu, 19 Nov 1998 15:44:08 +0100

On Thu, Nov 19, 1998 at 09:18:52AM -0500, [email-masked] wrote:
> be on the nice to have, but not mandatory list.

If you think so it's allright by me. Let's go ahead. 

> At this point we have

I don't really worry that we can't handle the bugs but that some
may get lost. My inbox holds about 10 to 20 bug reports already
and try as I might I can't remember the status of some of them.
Anyway, it's not necessarily a showstopper.

> Since late next week was the time frame I had in mind, we agree.

Well, I had 2.1.2 in mind for the direction patch only (and there's
a lot of new stuff in since 2.1.1).

Bye

Dominik

---

From: [email-masked] (Paul D. Smith)
Date: 19 Nov 1998 09:51:56 -0500

I have the tools to make a release, if Steve isn't available.

-- 
-------------------------------------------------------------------------------
 Paul D. Smith <[email-masked]>         Network Management Development
 "Please remain calm...I may be mad, but I am a professional." --Mad Scientist
-------------------------------------------------------------------------------
   These are my opinions---Nortel Networks takes no responsibility for them.
--
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: Teemu Hirsimaki <[email-masked]>
Date: Sun, 22 Nov 1998 23:24:47 +0200 (EET)

> to experiment with such parameters if there is a reasonable default.

Ok, you are right.  I just always would want to have all as
configurable as possible. :)

I played a little with the scoring function and ended up to the
following: 

score = 1024 * offset / distance + 2 * distance + 2 * offset;

It does not penalty angle so much as the previous function.

I also thought the feature that the function would remember the window
we came from.  However, I think it would be too confusing, if the
target window would depend on the last window.

Then I also found an annoying feature in the Focus function.  If I try
to focus to a window A on other page and the mouse pointer lands on
other window B, the focus goes to B instead of A.  I really do not
want to raise the target window and moving the mouse pointer does not
help if the window is under other windows.

Index: builtins.c
===================================================================
RCS file: /home/thirsima/cvsroot/fvwm/fvwm/builtins.c,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 builtins.c
3898c3898
<     fvwm_msg(ERR, "Direction","No such direction %s", tmp);
---
>     fvwm_msg(ERR, "Direction", "Invalid direction %s", tmp);
3968c3968,3969
<     score = 1000 * offset / distance + distance;
---
>     score = 1024 * offset / distance + 2 * distance + 2 * offset;
> 
Index: fvwm2.1
===================================================================
RCS file: /home/thirsima/cvsroot/fvwm/fvwm/fvwm2.1,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 fvwm2.1
1136a1137,1144
> .IP "Direction \fIdirection\fP (\fIconditions\fP) \fIcommand\fP"
> 
> Performs \fIcommand\fP (typically Focus) on the closest window in the
> given \fIdirection\fP.  Windows which do not satisfy all
> \fIconditions\fP are ignored.  Conditions are the same as for
> \fICurrent\fP with the addition of CirculateHit which overrides the
> CirculateSkip style attribute and CirculateHitIcon which overrides the
> CirculateSkipIcon style attribute for iconified windows.