
/*
 * paned.c --
 *
 * This file implements the paned widget which is a combination of the
 * gtk paned widget (TODO? and the gtk frame 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:
   2001-06: Begin of developement
 */

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

typedef struct
{
   /* Paned options */
   Gnocl_ObjBool orientation;
   Gnocl_ListBool   resize;
   Gnocl_ListBool   shrink;
   Gnocl_ListBool   children;
} PanedOptions;

static const GnoclWidgetOptions widgetOptions[] =
{
   { "-orientation", GNOCL_OBJ, GNOCL_OFFSET( PanedOptions, orientation ) },
   { "-resize", GNOCL_LIST, GNOCL_OFFSET( PanedOptions, resize ) },
   { "-shrink", GNOCL_LIST, GNOCL_OFFSET( PanedOptions, shrink ) },
   { "-children", GNOCL_LIST, GNOCL_OFFSET( PanedOptions, children ) },
   { 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;
   GtkPaned   *paned;
} PanedParams;

static void freeOpts( PanedOptions *p )
{
   GNOCL_FREE_TCL_OPT( p->orientation );
   GNOCL_FREE_TCL_OPT( p->resize );
   GNOCL_FREE_TCL_OPT( p->shrink );
   GNOCL_FREE_TCL_OPT( p->children );
}

static void destroyFunc( GtkWidget *widget, gpointer data )
{
   PanedParams *p = (PanedParams *)data;

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

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

static int addChildren( GtkPaned *paned, Tcl_Interp *interp, Tcl_Obj *children, 
      Tcl_Obj *resize, Tcl_Obj *shrink )
{
   int n, noChilds;

   if( Tcl_ListObjLength( interp, children, &noChilds ) != TCL_OK 
         || noChilds != 2 )
   {
      Tcl_SetResult( interp, "widget-list must contain 2 elements", 
            TCL_STATIC );
      return TCL_ERROR;
   }

   for( n = 0; n < 2; ++n )
   {
      GtkWidget *childWidget;
      Tcl_Obj *tp;
      int     resizeFlag;
      int     shrinkFlag;
      if( Tcl_ListObjIndex( interp, children, n, &tp ) != TCL_OK )
         return TCL_ERROR;

      childWidget = gnoclChildNotPacked( Tcl_GetString( tp ), interp );
      if( childWidget == NULL )
         return TCL_ERROR;

      if( resize )
      {
         if( Tcl_ListObjLength( interp, resize, &noChilds ) != TCL_OK 
               || noChilds < 1 || noChilds > 2 )
         {
            Tcl_SetResult( interp, "resize-list must contain 1 or 2 elements", 
                  TCL_STATIC );
            return TCL_ERROR;
         }
         if( noChilds == 1 )
            tp = resize;
         else if( Tcl_ListObjIndex( interp, resize, n, &tp ) != TCL_OK )
            return TCL_ERROR;
         if( Tcl_GetBooleanFromObj( interp, tp, &resizeFlag ) != TCL_OK )
            return TCL_ERROR;
      }
      else
         resizeFlag = FALSE;

      if( shrink )
      {
         if( Tcl_ListObjLength( interp, shrink, &noChilds ) != TCL_OK 
               || noChilds < 1 || noChilds > 2 )
         {
            Tcl_SetResult( interp, "shrink-list must contain 1 or 2 elements", 
                  TCL_STATIC );
            return TCL_ERROR;
         }
         if( noChilds == 1 )
            tp = shrink;
         else if( Tcl_ListObjIndex( interp, shrink, n, &tp ) != TCL_OK )
            return TCL_ERROR;
         if( Tcl_GetBooleanFromObj( interp, tp, &shrinkFlag ) != TCL_OK )
            return TCL_ERROR;
      }
      else
         shrinkFlag = (n == 0) ? TRUE : FALSE;
         
      if( n == 0 )
         gtk_paned_pack1( paned, childWidget, resizeFlag, shrinkFlag );
      else
         gtk_paned_pack2( paned, childWidget, resizeFlag, shrinkFlag );
   }

   return TCL_OK;
}

/*
   test, if params are valid
   set params and show window with new settings
*/
static int panedConfigure( Tcl_Interp *interp, PanedParams *para, 
      PanedOptions *popt )
{
   /* FIXME: create or delete frame if necessary 
             also change: gnoclMemNameAndWidget( name, widget );
   */
   if( popt->children.val )
   {
      int ret = addChildren( para->paned, interp, popt->children.val, 
            popt->resize.val, popt->shrink.val );
      if( ret != TCL_OK )
         return ret;
   }

   return TCL_OK;
}

#if 0
static int panedFuncAdd( PanedParams *pp, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[], int begin )
{
   typedef struct 
   {
      Gnocl_BoolBool expand;
      Gnocl_BoolBool fill;
      Gnocl_IntBool padding;
   } PackOptions;
   PackOptions params = { 
         { 1, 0 }, 
         { 1, 0 }, 
         { 0, 0 },
      };
   GnoclWidgetOptions options[] = 
   {
      { "-expand", GNOCL_BOOL, GNOCL_OFFSET( PackOptions, expand ) },
      { "-fill", GNOCL_BOOL, GNOCL_OFFSET( PackOptions, fill ) },
      { "-padding", GNOCL_INT, GNOCL_OFFSET( PackOptions, padding ) },
      { NULL, 0, 0 }
   };

   /*
   int noChilds;
   int n;
   if( objc < 3 || objc % 2 == 0 )
   {
      Tcl_WrongNumArgs( interp, 2, objv, "widget-list ?option val ...?" );
      return TCL_ERROR;
   }
   if( Tcl_ListObjLength( interp, objv[2], &noChilds ) != TCL_OK 
         || noChilds < 1 )
   {
      Tcl_SetResult( interp, "widget-list must be proper list", 
            TCL_STATIC );
      return TCL_ERROR;
   }
   */

   if( gnoclParseAllWidgetOpts( interp, objc, objv,  2,
         options, (char *)&params ) != TCL_OK )
      return TCL_ERROR;

   return addChildren( pp->paned, interp, objv[2], 
      params.expand.val, params.fill.val, params.padding.val, begin );
}
#endif

static int panedFunc( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   static char *cmds[] = {  GNOCL_STD_CMD_NAMES,
         "configure", "add", "addBegin", "addEnd", NULL };
   enum cmdIdx { GNOCL_STD_CMD_ENUMS,
         ConfigureIdx, AddIdx, BeginIdx, EndIdx };

   PanedParams *para = (PanedParams *)data;
   int idx;
   GtkWidget *widget = GTK_WIDGET( para->paned );

   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:
            {
               PanedOptions opt;
               int ret = TCL_ERROR;

               memset( &opt, 0, sizeof( opt ) );
               if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
                     widgetOptions, (char *)&opt ) == TCL_OK )
                  ret = panedConfigure( interp, para, &opt );
               freeOpts( &opt );
               if( ret != TCL_OK )
                  return TCL_ERROR;
            }
            break;
#if 0
      case AddIdx:
      case BeginIdx:
      case EndIdx:
            return panedFuncAdd( para, interp, objc, objv, idx != EndIdx );
#endif
   }
   return TCL_OK;
}

int gnoclPanedCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   PanedOptions opt;
   PanedParams *para;
   GtkOrientation orient = GTK_ORIENTATION_HORIZONTAL;
   int ret;
   GtkWidget *widget;
   
   memset( &opt, 0, sizeof( opt ) );
   if( gnoclParseAllWidgetOpts( interp, objc, objv, 0,
         widgetOptions, (char *)&opt ) != TCL_OK )
      return TCL_ERROR;


   para = g_new( PanedParams, 1 );
   para->name = gnoclGetAutoWidgetId( );
   para->interp = interp;
   if( opt.orientation.changed )
   {
      if( gnoclGetOrientationType( interp, opt.orientation.val,
            &orient ) != TCL_OK )
      {
         freeOpts( &opt );
         return TCL_ERROR;
      }
   }

   if( orient == GTK_ORIENTATION_HORIZONTAL )
      widget = gtk_hpaned_new( );
   else
      widget = gtk_vpaned_new( );
   para->paned = GTK_PANED( widget );
   
   ret = panedConfigure( interp, para, &opt );
   freeOpts( &opt );
   if( ret != TCL_OK )
   {
      gtk_widget_destroy( GTK_WIDGET( para->paned ) );
      return TCL_ERROR;
   }

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

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

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

   gtk_signal_connect( GTK_OBJECT( widget ), "destroy", 
      GTK_SIGNAL_FUNC( destroyFunc ), para );
 
   return TCL_OK;
}

