
/*
 * toolbar.c --
 *
 * This file implements the toolbar 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-02: percent subst. in command
            added STD_OPTIONS
        12: icon text qualified by "%#" or "%/"
   2001-03: Begin of developement
 */

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

typedef struct
{
   Gnocl_ObjBool orientation;
   Gnocl_StringBool style;

   GtkToolbar *widget;
} ToolbarParams;

static const GnoclWidgetOptions toolbarOptions[] =
{
   { "-orientation", GNOCL_OBJ, GNOCL_OFFSET( ToolbarParams, orientation ) },
   { "-style", GNOCL_STRING, GNOCL_OFFSET( ToolbarParams, style ) },
   { NULL, 0, 0 }
};

typedef struct 
{
   GNOCL_STD_OPTIONS_VARS;

   Gnocl_ObjBool text;
   /* Gnocl_ObjBool tooltip; */
   Gnocl_ObjBool tooltip_private;
   Gnocl_ObjBool icon;
   Gnocl_StringBool command;
} ItemOptions;

GnoclWidgetOptions options[] = 
{
   GNOCL_STD_OPTIONS_NAMES( ItemOptions ),
   { "-text", GNOCL_OBJ, GNOCL_OFFSET( ItemOptions, text ) },
   /* { "-tooltip", GNOCL_OBJ, GNOCL_OFFSET( ItemOptions, tooltip ) }, */
   { "-tooltip_private", GNOCL_OBJ, 
         GNOCL_OFFSET( ItemOptions, tooltip_private ) },
   { "-icon", GNOCL_OBJ, GNOCL_OFFSET( ItemOptions, icon ) },
   { "-command", GNOCL_STRING, GNOCL_OFFSET( ItemOptions, command ) },
   { NULL, 0, 0 }
};

typedef struct
{
   char       *command;
   char       *name;
   Tcl_Interp *interp;
   GtkWidget  *widget;
} ItemParams;


static void freeItemOpts( ItemOptions *p )
{
   GNOCL_STD_OPTIONS_FREE( p );

   GNOCL_FREE_TCL_OPT( p->text );
   /* GNOCL_FREE_TCL_OPT( p->tooltip ); */
   GNOCL_FREE_TCL_OPT( p->tooltip_private );
   GNOCL_FREE_TCL_OPT( p->icon );
   GNOCL_FREE_MEM_OPT( p->command );
}

/*
   test, if params are valid
   set params and show window with new settings
*/
static int toolbarConfigure( Tcl_Interp *interp, ToolbarParams *pp )
{
   GtkToolbarStyle style = GTK_TOOLBAR_BOTH;

   if( pp->style.val )
   {
      if( !strcmp( pp->style.val, "icons" ) )
         style = GTK_TOOLBAR_ICONS;
      else if( !strcmp( pp->style.val, "text" ) )
         style = GTK_TOOLBAR_TEXT;
      else if( !strcmp( pp->style.val, "both" ) )
         style = GTK_TOOLBAR_BOTH;
      else
      {
         Tcl_AppendResult( interp, "wrong parameter \"", 
               pp->style.val, "\" for option style. Must be one of"
               "icons, text, or both", (char *)NULL );
         return TCL_ERROR;
      }
   } 
   gtk_toolbar_set_style( pp->widget, style );

   if( pp->orientation.changed )
   {
      GtkOrientation orientation;
      if( gnoclGetOrientationType( interp, pp->orientation.val, &orientation )
            != TCL_OK )
         return TCL_ERROR;
      gtk_toolbar_set_orientation( pp->widget, orientation );
   }

   return TCL_OK;
}

static int doCommand( ItemParams *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 toolBarItemCmdFunc( GtkWidget *widget, gpointer data )
{
   ItemParams *para = (ItemParams *)data;
   doCommand( para );
}

static void itemDestroyFunc( GtkWidget *widget, gpointer data )
{
   ItemParams *p = (ItemParams *)data;

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

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

int itemConfigure( Tcl_Interp *interp, ItemParams *para, 
      ItemOptions *popt )
{
   GNOCL_STD_OPTIONS_HANDLE( popt, para->widget );

   if( popt->command.changed )
   {
      g_free( para->command );
      if( popt->command.val && popt->command.val[0] )
      {
         para->command = popt->command.val;
         popt->command.val = NULL;
      }
      else
         para->command = NULL;
   }

   /* FIXME: configure tooltip, icon... */
   return TCL_OK;
}


static int toolbarItemFunc( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   static char *cmds[] = { GNOCL_STD_CMD_NAMES, "invoke", 
         "configure", NULL };
   enum cmdIdx { GNOCL_STD_CMD_ENUMS, InvokeIdx, ConfigureIdx };

   ItemParams *para = (ItemParams *)data;
   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, para->widget );
            break;
      case InvokeIdx:
            if( GTK_WIDGET_IS_SENSITIVE( para->widget ) )
               return doCommand( para );;
            break;
      case ConfigureIdx:
      {
         ItemOptions opt;
         int ret = TCL_ERROR;
         memset( &opt, 0, sizeof( opt ) );
         if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
               options, (char *)&opt ) == TCL_OK )
            ret = itemConfigure( interp, para, &opt );
         freeItemOpts( &opt );
         if( ret != TCL_OK )
            return TCL_ERROR;
      }
      break;
   }
   return TCL_OK;
}

static int toolbarFuncAdd( ToolbarParams *pp, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[], int atEnd )
{

   const char *type;
   GtkWidget *icon = NULL;
   ItemParams *para = NULL;

   if( objc < 3 )
   {
      Tcl_WrongNumArgs( interp, 2, objv, "type ?option val ...?" );
      return TCL_ERROR;
   }
   type = Tcl_GetString( objv[2] );
   if( !strcmp( type, "space" ) )
   {
      if( objc != 3 )
      {
         Tcl_WrongNumArgs( interp, 3, objv, "" );
         return TCL_ERROR;
      }
      if( atEnd )
         gtk_toolbar_append_space( pp->widget );
      else
         gtk_toolbar_prepend_space( pp->widget );
   }
   else if( !strcmp( type, "widget" ) )
   {
      ItemOptions opt;
      GtkWidget *child;
      GtkToolbarChildType childType = GTK_TOOLBAR_CHILD_WIDGET;
      int        ret;

      if( objc < 4 )
      {
         Tcl_WrongNumArgs( interp, 3, objv, "widget-ID ?option val ...?" );
         return TCL_ERROR;
      }

      child = gnoclGetWidgetFromName( Tcl_GetString( objv[3] ), interp );
      if( child == NULL )
         return TCL_ERROR;

      #if 0
      /* FIXME: it seems, that only GTK_TOOLBAR_CHILD_WIDGET works! */

      /* the order of the ifs are important! */
      if( GTK_IS_RADIO_BUTTON( child ) )
         childType = GTK_TOOLBAR_CHILD_RADIOBUTTON;
      else if( GTK_IS_TOGGLE_BUTTON( child ) )
         childType = GTK_TOOLBAR_CHILD_TOGGLEBUTTON;
      else if( GTK_IS_BUTTON( child ) )
         childType = GTK_TOOLBAR_CHILD_BUTTON;
      printf( "childType: %d\n", childType );
      #endif
      
      memset( &opt, 0, sizeof( opt ) );
      if( gnoclParseAllWidgetOpts( interp, objc, objv, 3,
            options, (char *)&opt ) != TCL_OK )
         return TCL_ERROR;
      if( opt.icon.changed )
      {
         if( gnoclGetPixmap( interp, NULL, opt.icon.val, &icon ) != TCL_OK )
            return TCL_ERROR;
      }

      para = g_new( ItemParams, 1 );
      para->command = NULL;
      para->interp = interp;

      para->widget = 
            (atEnd ?  gtk_toolbar_append_element : gtk_toolbar_prepend_element)
            ( pp->widget, childType, child, 
            gnoclGetString( opt.text.val ), 
            opt.tooltip.val, /* gnoclGetString( opt.tooltip.val ),  */
            gnoclGetString( opt.tooltip_private.val ), 
            icon, toolBarItemCmdFunc, para );
      ret = itemConfigure( interp, para, &opt );
      freeItemOpts( &opt );
      if( ret != TCL_OK )
         return TCL_ERROR;
   }
   else if( !strcmp( type, "item" ) )
   {
      ItemOptions opt;
      int         ret;

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

      if( opt.icon.changed )
      {
         if( gnoclGetPixmap( interp, NULL, opt.icon.val, &icon ) != TCL_OK )
            return TCL_ERROR;
      }

      para = g_new( ItemParams, 1 );
      para->command = NULL;
      para->interp = interp;
      para->widget = 
            (atEnd ?  gtk_toolbar_append_item : gtk_toolbar_prepend_item)
            ( pp->widget, 
            gnoclGetString( opt.text.val ), 
            opt.tooltip.val, /* gnoclGetString( opt.tooltip.val ), */
            gnoclGetString( opt.tooltip_private.val ), 
            icon, toolBarItemCmdFunc, para );
      ret = itemConfigure( interp, para, &opt );
      freeItemOpts( &opt );
      if( ret != TCL_OK )
         return TCL_ERROR;
   }
   else
   {
      Tcl_AppendResult( interp, "wrong type \"", 
            type, "\". Must be one of space, widget, or item", 
            (char *)NULL );
      return TCL_ERROR;
   }

   if( para != NULL )
   {
      para->name = gnoclGetAutoWidgetId( );

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

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

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

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

   return TCL_OK;
}

static int toolbarFunc( 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 };

   ToolbarParams *pp = (ToolbarParams *)data;
   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, GTK_WIDGET( pp->widget ) );
            break;
      case ConfigureIdx:
            {
               if( toolbarConfigure( interp, pp ) != TCL_OK )
                  return TCL_ERROR;
            }
            break;
      case AddIdx:      /* add is a shortcut for addEnd */
      case BeginIdx:
      case EndIdx:
            return toolbarFuncAdd( pp, interp, objc, objv, idx != BeginIdx );
   }
   return TCL_OK;
}

int gnoclToolbarCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   ToolbarParams *pp = g_new( ToolbarParams, 1 );
   char *name = gnoclGetAutoWidgetId( );
   
   memset( pp, 0, sizeof( *pp ) );
   if( gnoclParseAllWidgetOpts( interp, objc, objv, 0,
         toolbarOptions, (char *)pp ) != TCL_OK )
      return TCL_ERROR;

   pp->widget = GTK_TOOLBAR( gtk_toolbar_new( GTK_ORIENTATION_HORIZONTAL, 
         GTK_TOOLBAR_BOTH ) );
   if( toolbarConfigure( interp, pp ) != TCL_OK )
   {
      gtk_widget_destroy( GTK_WIDGET( pp->widget ) );
      return TCL_ERROR;
   }

   gnoclMemNameAndWidget( name, GTK_WIDGET( pp->widget ) );
   gtk_widget_show( GTK_WIDGET( pp->widget ) );

   Tcl_CreateObjCommand( interp, name, toolbarFunc, pp, NULL );

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

   return TCL_OK;
}

