    Allow an array to pass button names to macro dialog functions

    Instead of calling a dialog as
	    btn = dialog("What to do next?", "Stop", "Retry", "Skip", "Cancel")
    you can use an array, eg
	    btns[1] = "Stop"
	    btns[2] = "Retry"
	    btns[3] = "Skip"
	    btns[4] = "Cancel"
	    btn = dialog("What to do next?", btns)

    This allows you to test the validity of the button number like this:
	    if (!(btn in btns))
	        return "Cancel"
    and to use the button names in button value tests, as in
	    if (btns[btn] == "Stop")
	        doStop()

    In fact, by including a zero-indexed element, you can handle all results
    returned as the dialog button, eg
	    btns[0] = "Cancel"
	    ... # assign btns[1] to btns[4] as before
	    btn = dialog("What to do next?", btns)
	    # now we know that all possible button values are indices into btns
	    # so the following will not fail:
	    if (btns[btn] == "Stop")
	        ...

diff -ur nedit_official nedit_mod
diff -ur nedit_official/doc/help.etx nedit_mod/doc/help.etx
--- nedit_official/doc/help.etx	2006-09-30 19:18:17.000000000 +0200
+++ nedit_mod/doc/help.etx	2006-10-03 08:58:09.249259200 +0200
@@ -2541,13 +2541,16 @@
   Returns the contents of the clipboard as a macro string. Returns empty
   string on error.
 
-**dialog( message, btn_1_label, btn_2_label, ... )**
-  Pop up a dialog for querying and presenting information to the user. First
-  argument is a string to show in the message area of the dialog.
+**dialog( message[, btn_1_label[, btn_2_label[, ...]]] )**
+**dialog( message[, btn_label_array] )**
+  Pop up a dialog for querying and presenting information to the user. The
+  first argument is a string to show in the message area of the dialog.
   Additional optional arguments represent labels for buttons to appear along
-  the bottom of the dialog. Returns the number of the button pressed (the
-  first button is number 1), or 0 if the user closed the dialog via the window
-  close box.
+  the bottom of the dialog. These labels can also be given in the form of an
+  array of strings, with consecutive numeric indices starting at 1. Returns
+  the number of the button pressed (the first button is number 1), or 0 if
+  the user closed the dialog via the window close box or by pressing the
+  escape key.
 
 **exist_filename_dialog( title[, default_path[, filter]] )**
   Presents a file selection dialog with the given title to the user that 
@@ -2590,24 +2593,26 @@
 **length( string )**
   Returns the length of a string
 
-**list_dialog( message, text, btn_1_label, btn_2_label, ... )**
+**list_dialog( message, text[, btn_1_label[, btn_2_label[, ...]]] )**
+**list_dialog( message, text[, btn_label_array] )**
   Pop up a dialog for prompting the user to choose a line from the given text
   string. The first argument is a message string to be used as a title for the
-  fixed text describing the list. The second string provides the list data:
-  this is a text string in which list entries are separated by newline
-  characters. Additional optional arguments represent labels for
-  buttons to appear along the bottom of the dialog. Returns the line of text
-  selected by the user as the function value (without any newline separator) or
-  the empty string if none was selected, and number of the button pressed (the
-  first button is number 1), in $list_dialog_button. If the user closes the
-  dialog via the window close box, the function returns the empty string, and
-  $list_dialog_button returns 0.
+  fixed text describing the list. The second string provides the list data: this
+  is a text string in which list entries are separated by newline characters.
+  Additional optional arguments represent labels for buttons to appear along the
+  bottom of the dialog. These labels can also be given in the form of an array
+  of strings, with consecutive numeric indices starting at 1. Returns the line
+  of text selected by the user as the function value (without any newline
+  separator) or the empty string if none was selected, and number of the button
+  pressed (the first button is number 1), in $list_dialog_button. If the user
+  closes the dialog via the window close box, the function returns the empty
+  string, and $list_dialog_button returns 0.
 
 **max( n1, n2, ... )**
-  Returns the maximum value of all of its arguments
+  Returns the maximum value of all of its arguments.
 
 **min( n1, n2, ... )**
-  Returns the minimum value of all of its arguments
+  Returns the minimum value of all of its arguments.
 
 **new_filename_dialog( title[, default_path[, default_filename]] )**
   Presents a file selection dialog with the given title to the user that 
@@ -2698,15 +2703,17 @@
   argument can specify how the separation_string is interpreted. The default
   is "literal". The returned value is an array with keys beginning at 0.
 
-**string_dialog( message, btn_1_label, btn_2_label, ... )**
+**string_dialog( message[, btn_1_label[, btn_2_label[, ...]]] )**
+**string_dialog( message[, btn_label_array] )**
   Pops up a dialog prompting the user to enter information. The first argument
-  is a string to show in the message area of the dialog. Additional
-  optional arguments represent labels for buttons to appear along the bottom of
-  the dialog. Returns the string entered by the user as the function value,
-  and number of the button pressed (the first button is number 1), in
-  $string_dialog_button. If the user closes the dialog via the window close
-  box, the function returns the empty string, and $string_dialog_button returns
-  0.
+  is a string to show in the message area of the dialog. Additional optional
+  arguments represent labels for buttons to appear along the bottom of the
+  dialog. These labels can also be given in the form of an array of strings,
+  with consecutive numeric indices starting at 1. Returns the string entered by
+  the user as the function value, and number of the button pressed (the first
+  button is number 1), in $string_dialog_button. If the user closes the dialog
+  via the window close box, the function returns the empty string, and
+  $string_dialog_button returns 0.
 
 **string_compare(string1, string2 [, consider-case])**
   Compare two  strings and return 0 if they are equal, -1 if string1 is less
diff -ur nedit_official/source/macro.c nedit_mod/source/macro.c
--- nedit_official/source/macro.c	2006-05-31 18:39:23.000000000 +0200
+++ nedit_mod/source/macro.c	2006-09-18 15:13:25.187500000 +0200
@@ -353,6 +353,8 @@
 static int readIntArg(DataValue dv, int *result, char **errMsg);
 static int readStringArg(DataValue dv, char **result, char *stringStorage,
     	char **errMsg);
+static DataValue *listArrayToArgList(DataValue *dvArray, int start, int end,
+        int *len, char **errMsg);
 /* DISABLED FOR 5.4
 static int backlightStringMV(WindowInfo *window, DataValue *argList,
 	int nArgs, DataValue *result, char **errMsg);
@@ -2838,7 +2840,8 @@
     Widget dialog, btn;
     int i, nBtns;
     XmString s1, s2;
-    
+    DataValue *arrayList;
+
     /* Ignore the focused window passed as the function argument and put
        the dialog up over the window which is executing the macro */
     window = MacroRunWindow();
@@ -2861,21 +2864,38 @@
         return False;
     }
 
-    /* check that all button labels can be read */
-    for (i=1; i<nArgs; i++) {
-        if (!readStringArg(argList[i], &btnLabel, btnStorage, errMsg)) {
+    /* now for the non-obligatory button label arguments */
+    argList += 1;
+    nArgs -= 1;
+
+    arrayList = NULL; /* pointer for converted array argument for buttons */
+
+    if (nArgs == 1 && argList[0].tag == ARRAY_TAG) {
+        arrayList = listArrayToArgList(&argList[0], 1, 0, &nArgs, errMsg);
+        if (nArgs < 0) {
             return False;
         }
+        if (arrayList == NULL) {
+            nArgs = 0; /* no button argumants */
+        } else {
+            argList = arrayList;
+        }
     }
+    /* check that all button labels can be read */
+    for (i = 0; i < nArgs; i++)
+      if (!readStringArg(argList[i], &btnLabel, btnStorage, errMsg))
+      {
+           XtFree((String)arrayList);
+          return False;
+      }
 
     /* pick up the first button */
-    if (nArgs == 1) {
+    if (nArgs == 0) {
         btnLabel = "OK";
         nBtns = 1;
     }
     else {
-        nBtns = nArgs - 1;
-        argList++;
+        nBtns = nArgs;
         readStringArg(argList[0], &btnLabel, btnStorage, errMsg);
     }
 
@@ -2885,7 +2905,7 @@
     XtSetArg(al[ac], XmNmessageString, s1=MKSTRING(message)); ac++;
     XtSetArg(al[ac], XmNokLabelString, s2=XmStringCreateSimple(btnLabel)); ac++;
     dialog = CreateMessageDialog(window->shell, "macroDialog", al, ac);
-    if (1 == nArgs)
+    if (0 == nArgs)
     {
         /*  Only set margin width for the default OK button  */
         XtVaSetValues(XmMessageBoxGetChild(dialog, XmDIALOG_OK_BUTTON),
@@ -2920,6 +2940,8 @@
     	XmStringFree(s1);
     }
 
+    XtFree((String)arrayList); /* don't need the array copy anymore */
+
 #ifdef LESSTIF_VERSION
     /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
        the escape key for closing the dialog (probably because the
@@ -3015,7 +3037,8 @@
     XmString s1, s2;
     Arg al[20];
     int ac;
-    
+    DataValue *arrayList;
+
     /* Ignore the focused window passed as the function argument and put
        the dialog up over the window which is executing the macro */
     window = MacroRunWindow();
@@ -3037,19 +3060,39 @@
     if (!readStringArg(argList[0], &message, stringStorage, errMsg)) {
         return False;
     }
+
+    /* now for the non-obligatory button label arguments */
+    argList += 1;
+    nArgs -= 1;
+
+    arrayList = NULL; /* pointer for converted array argument for buttons */
+
+    if (nArgs > 0 && argList[0].tag == ARRAY_TAG) {
+        arrayList = listArrayToArgList(&argList[0], 1, 0, &nArgs, errMsg);
+        if (nArgs < 0) {
+            return False;
+        }
+        if (arrayList == NULL) {
+            nArgs = 0; /* no button argumants */
+        } else {
+            argList = arrayList;
+        }
+    }
     /* check that all button labels can be read */
-    for (i=1; i<nArgs; i++) {
+    for (i = 0; i < nArgs; i++) {
         if (!readStringArg(argList[i], &btnLabel, stringStorage, errMsg)) {
+            XtFree((String)arrayList);
             return False;
         }
     }
-    if (nArgs == 1) {
+
+    /* pick up the first button */
+    if (nArgs == 0) {
         btnLabel = "OK";
         nBtns = 1;
     }
     else {
-        nBtns = nArgs - 1;
-        argList++;
+        nBtns = nArgs;
         readStringArg(argList[0], &btnLabel, btnStorage, errMsg);
     }
 
@@ -3059,7 +3102,7 @@
     XtSetArg(al[ac], XmNselectionLabelString, s1=MKSTRING(message)); ac++;
     XtSetArg(al[ac], XmNokLabelString, s2=XmStringCreateSimple(btnLabel)); ac++;
     dialog = CreatePromptDialog(window->shell, "macroStringDialog", al, ac);
-    if (1 == nArgs)
+    if (0 == nArgs)
     {
         /*  Only set margin width for the default OK button  */
         XtVaSetValues(XmSelectionBoxGetChild(dialog, XmDIALOG_OK_BUTTON),
@@ -3096,7 +3139,9 @@
     	XtAddCallback(btn, XmNactivateCallback, stringDialogBtnCB, window);
     	XmStringFree(s1);
     }
-    
+
+    XtFree((String)arrayList); /* don't need the array copy anymore */
+
 #ifdef LESSTIF_VERSION
     /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
        the escape key for closing the dialog (probably because the
@@ -3530,8 +3575,8 @@
     int tabDist;
     Arg al[20];
     int ac;
+    DataValue *arrayList;
 
-  
     /* Ignore the focused window passed as the function argument and put
        the dialog up over the window which is executing the macro */
     window = MacroRunWindow();
@@ -3562,19 +3607,38 @@
       return False;
     }
 
+    /* now for the non-obligatory button label arguments */
+    argList += 2;
+    nArgs -= 2;
+
+    arrayList = NULL; /* pointer for converted array argument for buttons */
+
+    if (nArgs > 0 && argList[0].tag == ARRAY_TAG) {
+        arrayList = listArrayToArgList(&argList[0], 1, 0, &nArgs, errMsg);
+        if (nArgs < 0) {
+            return False;
+        }
+        if (arrayList == NULL) {
+            nArgs = 0; /* no button argumants */
+        } else {
+            argList = arrayList;
+        }
+    }
     /* check that all button labels can be read */
-    for (i=2; i<nArgs; i++)
-      if (!readStringArg(argList[i], &btnLabel, btnStorage, errMsg))
-          return False;
+    for (i = 0; i < nArgs; i++) {
+        if (!readStringArg(argList[i], &btnLabel, btnStorage, errMsg)) {
+            XtFree((String)arrayList);
+            return False;
+        }
+    }
 
     /* pick up the first button */
-    if (nArgs == 2) {
+    if (nArgs == 0) {
       btnLabel = "OK";
       nBtns = 1;
     }
     else {
-      nBtns = nArgs - 2;
-      argList += 2;
+      nBtns = nArgs;
       readStringArg(argList[0], &btnLabel, btnStorage, errMsg);
     }
 
@@ -3648,8 +3712,8 @@
     free(tmp);                /* don't need this anymore */
     nlines = n;
     if (nlines == 0) {
-      test_strings[0] = MKSTRING("");
-      nlines = 1;
+        test_strings[0] = MKSTRING("");
+        nlines = 1;
     }
 
     /* Create the selection box dialog widget and its dialog shell parent */
@@ -3661,7 +3725,7 @@
     XtSetArg(al[ac], XmNlistVisibleItemCount, (nlines > 10) ? 10 : nlines); ac++;
     XtSetArg(al[ac], XmNokLabelString, s2=XmStringCreateSimple(btnLabel)); ac++;
     dialog = CreateSelectionDialog(window->shell, "macroListDialog", al, ac);
-    if (2 == nArgs)
+    if (0 == nArgs)
     {
         /*  Only set margin width for the default OK button  */
         XtVaSetValues(XmSelectionBoxGetChild(dialog, XmDIALOG_OK_BUTTON),
@@ -3711,7 +3775,9 @@
       XtAddCallback(btn, XmNactivateCallback, listDialogBtnCB, window);
       XmStringFree(s1);
     }
-    
+
+    XtFree((String)arrayList); /* don't need the array copy anymore */
+
 #ifdef LESSTIF_VERSION
     /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle
        the escape key for closing the dialog. */
@@ -5783,3 +5849,59 @@
     *errMsg = "%s called with unknown object";
     return False;
 }
+
+/*
+** Read a macro "list" array, whose keys are based on integers in sequence, and
+** shallow-copy the data values to an argList-like array, allocated using
+** XtMalloc(). The indices must increase by one from the first, start, to the
+** last, at one less than end. If an index value is not a valid key, the
+** sequence stops. If end is less than start, the sequence is stopped at the
+** last key found. If no elements are found, returns NULL. If an error occurs,
+** *errMsg will be non-NULL and *len less than 0.
+*/
+static DataValue *listArrayToArgList(DataValue *dvArray, int start, int end,
+        int *len, char **errMsg)
+{
+    char stringStorage[TYPE_INT_STR_SIZE(int)];
+    int i, j, n;
+    DataValue dvEntry;
+    DataValue *argList;
+
+    *len = -1;
+
+    if (dvArray->tag != ARRAY_TAG) {
+        *errMsg = "%s expecting an array argument";
+        return NULL;
+    }
+
+    sprintf(stringStorage, "%d", n);
+    /* count the number of entries in the array */
+    n = 0;
+    for (i = start; i < end || end < start; ++i) {
+        sprintf(stringStorage, "%d", i);
+        if (!ArrayGet(dvArray, stringStorage, &dvEntry))
+            break;
+        ++n;
+    }
+    if (n == 0) {
+        return NULL; /* no such entries found - no problem really */
+    }
+
+    /* allocate result */
+    argList = (DataValue *)XtMalloc(n * sizeof (DataValue));
+    if (!argList) {
+        *errMsg = "%s failed to allocate an argument list from an array";
+        return NULL;
+    }
+
+    *len = n;
+    /* now copy in values */
+    j = 0;
+    for (i = start; i < end || end < start; ++i) {
+        sprintf(stringStorage, "%d", i);
+        if (!ArrayGet(dvArray, stringStorage, &argList[j]))
+            break;
+        ++j;
+    }
+    return argList;
+}
