/* Widget based utility functions.
   Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
   2005, 2006, 2007 Free Software Foundation, Inc.
   
   Authors: 1994, 1995, 1996 Miguel de Icaza
            1994, 1995 Radek Doulik
	    1995  Jakub Jelinek
	    1995  Andrej Borsenkow

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

 */

/** \file wtools.c
 *  \brief Source: widget based utility functions
 */

#include <config.h>

#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include "global.h"
#include "tty.h"
#include "color.h"		/* dialog_colors */
#include "dialog.h"
#include "widget.h"
#include "wtools.h"
#include "key.h"		/* mi_getch() */
#include "background.h"		/* parent_call */
#include "strutil.h"


Listbox *
create_listbox_window_delta (int delta_x, int delta_y, int cols, int lines, const char *title, const char *help)
{
    int xpos, ypos, len;
    Listbox *listbox = g_new (Listbox, 1);
    const char *cancel_string = _("&Cancel");

    /* Adjust sizes */
    lines = (lines > LINES - 6) ? LINES - 6 : lines;

    if (title && (cols < (len = str_term_width1 (title) + 2)))
	cols = len;

    /* no &, but 4 spaces around button for brackets and such */
    if (cols < (len = str_term_width1 (cancel_string) + 3))
	cols = len;

    cols = cols > COLS - 6 ? COLS - 6 : cols;
    xpos = (COLS - cols + delta_x) / 2;
    ypos = (LINES - lines + delta_y) / 2 - 2;
    /* Create components */
    listbox->dlg =
	create_dlg (ypos, xpos, lines + 6, cols + 4, dialog_colors, NULL,
		    help, title, DLG_REVERSE);

    listbox->list = listbox_new (2, 2, lines, cols, NULL);

    add_widget (listbox->dlg,
		button_new (lines + 3, (cols / 2 + 2) - len / 2, B_CANCEL,
			    NORMAL_BUTTON, cancel_string, 0));
    add_widget (listbox->dlg, listbox->list);

    return listbox;
}

Listbox *
create_listbox_window (int cols, int lines, const char *title, const char *help)
{
    return create_listbox_window_delta (0, 0, cols, lines, title, help);
}

/* Returns the number of the item selected */
int run_listbox (Listbox *l)
{
    int val;
    
    run_dlg (l->dlg);
    if (l->dlg->ret_value == B_CANCEL)
	val = -1;
    else
	val = l->list->pos;
    destroy_dlg (l->dlg);
    g_free (l);
    return val;
}


static Dlg_head *last_query_dlg;

static int sel_pos = 0;

/* Used to ask questions to the user */
int
query_dialog (const char *header, const char *text, int flags, int count, ...)
{
    va_list ap;
    Dlg_head *query_dlg;
    WButton *button;
    WButton *defbutton = NULL;
    int win_len = 0;
    int i;
    int result = -1;
    int xpos, ypos;
    int cols, lines;
    char *cur_name;
    static const int *query_colors;

    /* set dialog colors */
    if (flags & D_ERROR)
	query_colors = alarm_colors;
    else
	query_colors = dialog_colors;

    if (header == MSG_ERROR)
	header = _("Error");

    if (count > 0) {
	va_start (ap, count);
	for (i = 0; i < count; i++) {
	    char *cp = va_arg (ap, char *);
	    win_len += str_term_width1 (cp) + 6;
	    if (strchr (cp, '&') != NULL)
		win_len--;
	}
	va_end (ap);
    }

    /* count coordinates */
    str_msg_term_size (text, &lines, &cols);
    cols = 6 + max (win_len, max (str_term_width1 (header), cols));
    lines += 4 + (count > 0 ? 2 : 0);
    xpos = COLS / 2 - cols / 2;
    ypos = LINES / 3 - (lines - 3) / 2;

    /* prepare dialog */
    query_dlg =
	create_dlg (ypos, xpos, lines, cols, query_colors, NULL,
		    "[QueryBox]", header, DLG_NONE);

    if (count > 0) {
	cols = (cols - win_len - 2) / 2 + 2;
	va_start (ap, count);
	for (i = 0; i < count; i++) {
	    cur_name = va_arg (ap, char *);
	    xpos = str_term_width1 (cur_name) + 6;
	    if (strchr (cur_name, '&') != NULL)
		xpos--;

	    button =
		button_new (lines - 3, cols, B_USER + i, NORMAL_BUTTON,
			    cur_name, 0);
	    add_widget (query_dlg, button);
	    cols += xpos;
	    if (i == sel_pos)
		defbutton = button;
	}
	va_end (ap);

	add_widget (query_dlg, label_new (2, 3, text));

	if (defbutton)
	    dlg_select_widget (defbutton);

	/* run dialog and make result */
	run_dlg (query_dlg);
	switch (query_dlg->ret_value) {
	case B_CANCEL:
	    break;
	default:
	    result = query_dlg->ret_value - B_USER;
	}

	/* free used memory */
	destroy_dlg (query_dlg);
    } else {
	add_widget (query_dlg, label_new (2, 3, text));
	add_widget (query_dlg,
		    button_new (0, 0, 0, HIDDEN_BUTTON, "-", 0));
	last_query_dlg = query_dlg;
    }
    sel_pos = 0;
    return result;
}

void query_set_sel (int new_sel)
{
    sel_pos = new_sel;
}


/* Create message dialog */
static struct Dlg_head *
do_create_message (int flags, const char *title, const char *text)
{
    char *p;
    Dlg_head *d;

    /* Add empty lines before and after the message */
    p = g_strconcat ("\n", text, "\n", (char *) NULL);
    query_dialog (title, p, flags, 0);
    d = last_query_dlg;
    init_dlg (d);
    g_free (p);

    return d;
}


/*
 * Create message dialog.  The caller must call dlg_run_done() and
 * destroy_dlg() to dismiss it.  Not safe to call from background.
 */
struct Dlg_head *
create_message (int flags, const char *title, const char *text, ...)
{
    va_list args;
    Dlg_head *d;
    char *p;

    va_start (args, text);
    p = g_strdup_vprintf (text, args);
    va_end (args);

    d = do_create_message (flags, title, p);
    g_free (p);

    return d;
}


/*
 * Show message dialog.  Dismiss it when any key is pressed.
 * Not safe to call from background.
 */
static void
fg_message (int flags, const char *title, const char *text)
{
    Dlg_head *d;

    d = do_create_message (flags, title, text);
    mi_getch ();
    dlg_run_done (d);
    destroy_dlg (d);
}


/* Show message box from background */
#ifdef WITH_BACKGROUND
static void
bg_message (int dummy, int *flags, char *title, const char *text)
{
    (void) dummy;
    title = g_strconcat (_("Background process:"), " ", title, (char *) NULL);
    fg_message (*flags, title, text);
    g_free (title);
}
#endif				/* WITH_BACKGROUND */


/* Show message box, background safe */
void
message (int flags, const char *title, const char *text, ...)
{
    char *p;
    va_list ap;
    union {
	void *p;
	void (*f) (int, int *, char *, const char *);
    } func;

    va_start (ap, text);
    p = g_strdup_vprintf (text, ap);
    va_end (ap);

    if (title == MSG_ERROR)
	title = _("Error");

#ifdef WITH_BACKGROUND
    if (we_are_background) {
	func.f = bg_message;
	parent_call (func.p, NULL, 3, sizeof (flags), &flags,
		     strlen (title), title, strlen (p), p);
    } else
#endif				/* WITH_BACKGROUND */
	fg_message (flags, title, p);

    g_free (p);
}


/* {{{ Quick dialog routines */

#define I18N(x) (do_int && *x ? (x = _(x)): x)

int
quick_dialog_skip (QuickDialog *qd, int nskip)
{
    Dlg_head *dd;

    WRadio *r;
    int xpos;
    int ypos;
    int return_val;
    WInput *input;
    QuickWidget *qw;
    int do_int;

    if (!qd->i18n) {
	qd->i18n = 1;
	do_int = 1;
	if (*qd->title)
	    qd->title = _(qd->title);
    } else
	do_int = 0;

    if (qd->xpos == -1)
	dd = create_dlg (0, 0, qd->ylen, qd->xlen, dialog_colors, NULL,
			 qd->help, qd->title,
			 DLG_CENTER | DLG_TRYUP | DLG_REVERSE);
    else
	dd = create_dlg (qd->ypos, qd->xpos, qd->ylen, qd->xlen,
			 dialog_colors, NULL, qd->help, qd->title,
			 DLG_REVERSE);

    for (qw = qd->widgets; qw->widget_type; qw++) {
	xpos = (qd->xlen * qw->relative_x) / qw->x_divisions;
	ypos = (qd->ylen * qw->relative_y) / qw->y_divisions;

	switch (qw->widget_type) {
	case quick_checkbox:
	    qw->widget = (Widget *)check_new (ypos, xpos, *qw->result, I18N (qw->text));
	    break;

	case quick_radio:
	    r = radio_new (ypos, xpos, qw->hotkey_pos, const_cast(const char **, qw->str_result));
	    r->pos = r->sel = qw->value;
	    qw->widget = (Widget *)r;
	    break;

	case quick_button:
	    qw->widget = (Widget *)
		button_new (ypos, xpos, qw->value,
			    (qw->value ==
			     B_ENTER) ? DEFPUSH_BUTTON : NORMAL_BUTTON,
			    I18N (qw->text), (bcback) qw->cb);
	    break;

	    /* We use the hotkey pos as the field length */
	case quick_input:
	    input =
		input_new (ypos, xpos, INPUT_COLOR, qw->hotkey_pos,
			   qw->text, qw->histname, INPUT_COMPLETE_DEFAULT);
	    input->is_password = qw->value == 1;
	    input->point = 0;
	    if (qw->value & 2)
		input->completion_flags |= INPUT_COMPLETE_CD;
	    qw->widget = (Widget *)input;
	    break;

	case quick_label:
	    qw->widget = (Widget *)label_new (ypos, xpos, I18N (qw->text));
	    break;

	default:
	    qw->widget = 0;
	    fprintf (stderr, "QuickWidget: unknown widget type\n");
	    break;
	}
	add_widget (dd, qw->widget);
    }

    while (nskip--)
	dd->current = dd->current->next;

    run_dlg (dd);

    /* Get the data if we found something interesting */
    if (dd->ret_value != B_CANCEL) {
	for (qw = qd->widgets; qw->widget_type; qw++) {
	    Widget *w = qw->widget;

	    switch (qw->widget_type) {
	    case quick_checkbox:
		*qw->result = ((WCheck *) w)->state & C_BOOL;
		break;

	    case quick_radio:
		*qw->result = ((WRadio *) w)->sel;
		break;

	    case quick_input:
		if (qw->value & 2)
		    *qw->str_result =
			tilde_expand (((WInput *) w)->buffer);
		else
		    *qw->str_result = g_strdup (((WInput *) w)->buffer);
		break;
	    }
	}
    }
    return_val = dd->ret_value;
    destroy_dlg (dd);

    return return_val;
}

int quick_dialog (QuickDialog *qd)
{
    return quick_dialog_skip (qd, 0);
}

/* }}} */

/* {{{ Input routines */

#define INPUT_INDEX 2

/*
 * Show dialog, not background safe.
 *
 * If the arguments "header" and "text" should be translated,
 * that MUST be done by the caller of fg_input_dialog_help().
 *
 * The argument "history_name" holds the name of a section
 * in the history file. Data entered in the input field of
 * the dialog box will be stored there.
 * 
 */
static char *
fg_input_dialog_help (const char *header, const char *text, const char *help,
		      const char *history_name, const char *def_text)
{
    QuickDialog Quick_input;
    QuickWidget quick_widgets[] = {
	{quick_button, 6, 10, 1, 0, N_("&Cancel"), 0, B_CANCEL, 0, 0,
	 NULL, NULL, NULL},
	{quick_button, 3, 10, 1, 0, N_("&OK"), 0, B_ENTER, 0, 0, NULL, NULL, NULL},
	{quick_input, 4, 80, 0, 0, "", 58, 0, 0, 0, NULL, NULL, NULL},
	{quick_label, 4, 80, 2, 0, "", 0, 0, 0, 0, NULL, NULL, NULL},
	NULL_QuickWidget
    };

    int len;
    int i;
    int lines, cols;
    int ret;
    char *my_str;
    char histname[64] = "inp|";
    char *p_text;

    if (history_name != NULL && *history_name != '\0') {
	g_strlcpy (histname + 3, history_name, 61);
	quick_widgets[2].histname = histname;
    }

    msglen (text, &lines, &cols);
    len = max (str_term_width1 (header), cols) + 4;
    len = max (len, 64);

    /* The special value of def_text is used to identify password boxes
       and hide characters with "*".  Don't save passwords in history! */
    if (def_text == INPUT_PASSWORD) {
	quick_widgets[INPUT_INDEX].value = 1;
	histname[3] = 0;
	def_text = "";
    } else {
	quick_widgets[INPUT_INDEX].value = 0;
    }

#ifdef ENABLE_NLS
    /* 
     * An attempt to place buttons symmetrically, based on actual i18n
     * length of the string. It looks nicer with i18n (IMO) - alex
     */
    quick_widgets[0].text = _(quick_widgets[0].text);
    quick_widgets[1].text = _(quick_widgets[1].text);
    quick_widgets[0].relative_x = len / 2 + 4;
    quick_widgets[1].relative_x =
	len / 2 - (str_term_width1 (quick_widgets[1].text) + 9);
    quick_widgets[0].x_divisions = quick_widgets[1].x_divisions = len;
#endif				/* ENABLE_NLS */

    Quick_input.xlen = len;
    Quick_input.xpos = -1;
    Quick_input.title = header;
    Quick_input.help = help;
    Quick_input.i18n = 1; /* The dialog is already translated. */
    p_text = g_strstrip (g_strdup (text));
    quick_widgets[INPUT_INDEX + 1].text = p_text;
    quick_widgets[INPUT_INDEX].text = def_text;

    for (i = 0; i < 4; i++)
	quick_widgets[i].y_divisions = lines + 6;
    Quick_input.ylen = lines + 6;

    for (i = 0; i < 3; i++)
	quick_widgets[i].relative_y += 2 + lines;

    quick_widgets[INPUT_INDEX].str_result = &my_str;

    Quick_input.widgets = quick_widgets;
    ret = quick_dialog (&Quick_input);
    g_free (p_text);

    if (ret != B_CANCEL) {
	return my_str;
    } else
	return 0;
}

/*
 * Show input dialog, background safe.
 *
 * If the arguments "header" and "text" should be translated,
 * that MUST be done by the caller of these wrappers.
 */
char *
input_dialog_help (const char *header, const char *text, const char *help,
		   const char *history_name, const char *def_text)
{
    union {
	void *p;
	char * (*f) (const char *, const char *, const char *,
		      const char *, const char *);
    } func;
#ifdef WITH_BACKGROUND
    if (we_are_background)
    {
	func.f = fg_input_dialog_help;
	return parent_call_string (func.p, 5,
				   strlen (header), header, strlen (text),
				   text, strlen (help), help,
				   strlen (history_name), history_name,
				   strlen (def_text), def_text);
    }
    else
#endif				/* WITH_BACKGROUND */
	return fg_input_dialog_help (header, text, help, history_name, def_text);
}

/* Show input dialog with default help, background safe */
char *input_dialog (const char *header, const char *text,
		    const char *history_name, const char *def_text)
{
    return input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text);
}

char *
input_expand_dialog (const char *header, const char *text, 
		     const char *history_name, const char *def_text)
{
    char *result;
    char *expanded;

    result = input_dialog (header, text, history_name, def_text);
    if (result) {
	expanded = tilde_expand (result);
	g_free (result);
	return expanded;
    }
    return result;
}

/* }}} */

/* }}} */
/*
  Cause emacs to enter folding mode for this file:
  Local variables:
  end:
*/
