/* @(#)viewopts.c	(c) copyright	10/18/86 (Dan Heller) */

#include "mush.h"

struct viewopts {
    char *v_opt;
    char *v_prompt;
    char *v_description;
#ifdef SUNTOOL
    Panel_item v_choice;
    Panel_item v_text;
#endif /* SUNTOOL */
};

#ifdef SUNTOOL
short dat_cycle_cursor[] = {
    0x07C0, 0x0FE0, 0x1834, 0x301C, 0x601C, 0x203C, 0x0000, 0x0000,
    0x7808, 0x700C, 0x7018, 0x5830, 0x0FE0, 0x07C0, 0x0000, 0x0000

};
mpr_static(cycle,           16, 16, 1, dat_cycle_cursor);
#endif /* SUNTOOL */

/*
 * struct contains the option, a prompt if it has a string value, whether
 * or not it applies to non suntools, line mode, or both, and a
 * string describing what the option does. If the prompt string starts
 * with a minus sign, then the value can be set without a value. This
 * is there to indicate to option_line to print a toggle (cycle) pixrect
 * and to print TRUE/FALSE telling whether the value is on or off regardless
 * of it's "string" value.
 */
struct viewopts viewopts[] = {
    { "alwaysignore", NULL,
	"Always ignore the message headers on the 'ignored' list." },
    { "ask", NULL,
	"Prompts for a subject on outgoing mail." },
    { "askcc", NULL,
	"Ask for list of Carbon Copy recipients whenever sending mail." },
    { "autodelete", NULL,
	"Automatically delete ALL READ messages whenever you update mail." },
    { "autoedit", NULL,
	"Automatically enter editor for REPLIES only (not toolmode)." },
    { "autoinclude", NULL,
	"Include a copy of author's message each time you reply to mail." },
    { "autoprint", NULL,
	"Display the next message on the list when you delete a message." },
    { "auto_route", "-Host/Path:",
	"Remove redundant uucp addresses when replying to messages." },
    { "autosign", "-Filename:",
	"Add file (~/.signature if set but no value) at end of all letters." },
    { "autosign2", "Addr:File:",
	"Signature to use for specific addresses. \"addr, ... : <signature>\""},
    { "cdpath", "Path:",
	"Path to search for directories when the \"cd\" command is issued." },
    { "cmd_help", "Path:",
	"Location of the general help file for line and curses modes." },
    { "complete", "Character:",
	"The character typed to cause a word completion to occur." },
    { "compose_icon", "-Filename:",
	"Alternate pixmap to use when compose window is closed to an icon." },
    { "crt", "Lines:",
	"The number of lines a message must have for 'pager' to be invoked." },
    { "crt_win", "Lines:",
	"Lines in the tool mode text subwindow for paging messages." },
    { "curses_help", "-Commands:",
	"List of curses commands whose bindings appear in the help display." },
    { "date_received", NULL,
	"Time displayed for message headers shows date received (or sent)." },
    { "dead", "Filename:",
	"The name of the file to store dead mail (default = ~/dead.letter)." },
    { "domain_route", "-Host/Path:",
	"Cause short-circuiting of domain addresses when auto-routing." },
    { "dot", NULL,
	"Allow \".\" on a line by itself to send letter." },
    { "edit_hdrs", NULL,
	"Allow headers of messages to be edited using your editor." },
    { "editor", "Editor:",
	"Editor for message editing (default = env EDITOR or \"vi\")." },
    { "escape", "Character:",
	"Escape character for extended editing commands (default = ~)." },
    { "fignore", "Patterns:",
	"Filename extensions or patterns ignored in completions." },
    { "folder", "Pathname:",
	"Full pathname to the directory where personal folders are kept." },
    { "fortune", "-Flag:",
	"Add fortune to end of letters.  Flag to \"fortune\" is optional." },
    { "fortunates", "Users:",
	"Those who will receive fortunes if fortune is set (default: All)." },
    { "hdr_format", "Format:",
	"Formatting string for headers.  \"headers -?\" or help hdr_format." },
    { "history", "Number:",
	"How many commands to remember (like csh)." },
    { "hold", NULL,
	"Read but not deleted messages are saved in spool -- not mbox." },
    { "home", "Directory:",
	"The user's home directory." },
    { "hostname", "Hostname:",
	"User-definable name for the name of your machine." },
    { "ignore_bang", NULL,
	"Ignore '!' as a history reference.  Otherwise, escape by: \\!" },
    { "ignoreeof", "-Command:",
	"Ignores ^D as exit, or (if set), execute \"command\"." },
    { "indent_str", "String:",
	"String to offset included messages within your letters." },
    { "in_reply_to", "-String:",
	"When responding to mail, add In-Reply-To: to message headers." },
    { "keepsave", NULL,
	"Prevents messages from being marked as `deleted' when you `save'." },
    { "known_hosts", "Host list:",
	"List of hosts that your site is known to uucp mail to." },
    { "logfile", "Filename:",
	"Log outgoing mail headers only.  Message text not logged." },
    { "mail_icon", "Filename:",
	"Alternate pixmap to use when tool is closed to an icon." },
    { "mbox", "Filename:",
	"Filename to use instead of ~/mbox for default mailbox." },
    { "metoo", NULL,
	"When replying to mail, metoo preserves your name on mailing list." },
    { "mil_time", NULL,
	"24-hour military time format is used whenever a time is printed." },
    { "msg_win", "Lines:",
	"Number of lines in the message composition window for tool mode." },
    { "newline", "-Command:",
	"Ignore RETURN.  If set to a command, execute that command." },
    { "newmail_icon", "Filename:",
	"Alternate icon shown when new mail is available." },
    { "no_expand", NULL,
	"Prevents expansion of Mush aliases in outgoing mail headers." },
    { "no_hdrs", NULL,
	"If set, personalized headers are NOT inserted to outgoing mail." },
    { "no_reverse", NULL,
	"Disables reverse video in curses mode -- uses \"bold\" in tool mode."},
    { "nonobang", NULL,
	"Suppresses errors from unsuccessful history references." },
    { "nosave", NULL,
	"Prevents aborted mail from being saved in $dead." },
    { "output", NULL,
	"The message list produced as output of the last command." },
    { "pager", "Program:",
	"Program name to be used as a pager for messages longer than crt." },
    { "pre_indent_str", "String:",
	"String to precede message text interpolated into message body." },
    { "post_indent_str", "String:",
	"String to succeed message text interpolated into message body." },
    { "print_cmd", "Program:",
	"Alternate program to use to send messages to the printer." },
    { "printer", "Printer:",
	"Printer to send messages to (default = environment PRINTER)." },
    { "prompt", "String:",
	"Your prompt.  \"help prompt\" for more information." },
    { "quiet", "-Conditions:",
	"Turn off verbose messages and error bells in various conditions." },
    { "realname", "Name:",
	"Your real name." },
    { "record", "Filename:",
	"Save all outgoing mail in specified filename." },
    { "reply_to_hdr", "Headers:",
	"List of headers use to construct reply addresses from a message." },
    { "save_empty", NULL,
	"Folders which have all messages deleted are NOT removed on updates." },
    { "screen", "# of Headers:",
	"Number of headers to print in non-suntools (text) mode." },
    { "screen_win", "# of Headers:",
	"Set the size of the header window for the tool mode only." },
    { "show_deleted", NULL,
	"Show deleted messages in headers listings (unused in curses mode)." },
    { "show_hdrs", "Headers:",
	"When displaying a message, show list of \"headers\" only." },
    { "sendmail", "Program:",
	"Program to use to deliver mail instead of using the default."},
    { "sort", "-Option:",
	"Pre-sorting of messages on mush startup (set to valid sort option)." },
    { "squeeze", NULL,
	"When reading messages, squeeze all blank lines into one." },
    { "status", NULL,
	"The success or failure status of the most recent command." },
    { "thisfolder", "Folder:",
	"This read-only variable gives the current folder name." },
    { "tool_help", "Path:",
	"Location of the help file for tool mode."  },
    { "toplines", "Lines:",
	"Number of lines to print of a message for the 'top' command."  },
    { "tmpdir", "Directory:",
	"Directory to use for temporary files used by Mush." },
    { "unix", NULL,
	"Non-mush commands are considered to be UNIX commands." },
    { "verify", NULL,
	"Verify before acting in various situations, such as sending mail." },
    { "visual", "Visual editor:",
	"Visual editor for messages (default = $editor or env VISUAL)."},
    { "warning", NULL,
	"Print warning messages for non-fatal errors." },
    { "wrap", NULL,
	"After referencing last message, message pointer wraps to start." },
    { "wrapcolumn", "-Column to wrap [78]:",
	"Column at which to wrap lines when composing messages." },
};

#ifdef SUNTOOL

#define OPTIONS_PANEL_WIDTH	550

int set_value(), toggle_value(), help_opt();

Frame opts_frame;
Panel opts_panel;
Panel_item desc_msg;
Panel_item file_text_item;

static void
frame_done()
{
#ifdef SUN_4_0 /* SunOS 4.0+ */
    window_set(opts_frame, WIN_SHOW, FALSE, NULL);
#else /* SUN_4_0 */
    /* not enough fd's to keep it lying around for SunOS 3.X */
    window_destroy(opts_frame);
    opts_frame = (Frame) 0;
#endif /* SUN_4_0 */
}

static void
opts_help()
{
    help(0, "options", tool_help);
}

static void
opts_save_load(item)
Panel_item item;
{
    int (*func)() = (int (*)())panel_get(item, PANEL_CLIENT_DATA);
    int result;
    char buf[MAXPATHLEN];
    char *argv[3], *file = panel_get_value(file_text_item);

    if (!*file) {
	result = (*func)(0, DUBL_NULL);
	file = ".mushrc";
    } else {
	argv[1] = file;
	argv[2] = NULL;
	result = (*func)(2, argv);
    }
    switch (result) {
	case 0:
	    sprintf(buf, "%s %s",
		(func == source)? "Loaded options from" : "Saved options to",
		file);
	when -1:
	    sprintf(buf, "%s: %s", file, sys_errlist[errno]);
	when -2:
	    sprintf(buf, "%s is a directory.", file);
	when -3:
	    /* save_opts() returns -3 if user doesn't confirm overwrite */
	    strcpy(buf, "Save operation aborted.");
    }
    panel_set(desc_msg, PANEL_LABEL_STRING, buf, NULL);
}

static void
unset_opts()
{
    cmd_line("unset *", NULL);
}

static void
reset_opts()
{
    source(0, DUBL_NULL);
}

/*
 * Public routine which creates a subframe which contains two panels.
 * The first contains options for loading and saving options from a
 * file (text item) and so on... the second panel contains all the items
 * which correspond to each mush variable that exists.
 */
void
view_options()
{
    extern Notify_value fkey_interposer();
    register char *p;
    int count;

    if (opts_frame) {
	window_set(opts_frame, WIN_SHOW, TRUE, NULL);
	opts_panel_item(NULL);
	return;
    }
#ifdef SUN_3_5
    if (nopenfiles(0) < 3) {
	ok_box("Too many frames; close one first!\n");
	return;
    }
#endif /* SUN_3_5 */

    opts_frame = window_create(tool, FRAME,
	FRAME_DONE_PROC,	frame_done,
	FRAME_LABEL,		"Mush Options",
	FRAME_NO_CONFIRM,	TRUE,
	FRAME_SHOW_LABEL,	TRUE,
	WIN_WIDTH,		OPTIONS_PANEL_WIDTH,
	NULL);

    opts_panel = window_create(opts_frame, PANEL,
	WIN_WIDTH,		OPTIONS_PANEL_WIDTH,
	NULL);
    (void) notify_interpose_event_func(opts_panel,
	fkey_interposer, NOTIFY_SAFE);
    panel_create_item(opts_panel, PANEL_BUTTON,
	PANEL_LABEL_IMAGE,
	    panel_button_image(opts_panel, "Done", 4, mush_font),
	PANEL_NOTIFY_PROC,	frame_done,
	NULL);
    panel_create_item(opts_panel, PANEL_BUTTON,
	PANEL_LABEL_IMAGE,
	    panel_button_image(opts_panel, "Help", 4, mush_font),
	PANEL_NOTIFY_PROC,	opts_help,
	NULL);
    panel_create_item(opts_panel, PANEL_BUTTON,
	PANEL_LABEL_IMAGE,
	    panel_button_image(opts_panel, "Save", 4, mush_font),
	PANEL_NOTIFY_PROC,	opts_save_load,
	PANEL_CLIENT_DATA,	save_opts,
	NULL);
    panel_create_item(opts_panel, PANEL_BUTTON,
	PANEL_LABEL_IMAGE,
	    panel_button_image(opts_panel, "Load", 4, mush_font),
	PANEL_NOTIFY_PROC,	opts_save_load,
	PANEL_CLIENT_DATA,	source,
	NULL);
    panel_create_item(opts_panel, PANEL_BUTTON,
	PANEL_LABEL_IMAGE,
	    panel_button_image(opts_panel, "Clear", 5, mush_font),
	PANEL_NOTIFY_PROC,	unset_opts,
	NULL);
    panel_create_item(opts_panel, PANEL_BUTTON,
	PANEL_LABEL_IMAGE,
	    panel_button_image(opts_panel, "Restart", 7, mush_font),
	PANEL_NOTIFY_PROC,	reset_opts,
	NULL);
    file_text_item = panel_create_item(opts_panel, PANEL_TEXT,
	PANEL_LABEL_STRING,	"Save/Load File:",
	PANEL_VALUE_DISPLAY_LENGTH, 30,
	NULL);
    desc_msg = panel_create_item(opts_panel, PANEL_MESSAGE,
	PANEL_LABEL_STRING,	"Help Descriptions -- Click on Variable Name",
	NULL);
    window_fit_height(opts_panel);

    /* reuse opts_panel -- we don't need the other one */
    opts_panel = window_create(opts_frame, PANEL,
	WIN_BELOW,			opts_panel,
	WIN_X,				0,
	WIN_COLUMN_GAP,			120,
	WIN_TOP_MARGIN,			10,
	WIN_LEFT_MARGIN,		10,
	WIN_WIDTH,			OPTIONS_PANEL_WIDTH,
	PANEL_VERTICAL_SCROLLBAR,	scrollbar_create(NULL),
	NULL);
    (void) notify_interpose_event_func(opts_panel,
	fkey_interposer, NOTIFY_SAFE);

    for (count = 0; count < ArraySize(viewopts); count++) {
	panel_create_item(opts_panel, PANEL_MESSAGE,
	    PANEL_ITEM_X,	ATTR_COL(0),
	    PANEL_ITEM_Y,	ATTR_ROW(count),
	    PANEL_LABEL_STRING,	viewopts[count].v_opt,
	    PANEL_NOTIFY_PROC,	help_opt,
	    PANEL_CLIENT_DATA,	count,
	    NULL);

	if (!(p = viewopts[count].v_prompt) || *p == '-') {
	    if (p && *p)
		p++;
	    viewopts[count].v_choice = panel_create_item(opts_panel,
		PANEL_CHOICE,
		PANEL_LABEL_IMAGE,	&cycle,
		PANEL_LAYOUT,		PANEL_HORIZONTAL,
		PANEL_CHOICE_STRINGS,	"False", "True", NULL,
		PANEL_DISPLAY_LEVEL,	PANEL_CURRENT,
		PANEL_ITEM_X,		ATTR_COL(1),
		PANEL_ITEM_Y,		ATTR_ROW(count),
		PANEL_NOTIFY_PROC,	toggle_value,
		PANEL_CLIENT_DATA,	count,
		NULL);
	}
	if (p) {
	    viewopts[count].v_text = panel_create_item(opts_panel, PANEL_TEXT,
		PANEL_VALUE_DISPLAY_LENGTH,	10,
		PANEL_VALUE_UNDERLINED,		TRUE,
		PANEL_LABEL_STRING,		p,
		PANEL_ITEM_X,			ATTR_COL(2),
		PANEL_ITEM_Y,			ATTR_ROW(count),
		PANEL_NOTIFY_PROC,		set_value,
		PANEL_CLIENT_DATA,		count,
		NULL);
	}
    }
    /* set the panel items' values */
    opts_panel_item(NULL);

    window_set(opts_panel,
	WIN_HEIGHT,	400,
	WIN_FIT_HEIGHT,	0,
	NULL);
    window_set(opts_frame, WIN_SHOW, TRUE, NULL);
}

/*
 * Sets the items in the panels to reflect that variable's value.
 * If "var" is NULL, do it for all the items.
 */
void
opts_panel_item(var)
char *var;
{
    int count;
    char *value;

    if (!opts_frame)
	return;

    for (count = 0; count < ArraySize(viewopts); count++) {
	if (var && strcmp(var, viewopts[count].v_opt))
	    continue;
	value = do_set(set_options, viewopts[count].v_opt);

	if (!viewopts[count].v_prompt || *viewopts[count].v_prompt == '-')
	    panel_set_value(viewopts[count].v_choice, value != NULL);
	if (viewopts[count].v_prompt)
	    panel_set_value(viewopts[count].v_text, value? value : "");
	if (var)
	    break;
    }
}

/*
 * Callback for choice items -- for variables that have boolean settings.
 * CLIENT_DATA is the index in the viewopts array.
 */
static
toggle_value(item, value)
Panel_item item;
int value;
{
    int count = (int) panel_get(item, PANEL_CLIENT_DATA);
    char *p, *argv[4];
    char *text_value = NULL;

    if (check_internal(viewopts[count].v_opt)) {
	panel_set(desc_msg, PANEL_LABEL_STRING,
	    "This is an internal variable which cannot be changed.",
	    NULL);
	/* can't change it - restore previous setting */
	panel_set_value(viewopts[count].v_choice, !value);
	return -1;
    }

    if (p = viewopts[count].v_prompt) /* set equal */
	text_value = panel_get_value(viewopts[count].v_text);

    if (!value) {
	if (un_set(&set_options, viewopts[count].v_opt) == -1) {
	    /* can't change it - restore previous setting */
	    panel_set_value(viewopts[count].v_choice, !value);
	    return -1;
	}
    } else {
	/* Turn it on if it's entirely boolean or bool/str, but no str value */
	if (!p || text_value && !*text_value) {
	    argv[0] = viewopts[count].v_opt; /* it's a boolean */
	    argv[1] = NULL;
	} else {
	    /* string value -- determine the text from the typed in value */
	    argv[0] = viewopts[count].v_opt;
	    argv[1] = "=";
	    argv[2] = text_value;
	    argv[3] = NULL;
	}
	if (add_option(&set_options, argv) != 1) {
	    /* can't change it - restore previous setting */
	    panel_set_value(viewopts[count].v_choice, !value);
	    return -1;
	}
    }

    if (!strcmp(viewopts[count].v_opt, "no_reverse") ||
	!strcmp(viewopts[count].v_opt, "show_deleted"))
	do_hdrs(0, DUBL_NULL, NULL);

    return 0;
}

/* callback for text items -- set vars to the string typed. */
static
set_value(item, event)
Panel_item item;
Event *event;
{
    int count = (int)panel_get(item, PANEL_CLIENT_DATA);
    char *p, *argv[4], *value;

    if (event_id(event) == '\t')
	return (int) PANEL_NEXT;

    p = viewopts[count].v_prompt;
    value = panel_get_value(item);

    if (check_internal(viewopts[count].v_opt)) {
	panel_set(desc_msg, PANEL_LABEL_STRING,
	    "This is an internal variable which cannot be changed.",
	    NULL);
	return (int) PANEL_NONE;
    }

    /*
     * You can "unset" string-only values by entering a blank string.
     * If the "prompt" starts with a -, then you can only "unset" the
     * variable by setting the associated choice item to false.
     */
    if (*p != '-' && !*value) {
	(void) un_set(&set_options, viewopts[count].v_opt);
	return (int) PANEL_NONE; /* do not advance caret */
    }
    /* Turn it on, but not to a value */
    if (!*value) {
	argv[0] = viewopts[count].v_opt; /* it's a boolean */
	argv[1] = NULL;
    } else {
	/* string value -- determine the text from the typed in value */
	argv[0] = viewopts[count].v_opt;
	argv[1] = "=";
	argv[2] = value;
	argv[3] = NULL;
    }

    if (add_option(&set_options, argv) == 1 && p && *p == '-')
	panel_set_value(viewopts[count].v_choice, TRUE);

    return (int) PANEL_NONE;
}

/* when user clicks on variable label itself */
static
help_opt(item, event)
Panel_item item;
Event *event;
{
    int count = (int)panel_get(item, PANEL_CLIENT_DATA);

    panel_set(desc_msg,
	PANEL_LABEL_STRING, viewopts[count].v_description,
	NULL);
    return 0;
}

#endif /* SUNTOOL */

/*
 * return a string describing a variable.
 * parameters: count, str, buf.
 * If str != NULL, check str against ALL variables
 * in viewopts array.  The one that matches, set count to it and 
 * print up all the stuff from the viewopts[count] into the buffer
 * space in "buf" and return it.
 */
char *
variable_stuff(count, str, buf)
register char *str, *buf;
{
    if (str)
	for (count = 0; count < ArraySize(viewopts); count++)
	    if (!strcmp(str, viewopts[count].v_opt))
		break;
    if (count >= ArraySize(viewopts)) {
	(void) sprintf(buf, "%s: Not a default %s variable.",
			   str? str : itoa(count), prog_name);
	return NULL;
    }
    return sprintf(buf, "%s: %s",
	viewopts[count].v_opt, viewopts[count].v_description);
}
