Provide support for behaviour keywords in list_dialog() The keywords, if any, can be supplied as individual string arguments to list_dialog(). They will be checked for validity until either an empty string is found or a non-keyword string is found, which is then used as the first button label. Currently the keywords are "single_sel", "multi_sel", "browse_sel" and "extend_sel" (specifying selection policy for the list; only one of these is allowed), and "string_entry" (whether a freeform string can be entered). If only one entry can be selected ("single_sel" and "browse_sel), this will be the text of the entered string (if "string_entry" is active and the string is not empty), or the value of the selected list line. If multiple selections can be made from the list, they are copied into the result, separated with newlines. Any non-empty string (if "string_entry" is active) will get added at the end in the same way. This patch is intended to replace the ListMultiSel patch, avoiding the creation of a new list_multisel_dialog() function. diff -ur nedit_official nedit_mod 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-29 18:14:46.984375000 +0200 @@ -3530,51 +3530,106 @@ int tabDist; Arg al[20]; int ac; + Boolean isMultiLine = False; + unsigned char selPolicy = isMultiLine ? XmMULTIPLE_SELECT : XmSINGLE_SELECT; + Boolean haveSelPolicy = False, done, skip, isErr; + Boolean isString = False, haveString = False; - /* Ignore the focused window passed as the function argument and put the dialog up over the window which is executing the macro */ window = MacroRunWindow(); cmdData = window->macroCmdData; - + /* Dialogs require macro to be suspended and interleaved with other macros. This subroutine can't be run if macro execution can't be interrupted */ if (!cmdData) { - *errMsg = "%s can't be called from non-suspendable context"; - return False; + *errMsg = "%s can't be called from non-suspendable context"; + return False; } /* Read and check the arguments. The first being the dialog message, and the rest being the button labels */ if (nArgs < 2) { - *errMsg = "%s subroutine called with no message, string or arguments"; - return False; + *errMsg = "%s subroutine called with no message, string or arguments"; + return False; } if (!readStringArg(argList[0], &message, stringStorage, errMsg)) - return False; + return False; if (!readStringArg(argList[1], &text, textStorage, errMsg)) - return False; + return False; if (!text || text[0] == '\0') { - *errMsg = "%s subroutine called with empty list data"; - return False; + *errMsg = "%s subroutine called with empty list data"; + return False; + } + + /* now for keywords and the non-obligatory button label arguments */ + argList += 2; + nArgs -= 2; + + /* check for keywords, advancing past each one recognised */ + while (nArgs) { + done = skip = False; + if (!readStringArg(argList[0], &btnLabel, btnStorage, errMsg)) { + return False; + } + /* test multiselection settings */ + isErr = False; + if (!strcmp(btnLabel, "single_sel") || !strcmp(btnLabel, "multi_sel") || + !strcmp(btnLabel, "extend_sel") || !strcmp(btnLabel, "browse_sel")){ + tmp = "%s call has multiple multiselection settings"; + isErr = haveSelPolicy; + switch (*btnLabel) { + case 's': selPolicy = XmSINGLE_SELECT; break; + case 'm': selPolicy = XmMULTIPLE_SELECT; break; + case 'e': selPolicy = XmEXTENDED_SELECT; break; + case 'b': selPolicy = XmBROWSE_SELECT; break; + default: + *errMsg = "%s call has invalid selection type setting"; + return False; + } + skip = haveSelPolicy = True; + } + else if (strcmp(btnLabel, "string_entry") == 0) { + tmp = "%s call has multiple string-entry settings"; + isErr = haveString; + isString = True; + skip = haveString = True; + } + else if (!skip && strcmp(btnLabel, "") == 0) { + done = skip = True; + } + else + done = True; /* not a recognised keyword */ + + if (isErr) { + *errMsg = tmp; + return False; + } + if (skip) { + ++argList; + --nArgs; + } + if (done) + break; } /* check that all button labels can be read */ - for (i=2; i 10) ? 10 : nlines); ac++; + XtSetArg(al[ac], XmNlistVisibleItemCount, (nlines > 25) ? 25 : 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), @@ -3684,16 +3739,18 @@ /* modify the list */ XtVaSetValues(XmSelectionBoxGetChild(dialog, XmDIALOG_LIST), - XmNselectionPolicy, XmSINGLE_SELECT, + XmNselectionPolicy, selPolicy, XmNuserData, (XtPointer)text_lines, NULL); + XtVaSetValues(dialog, XmNmustMatch, False, NULL); /* Unmanage unneeded widgets */ XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON)); XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON)); XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON)); - XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT)); XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL)); - + if (!isString) + XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT)); + /* Make callback for the unmanaged cancel button (which can still get executed via the esc key) activate close box action */ XtAddCallback(XmSelectionBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON), @@ -3711,7 +3768,7 @@ XtAddCallback(btn, XmNactivateCallback, listDialogBtnCB, window); XmStringFree(s1); } - + #ifdef LESSTIF_VERSION /* Workaround for Lesstif (e.g. v2.1 r0.93.18) that doesn't handle the escape key for closing the dialog. */ @@ -3743,39 +3800,78 @@ char *text; char **text_lines; int btnNum; - int n_sel, *seltable, sel_index = 0; + int n_sel, *seltable = NULL, sel_index = 0; Widget theList; size_t length; + unsigned char selPolicy; + Boolean isMultiLine; + Widget theString; + char *str_entry; /* shouldn't happen, but would crash if it did */ if (cmdData == NULL) return; + /* Return the string in the selection text area (if any) */ + theString = XmSelectionBoxGetChild(cmdData->dialog, XmDIALOG_TEXT); + str_entry = XtIsManaged(theString) ? XmTextGetString(theString) : NULL; + if (str_entry && str_entry[0] == '\0') { + XtFree(str_entry); + str_entry = NULL; + } + theList = XmSelectionBoxGetChild(cmdData->dialog, XmDIALOG_LIST); + /* single or multi? */ + XtVaGetValues(theList, XmNselectionPolicy, &selPolicy, NULL); + isMultiLine = (selPolicy != XmSINGLE_SELECT && + selPolicy != XmBROWSE_SELECT); /* Return the string selected in the selection list area */ XtVaGetValues(theList, XmNuserData, &text_lines, NULL); if (!XmListGetSelectedPos(theList, &seltable, &n_sel)) { - n_sel = 0; - } - else { - sel_index = seltable[0] - 1; - XtFree((XtPointer)seltable); + n_sel = -1; } - if (!n_sel) { - text = PERM_ALLOC_STR(""); - length = 0; + if (n_sel < 0 || (!isMultiLine && str_entry)) { + /* if we have no list selection, or we are in single selction mode, + and have a non-empty string from the dialog text, use that string */ + text = str_entry ? AllocStringCpy(str_entry) : PERM_ALLOC_STR(""); + length = strlen(text); + n_sel = 0; } else { - length = strlen((char *)text_lines[sel_index]); - text = AllocString(length + 1); - strcpy(text, text_lines[sel_index]); + /* count up the total size of the result, with any string_entry */ + int i; + size_t len; + char *cp; + length = 0; + for (i = 0; i < n_sel; i++) { + sel_index = seltable[i] - 1; + length += strlen((char *)text_lines[sel_index]) + 1; + } + if (str_entry) + length += strlen(str_entry) + 1; + /* allocate result */ + cp = text = AllocString(length); + /* and copy strings, separated by '\n' */ + for (i = 0; i < n_sel; i++) { + sel_index = seltable[i] - 1; + len = strlen((char *)text_lines[sel_index]); + strcpy(cp, text_lines[sel_index]); + cp += len; + *cp++ = '\n'; + } + if (str_entry) { + strcpy(cp, str_entry); + } + text[--length] = '\0'; } /* don't need text_lines anymore: free it */ for (sel_index = 0; text_lines[sel_index]; sel_index++) XtFree((XtPointer)text_lines[sel_index]); XtFree((XtPointer)text_lines); + XtFree((XtPointer)seltable); + XtFree((XtPointer)str_entry); retVal.tag = STRING_TAG; retVal.val.str.rep = text;