# ============================================================================== # array_utils.nm - Tony Balinski # # This file contains NEdit macro utility functions # # make_array_from_list # make_array # make_arraylist # make_arraylist1 # shift_arraylist # array_has_value # cat_arraylist # cat_arraylist1 # arrayval_5ask # array_ask_keys # array_ask_vals # array_select_entry # ============================================================================== NEDIT_require_macro_file("extensions.nm") # ============================================================================== # make_array_from_list(list_array[, start]): builds an array from the key-value # pairs from contiguous values in list_array, and returns it. The keys to # list_array must be consecutive numbers, starting with start (or zero). # There must be an even number of them. # ============================================================================== define make_array_from_list { list_array = $1 start = 0 if ($n_args > 1) start = $2 array = $empty_array # now add to array until we have no more elements in list_array[] for (i = start; i in list_array; i += 2) { key = list_array[i] val = list_array[i + 1] array[key] = val } return array } # ============================================================================== # make_array(key, value, ...): builds an array from the key-value pairs, and # returns it. If an odd number of arguments is given, does not return # anything, which should cause an error. # ============================================================================== define make_array { return make_array_from_list($args, 1) } # ============================================================================== # make_arraylist(value, ...): builds an array from the values, using numeric # indices starting at zero, and returns it. # ============================================================================== define make_arraylist { # prepare the array (results in an empty array if $args[] == 0) if ($args[] == 0) return $args array = $empty_array # copy $args[] to array[], starting at index 0 for (i = 0; i < $args[]; i++) { array[i] = $args[i + 1] } return array } # ============================================================================== # make_arraylist1(value, ...): builds an array from the values, using numeric # indices starting at one, and returns it. # ============================================================================== define make_arraylist1 { return $args } # ============================================================================== # shift_arraylist(offset, oldarray [, low, high]): builds an array from the # values in oldarray (accessed through numeric indices): the new array has # indices shifted by offset compared to the old, and if low and high # are given, only those new indices from low to high are kept. # ============================================================================== define shift_arraylist { offset = $1 oldarray = $2 NaN = ":" lo = NaN hi = NaN if ($n_args > 2) lo = $3 if ($n_args > 3) hi = $4 if (oldarray[] == 0) return oldarray array = $empty_array for (k in oldarray) { i = k + offset ok = ((lo == NaN) || (lo <= i)) && ((lo == NaN) || (i < hi)) if (ok) array[i] = oldarray[k] } return array } # ============================================================================== # array_has_value(array, value): determines whether array has an entry whose # value is that given. Returns an array of keys to the value, indexed by # number. This will be an empty array if value was not found. # ============================================================================== define array_has_value { array = $1 value = $2 res = make_array() i = 0 for (key in array) if (array[key] == value) res[i++] = key return res } # ============================================================================== # cat_arraylist(array [, separator]): concatenates a zero-based array to a # string, adding the separator (default "\n") between each piece. Note # that separators are not added after the last entry. This is the # "opposite" of split - the following identity is true: # # str == cat_arraylist(split(str, "\n"), "\n") # ============================================================================== define cat_arraylist { str = "" array = $1 separator = "\n" if ($n_args > 1) separator = $2 sep = "" for (i = 0; i in array; i++) { str = str sep array[i] sep = separator } return str } # ============================================================================== # cat_arraylist1(array [, separator]): concatenates a one-based array to a # string, adding the separator (default "\n") between each piece. Note # that separators are not added after the last entry. # ============================================================================== define cat_arraylist1 { str = "" array = $1 separator = "\n" if ($n_args > 1) separator = $2 sep = "" for (i = 1; i in array; i++) { str = str sep array[i] sep = separator } return str } # ============================================================================== # arrayval_5ask(prompt, len, more, string_dlg, buttons [, lines]): asks for an # array value from a choice of up to 5 (as buttons), and/or a line. # buttons must be indexed from 0 to len - 1, and len must be less than 5. # If lines is present list_dialogs will be used. If string_dlg is false, # dialog will be used instead of string_dialog and ["line"] will not be # set. Uses an array return value with # ["more"] "More" button pressed - ask for other ranges # ["null"] dismissed - take no further action # ["button"] the string for a button, or an entered string # ["line"] the selected line or typed text, if any # ["index"] the number of the button, if any # ============================================================================== define arrayval_5ask { prompt = $1 len = $2 more = $3 string_dlg = $4 buttons = $5 use_list = 0 lines = "" if ($n_args == 6) { use_list = 1 lines = $6 } c1 = "" c2 = "" c3 = "" c4 = "" c5 = "" if (len > 0) c1 = buttons[0] if (len > 1) c2 = buttons[1] if (len > 2) c3 = buttons[2] if (len > 3) c4 = buttons[3] if (len > 4) c5 = buttons[4] if (use_list) { if (len == 0) { c = "" res = 0 dismiss = 0 } else if (len == 1) c = list_dialog(prompt, lines, c1, "Cancel") else if (len == 2) c = list_dialog(prompt, lines, c1, c2, "Cancel") else if (len == 3) c = list_dialog(prompt, lines, c1, c2, c3, "Cancel") else if (len == 4) c = list_dialog(prompt, lines, c1, c2, c3, c4, "Cancel") else if (len == 5 && !more) c = list_dialog(prompt, lines, c1, c2, c3, c4, c5, "Cancel") else if (more) { len = 6 c = list_dialog(prompt, lines, c1, c2, c3, c4, c5, "More", "Cancel") } if (len > 0) { dismiss = len + 1 res = $list_dialog_button } } else if (string_dlg) { if (len == 0) c = string_dialog(prompt, "OK", "Cancel") else if (len == 1) c = string_dialog(prompt, c1, "OK", "Cancel") else if (len == 2) c = string_dialog(prompt, c1, c2, "OK", "Cancel") else if (len == 3) c = string_dialog(prompt, c1, c2, c3, "OK", "Cancel") else if (len == 4) c = string_dialog(prompt, c1, c2, c3, c4, "OK", "Cancel") else if (len == 5 && !more) c = string_dialog(prompt, c1, c2, c3, c4, c5, "OK", "Cancel") else if (more) { len = 6 c = string_dialog(prompt, c1, c2, c3, c4, c5, "More", "OK", "Cancel") } dismiss = len + 2 res = $string_dialog_button } else # string_dlg false - use dialog { c = "" if (len == 0) { res = 0 dismiss = 0 } else if (len == 1) res = dialog(prompt, c1, "Cancel") else if (len == 2) res = dialog(prompt, c1, c2, "Cancel") else if (len == 3) res = dialog(prompt, c1, c2, c3, "Cancel") else if (len == 4) res = dialog(prompt, c1, c2, c3, c4, "Cancel") else if (len == 5 && !more) res = dialog(prompt, c1, c2, c3, c4, c5, "Cancel") else if (more) { len = 6 res = dialog(prompt, c1, c2, c3, c4, c5, "More", "Cancel") } if (len > 0) dismiss = len + 1 } result = $empty_array if (len == res && len == 6) # More pressed result["more"] = "" else if (res == dismiss || res == 0) # Dismiss pressed result["null"] = "" else # we have a string and OK (or a name button) was pressed: analyse c { if (string_dlg || use_list) result["line"] = c result["index"] = res - 1 if ((res - 1) in buttons) result["button"] = buttons[res - 1] } return result } # ============================================================================== # array_ask_keys(prompt, array, [string_dlg [, lines]]): asks for an array key # from the ones in array, up to 5 at a time (as buttons), and/or a line. # If string_dlg is true, the line is the string result of a string dialog. # If lines is present list_dialogs will be used (even if string_dlg is # true) to retrieve the line value. Uses an array return value with # some of the following keys: # ["null"] dismissed: no other values - take no further action # ["key"] the string for a key - may not be present with # string_dlg true and "OK" pressed # ["line"] the selected line or typed text, if string_dlg or lines # used - may be empty # ============================================================================== define array_ask_keys { prompt = $1 array = $2 lines = "" use_list = 0 lines = "" string_dlg = 0 if ($n_args == 3) { string_dlg = $3 } if ($n_args >= 4) { use_list = 1 lines = $4 } buttons = $empty_array res = buttons i = 0 n = array[] if (!n && string_dlg) { line = string_dialog(prompt, "OK", "Cancel") if ($string_dialog_button == 1) res["line"] = line else res["null"] = "" return res } for (k in array) { buttons[i++] = k # add next key to buttons if (i == n) # no more keys in array: ask for buttons { if (use_list) res = arrayval_5ask(prompt, i, 0, string_dlg, buttons, lines) else res = arrayval_5ask(prompt, i, 0, string_dlg, buttons) } else if (i == 5) # button limit reached: ask { n -= 5 if (use_list) res = arrayval_5ask(prompt, i, 1, string_dlg, buttons, lines) else res = arrayval_5ask(prompt, i, 1, string_dlg, buttons) if ("more" in res) # more selected: carry on { delete buttons[] i = 0 delete res["more"] } else break # something selected: quit loop } } if ("null" in res) return res else { if ("button" in res) { res["key"] = res["button"] delete res["button"] } return res } } # ============================================================================== # array_ask_vals(prompt, array, [string_dlg [, lines]]): asks for an array value # from the ones in array, up to 5 at a time (as buttons), and/or a line. # The array must have numeric indices starting at 0. # If string_dlg is true, the line is the string result of a string dialog. # If lines is present list_dialogs will be used (even if string_dlg is # true) to retrieve the line value. Uses an array return value with # some of the following keys: # ["null"] dismissed: no other values - take no further action # ["key"] the string for a key - may not be present with # string_dlg true and "OK" pressed # ["value"] the key's value or typed in line # ["line"] the selected line or typed text, if string_dlg or lines # used - may be empty # ============================================================================== define array_ask_vals { prompt = $1 array = $2 lines = "" use_list = 0 lines = "" string_dlg = 0 if ($n_args == 3) { string_dlg = $3 } if ($n_args >= 4) { use_list = 1 lines = $4 } buttons = $empty_array keys = buttons res = buttons i = 0 n = array[] if (!n && string_dlg) { line = string_dialog(prompt, "OK", "Cancel") if ($string_dialog_button == 1) { res["line"] = line res["value"] = line } else res["null"] = "" return res } for (k in array) { buttons[i] = array[k] # add next key to buttons keys[i] = k ++i if (i == n) # no more keys in array: ask for buttons { if (use_list) res = arrayval_5ask(prompt, i, 0, string_dlg, buttons, lines) else res = arrayval_5ask(prompt, i, 0, string_dlg, buttons) } else if (i == 5) # button limit reached: ask { n -= 5 if (use_list) res = arrayval_5ask(prompt, i, 1, string_dlg, buttons, lines) else res = arrayval_5ask(prompt, i, 1, string_dlg, buttons) if ("more" in res) # more selected: carry on { delete buttons[] delete keys[] i = 0 delete res["more"] } else break # something selected: quit loop } } if ("null" in res) return res else { if ("button" in res) { res["key"] = keys[res["index"]] res["value"] = res["button"] delete res["button"] delete res["index"] } return res } } # ------------------------------------------------------------------------------ # array_select_entry_flatten_str(s [, len [, quot]]): returns s with all # newlines changed to '/' and tabs and space sequences changed to ' '. If # the result is longer than len + 3, remove everything after len and add # "..."). If the quot parameter is passed, its first character will be # added at either end of the string, not included in the length count, # before any added "...". # ------------------------------------------------------------------------------ define array_select_entry_flatten_str { len = 0 quot = "" if ($n_args >= 2) len = $2 if ($n_args >= 3) quot = substring(replace_in_string($3, "\\s*", "", "regex", "copy"), 0, 1) s = replace_in_string($1, "\\s*\n\\s*", "/", "regex", "copy") s = replace_in_string(s, "\\s+", " ", "regex", "copy") slen = length(s) if (len == 0) len = slen if (slen > len + 3) s = quot substring(s, 0, len) quot "..." else if (slen > 0) s = quot s quot return s } # ------------------------------------------------------------------------------ # array_select_entry_make_cols(s): for each line in s, if it contains a tab, # reformat it to line up everything following the tab on each such line. # ------------------------------------------------------------------------------ define array_select_entry_make_cols { s = $1 # measure max tab position in the string given list = split(s, "\n") maxpos = 0 maxstr = "" for (i = 0; i in list; i++) { pos = search_string(list[i], "\t", 0) if (maxpos < pos) { maxpos = pos maxstr = substring(list[i], 0, pos) } } # now reassemble the string using maxpos as a guide res = "" sep = "" for (i = 0; i in list; i++) { s = list[i] pos = search_string(s, "\t", 0) if (pos >= 0) { t = substring(s, 0, pos) u = substring(s, pos + 1) res = res sep ljust(t, maxstr) " " u } else res = res sep s sep = "\n" } return res } # ============================================================================== # array_select_entry(prompt, array [, button1, ... button7]): uses a # list_dialog() to ask the user to select an array entry (which must # contain only scalar values). Returns an array with the indices # ["key"] the string for a key - may not be present with # string_dlg true and "OK" pressed # ["value"] the key's value or typed in line # ["btnnum"] the button number # ["button"] the button text # The returned array is empty if no button was pressed. ["key"] and # ["value"] may not be present if no entry was selected. If no button was # given, only ["key"] and ["value"] may be returned. # ============================================================================== define array_select_entry { # pick up arguments if ($n_args > 9) array_select_entry_TooManyButtons() # cause failure prompt = $1 array = $2 b = $empty_array nbtns = $n_args - 2 for (i = 1; i <= nbtns; i++) b[i] = $args[i + 2] # set up result array res = $empty_array # build up list_dialog()'s list keys = $empty_array i = 0 nkeys = array[] lines = "" sep = "" if (nkeys == 0) lines = "" else { for (key in array) { ++i keys[i] = key val = array[key] if (valid_number(key)) skey = key else if (key == "") skey = "" else skey = array_select_entry_flatten_str(key, 20, "\"") if (valid_number(val)) sval = val else if (val == "") sval = "" else sval = array_select_entry_flatten_str(val, 50, "\"") lines = lines sep rjust_s(i, nkeys) ": " skey "\t" sval sep = "\n" } lines = array_select_entry_make_cols(lines) } # now call up the dialog if (nbtns == 0) s = list_dialog(prompt, lines, "OK") else if (nbtns == 1) s = list_dialog(prompt, lines, b[1]) else if (nbtns == 2) s = list_dialog(prompt, lines, b[1], b[2]) else if (nbtns == 3) s = list_dialog(prompt, lines, b[1], b[2], b[3]) else if (nbtns == 4) s = list_dialog(prompt, lines, b[1], b[2], b[3], b[4]) else if (nbtns == 5) s = list_dialog(prompt, lines, b[1], b[2], b[3], b[4], b[5]) else if (nbtns == 6) s = list_dialog(prompt, lines, b[1], b[2], b[3], b[4], b[5], b[6]) else # if (nbtns == 7) # already checked s = list_dialog(prompt, lines, b[1], b[2], b[3], b[4], b[5], b[6], b[7]) btn = $list_dialog_button # get the key number from the returned string s i = 0 if (nkeys > 0) i = number(s) if (i in keys) { key = keys[i] res["key"] = key res["value"] = array[key] } if (btn != 0 && btn <= nbtns) { res["btnnum"] = btn res["button"] = b[btn] } return res }