/* Widgets for the Midnight Commander, Tk interface code
   Copyright (C) 1994 Radek Doulik
   Copyright (C) 1994, 1995 Miguel de Icaza
   Copyright (C) 1995 Jakub Jelinek

   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., 675 Mass Ave, Cambridge, MA 02139, USA.

   TODO: Destroy all the command names that are created for each
   widget during runtime.

 */
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "tkmain.h"
#include "dlg.h"

static char
widget_number (Widget *w)
{
    Dlg_head *h = w->parent;
    const int count = h->count;
    Widget_Item *item;
    int i;

    item = h->first;
    for (i = 0; i < count; i++){
	if (item->widget == w)
	    return i;
	item = item->next;
    }
    /* Actually, if we get this far, this message should not appear */
    fprintf (stderr, "Inconsistent widget\n");
    exit (1);
}

/* Returns a new command prefixed with an x:
   x.query.$the_frame.b0

   for the command of the query dialog box button 0

   This allow us to control the widget with the command:
   .query.$the_frame.b0
   
   and to access the C code callback with the:
   x.query.$the_frame.b0
   command.
*/
char *
tk_new_command (widget_data parent, void *widget,
		       tcl_callback callback, char id)
{
    char buffer [12];
    Widget *w = (Widget *) widget;
    char *cmd, *format; 

    if (((char *) parent) [1] == 0)
	format = "%s%c%d";
    else
	format = ".%s%c%d";

    /* wdata holds the frame information, it will be replaced with the
     * the widget command name
     */
    sprintf (buffer, format, w->frame, id, widget_number (w));
    cmd = copy_strings ("x", (char *)parent, buffer, 0);
    w->wdata = (widget_data) cmd;

    if (callback)
	Tcl_CreateCommand (interp, cmd, callback, w, NULL);
    return cmd;
}


/* Button routines */

/* Called from the Tcl/Tk program when a button has been pressed */
static int
tk_button_callback (ClientData cd, Tcl_Interp *interp, int argc, char *argv [])
{
    WButton *b  = (WButton *) cd;
    Dlg_head *h = (Dlg_head *) b->widget.parent;
    int stop = 0;
    
    if (b->callback)
	stop = (*b->callback)(b->action, b->callback_data);
    if (!b->callback || stop){
	h->running = 0;
	h->ret_value = b->action;
    }
    return TCL_OK;
}

/* Removes the [ and ] from the text mode button version */

static char *
clean_button (char *text)
{
    static char buffer [20];
    char *p, *q;

    if ((strlen (text) + 1) >= sizeof (buffer)){
	return text;
    }
    strcpy (buffer, text);
    for (p = buffer; *p == ' ' || *p == '['; p++)
	;
    for (q = buffer + strlen (buffer); q > buffer && 
        (!*q || *q == ' ' || *q == ']'); q--);
    *(++q) = 0;
    
    return p;
}
			   
void
x_button_set (WButton *b, char *text)
{
    char *cmd = wtcl_cmd (b->widget);
    
    tk_evalf ("%s configure -text {%s}", cmd+1, clean_button (text));
}

/* Creates the button $parent.$newname and executes $newname command */
int
x_create_button (Dlg_head *h, widget_data parent, WButton *b)
{
    char *cmd;
    
    cmd = tk_new_command (parent, b, tk_button_callback, 'b');
    tk_evalf ("newbutton %s %s {%s}", cmd+1, cmd, clean_button (b->text));
    return 1;
}



/* Radio button routines */
static int
tk_radio_callback (ClientData cd, Tcl_Interp *interp, int argc, char *argv [])
{
    WRadio   *r = (WRadio *) cd;

    if (STREQ (argv [1], "select")){
	r->pos = r->sel = atoi (argv [2]);
    }
    (r->widget.callback)(r->widget.parent, r, WIDGET_KEY, ' ');
    return TCL_OK;
}

int
x_create_radio (Dlg_head *h, widget_data parent, WRadio *r)
{
    int i;
    char *cmd;

    cmd = tk_new_command (parent, r, tk_radio_callback, 'r');
    tk_evalf ("newradio %s", cmd+1);
    
    for (i = 0; i < r->count; i++){
	tk_evalf ("radio_item %d {%s} %s %d", i, r->texts [i], cmd,
	    r->sel == i);
    }
    return 1;
}

/* Checkbuttons routines */

static int
tk_check_callback (ClientData cd, Tcl_Interp *interp, int argc, char *argv [])
{
    WCheck   *c = (WCheck *) cd;
    Dlg_head *h = c->widget.parent;

    (*c->widget.callback)(h, c, WIDGET_KEY, ' ');
    return TCL_OK;
}

int
x_create_check (Dlg_head *h, widget_data parent, WCheck *c)
{
    char *cmd;

    cmd = tk_new_command (parent, c, tk_check_callback, 'c');
    tk_evalf ("newcheck %s %s {%s} %d", cmd+1, cmd, c->text, c->state);

    return 1;
}


/* Input line */

/* This callback command accepts the following commands:
** 
** $win mouse x-position
** $win setmark x-position
*/
static int
tk_input_callback (ClientData cd, Tcl_Interp *interp, int argc, char *av [])
{
    Gpm_Event e;
    WInput *in = (WInput *) cd;
    
    if (strcmp (av [1], "mouse") == 0){
	e.x = atoi (av [2]) + 1;
	e.y = 0;
	e.type = GPM_DOWN;
	input_event (&e, (WInput *) cd);
	return TCL_OK;
    } else if (strcmp (av [1], "setmark") == 0){
	in->mark = in->point;
	return TCL_OK;
    }
    
    return TCL_OK;
}

void
tk_update_input (WInput *in)
{
    char *name = wtcl_cmd (in->widget);
    int  p;
    
    if (!name)
	return;

    name++;
    if (in->is_password){
	tk_evalf ("%s delete 0 end", name);
	tk_evalf ("%s insert end {*secret*}", name);
	p = 9;
    } else {
	tk_evalf ("entry_save_sel %s", name);
	if (in->inserted_one && isascii (in->inserted_one) && in->inserted_one > ' '){
	    tk_evalf ("%s insert insert {%c}", name, in->inserted_one);
	    in->inserted_one = 0;
	} else {
	    tk_evalf ("%s delete 0 end", name);
	    tk_evalf ("%s insert end {%s}", name, in->buffer);
	}
	p = in->point;
    }
    tk_evalf ("%s icursor %d; entry_restore_sel %s", name, p, name);
}

int
x_create_input (Dlg_head *h, widget_data parent, WInput *in)
{
    char *cmd;

    cmd = tk_new_command (parent, in, tk_input_callback, 'i');
    tk_evalf ("newinput %s {%s}", cmd+1, in->buffer ? in->buffer: "");
    return 1;
}

void
x_listbox_select_nth (WListbox *l, int nth)
{
    if (!wtcl_cmd (l->widget))
	return;
    
    tk_evalf ("listbox_sel %s %d", wtk_win (l->widget), nth);
}

void
x_listbox_delete_nth (WListbox *l, int nth)
{
    tk_evalf ("%s delete %d", wtk_win (l->widget), nth);
}

int
x_create_listbox (Dlg_head *h, widget_data parent, WListbox *l)
{
    char *cmd;
    int  i;
    WLEntry *e;
    
    cmd = tk_new_command (parent, l, 0, 'x');
    tk_evalf ("listbox %s -width %d -selectmode single", cmd+1, l->widget.cols);

    /* As Jakub said on his very fine xvwidget.c: the user could add some
     * entries to the listbox before the tk-listbox was created, so we have
     * to add it manually :-)
     */
    i = 0;
    if ((e = l->list) == NULL)
	return 1;

    do {
	tk_evalf ("%s insert %d {%s}", cmd+1, i, e->text);
	if (e == l->current)
	    x_listbox_select_nth (l, i);
	e = e->next;
	i++;
    } while (e != l->list);
    return 1;
}

void
x_list_insert (WListbox *l, WLEntry *p, WLEntry *e)
{
    int i;
    char *t = e->text;

    if (wtcl_cmd (l->widget) == NULL)
	return;

    for (i = 0; p != NULL && p != e; e = e->next, i++)
	;
    tk_evalf ("%s insert %d {%s}", wtk_win (l->widget), i-1, t);
}

int
x_create_label (Dlg_head *g, widget_data parent, WLabel *l)
{
    char *cmd;

    cmd = tk_new_command (parent, l, 0, 'l');
    tk_evalf ("label %s -text {%s}", cmd+1, l->text);
    return 1;
}


void
x_label_set_text (WLabel *label, char *text)
{
    char *lname;

    if (!wtcl_cmd (label->widget))
	return;
    
    tk_evalf ("%s configure -text {%s}", wtk_win (label->widget),
	      text ? text : "");
}


static int
tk_buttonbar_callback (ClientData cd, Tcl_Interp *in, int ac, char *av [])
{
    WButtonBar *bb = (WButtonBar *) cd;
    Dlg_head *h = (Dlg_head *) bb->widget.parent;
    int which = atoi (av [1]);

    (*bb->labels [which].function)(bb->labels [which].data);
    return TCL_OK;
}

int
x_create_buttonbar (Dlg_head *h, widget_data parent, WButtonBar *bb)
{
    char *cmd;
    int  i;
    
    cmd = tk_new_command (parent, bb, tk_buttonbar_callback, 'n');
    tk_evalf ("frame %s", cmd+1);
    for (i = 0; i < 10; i++){
	tk_evalf ("newbutton %s.%d {%s %d} {%d %s}", cmd+1, i, cmd, i, i+1,
		  bb->labels [i].text ? bb->labels [i].text : "");
	tk_evalf ("%s.%d configure -padx 1", cmd+1, i);
	tk_evalf ("pack %s.%d -side left -fill x -expand 1 -ipady 0",
	    cmd+1, i);
    }
    return 1;
}

void
x_redefine_label (WButtonBar *bb, int idx)
{
    if (!bb->widget.wdata)
	return;

    if (!*(char *) bb->widget.wdata)
	return;
    
    if (!bb->labels [idx-1].text)
	return;
    tk_evalf ("%s.%d configure -text {%d %s}",
        wtk_win (bb->widget), idx-1, idx, bb->labels [idx-1].text);
}

/* destructor for the Tk widgets */
void
x_destroy_cmd (void *w)
{
    Widget *widget = (Widget *)w;
    
    if (widget->wdata){
	Tcl_DeleteCommand (interp, wtcl_cmd (*widget));
	tk_evalf ("destroy %s", wtk_win (*widget));
	free (wtcl_cmd (*widget));
    }
}

int
x_find_buttonbar_check (WButtonBar *bb, Widget *paneletc)
{
    return (strcmp ((char *) bb->widget.wcontainer,
		    (char *)paneletc->wcontainer));
}

/* The only parameter accepted is the size of the widget width */
static int
tk_gauge_callback (ClientData cd, Tcl_Interp *in, int ac, char *av [])
{
    WGauge *g = (WGauge *) cd;

    g->pixels = atoi (av [1]);
    return TCL_OK;
}

void
x_create_gauge (Dlg_head *h, widget_data parent, WGauge *g)
{
    char *cmd;

    cmd = tk_new_command (parent, g, tk_gauge_callback, 'g');
    tk_evalf ("newgauge %s", cmd+1);
}

static void
x_gauge_display (WGauge *g)
{
    tk_evalf ("%s coords gauge 0 0 %d 90", wtk_win (g->widget),
	      (int) (g->current*g->pixels)/g->max);
}

void
x_gauge_show (WGauge *g)
{
    tk_evalf ("gauge_%s %s", g->shown ? "shown":"hidden", wtk_win (g->widget));
    if (g->shown)
	x_gauge_display (g);
}

void
x_gauge_set_value (WGauge *g, int max, int current)
{
    if (g->shown)
	x_gauge_display (g);
}
