/
ColdCore-3.0a8/
ColdCore-3.0a8/src/
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
};