
/*
 * gnoclButton.c --
 *
 * This file implements the button widget
 *
 * Copyright (c) 2001 - 2002 P.G. Baum
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 */

/*
   History:
   2002-01: new command "invoke"
        09: underlined accelerators
   2001-03: Begin of developement
 */

#include "gnocl.h"
#include <string.h>
#include <assert.h>

typedef struct
{
   GNOCL_STD_OPTIONS_VARS;

   Gnocl_ObjBool    text;
   Gnocl_StringBool command;
   /* char *child; */ /* FIXME: button with arbitrary child widget */
} ButtonOptions;

static const GnoclWidgetOptions widgetOptions[] =
{
   GNOCL_STD_OPTIONS_NAMES( ButtonOptions ),
   { "-text", GNOCL_OBJ, GNOCL_OFFSET( ButtonOptions, text ) },
   { "-command", GNOCL_STRING, GNOCL_OFFSET( ButtonOptions, command ) },
   /* { "-child", GNOCL_STRING, OffsetOf( ButtonOptions, child ) }, */
   { NULL, 0, 0 }
};

/*
   This struct contains only the necessary states to remember.
   Option values can be inquired via gtk
*/
typedef struct
{
   char       *name;
   Tcl_Interp *interp;
   GtkButton  *button;
   char       *command;
} ButtonParams;

static void freeOpts( ButtonOptions *p )
{
   GNOCL_STD_OPTIONS_FREE( p );
   GNOCL_FREE_MEM_OPT( p->command );
}

static int doCommand( ButtonParams *para )
{
   if( para->command )
   {
     static Gnocl_PercSubst ps[] = {
         { 'w', GNOCL_STRING },  /* widget */
         { 0 }
      };

      ps[0].val.str = para->name;
      return gnoclPercentSubstAndEval( para->interp, ps, para->command );
   }
   return TCL_OK;
}

static void buttonCmdFunc( GtkWidget *widget, gpointer data )
{
   ButtonParams *para = (ButtonParams *)data;
   doCommand( para );
}

static void buttonDestroyFunc( GtkWidget *widget, gpointer data )
{
   ButtonParams *p = (ButtonParams *)data;

   gnoclForgetWidgetFromName( p->name );
   Tcl_DeleteCommand( p->interp, p->name );

   g_free( p->command );
   g_free( p->name );
   g_free( p );
}

static int buttonConfigure( Tcl_Interp *interp, ButtonParams *para, 
      ButtonOptions *popt )
{
   GNOCL_STD_OPTIONS_HANDLE( popt, GTK_WIDGET( para->button ) );

   if( popt->command.changed )
   {
      g_free( para->command );
      para->command = popt->command.val; /* transfer ownership */
      popt->command.val = NULL;
   }

   if( popt->text.changed )
   {
      #if 0
      /* FIXME */
      if( pp->text.val )
      {
         if( pp->childWidget &&
               GTK_CHECK_TYPE( pp->childWidget, GTK_TYPE_LABEL ) )
         {
            gtk_label_set_text( GTK_LABEL( pp->childWidget ), 
                  pp->text.val );
         }
         else
         {
            if( pp->childWidget )
            {
               gtk_container_remove( GTK_CONTAINER( pp->widget ), 
                     pp->childWidget );
               gtk_widget_destroy( pp->childWidget );
            }
            pp->childWidget = gtk_label_new( pp->text.val );
            gtk_container_add( GTK_CONTAINER( pp->widget ), pp->childWidget );
         }
      } 
      else if( pp->childWidget )
      {
         gtk_container_remove( GTK_CONTAINER( pp->widget ), pp->childWidget );
         gtk_widget_destroy( pp->childWidget );
      }
      #endif
   }

   return TCL_OK;
}

static int buttonFunc( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   static char *cmds[] = { GNOCL_STD_CMD_NAMES,
         "configure", "invoke",
         NULL };
   enum cmdIdx { GNOCL_STD_CMD_ENUMS, ConfigureIdx, InvokeIdx };
   ButtonParams *para = (ButtonParams *)data;
   GtkWidget *widget = GTK_WIDGET( para->button );
   int idx;

   if( Tcl_GetIndexFromObj( interp, objv[1], cmds, "command", 
         TCL_EXACT, &idx ) != TCL_OK )
      return TCL_ERROR;

   switch( idx )
   {
      GNCOL_STD_CMD_CASE( interp, objc, objv, widget );
            break;

      case ConfigureIdx:
            {
               ButtonOptions opt;
               int ret = TCL_ERROR;
               memset( &opt, 0, sizeof( opt ) );
               if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
                     widgetOptions, (char *)&opt ) == TCL_OK )
                  ret = buttonConfigure( interp, para, &opt );

               freeOpts( &opt );
               if( ret != TCL_OK )
                  return TCL_ERROR;
            }
            break;
      case InvokeIdx:
            if( GTK_WIDGET_IS_SENSITIVE( GTK_WIDGET( para->button ) ) )
               gtk_button_clicked( para->button ); 
            break;
   }

   return TCL_OK;
}

int gnoclButtonCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   ButtonOptions opt;
   ButtonParams *para;
   int          ret;
   
   memset( &opt, 0, sizeof( opt ) );
   if( gnoclParseAllWidgetOpts( interp, objc, objv, 0,
         widgetOptions, (char *)&opt ) != TCL_OK )
      return TCL_ERROR;

   para = g_new( ButtonParams, 1 );
   para->interp = interp;
   para->command = NULL;
   para->name = gnoclGetAutoWidgetId();
   para->button = NULL;

   /*
   if( pp->stock.val )
   {
      pp->widget = getStockButton( interp, pp->stock.val );
      if( pp->widget == NULL )
         return TCL_ERROR;
   }
   else
      pp->widget = GTK_BUTTON( gtk_button_new( ) );
   */
   if( opt.text.changed )
   {
      /* TODO: use gnoclFindLabel to configure text */
      GnoclStringType type = gnoclGetStringType( opt.text.val );
      char *txt = NULL;
      if( type | GNOCL_STR_STOCK )
      {
         txt = gnoclGetStockButtonName( NULL, opt.text.val );
         if( txt )
            para->button = GTK_BUTTON( gnome_stock_button( txt ) );
      }
      if( txt == NULL )
         txt = gnoclGetString( opt.text.val );

      if( para->button == NULL )
         para->button = GTK_BUTTON( gtk_button_new_with_label( txt ) );
      opt.text.changed = 0;   /* invalidate option */
      if( type & GNOCL_STR_UNDERLINE )
      {
         guint key = gnoclLabelParseULine( GTK_WIDGET( para->button ), txt );
         if( key != GDK_VoidSymbol )
         {
            gtk_widget_add_accelerator( GTK_WIDGET( para->button ), 
                  "clicked", gnoclGetAccelGroup(), 
                  key, GDK_MOD1_MASK, GTK_ACCEL_LOCKED );
         }

      }
   }

   ret = buttonConfigure( interp, para, &opt );
   freeOpts( &opt );
   if( ret != TCL_OK )
   {
      /* FIXME: g_free pp->name ... */
      gtk_widget_destroy( GTK_WIDGET( para->button ) );
      return TCL_ERROR;
   }
   gtk_signal_connect( GTK_OBJECT( para->button ), "clicked", 
         GTK_SIGNAL_FUNC( buttonCmdFunc ), para );
   gtk_signal_connect( GTK_OBJECT( para->button ), "destroy", 
         GTK_SIGNAL_FUNC( buttonDestroyFunc ), para );

   gnoclMemNameAndWidget( para->name, GTK_WIDGET( para->button ) );
   gtk_widget_show( GTK_WIDGET( para->button ) );

   Tcl_CreateObjCommand( interp, para->name, buttonFunc, para, NULL );
   Tcl_SetObjResult( interp, Tcl_NewStringObj( para->name, -1 ) );

   return TCL_OK;
}

