    Access macro editor with a shift-click

    By shift+(mouse-)clicking on a macro or windows background menu item,
    you invoke the macro editor dialog. Very useful if, like me, you want
    to access these easily for frequent changes.

diff -ur nedit_official nedit_mod
diff -ur nedit_official/source/userCmds.c nedit_mod/source/userCmds.c
--- nedit_official/source/userCmds.c	2007-10-02 17:47:09.000000000 +0200
+++ nedit_mod/source/userCmds.c	2008-02-27 01:44:41.000000000 +0100
@@ -238,7 +238,7 @@
 static Widget MacroPasteReplayBtn = NULL;
 static Widget BGMenuPasteReplayBtn = NULL;
 
-static void editMacroOrBGMenu(WindowInfo *window, int dialogType);
+static void editMacroOrBGMenu(WindowInfo *window, int dialogType, int entry);
 static void dimSelDepItemsInMenu(Widget menuPane, menuItemRec **menuList,
 	int nMenuItems, int sensitive);
 static void rebuildMenuOfAllWindows(int menuType);
@@ -272,7 +272,12 @@
 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData);
 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event);
 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData);
+static int checkEventModMask(const XmAnyCallbackStruct *cbs, unsigned flags,
+        unsigned match);
 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData);
+static void macroOrBgMenuCB(Widget w, WindowInfo *window, XtPointer callData,
+        int menuType, menuItemRec **menuItems, int nItems, int offset,
+        String command);
 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData);
 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData) ;
 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData);
@@ -729,17 +734,22 @@
 */
 void EditMacroMenu(WindowInfo *window)
 {
-    editMacroOrBGMenu(window, MACRO_CMDS);
+    editMacroOrBGMenu(window, MACRO_CMDS, -1);
 }
 void EditBGMenu(WindowInfo *window)
 {
-    editMacroOrBGMenu(window, BG_MENU_CMDS);
+    editMacroOrBGMenu(window, BG_MENU_CMDS, -1);
 }
 
-static void editMacroOrBGMenu(WindowInfo *window, int dialogType)
+/*
+** Open the appropriate dialog, ready for editing the entry whose index is
+** given; this should be -1 for "New".
+*/
+static void editMacroOrBGMenu(WindowInfo *window, int dialogType, int entry)
 {   
     Widget form, accLabel, pasteReplayBtn;
     Widget nameLabel, cmdLabel, okBtn, applyBtn, closeBtn;
+    Widget dialog = NULL;
     userCmdDialog *ucd;
     char *title;
     XmString s1;
@@ -747,13 +757,26 @@
     Arg args[20];
 
     /* if the dialog is already displayed, just pop it to the top and return */
-    if (dialogType == MACRO_CMDS && MacroCmdDialog != NULL) {
-    	RaiseDialogWindow(MacroCmdDialog);
-    	return;
-    }
-    if (dialogType == BG_MENU_CMDS && BGMenuCmdDialog != NULL) {
-    	RaiseDialogWindow(BGMenuCmdDialog);
-    	return;
+    dialog = (dialogType == MACRO_CMDS)   ? MacroCmdDialog  :
+             (dialogType == BG_MENU_CMDS) ? BGMenuCmdDialog : NULL;
+    if (dialog != NULL) {
+        WidgetList children;
+        Cardinal nChildren;
+
+        RaiseDialogWindow(dialog);
+        /* retrieve the userCmdDialog from the user data of the single child
+           of the dialog */
+        XtVaGetValues(dialog, XmNchildren, &children,
+                              XmNnumChildren, &nChildren, NULL);
+        ucd = NULL;
+        if (nChildren == 1) {
+            XtVaGetValues(children[0], XmNuserData, &ucd, NULL);
+            XtVaGetValues(dialog, XmNuserData, &ucd, NULL);
+            if (ucd && 0 <= entry && entry < ucd->nMenuItems) {
+                SelectManagedListItem(ucd->managedList, entry);
+            }
+        }
+        return;
     }
 
     /* Create a structure for keeping track of dialog state */
@@ -786,6 +809,7 @@
     form = XtVaCreateManagedWidget("editMacroCommands", xmFormWidgetClass,
 	    ucd->dlogShell, XmNautoUnmanage, False,
 	    XmNresizePolicy, XmRESIZE_NONE, NULL);
+    XtVaSetValues(form, XmNuserData, ucd, NULL); /* save ucd pointer in form */
     XtAddCallback(form, XmNdestroyCallback, destroyCB, ucd);
     AddMotifCloseCallback(ucd->dlogShell, closeCB, ucd);
  
@@ -1014,8 +1038,8 @@
        event handler manage it instead */
     disableTextW(ucd->accTextW);
 
-    /* initializs the dialog fields to match "New" list item */
-    updateDialogFields(NULL, ucd); 
+    /* initialize the dialog fields to match "New" list item */
+    updateDialogFields(NULL, ucd);
       
     /* Set initial default button */
     XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
@@ -1036,6 +1060,10 @@
     
     /* Realize all of the widgets in the new dialog */
     RealizeWithoutForcingPosition(ucd->dlogShell);
+
+    /* do we have a particular entry in mind? */
+    if (0 <= entry && entry < ucd->nMenuItems)
+        SelectManagedListItem(ucd->managedList, entry);
 }
 
 /*
@@ -2237,6 +2265,30 @@
     	    XmToggleButtonGetState(w));
 }
 
+static int checkEventModMask(const XmAnyCallbackStruct *cbs, unsigned flags,
+        unsigned match)
+{
+    unsigned btnState;
+    if (cbs && cbs->event) {
+        switch (cbs->event->type) {
+        case ButtonPress:
+        case ButtonRelease:
+            btnState = cbs->event->xbutton.state;
+            break;
+        /* don't handle keyboard events (btnState = cbs->event->xkey.state) */
+        case KeyPress:
+        case KeyRelease:
+        default:
+            return False;
+        }
+        match &= flags;
+        if ((btnState & flags) == match) {
+            return True;
+        }
+    }
+    return False;
+}
+
 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData) 
 {
     XtArgVal userData;
@@ -2256,14 +2308,31 @@
     	    ((XmAnyCallbackStruct *)callData)->event, params, 1);
 }
 
-static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData) 
+/* common code shared by the macro/bg menus' macro invocation */
+static void macroOrBgMenuCB(Widget w, WindowInfo *window, XtPointer callData,
+        int menuType, menuItemRec **menuItems, int nItems, int offset,
+        String command)
 {
     XtArgVal userData;
     int index;
     char *params[1];
+    XmAnyCallbackStruct *anyCBdata = (XmAnyCallbackStruct *)callData;
+    Boolean doEditMacro = checkEventModMask(anyCBdata, ShiftMask, ShiftMask);
 
     window = WidgetToWindow(MENU_WIDGET(w));
 
+    /* get the index of the macro command and verify that it's in range */
+    XtVaGetValues(w, XmNuserData, &userData, NULL);
+    index = (int)userData - offset;
+    if (index < 0 || index >= nItems)
+        return;
+
+    /* just go into the macro editor if that's what's needed */
+    if (doEditMacro) {
+        editMacroOrBGMenu(window, menuType, index);
+        return;
+    }
+
     /* Don't allow users to execute a macro command from the menu (or accel)
        if there's already a macro command executing.  NEdit can't handle
        running multiple, independent uncoordinated, macros in the same
@@ -2273,42 +2342,24 @@
        level, however, a call here with a macro running means that THE USER
        is explicitly invoking another macro via the menu or an accelerator. */
     if (window->macroCmdData != NULL) {
-	XBell(TheDisplay, 0);
-	return;
+        XBell(TheDisplay, 0);
+        return;
     }
-    
-    /* get the index of the macro command and verify that it's in range */
-    XtVaGetValues(w, XmNuserData, &userData, NULL);
-    index = (int)userData - 10;
-    if (index <0 || index >= NMacroMenuItems)
-    	return;
-    
-    params[0] = MacroMenuItems[index]->name;
-    XtCallActionProc(window->lastFocus, "macro_menu_command",
-    	    ((XmAnyCallbackStruct *)callData)->event, params, 1);
+    /* execute the macro */
+    params[0] = menuItems[index]->name;
+    XtCallActionProc(window->lastFocus, command, anyCBdata->event, params, 1);
 }
 
-static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData) 
+static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData) 
 {
-    XtArgVal userData;
-    int index;
-    char *params[1];
+    macroOrBgMenuCB(w, window, callData, MACRO_CMDS, MacroMenuItems,
+            NMacroMenuItems, 10, "macro_menu_command");
+}
 
-    /* Same remark as for macro menu commands (see above). */
-    if (window->macroCmdData != NULL) {
-	XBell(TheDisplay, 0);
-	return;
-    }
-    
-    /* get the index of the macro command and verify that it's in range */
-    XtVaGetValues(w, XmNuserData, &userData, NULL);
-    index = (int)userData - 10;
-    if (index <0 || index >= NBGMenuItems)
-    	return;
-    
-    params[0] = BGMenuItems[index]->name;
-    XtCallActionProc(window->lastFocus, "bg_menu_command",
-    	    ((XmAnyCallbackStruct *)callData)->event, params, 1);
+static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData) 
+{
+    macroOrBgMenuCB(w, window, callData, BG_MENU_CMDS, BGMenuItems,
+            NBGMenuItems, 10, "bg_menu_command");
 }
 
 /*
