new object $menu: $user_interfaces; var $dmi_data descriptions = #[]; var $has_commands local = #[]; var $has_commands remote = #[]; var $has_commands shortcuts = #[]; var $menu menu = 0; var $menu menu_choice = 0; var $root created_on = 866642150; var $root flags = ['variables, 'methods, 'code, 'core]; var $root inited = 1; var $root managed = [$menu]; var $root manager = $menu; public method .bar() { arg @text; var l, str; if (!text) { return "=============================================================================="; } else { str = ("=[" + (text[1])) + "]"; for l in [1 .. 75 - ((text[1]).length())] str = str + "="; return str; } }; public method .do_menu() { arg @menu; var l, str, options, title, prompt, temp, instr, helptext, accept_any, accept_skip, accept_exit, accept_numeric, accept_return, myline, mylist, reloop; if ((menu.length()) < 3) throw(~menu, "Menu must consist of title, prompt text, and at least one option."); // Obtain the title and prompt text: title = menu[1]; menu = menu.delete(1); prompt = menu[1]; menu = menu.delete(1); options = []; .tell(""); pause(); .menubar(title); for l in [1 .. menu.length()] { switch (type((menu[l]) || 'blank)) { case 'symbol: switch ((menu[l]) || 'blank) { case 'accept_any: // Accept any input accept_any = 1; case 'accept_numeric: // Accept numeric input accept_numeric = 1; case 'back: // .tell(" [-] Back Up"); options = [@options, "-"]; case 'blank: // Probably not needed. .tell(""); case 'exit: // .tell(" [X] Exit"); options = [@options, "X"]; accept_exit = 1; case 'help: // .tell(" [?] Help"); options = [@options, "?"]; case 'nothing: // Nothing; not even a blank line. case 'return: accept_return = 1; case 'skip: // .tell(" [+] Skip Set"); options = [@options, "+"]; accept_skip = 1; } case 'list: // One of the elements is a list. Split this // up as we do with normal strings, columnize // it, store the options, then columnize it. mylist = []; for myline in (menu[l]) { myline.replace("=> ", "=>"); temp = myline.explode("=>"); if ((temp.length()) == 1) { // It's just a string. Print it. // .tell(" "+temp[1]); mylist = mylist.add((temp[1]) + " "); } else { // It's a menu choice and txt. mylist = mylist.add(((("[" + ((temp[1]).uppercase())) + "] ") + (temp[2])) + " "); // .tell(" ["+temp[1].uppercase()+"] "+temp[2]); options = [@options, ((temp[1])[1]).uppercase()]; } } mylist = mylist.lcolumnize(); for myline in (mylist) .tell(" " + myline); case 'string: // Break things up into choice, text: (menu[l]).replace("=> ", "=>"); temp = (menu[l]).explode("=>"); if ((temp.length()) == 1) { // It's just a string. Print it. .tell(" " + (temp[1])); } else { // It's a menu choice and txt. switch (temp[1]) { case "?": // .tell(" ["+temp[1].uppercase()+"] Help"); // helptext=temp[2]; // options=[@options,temp[1][1].uppercase()]; default: // Print the choice and text: .tell(((" [" + ((temp[1]).uppercase())) + "] ") + (temp[2])); options = [@options, ((temp[1])[1]).uppercase()]; } } } } .menubar(); pause(); // .tell(""); reloop = 1; while (reloop == 1) { reloop = 0; .tell(""); .non_terminated_tell(prompt); instr = tostr(.prompt(" ")); if (((instr.length()) > 1) && ((instr[1]) == "~")) { // .prompt_off(); .tell(""); .menuline("Command Line: " + ((instr.subrange(2)).chop(40))); reloop = 1; ((.connections())[1]).parse_line(instr.subrange(2)); .tell(""); .menuline(); // .prompt_on(); } if (instr == "abort") { .tell("Aborted! Exiting menu."); throw(~abort, "Aborted! Exiting menu.", 'notraceback); } } if (helptext && (instr == "?")) { .atell("[help!]"); .atell(helptext); .prompt("Press Return> "); } // Yell at the user if a valid option is not selected: if ((!(accept_return && (instr == ""))) && ((!(accept_numeric && (instr.is_numeric()))) && ((!accept_any) && ((instr in options) == 0)))) { .tell(""); .tell(("Valid options are " + ([@options, "Abort"].to_english())) + "."); menu_choice = 'invalid; } // 'skip, 'exit and such can be specified in the menu call. If these // are included, accept_skip, accept_exit, etc. will be set to 1. If // the user then selects the approprate character, 'skip, 'exit, etc. // will be returned instead of the character. Why? I don't know. if (accept_skip && (instr == "+")) instr = 'skip; if (accept_exit && (instr == "X")) instr = 'exit; if (accept_return && (instr == "")) instr = 'return; menu_choice = instr; return instr; }; public method .do_menu_nice() { arg @args; var choicelist, menu, selections, title, prompt, x, choice, obj, meth, arguments, unassigned, letter; // Generate a nice menu from a list of choices; run the menu // and call all appropriate methods // Initialize lots of things choicelist = "abcdefghijklmnopqrstuvwyz0123456789"; menu = []; selections = #[]; unassigned = []; if ((filter x in (args) where (type(x) == 'list).length()) > (choicelist.length())) return .do_menu_split(@args); // Get the menu's title and prompt title = args[1]; prompt = args[2]; args = args.subrange(3); // Now parse the menu choices for x in (args) { switch (type(x)) { case 'string: // If it's a string, just copy it to the menu menu = menu + [x]; case 'list: // If it's not a string, we need to parse it // First, we get the choice choice = x[1]; // Now we get the method to be called if this // option is selected if (type(x[2]) == 'objnum) { obj = x[2]; x = x.subrange(3); } else { obj = sender(); x = x.subrange(2); } meth = x[1]; arguments = x.subrange(2); choice = ((choice.replace("$", "")).replace("'", "")).replace("_", " "); letter = (choice.replace(" ", ""))[1]; // Find the first letter of the choice. If it's already // in use, add this choice to an 'unassigned' list. // Otherwise, use that letter for this choice if (letter in choicelist) { choice = (letter + "=>") + choice; menu = menu + [choice]; choicelist = choicelist.replace(letter, ""); selections = selections.add(letter, [obj, meth, arguments]); } else { unassigned = unassigned + [[choice, obj, meth, arguments]]; } default: throw(~menu, "Bad menu parameter."); } } // Now go through the unassigned list and assign letters to all // remaining choices if possible for choice in (unassigned) { letter = choicelist[1]; menu = menu + [(letter + "=>") + (choice[1])]; selections = selections.add(letter, [choice[2], choice[3], choice[4]]); choicelist = choicelist.subrange(2); } // Ok, do the menu while (choice != 'exit) { choice = .do_menu(title, prompt, @menu, 'exit); if (choice && (choice != 'exit)) { meth = selections[choice]; (meth[1]).(meth[2])(@meth[3]); } } }; public method .do_menu_split() { arg @args; var x, choicelist, numgroups, pergroup, extra, title, prompt, len, arglen, menu, groups, choice; choicelist = "abcdefghijklmnopqrstuvwyz0123456789"; title = args[1]; prompt = args[2]; menu = []; groups = []; args = args.subrange(3); len = choicelist.length(); arglen = args.length(); numgroups = arglen / len; if ((arglen % len) != 0) numgroups++; pergroup = arglen / numgroups; extra = arglen % numgroups; for x in [1 .. numgroups] { menu = menu + [((tostr(x) + "=>") + (x.n_to_nth())) + " list of choices"]; if (extra) { groups = groups + [args.subrange(1, pergroup + 1)]; args = args.subrange(pergroup + 2); extra--; } else { groups = groups + [args.subrange(1, pergroup)]; args = args.subrange(pergroup + 1); } } while (choice != 'exit) { choice = .do_menu(title, prompt, @menu, 'exit); if (choice != 'exit) .do_menu_nice(title, prompt, @groups[toint(choice)]); } }; public method .menu() { return menu; // Returns the current (sub)-menu. }; public method .menu_choice() { return menu_choice; }; public method .menubar() { arg @text; var l, str; if (!text) { .tell("=============================================================================="); } else { str = ("=[" + (text[1])) + "]"; for l in [1 .. 75 - ((text[1]).length())] str = str + "="; .tell(str); } }; public method .menuchoice() { arg choice, text; .tell(((" [" + choice) + "] ") + text); }; public method .menuline() { arg @text; var l, str; if (!text) { .tell("------------------------------------------------------------------------------"); } else { str = ("=[`[34m" + (text[1])) + "`[37m]"; for l in [1 .. 75 - ((text[1]).length())] str = str + "-"; .tell(str); } }; public method .menutext() { arg text; if (!text) .tell(""); else .tell(" " + text); }; public method .set_menu() { arg val; menu = val; // Set the current (sub)-menu };