
/*
 * gnoclEntry.c --
 *
 * This file implements the entry widget
 *
 * Copyright (c) 2001 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:
        09: added GnomeEntry, GnomeFileEntry, GnomeNumberEntry, 
            GnomePixmapEntry and GnomeIconEntry
   2001-03: Begin of developement
*/

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

typedef struct
{
   GNOCL_STD_OPTIONS_VARS;

   Gnocl_StringBool  variable;
   Gnocl_StringBool  title;
   Gnocl_IntBool     maxLength;
   Gnocl_BoolBool    editable;
   Gnocl_ObjBool     type;
   Gnocl_ObjBool     history;
   Gnocl_StringBool  historyId;
} EntryOptions;

static const GnoclWidgetOptions widgetOptions[] = 
{
   GNOCL_STD_OPTIONS_NAMES( EntryOptions ),

   { "-variable", GNOCL_STRING, GNOCL_OFFSET( EntryOptions, variable ) },
   { "-title", GNOCL_STRING, GNOCL_OFFSET( EntryOptions, title ) },
   { "-maxLength", GNOCL_INT, GNOCL_OFFSET( EntryOptions, maxLength ) },
   { "-editable", GNOCL_BOOL, GNOCL_OFFSET( EntryOptions, editable ) },
   { "-type", GNOCL_OBJ, GNOCL_OFFSET( EntryOptions, type ) },
   { "-history", GNOCL_LIST, GNOCL_OFFSET( EntryOptions, history ) },
   { "-historyId", GNOCL_STRING, GNOCL_OFFSET( EntryOptions, historyId ) },
 
   { NULL, 0, 0 }
};

typedef enum { Gnocl_EntryDefault, Gnocl_EntryGnome, 
      Gnocl_EntryPixmap, Gnocl_EntryIcon,
      Gnocl_EntryFile, Gnocl_EntryNumber } Gnocl_EntryType;

typedef struct
{
   char        *name;
   char        *variable;
   Gnocl_EntryType type;
   GtkWidget   *widget;
   GnomeEntry  *gnomeEntry;
   GtkEntry    *entry;
   Tcl_Interp  *interp;
} EntryParams;


static void freeOpts( EntryOptions *p )
{
   GNOCL_STD_OPTIONS_FREE( p );
   GNOCL_FREE_MEM_OPT( p->variable );
   GNOCL_FREE_MEM_OPT( p->title );
   GNOCL_FREE_TCL_OPT( p->type );
   GNOCL_FREE_TCL_OPT( p->history );
   GNOCL_FREE_MEM_OPT( p->historyId );
}


static char *entryTraceFunc( ClientData data,
      Tcl_Interp *interp, char *name1, char *name2, int flags)
{
   EntryParams *p = (EntryParams *)data;
   /* 
   Tcl_Obj *obj = Tcl_GetVar2Ex( interp, name1, name2, 0 ); 
   if( obj != NULL )
      gtk_entry_set_text( GTK_ENTRY( p->entry ), Tcl_GetString( obj ) );
   */

   char *txt = name1 ? Tcl_GetVar2( interp, name1, name2, 0 ): NULL;
   assert( p->interp == interp );

   if( txt != NULL )
      gtk_entry_set_text( p->entry, txt );

   return NULL;
}

static void entryChangedFunc ( GtkWidget *widget, gpointer data )
{
   EntryParams *p = (EntryParams *)data;
   /* TODO: what to do if entry is not a valid number? 
   if( p->type == Gnocl_EntryNumber )
   {
      Tcl_Obj *obj = Tcl_NewDoubleObj( gnome_number_entry_get_number( 
         GNOME_NUMBER_ENTRY( p->widget ) ) );
      Tcl_SetVar2Ex( p->interp, p->variable, NULL, obj, 0 );
   }
   */
   char *val = gtk_entry_get_text( p->entry ); 
   Tcl_SetVar( p->interp, p->variable, val, 0 );
}

static void entryDestroyFunc ( GtkWidget *widget, gpointer data )
{
   EntryParams *p = (EntryParams *)data;

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

   gnoclAttacheVariable( &p->variable, NULL,
         GTK_OBJECT( p->entry ), 
         "changed", GTK_SIGNAL_FUNC( entryChangedFunc ),
         p->interp, entryTraceFunc, p );

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

static int entryConfigure( Tcl_Interp *interp, EntryParams *para,
      EntryOptions *popt )
{
   if( popt->type.changed )
   {
      char *types[] = { "default", "gnome", "pixmap", "icon", 
            "file", "number", NULL };
      Gnocl_EntryType gTypes[] = { Gnocl_EntryDefault, Gnocl_EntryGnome,
            Gnocl_EntryPixmap, Gnocl_EntryIcon, 
            Gnocl_EntryFile, Gnocl_EntryNumber };

      int idx;
      if( Tcl_GetIndexFromObj( interp, popt->type.val, types, 
            "entry type", TCL_EXACT, &idx ) != TCL_OK )
         return TCL_ERROR;

      para->type = gTypes[idx];
   }
   /* TODO: what to do if widget does exist -> reconfigure */
   if( para->widget == NULL )
   {
      switch( para->type )
      {
         case Gnocl_EntryDefault:
               para->entry = GTK_ENTRY( gtk_entry_new( ) );
               para->gnomeEntry = NULL;
               para->widget = GTK_WIDGET( para->entry );
               break;
         case Gnocl_EntryGnome:
               para->widget = gnome_entry_new( NULL );
               para->gnomeEntry = GNOME_ENTRY( para->widget );
               break;
         case Gnocl_EntryPixmap:
               para->widget = gnome_pixmap_entry_new( NULL, NULL, 1 );
               para->gnomeEntry = GNOME_ENTRY( gnome_pixmap_entry_gnome_entry( 
                     GNOME_PIXMAP_ENTRY( para->widget ) ) );
               break;
         case Gnocl_EntryIcon:
               para->widget = gnome_icon_entry_new( NULL, NULL );
               para->gnomeEntry = GNOME_ENTRY( gnome_icon_entry_gnome_entry( 
                     GNOME_ICON_ENTRY( para->widget ) ) );
               break;
         case Gnocl_EntryFile:
               para->widget = gnome_file_entry_new( NULL, NULL );
               para->gnomeEntry = GNOME_ENTRY( gnome_file_entry_gnome_entry( 
                     GNOME_FILE_ENTRY( para->widget ) ) );
               break;
         case Gnocl_EntryNumber:
               para->widget = gnome_number_entry_new( NULL, NULL );
               para->gnomeEntry = GNOME_ENTRY( gnome_number_entry_gnome_entry( 
                     GNOME_NUMBER_ENTRY( para->widget ) ) );
               break;
      }
      if( para->gnomeEntry != NULL )
         para->entry = GTK_ENTRY( gnome_entry_gtk_entry( para->gnomeEntry ) );
         
   }

   GNOCL_STD_OPTIONS_HANDLE( popt, GTK_WIDGET( para->widget ) );

   if( popt->variable.changed )
   {
      gnoclAttacheVariable( &para->variable, &popt->variable.val,
            GTK_OBJECT( para->entry ), 
            "changed", GTK_SIGNAL_FUNC( entryChangedFunc ),
            interp, entryTraceFunc, para );
   }

   if( popt->title.changed )
   {
      switch( para->type )
      {
         case Gnocl_EntryDefault:
         case Gnocl_EntryGnome:
         case Gnocl_EntryPixmap:
         case Gnocl_EntryIcon:
               Tcl_SetResult( interp, 
                     "-title no valid option for this entry type.", 
                     TCL_STATIC );
               return TCL_ERROR;
         case Gnocl_EntryFile:
               gnome_file_entry_set_title( GNOME_FILE_ENTRY( para->widget ),
                     popt->title.val );
               break;
         case Gnocl_EntryNumber:
               gnome_number_entry_set_title( 
                     GNOME_NUMBER_ENTRY( para->widget ), popt->title.val );
               break;
      }
   }

   if( popt->history.changed )
   {
      GList *items = NULL;
      int n, no;
      int ret = Tcl_ListObjLength( interp, popt->history.val, &no );
      if( ret != TCL_OK )
         return ret;

      if( para->gnomeEntry == NULL )
      {
         Tcl_SetResult( interp, "History not available for this entry type.", 
               TCL_STATIC );
         return TCL_ERROR;
      }
      
      for( n = 0; n < no; ++n )
      {
         Tcl_Obj *tp;
         int ret = Tcl_ListObjIndex( interp, popt->history.val, n, &tp );
         if( ret != TCL_OK )
            return ret;
         items = g_list_append( items, 
               (char *)gnoclGetStringFromObj( tp, NULL ) );
      }
      gtk_combo_set_popdown_strings( GTK_COMBO( para->gnomeEntry ), items );
   }

   if( popt->historyId.changed )
   {
      if( para->gnomeEntry == NULL )
      {
         Tcl_SetResult( interp, "History not available for this entry type.", 
               TCL_STATIC );
         return TCL_ERROR;
      }
      gnome_entry_set_history_id( para->gnomeEntry, popt->historyId.val );
   }

   if( popt->maxLength.changed )
      gtk_entry_set_max_length( para->entry, popt->maxLength.val );

   if( popt->editable.changed )
      gtk_entry_set_editable( para->entry, popt->editable.val );

   entryTraceFunc( para, interp, para->variable, NULL, 0 );

   return TCL_OK;
}

static int entryFunc( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   static char *cmds[] = { GNOCL_STD_CMD_NAMES,
         "configure", 
         "getValue", "setValue",
         "loadHistory", "saveHistory",
         "appendHistory", "prependHistory",
         NULL };
   enum cmdIdx { GNOCL_STD_CMD_ENUMS, ConfigureIdx, 
         GetValueIdx, SetValueIdx,
         LoadHistoryIdx, SaveHistoryIdx,
         AppendHistoryIdx, PrependHistoryIdx };
   EntryParams *para = (EntryParams *)data;
   GtkWidget *widget = GTK_WIDGET( para->entry );
   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:
            {
               EntryOptions opt;
               int ret = TCL_ERROR;
               memset( &opt, 0, sizeof( opt ) );
               if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
                     widgetOptions, (char *)&opt ) == TCL_OK )
                  ret = entryConfigure( interp, para, &opt );

               freeOpts( &opt );
               if( ret != TCL_OK )
                  return TCL_ERROR;
            }
            break;
      case AppendHistoryIdx:
      case PrependHistoryIdx:
            {
               char *txt;
               if( objc != 3 )
               {
                  Tcl_WrongNumArgs( interp, 2, objv, "text" );
                  return TCL_ERROR;
               }
               if( para->gnomeEntry == NULL )
               {
                  Tcl_SetResult( interp, 
                        "History not available for this entry type.", 
                        TCL_STATIC );
                  return TCL_ERROR;
               }
               txt = Tcl_GetString( objv[2] );
               if( idx == AppendHistoryIdx )
                  gnome_entry_append_history( para->gnomeEntry, 1, txt );
               else
                  gnome_entry_prepend_history( para->gnomeEntry, 1, txt );
            }
            break;
      case LoadHistoryIdx:
      case SaveHistoryIdx:
            {
               /* TODO: 
                        - save historyID ? 
                        - error if historyID == NULL ?
               */
               if( objc != 2 )
               {
                  Tcl_WrongNumArgs( interp, 2, objv, NULL );
                  return TCL_ERROR;
               }
               if( para->gnomeEntry == NULL )
               {
                  Tcl_SetResult( interp, 
                        "History not available for this entry type.", 
                        TCL_STATIC );
                  return TCL_ERROR;
               }
               if( idx == LoadHistoryIdx )
                  gnome_entry_load_history( para->gnomeEntry );
               else
                  gnome_entry_save_history( para->gnomeEntry );
            }
            break;
      case GetValueIdx:
            {
               Tcl_Obj *obj;
               if( objc != 2 )
               {
                  Tcl_WrongNumArgs( interp, 2, objv, NULL );
                  return TCL_ERROR;
               }
               /* TODO: what to do if entry is not a valid number?
               if( para->type == Gnocl_EntryNumber )
               {
                  obj = Tcl_NewDoubleObj( gnome_number_entry_get_number( 
                        GNOME_NUMBER_ENTRY( para->widget ) ) );
               }
               else
               */
               obj = Tcl_NewStringObj( gtk_entry_get_text(
                     para->entry ), -1 );
               Tcl_SetObjResult( interp, obj );
            }
            break;
      case SetValueIdx:
            {
               const char *txt;
               if( objc != 3 )
               {
                  Tcl_WrongNumArgs( interp, 2, objv, "value" );
                  return TCL_ERROR;
               }

               txt = Tcl_GetString( objv[2] );
               gtk_entry_set_text( para->entry, txt );
            }
            break;
   }

   return TCL_OK;
}

int gnoclEntryCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{

   EntryOptions opt;
   EntryParams *para;

   memset( &opt, 0, sizeof( opt ) );
   if( gnoclParseAllWidgetOpts( interp, objc, objv, 0,
         widgetOptions, (char *)&opt ) != TCL_OK )
      return TCL_ERROR;

   para = g_new( EntryParams, 1 );
   para->widget = NULL;
   para->entry = NULL;
   para->type = Gnocl_EntryDefault;
   para->name = gnoclGetAutoWidgetId();
   para->interp = interp;
   para->variable = NULL;

   if( entryConfigure( interp, para, &opt ) != TCL_OK )
   {
      /* FIXME: g_free para->name ... */
      gtk_widget_destroy( GTK_WIDGET( para->entry ) );
      return TCL_ERROR;
   }

   gtk_signal_connect( GTK_OBJECT( para->widget ), "destroy", 
         GTK_SIGNAL_FUNC( entryDestroyFunc ), para );

   gnoclMemNameAndWidget( para->name, para->widget );
   gtk_widget_show( para->widget );

   Tcl_CreateObjCommand( interp, para->name, entryFunc, para, NULL );

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

   return TCL_OK;
}

