
/*
 * menuBar.c --
 *
 * This file implements the menuBar 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:
        09: accelerator for menuItems
   2001-03: Begin of developement
 */

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

typedef struct
{
   Gnocl_ObjBool shadow;
   Gnocl_ListBool   children;
} MenuBarOptions;

static const GnoclWidgetOptions widgetOptions[] =
{
   { "-shadow", GNOCL_OBJ, GNOCL_OFFSET( MenuBarOptions, shadow ) },
   { "-children", GNOCL_LIST, GNOCL_OFFSET( MenuBarOptions, children ) },
   { NULL, 0, 0 }
};

typedef struct
{
   Tcl_Interp *interp;
   char       *name;
   GtkMenuBar *widget;
} MenuBarParams;

static void freeOpts( MenuBarOptions *p )
{
   GNOCL_FREE_TCL_OPT( p->shadow );
   GNOCL_FREE_TCL_OPT( p->children );
}


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

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

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


static int menuBarAddChildren( MenuBarParams *pp, Tcl_Interp *interp,
      Tcl_Obj *children, int begin )
{
   int n, noChilds;

   if( Tcl_ListObjLength( interp, children, &noChilds ) != TCL_OK 
         || noChilds < 1 )
   {
      Tcl_SetResult( interp, "widget-list must be proper list", 
            TCL_STATIC );
      return TCL_ERROR;
   }
   for( n = 0; n < noChilds; ++n )
   {
      GtkWidget *childWidget;
      Tcl_Obj   *tp;
      char      *childName;
      guint     key;

      if( Tcl_ListObjIndex( interp, children, n, &tp ) != TCL_OK )
         return TCL_ERROR;
      childName = Tcl_GetString( tp );

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

      if( !GTK_CHECK_TYPE( childWidget, GTK_TYPE_MENU_ITEM ) )
      {
         Tcl_AppendResult( interp, "child window \"", 
               Tcl_GetString( tp ), "\" is not a menu item.", (char *)NULL );
         return TCL_ERROR;
      }

      key = gnoclMenu_getKey( childWidget );
      if( key != GDK_VoidSymbol )
      {
         gtk_widget_add_accelerator( GTK_WIDGET( childWidget ), 
               "activate_item", gnoclGetAccelGroup(), key, 
               GDK_MOD1_MASK, GTK_ACCEL_LOCKED );
      }

      /* FIXME: gtk_menu_bar_insert */
      if( begin )
         gtk_menu_bar_append( pp->widget, childWidget );
      else
         gtk_menu_bar_prepend( pp->widget, childWidget );
   }

   return TCL_OK;
}

static int menuBarFuncAdd( MenuBarParams *pp, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[], int begin )
{
   if( objc != 3 )
   {
      Tcl_WrongNumArgs( interp, 2, objv, "widget-list" );
      return TCL_ERROR;
   }

   return menuBarAddChildren( pp, interp, objv[2], begin );
}

/*
   test, if params are valid
   set params and show window with new settings
*/
static int menuBarConfigure( Tcl_Interp *interp, MenuBarParams *para,
      MenuBarOptions *popt )
{
   if( popt->shadow.changed )
   {
      GtkShadowType shadow = GTK_SHADOW_OUT;
      if( gnoclGetShadowType( interp, popt->shadow.val, &shadow ) != TCL_OK )
         return TCL_ERROR;
      gtk_menu_bar_set_shadow_type( para->widget, shadow );
   }
   
   if( popt->children.val != NULL )
      menuBarAddChildren( para, interp, popt->children.val, 1 );

   return TCL_OK;
}

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

   MenuBarParams *para = (MenuBarParams *)data;
   GtkWidget *widget = GTK_WIDGET( para->widget );
   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:
            {
               MenuBarOptions opt;
               int ret = TCL_ERROR;

               memset( &opt, 0, sizeof( opt ) );
               if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
                     widgetOptions, (char *)&opt ) == TCL_OK )
                  ret = menuBarConfigure( interp, para, &opt );

               freeOpts( &opt );
               if( ret != TCL_OK )
                  return TCL_ERROR;
            }
            break;
      case AddIdx:
      case BeginIdx:
      case EndIdx:
            return menuBarFuncAdd( para, interp, objc, objv, idx != EndIdx );
   }
   return TCL_OK;
}

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

   para = g_new( MenuBarParams, 1 );
   para->widget = GTK_MENU_BAR( gtk_menu_bar_new() );
   if( menuBarConfigure( interp, para, &opt ) != TCL_OK )
   {
      gtk_widget_destroy( GTK_WIDGET( para->widget ) );
      return TCL_ERROR;
   }

   para->name = gnoclGetAutoWidgetId( );
   para->interp = interp;
   gnoclMemNameAndWidget( para->name, GTK_WIDGET( para->widget ) );
   gtk_widget_show( GTK_WIDGET( para->widget ) );

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

   Tcl_SetObjResult( interp, Tcl_NewStringObj( para->name, -1 ) );
   
   gtk_signal_connect( GTK_OBJECT( para->widget ), "destroy", 
      GTK_SIGNAL_FUNC( destroyFunc ), para );


   return TCL_OK;
}

