
/*
 * helperFuncs.c --
 *
 * This file implements some helper functions
 *
 * 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:
        09: gnoclFindLabel, gnoclLabelParseULine
   2001-07: Begin of developement
 */

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

const char *gnoclEventToString( GdkEventType et )
{
   typedef struct
   {
      GdkEventType ev;
      const char   *txt;
   } GnoclEventTxt;

   const GnoclEventTxt evntTxt[] = {
      { GDK_NOTHING          , "nothing" },  
      { GDK_DELETE           , "delete" },  
      { GDK_DESTROY          , "destroy" },  
      { GDK_EXPOSE           , "expose" },  
      { GDK_MOTION_NOTIFY    , "motion" },  
      { GDK_BUTTON_PRESS     , "buttonPress" },  
      { GDK_BUTTON_PRESS     , "button" },      /* synonym for buttonPress */
      { GDK_2BUTTON_PRESS    , "button2Press" },  
      { GDK_3BUTTON_PRESS    , "button3Press" },  
      { GDK_BUTTON_RELEASE   , "buttonRelease" },  
      { GDK_KEY_PRESS        , "keyPress" },    
      { GDK_KEY_PRESS        , "key" },         /* synonym for keyPress */
      { GDK_KEY_RELEASE      , "keyRelease" },  
      { GDK_ENTER_NOTIFY     , "enter" },  
      { GDK_LEAVE_NOTIFY     , "leave" },  
      { GDK_FOCUS_CHANGE     , "focusChanged" },  
      { GDK_CONFIGURE        , "configure" },  
      { GDK_MAP              , "map" },  
      { GDK_UNMAP            , "unmap" },  
      { GDK_PROPERTY_NOTIFY  , "property" },  
      { GDK_SELECTION_CLEAR  , "selectionClear" },  
      { GDK_SELECTION_REQUEST, "selectionRequest" },  
      { GDK_SELECTION_NOTIFY , "selectionNotify" },  
      { GDK_PROXIMITY_IN     , "proximityIn" },  
      { GDK_PROXIMITY_OUT    , "proximityOut" },  
      { GDK_DRAG_ENTER       , "dragEnter" },  
      { GDK_DRAG_LEAVE       , "dragLeave" },  
      { GDK_DRAG_MOTION      , "dragMotion" },  
      { GDK_DRAG_STATUS      , "dragStatus" },  
      { GDK_DROP_START       , "dropStart" },  
      { GDK_DROP_FINISHED    , "dropFinished" },  
      { GDK_CLIENT_EVENT     , "client" },  
      { GDK_VISIBILITY_NOTIFY, "visibility" },  
      { GDK_NO_EXPOSE        , "noExpose" },  
   };
   /* TODO: use GTree ? */
   int k;
   for( k = 0; k < sizeof( evntTxt ) / sizeof( *evntTxt ); ++k )
      if( evntTxt[k].ev == et )
         return evntTxt[k].txt;

   return NULL;
}

int gnoclPercentSubstAndEval( Tcl_Interp *interp, Gnocl_PercSubst *ps, 
      const char *orig_script )
{
   int        len = strlen( orig_script );
   const char *old_perc = orig_script;
   const char *perc;
   GString    *script = g_string_sized_new( len + 20 );
   int        ret;

   for( ; ( perc = strchr( old_perc, '%' ) ) != NULL;
         old_perc = perc + 2 )
   {
      g_string_sprintfa( script, "%.*s", perc - old_perc, old_perc );
      /* printf( "script: \"%s\"\n", script->str ); */
      if( perc[1] == '%' )
         g_string_append_c( script, '%' ); 
      else
      {
         int k = 0;
         while( ps[k].c && ps[k].c != perc[1] )
            ++k;
         if( ps[k].c == 0 )
         {
            printf( "DEBUG: unknown percent substitution %c\n", perc[1] );
            /*
            Tcl_AppendResult( interp, "unknown percent substitution" , 
                  (char *)NULL );
            g_string_free( script, 1 );
            return TCL_ERROR;
            */
            g_string_append_c( script, '%' ); 
            g_string_append_c( script, perc[1] ); 
         }
         else
         {
            switch( ps[k].type )
            {
               case GNOCL_STRING: 
                     /* FIXME: escape special characters: ' ', '\n', '\\' ...
                        or use Tcl_EvalObj? */
                     if( ps[k].val.str != NULL )
                     {
                        /* handle special characters correctly 
                        char *txt = Tcl_Merge( 1, ps[k].val.str );
                        g_string_sprintfa( script, "%s", txt );
                        Tcl_Free( txt );
                        */
                        g_string_sprintfa( script, "%s", ps[k].val.str );
                     }
                     break;
               case GNOCL_OBJ: 
                     if( ps[k].val.obj != NULL )
                     {
                        /* embedden 0s should be UTF encoded. Should
                           therefor also work. */
                        /*
                        g_string_sprintfa( script, "%s", 
                              Tcl_GetString( ps[k].val.obj ) );
                        */
                        char *argv[] = { Tcl_GetString( ps[k].val.obj ) };
                        char *txt = Tcl_Merge( 1, argv );
                        g_string_sprintfa( script, "%s", txt );
                        Tcl_Free( txt );
                     }
                     break;
               case GNOCL_INT:
                     g_string_sprintfa( script, "%d", ps[k].val.i );
                     break;
               case GNOCL_BOOL:
                     g_string_sprintfa( script, "%d", ps[k].val.b != 0 );
                     break;
               case GNOCL_DOUBLE:
                     g_string_sprintfa( script, "%f", ps[k].val.d );
                     break;
               default:
                     assert( 0 );
                     break;
            }
         }
      }
   }
   g_string_append( script, old_perc );

   /* printf( "DEBUG: script in percEval: %s\n", script->str ); */

   /* Tcl_EvalObj would be faster and more elegant, but incompatible: eg. 
   two consecutive percent substitutions without space */
   ret = Tcl_EvalEx( interp, script->str, -1, TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT );
   g_string_free( script, 1 );

   return ret;
}

int gnoclAttacheCommand( char **pold, char **pnew, GtkObject *obj,
      const char *signal, GtkSignalFunc func, gpointer data )
{
   if( pnew == NULL || *pnew == NULL || **pnew == 0 )
   {
      if( *pold )
      {
         gtk_signal_disconnect_by_func( obj, func, data );
         g_free( *pold );
         *pold = NULL;
      }
   }
   else
   {
      if( *pold == NULL )
         gtk_signal_connect( obj, signal, func, data );
      else
         g_free( *pold );
      *pold = *pnew;    /* transfer ownership */
      *pnew = NULL;
   }

   return TCL_OK;
}

int gnoclAttacheVariable( char **pold, char **pnew, GtkObject *obj,
      const char *signal, GtkSignalFunc gtkFunc, 
      Tcl_Interp *interp, Tcl_VarTraceProc tclFunc, 
      gpointer data )
{
   if( *pold )
      Tcl_UntraceVar( interp, *pold, TCL_TRACE_WRITES | TCL_GLOBAL_ONLY, 
            tclFunc, data );

   if( pnew == NULL || *pnew == NULL || **pnew == 0 )
   {
      if( *pold )
      {
         gtk_signal_disconnect_by_func( obj, gtkFunc, data );
         g_free( *pold );
         *pold = NULL;
      }
   }
   else
   {
      if( *pold == NULL )
         gtk_signal_connect( obj, signal, gtkFunc, data );
      else
         g_free( *pold );

      *pold = *pnew;    /* transfer ownership */
      *pnew = NULL;
      Tcl_TraceVar( interp, *pold, TCL_TRACE_WRITES | TCL_GLOBAL_ONLY, 
            tclFunc, data );
      /* call the tracefunction so that variable and widget state 
         are consistent */
      (*tclFunc)( data, interp, *pold, NULL, 0 );
   }

   return TCL_OK;
}

int gnoclAttacheCmdAndVar( int newCmdValid, char **oldCmd, char **newCmd, 
      int newVarValid, char **oldVar, char **newVar,
      const char *signal, 
      GtkObject *obj, GtkSignalFunc gtkFunc, 
      Tcl_Interp *interp, Tcl_VarTraceProc tclFunc, 
      gpointer data )
{
   int wasConnected = *oldVar != NULL || *oldCmd != NULL;

   if( newCmdValid )
   {
      g_free( *oldCmd );

      if( newCmd == NULL || *newCmd == NULL || **newCmd == 0 )
         *oldCmd = NULL;
      else
      {
         *oldCmd = *newCmd;    /* transfer ownership */
         *newCmd = NULL;
      }
   }

   if( newVarValid )
   {
      if( *oldVar )
         Tcl_UntraceVar( interp, *oldVar, 
               TCL_TRACE_WRITES | TCL_GLOBAL_ONLY, tclFunc, data );

      g_free( *oldVar );
      if( newVar == NULL || *newVar == NULL || **newVar == 0 )
         *oldVar = NULL;
      else
      {
         g_free( *oldVar );
         *oldVar = *newVar;    /* transfer ownership */
         *newVar = NULL;
         Tcl_TraceVar( interp, *oldVar, TCL_TRACE_WRITES | TCL_GLOBAL_ONLY, 
               tclFunc, data );
         /* call the tracefunction so that variable and widget state 
            are consistent */
         /* (*tclFunc)( data, interp, *oldVar, NULL, 0 ); */
      }
   }

   if( wasConnected == 0 && ( *oldCmd != NULL || *oldVar != NULL ) )
      gtk_signal_connect( obj, signal, gtkFunc, data );
   if( wasConnected && *oldCmd == NULL && *oldVar == NULL )
      gtk_signal_disconnect_by_func( obj, gtkFunc, data );

   return TCL_OK;
}

GtkLabel *gnoclFindLabel( GtkWidget *widget )
{
   if( widget == NULL )
      return NULL;

   if( GTK_IS_LABEL( widget ) )
      return GTK_LABEL( widget );
   
   if( GTK_IS_BIN( widget ) )
   {
      GtkBin *bin = GTK_BIN( widget );

      if( GTK_IS_LABEL( bin->child ) )
         return GTK_LABEL( bin->child );

      if( GTK_IS_CONTAINER( bin->child ) )
      {
         GList *childs = gtk_container_children( GTK_CONTAINER( bin->child ) );
         GList *cp;
         for( cp = g_list_first( childs ); cp != NULL; 
               cp = g_list_next( cp ) )
         {
            if( GTK_IS_LABEL( cp->data ) )
               return GTK_LABEL( cp->data );
         }
      }
   }
   return NULL;
}

guint gnoclLabelParseULine( GtkWidget *widget, char *text )
{
   GtkLabel *label = gnoclFindLabel( widget );
   if( label != NULL )
      return gtk_label_parse_uline( label, text );

   return GDK_VoidSymbol;
}


int gnoclGet2Boolean( Tcl_Interp *interp, Tcl_Obj *obj, int *b1, int *b2 )
{
   int no;
   if( Tcl_ListObjLength( interp, obj, &no ) == TCL_OK 
         && ( no == 2 || no == 1 ) )
   {
      if( no == 1 )
      {
         if( Tcl_GetBooleanFromObj( interp, obj, b1 ) != TCL_OK )
            return TCL_ERROR;
         *b2 = *b1;
         return TCL_OK;
      }
      else
      {
         Tcl_Obj *tp;
         if( Tcl_ListObjIndex( interp, obj, 0, &tp ) == TCL_OK )
         {
            if( Tcl_GetBooleanFromObj( interp, tp, b1 ) != TCL_OK )
               return TCL_ERROR;
            if( Tcl_ListObjIndex( interp, obj, 1, &tp ) == TCL_OK )
            {
               if( Tcl_GetBooleanFromObj( interp, tp, b2 ) != TCL_OK )
                  return TCL_ERROR;
            }
            return TCL_OK;
         }
      }
   }
   Tcl_AppendResult( interp, "Expected boolean value or list of "
         "two boolean values but got \"", Tcl_GetString( obj ), "\"", NULL );

   return TCL_ERROR;
}

int gnoclGet2Int( Tcl_Interp *interp, Tcl_Obj *obj, int *b1, int *b2 )
{
   int no;
   if( Tcl_ListObjLength( interp, obj, &no ) == TCL_OK 
         && ( no == 2 || no == 1 ) )
   {
      if( no == 1 )
      {
         if( Tcl_GetIntFromObj( interp, obj, b1 ) != TCL_OK )
            return TCL_ERROR;
         *b2 = *b1;
         return TCL_OK;
      }
      else
      {
         Tcl_Obj *tp;
         if( Tcl_ListObjIndex( interp, obj, 0, &tp ) == TCL_OK )
         {
            if( Tcl_GetIntFromObj( interp, tp, b1 ) != TCL_OK )
               return TCL_ERROR;
            if( Tcl_ListObjIndex( interp, obj, 1, &tp ) == TCL_OK )
            {
               if( Tcl_GetIntFromObj( interp, tp, b2 ) != TCL_OK )
                  return TCL_ERROR;
            }
            return TCL_OK;
         }
      }
   }
   Tcl_AppendResult( interp, "Expected integer value or list of "
         "two integer values but got \"", Tcl_GetString( obj ), "\"", NULL );

   return TCL_ERROR;
}

int gnoclGet2Double( Tcl_Interp *interp, Tcl_Obj *obj, double *b1, double *b2 )
{
   int no;
   if( Tcl_ListObjLength( interp, obj, &no ) == TCL_OK 
         && ( no == 2 || no == 1 ) )
   {
      if( no == 1 )
      {
         if( Tcl_GetDoubleFromObj( interp, obj, b1 ) != TCL_OK )
            return TCL_ERROR;
         *b2 = *b1;
         return TCL_OK;
      }
      else
      {
         Tcl_Obj *tp;
         if( Tcl_ListObjIndex( interp, obj, 0, &tp ) == TCL_OK )
         {
            if( Tcl_GetDoubleFromObj( interp, tp, b1 ) != TCL_OK )
               return TCL_ERROR;
            if( Tcl_ListObjIndex( interp, obj, 1, &tp ) == TCL_OK )
            {
               if( Tcl_GetDoubleFromObj( interp, tp, b2 ) != TCL_OK )
                  return TCL_ERROR;
            }
            return TCL_OK;
         }
      }
   }
   Tcl_AppendResult( interp, "Expected float value or list of "
         "two float values but got \"", Tcl_GetString( obj ), "\"", NULL );

   return TCL_ERROR;
}

