Subject: FVWM: Menu editing commands

From: Konrad Schroder <[email-masked]>
Date: Fri, 5 Sep 1997 18:00:04 -0700 (PDT)

"Just mail it to the fvwm mailing list, is the best way I guess."  Hm.

Enclosed is a patch to fvwm-2.0.45 that will allow a set of menus to be
established, and then modified afterwards, by adding functions
"AddAboveInMenu", "ReplaceInMenu", and "DeleteFromMenu" to complement the
existing "AddToMenu" function. 

This is useful at my lab, because we are trying to modularize our standard
user config;  we want to be able to allow the users to change whatever
they like of their environment, while the admin staff is still able to
change the defaults and be sure that no one is inadvertently left out.

This is pretty easy in the case of shells and so forth, since each
variable can be overwritten individually.  Fvwm2 accommodates changes to
the Styles pretty well even without this patch---but as for menus, users
can only add to the end, and if they want to make minor changes to one of
the other items, well, they have to inline the whole menu, or worse, the
whole config. 

I'd appreciate any feedback from fvwm's maintainers (or anyone who knows
what they're talking about, for that matter).  Thanks,

-------------------------------------------------------------------------
Konrad Schroder           http://www.hitl.washington.edu/people/perseant/
System Administrator                         [email-masked]
Human Interface Technology Lab                      Voice: (206) 616-1478
Box 352142, University of Washington, 98195, USA      FAX: (206) 543-5380

*** fvwm/builtins.c.dist	Wed Jan 22 05:37:28 1997
--- fvwm/builtins.c	Fri Sep 05 14:09:14 1997
***************
*** 468,471 ****
--- 468,570 ----
  #endif
  char *last_menu = NULL;
+ char *last_menu_arg = NULL;
+ int last_menu_flag = MA_NORMAL;
+ 
+ 
+ void delete_item_from_menu(XEvent *eventp, Window w, FvwmWindow *tmp_win,
+                           unsigned long context, char *action, int *Module)
+ {
+   MenuRoot *mr;
+ 
+   char *token, *rest,*item;
+   char *flag, *arg;
+ 
+ #ifdef USEDECOR
+   last_decor = NULL;
+ #endif
+   if(last_menu != NULL)
+     free(last_menu);
+   last_menu = NULL;
+ 
+   rest = GetNextToken(action,&token);
+   mr = FindPopup(token);
+   rest = GetNextToken(rest,&item);
+ 
+   if(mr)
+   {
+       DeleteFromMenu(mr,item);
+       MakeMenu(mr);
+   }
+   free(item);
+ }
+ 
+ /* This is the same as below, but controls placement too */
+ void add_item_above_in_menu(XEvent *eventp, Window w, FvwmWindow *tmp_win,
+                           unsigned long context, char *action, int *Module)
+ {
+   MenuRoot *mr;
+ 
+   char *token, *rest,*item;
+   char *flag, *arg;
+ 
+ #ifdef USEDECOR
+   last_decor = NULL;
+ #endif
+ 
+   rest = GetNextToken(action,&token);
+   mr = FindPopup(token);
+   if(mr == NULL)
+     mr = NewMenuRoot(token, 0);
+   if(last_menu != NULL)
+     free(last_menu);
+   last_menu = token;
+ 
+   rest = GetNextToken(rest,&arg);   /* the reference item */
+ 
+   rest = GetNextToken(rest,&item);  /* now the new item */
+ 
+   last_menu_arg  = arg;
+   last_menu_flag = MA_ABOVE;
+ 
+   AddToMenu(mr, item, rest, MA_ABOVE, arg);
+   free(item);
+   
+   MakeMenu(mr);
+   return;
+ }
+ 
+ void replace_item_in_menu(XEvent *eventp,Window w,FvwmWindow *tmp_win,
+ 		      unsigned long context,
+ 		      char *action, int *Module)
+ {
+   MenuRoot *mr;
+ 
+   char *token, *rest,*item;
+   char *flag, *arg;
+ 
+ #ifdef USEDECOR
+   last_decor = NULL;
+ #endif
+   last_menu = NULL;
+   last_menu_arg  = NULL;
+   last_menu_flag = MA_NORMAL;
+ 
+   rest = GetNextToken(action,&token);
+   mr = FindPopup(token);
+   if(mr == NULL)
+     mr = NewMenuRoot(token, 0);
+   if(last_menu != NULL)
+     free(last_menu);
+ 
+   rest = GetNextToken(rest,&arg);   /* the reference item */
+   rest = GetNextToken(rest,&item);  /* now the new item */
+ 
+   AddToMenu(mr, item, rest, MA_REPLACE, arg);
+   free(item);
+   
+   MakeMenu(mr);
+   return;
+ }
+ 
  void add_item_to_menu(XEvent *eventp,Window w,FvwmWindow *tmp_win,
  		      unsigned long context,
***************
*** 475,478 ****
--- 574,579 ----
  
    char *token, *rest,*item;
+   char *flag, *arg;
+   int i_flag = MA_NORMAL, i;
  
  #ifdef USEDECOR
***************
*** 487,493 ****
      free(last_menu);
    last_menu = token;
    rest = GetNextToken(rest,&item);
  
!   AddToMenu(mr, item,rest);
    free(item);
    
--- 588,597 ----
      free(last_menu);
    last_menu = token;
+   last_menu_arg  = NULL;
+   last_menu_flag = MA_NORMAL;
+ 
    rest = GetNextToken(rest,&item);
  
!   AddToMenu(mr, item, rest, MA_NORMAL, NULL);
    free(item);
    
***************
*** 517,521 ****
        rest = GetNextToken(action,&item);
        
!       AddToMenu(mr, item,rest);
        free(item);
        
--- 621,625 ----
        rest = GetNextToken(action,&item);
        
!       AddToMenu(mr, item, rest, last_menu_flag, last_menu_arg);
        free(item);
        
***************
*** 549,553 ****
    rest = GetNextToken(action,&item);
  
!   AddToMenu(mr, item,rest);
    free(item);
    
--- 653,657 ----
    rest = GetNextToken(action,&item);
  
!   AddToMenu(mr, item, rest, last_menu_flag, last_menu_arg);
    free(item);
    
***************
*** 591,595 ****
    rest = GetNextToken(rest,&item);
  
!   AddToMenu(mr, item,rest);
    free(item);
    
--- 695,699 ----
    rest = GetNextToken(rest,&item);
  
!   AddToMenu(mr, item, rest, MA_NORMAL, NULL);
    free(item);
    
*** fvwm/functions.c.dist	Fri Sep 05 13:16:50 1997
--- fvwm/functions.c	Fri Sep 05 14:15:08 1997
***************
*** 36,39 ****
--- 36,40 ----
  struct functions func_config[] =
  {
+   {"AddAboveInMenu",add_item_above_in_menu, F_ADDMENU_ABOVE, FUNC_NO_WINDOW},
  #ifdef MULTISTYLE
    {"AddButtonStyle",AddButtonStyle,  F_ADD_BUTTON_STYLE,    FUNC_NO_WINDOW},
***************
*** 68,71 ****
--- 69,73 ----
  #endif
    {"DestroyFunc",  destroy_menu,     F_DESTROY_MENU,        FUNC_NO_WINDOW},
+   {"DestroyMenuItem", delete_item_from_menu, F_DELMITEM,    FUNC_NO_WINDOW},
    {"DestroyMenu",  destroy_menu,     F_DESTROY_MENU,        FUNC_NO_WINDOW},
    {"DestroyModuleConfig", DestroyModConfig, F_DESTROY_MOD,  FUNC_NO_WINDOW},
***************
*** 112,115 ****
--- 114,118 ----
    {"Refresh",      refresh_function, F_REFRESH,             FUNC_NO_WINDOW},
    {"RefreshWindow",refresh_win_function, F_REFRESH,         FUNC_NEEDS_WINDOW},
+   {"ReplaceInMenu",replace_item_in_menu, F_REPLACEMENU,     FUNC_NO_WINDOW},
    {"Resize",       resize_window,    F_RESIZE,              FUNC_NEEDS_WINDOW},
    {"Restart",      restart_function, F_RESTART,             FUNC_NO_WINDOW},
*** fvwm/menus.c.dist	Thu Sep 04 12:08:52 1997
--- fvwm/menus.c	Fri Sep 05 13:02:24 1997
***************
*** 876,880 ****
--- 876,893 ----

+ MenuItem *DestroyMenuItem(MenuItem *mi)
+ {
+     MenuItem *tmp2 = mi->next;
  
+       if (mi->item != NULL) free(mi->item);
+       if (mi->item2 != NULL) free(mi->item2);
+       if (mi->action != NULL) free(mi->action);
+       if(mi->picture)
+ 	DestroyPicture(dpy,mi->picture);
+       if(mi->lpicture)
+ 	DestroyPicture(dpy,mi->lpicture);
+       free(mi);
+       return tmp2;
+ }
  
  void DestroyMenu(MenuRoot *mr)
***************
*** 908,921 ****
    while(mi != NULL)
      {
!       tmp2 = mi->next;
!       if (mi->item != NULL) free(mi->item);
!       if (mi->item2 != NULL) free(mi->item2);
!       if (mi->action != NULL) free(mi->action);
!       if(mi->picture)
! 	DestroyPicture(dpy,mi->picture);
!       if(mi->lpicture)
! 	DestroyPicture(dpy,mi->lpicture);
!       free(mi);
!       mi = tmp2;
      }
    free(mr);
--- 921,925 ----
    while(mi != NULL)
      {
!         mi = DestroyMenuItem(mi);
      }
    free(mr);
***************
*** 1167,1171 ****
--- 1171,1252 ----

+ /*
+  * GetReferenceMenuItem - get a reference menu item to delete, or add after.
+  */
  
+ MenuItem *GetReferenceMenuItem(MenuRoot *menu, char *ref)
+ {
+     MenuItem *mi_ref;
+ 
+     if(ref==NULL || ref[0]=='\0')
+         mi_ref = menu->first;
+     else
+     {
+         if(menu->first == NULL)
+             mi_ref = NULL;
+         else
+         {
+             for(mi_ref=menu->first; mi_ref; mi_ref = mi_ref->next)
+             {
+                 if(mi_ref->item && strcasecmp(mi_ref->item,ref)==0)
+                     break;
+             }
+             if(mi_ref==NULL)
+             {
+                 /* try again with item2 */
+                 for(mi_ref=menu->first; mi_ref; mi_ref = mi_ref->next)
+                 {
+                     if(mi_ref->item2 && strcasecmp(mi_ref->item2,ref)==0)
+                         break;
+                 }
+             }
+             if(mi_ref==NULL)
+             {
+                 /* now we're desperate...try partial matches */
+                 for(mi_ref=menu->first; mi_ref; mi_ref = mi_ref->next)
+                 {
+                     if((mi_ref->item
+                         && strncasecmp(mi_ref->item,ref,strlen(ref))==0)
+                        || (mi_ref->item2
+                            && strncasecmp(mi_ref->item2,ref,strlen(ref))==0))
+                         break;
+                 }
+             }
+         }
+     }
+     return mi_ref;
+ }
+ 
+ /*
+  * DeleteFromMenu - delete an item from a menu
+  */
+ 
+ void DeleteFromMenu(MenuRoot *menu, char *item)
+ {
+     MenuItem *delenda, *t;
+ 
+     delenda = GetReferenceMenuItem(menu,item);
+     if(delenda)
+     {
+         /* detach */
+         if(delenda->prev)
+             delenda->prev->next = delenda->next;
+         else
+             menu->first = delenda->next;
+         if(delenda->next)
+             delenda->next->prev = delenda->prev;
+         else
+             menu->last = delenda->prev;
+ 
+         /* destroy */
+         DestroyMenuItem(delenda);
+ 
+         /* clean up numbering */
+         menu->items=0;
+         for(t=menu->first; t; t=t->next)
+             t->item_num = menu->items++;
+     }
+ }
+ 
  /***********************************************************************
   *
***************
*** 1181,1184 ****
--- 1262,1267 ----
   *	action	- the string to possibly execute
   *	func	- the numeric function
+  * (ks) how     - additional flags to control placement
+  * (ks) ref     - menu item relative to which placement is done
   *
   * ckh - need to add boolean to say whether or not to expand for pixmaps,
***************
*** 1186,1193 ****
   *
   ***********************************************************************/
! void AddToMenu(MenuRoot *menu, char *item, char *action)
  {
!   MenuItem *tmp;
    char *start,*end;
  
    if(item == NULL)
--- 1269,1277 ----
   *
   ***********************************************************************/
! void AddToMenu(MenuRoot *menu, char *item, char *action, int how, char *ref)
  {
!   MenuItem *tmp, *mi_ref, *t;
    char *start,*end;
+   int i;
  
    if(item == NULL)
***************
*** 1195,1209 ****
  
    tmp = (MenuItem *)safemalloc(sizeof(MenuItem));
!   if (menu->first == NULL)
!     {
!       menu->first = tmp;
!       tmp->prev = NULL;
!     }
!   else
!     {
!       menu->last->next = tmp;
!       tmp->prev = menu->last;
!     }
!   menu->last = tmp;
    tmp->picture=NULL;
    tmp->lpicture=NULL;
--- 1279,1337 ----
  
    tmp = (MenuItem *)safemalloc(sizeof(MenuItem));
! 
!   /* If we need a reference, get one here.  NULL means none was available */
!   if(how!=MA_NORMAL)
!   {
!       mi_ref = GetReferenceMenuItem(menu,ref);
!   }
! 
!   switch(how)
!   {
!       /* all of these cases fall through to the standard case, if  */
!       /* no reference menu item could be found */
!   case MA_REPLACE:
!       if(mi_ref)
!       {
!           tmp->prev = mi_ref->prev;
!           tmp->next = mi_ref->next;
!           if(tmp->prev)
!               tmp->prev->next = tmp;
!           else
!               menu->first = tmp;
!           if(tmp->next)
!               tmp->next->prev = tmp;
!           else
!               menu->last = tmp;
!           DestroyMenuItem(mi_ref);
!           break;
!       }
!   case MA_ABOVE:
!       if(mi_ref)
!       {
!           tmp->next = mi_ref;
!           tmp->prev = mi_ref->prev;
!           mi_ref->prev = tmp;
!           if(tmp->prev)
!               tmp->prev->next = tmp;
!           else
!               menu->first = tmp;
!           break;
!       }
!   case MA_NORMAL:
!   default:
!       if (menu->first == NULL)
!       {
!           menu->first = tmp;
!           tmp->prev = NULL;
!       }
!       else
!       {
!           menu->last->next = tmp;
!           tmp->prev = menu->last;
!       }
!       menu->last = tmp;
!       break;
!   }
! 
    tmp->picture=NULL;
    tmp->lpicture=NULL;
***************
*** 1258,1265 ****
  
    tmp->action = stripcpy(action);
-   tmp->next = NULL;
    tmp->state = 0;
    tmp->func_type = find_func_type(tmp->action);
!   tmp->item_num = menu->items++;
  }
  
--- 1386,1401 ----
  
    tmp->action = stripcpy(action);
    tmp->state = 0;
    tmp->func_type = find_func_type(tmp->action);
!   if(how == MA_NORMAL)
!   {
!       tmp->next = NULL;
!       tmp->item_num = menu->items++;
!   }
!   else
!       for(menu->items=0, tmp=menu->first; tmp; tmp=tmp->next)
!       {
!           tmp->item_num = menu->items++;
!       }
  }
  
*** fvwm/menus.h.dist	Thu Sep 04 12:45:19 1997
--- fvwm/menus.h	Thu Sep 04 12:47:09 1997
***************
*** 54,57 ****
--- 54,62 ----
  #include "../libs/fvwmlib.h"
  
+ #define MA_NORMAL 0
+ #define MA_ABOVE  1
+ #define MA_BENEATH 2
+ #define MA_REPLACE 3
+ 
  typedef struct MenuItem
  {
*** fvwm/misc.h.dist	Thu Sep 04 13:41:18 1997
--- fvwm/misc.h	Fri Sep 05 14:17:11 1997
***************
*** 224,228 ****
  extern void       UngrabEm(void);
  extern MenuRoot   *NewMenuRoot(char *name, int function_or_popup);
! extern void       AddToMenu(MenuRoot *, char *, char *);
  extern void       MakeMenu(MenuRoot *);
  extern void       CaptureAllWindows(void);
--- 224,228 ----
  extern void       UngrabEm(void);
  extern MenuRoot   *NewMenuRoot(char *name, int function_or_popup);
! extern void       AddToMenu(MenuRoot *, char *, char *, int, char *);
  extern void       MakeMenu(MenuRoot *);
  extern void       CaptureAllWindows(void);
***************
*** 468,472 ****
--- 468,481 ----
  void GetMenuXPMFile(char *name, MenuItem *it);
  void GetMenuBitmapFile(char *name, MenuItem *it);
+ void delete_item_from_menu(XEvent *eventp,Window w,FvwmWindow *tmp_win,
+ 		      unsigned long context,
+ 		      char *action, int *Module);
+ void add_item_above_in_menu(XEvent *eventp,Window w,FvwmWindow *tmp_win,
+ 		      unsigned long context,
+ 		      char *action, int *Module);
  void add_item_to_menu(XEvent *eventp,Window w,FvwmWindow *tmp_win,
+ 		      unsigned long context,
+ 		      char *action, int *Module);
+ void replace_item_in_menu(XEvent *eventp,Window w,FvwmWindow *tmp_win,
  		      unsigned long context,
  		      char *action, int *Module);
*** fvwm/parse.h.dist	Fri Sep 05 13:16:33 1997
--- fvwm/parse.h	Fri Sep 05 14:06:30 1997
***************
*** 74,77 ****
--- 74,82 ----
  #define F_TITLESTYLE            56
  #define F_EXEC_SETUP            57
+ 
+ #define F_ADDMENU_ABOVE         58
+ #define F_DELMITEM              59
+ #define F_REPLACEMENU           60
+ 
  /* Functions which require a target window */
  #define F_RESIZE		100
*** fvwm/windows.c.dist	Thu Sep 04 13:33:13 1997
--- fvwm/windows.c	Thu Sep 04 13:34:04 1997
***************
*** 135,139 ****
    }
    mr=NewMenuRoot(tlabel,0);
!   AddToMenu(mr, tlabel, "TITLE");      
  
    next_desk = 0;
--- 135,139 ----
    }
    mr=NewMenuRoot(tlabel,0);
!   AddToMenu(mr, tlabel, "TITLE", MA_NORMAL, NULL);      
  
    next_desk = 0;
***************
*** 227,231 ****
          else
            sprintf(tlabel,"WindowListFunc %ld",t->w);
!         AddToMenu(mr, t_hot, tlabel);
  #ifdef MINI_ICONS
          /* Add the title pixmap */
--- 227,231 ----
          else
            sprintf(tlabel,"WindowListFunc %ld",t->w);
!         AddToMenu(mr, t_hot, tlabel, MA_NORMAL, NULL);
  #ifdef MINI_ICONS
          /* Add the title pixmap */

--
Visit the official FVWM web page at <URL:http://www.hpc.uh.edu/fvwm/>.
To unsubscribe from the list, send "unsubscribe fvwm" in the body of a
message to [email-masked].
To report problems, send mail to [email-masked].

---

From: Adam Kopacz <[email-masked]>
Date: Sat, 06 Sep 1997 12:17:47 +0200

Konrad Schroder wrote:
hi,

please STOP posting patches INLINE in a message! nobody can use this
!!!! :(
why you don't post as attachments (mime/uue.) ???????????? =>

= LINE WRAP (

-- 
Bye,                             Linux/Babes
  Adam Kopacz (kLospatz)    http://www.rat.de/Adam

---

From: [email-masked]
Date: Sat, 6 Sep 1997 11:17:07 -0400 (EDT)

On Sat, 6 Sep 1997, Adam Kopacz wrote:

: please STOP posting patches INLINE in a message! nobody can use this
: !!!! :(
: why you don't post as attachments (mime/uue.) ???????????? =>
: 
: >   /***********************************************************************
: >    *
: > ***************
   ^^^^^^^^^^^^^^^^^
: 
: = LINE WRAP (

Actually, the above isn't line wrap; the part i underlined above is the
beginning of the next `hunk' (to use patch's terminology), and the
single asterisk is actually supposed to be that way.

However, Adam's concern is somewhat valid, especially for longer
patches.  Best thing to do is to use a MIME attachment if you can.  For
shorter patches, i'd just give it a once-over to check for long lines
that might wrap.

--jim

############### jim knoble ######## [email-masked] #################

---

From: Kendrick Vargas <[email-masked]>
Date: Sat, 6 Sep 1997 12:18:15 -0400 (EDT)

> shorter patches, i'd just give it a once-over to check for long lines

I thought it was agreed to a while ago that patches (or anything sizable
for that matter) should not be attached, inlined, or anything... They
should be placed on an FTP server to be requested when someone would
actually want it, thereby reducing people getting pissed off by 20k gif's
in their inboxes or 200k binaries, etc...
			-peace

--- BEGIN GEEK CODE BLOCK ------------+-----------
GAT d- s:+ !a C+(+++) UI/L/S/B++(+++) | "In the morning glad I see
P>+ L+(++) E---- W+++ N+ o? K? w++++  |  My foe outstrech'd beneath the tree."
O--- M-- V PS+++@ PE Y-- PGP+ t++ 5   |               -The Poison Tree
X++ R- tv+ b DI++ D+ G e>*  h*(!) r-  |                William Blake
y*(+) ------ END GEEK CODE BLOCK -----+

---

From: Jason L Tibbitts III <[email-masked]>
Date: 06 Sep 1997 12:55:51 -0500

>>>>> "KV" == Kendrick Vargas <[email-masked]> writes:

KV> I thought it was agreed to a while ago that patches (or anything
KV> sizable for that matter) should not be attached, inlined, or
KV> anything...

I don't know who 'we' is, but the document you get when you subscribed asks
you not to send encoded binaries.  Patches are OK if you can get then to
come through correctly.  The list does not do line-wrapping; that is
completely dependent on the sending mailer.  If you're using something
useful instead of Netscape or its ilk you won't have problems with line
wrapping in patches.

KV> They should be placed on an FTP server to be requested when someone
KV> would actually want it, thereby reducing people getting pissed off by
KV> 20k gif's in their inboxes or 200k binaries, etc...

200k binaries simply won't get through the list software; I don't allow
them.  Patches are fine as long as the patch sender has the brains to use
software that doesn't muck with the message.

 - J<

---

From: Kendrick Vargas <[email-masked]>
Date: Sun, 7 Sep 1997 12:53:26 -0400 (EDT)

> software that doesn't muck with the message.

I guess I stand corrected.
			-peace

---

From: Kai Grossjohann <[email-masked]>
Date: 08 Sep 1997 09:58:38 +0200

>>>>> On Sat, 06 Sep 1997, Adam Kopacz said:

  Adam> please STOP posting patches INLINE in a message! nobody can use this
  Adam> !!!! :(
  Adam> why you don't post as attachments (mime/uue.) ???????????? =>

  Adam> = LINE WRAP (

Even Netscape's mail reader can be configured to turn off automatic
line wrap.  I have no problem with the patch, it comes through just
fine (though I don't use Mozilla).

There's no need to shout like that.  I don't think Konrad has done
anything wrong.

kai
-- 
I like both kinds of music.

---

From: Konrad Schroder <[email-masked]>
Date: Mon, 8 Sep 1997 09:51:52 -0700 (PDT)

[...]
> please STOP posting patches INLINE in a message! nobody can use this

Well, for the benefit of anyone who couldn't (for whatever reason), here
it is again, MIME-encoded.

  
  #ifdef USEDECOR
***************
*** 487,493 ****
      free(last_menu);
    last_menu = token;

--- 588,597 ----
      free(last_menu);
    last_menu = token;
+   last_menu_arg  = NULL;

***************
*** 517,521 ****

--- 621,625 ----

***************
*** 549,553 ****

--- 653,657 ----

***************
*** 591,595 ****

--- 695,699 ----

  

+ /*

    if(item == NULL)
--- 1269,1277 ----
   *
  
    if(item == NULL)
***************
*** 1195,1209 ****

  
  
--- 1386,1401 ----

  
+ #define MA_NORMAL 0
+ #define MA_ABOVE  1
+ #define MA_BENEATH 2
+ #define MA_REPLACE 3
+ 
  typedef struct MenuItem
  {
  
    next_desk = 0;
--- 135,139 ----
    }
  
    next_desk = 0;
***************
*** 227,231 ****
          else