
/*
 * gnoclMenuItem.c --
 *
 * This file implements the menuItem 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:
        02: added "invoke" to label and checkButton
   2002-01: bugfixes concerning -command and -variable handling
        12: added pixmap_menu_item
        11: added STD_OPTIONS
        10: check item
        09: underlined accelerators 
   2001-03: Begin of developement
 */

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

typedef struct
{
   /* opts for all types */
   GNOCL_STD_OPTIONS_VARS;
   Gnocl_ObjBool    text;
   Gnocl_ObjBool    icon;
   Gnocl_StringBool command;
   Gnocl_StringBool accelerator;
   Gnocl_StringBool submenu;

   /* opts for checkButton */
   Gnocl_StringBool variable;
   Gnocl_ObjBool    onValue;
   Gnocl_ObjBool    offValue;
} MenuItemOptions;

typedef struct
{
   char        *name;
   Tcl_Interp  *interp;
   GtkMenuItem *menuItem;
} MenuSeparatorParams;

typedef struct
{
   char        *name;
   Tcl_Interp  *interp;
   GtkMenuItem *menuItem;
   char        *command;
} MenuLabelParams;


typedef enum {  EMPTY = 0, 
                DONT_COMMAND = 1 << 0, 
                DONT_SET     = 1 << 1, 
                DONT_GET     = 1 << 2,
                ALL          = 0xFF } InSignalType;

typedef struct
{
   char         *name;
   Tcl_Interp   *interp;
   GtkMenuItem  *menuItem;
   char         *command;
   char         *variable;
   Tcl_Obj      *onValue;
   Tcl_Obj      *offValue;
   InSignalType inSignal;
} MenuCheckParams;

typedef enum { SEPARATOR, LABEL, CHECK, RADIO } ItemType;

static const GnoclWidgetOptions menuLabelOptions[] =
{
   GNOCL_STD_OPTIONS_NAMES( MenuItemOptions ),
   { "-text", GNOCL_OBJ, GNOCL_OFFSET( MenuItemOptions, text ) },
   { "-icon", GNOCL_OBJ, GNOCL_OFFSET( MenuItemOptions, icon ) },
   { "-command", GNOCL_STRING, GNOCL_OFFSET( MenuItemOptions, command ) },
   { "-accelerator", GNOCL_STRING, GNOCL_OFFSET( MenuItemOptions, 
         accelerator ) },
   { "-submenu", GNOCL_STRING, GNOCL_OFFSET( MenuItemOptions, submenu ) },
   { NULL, 0, 0 }
};

static const GnoclWidgetOptions menuCheckOptions[] =
{
   GNOCL_STD_OPTIONS_NAMES( MenuItemOptions ),
   { "-text", GNOCL_OBJ, GNOCL_OFFSET( MenuItemOptions, text ) },
   { "-command", GNOCL_STRING, GNOCL_OFFSET( MenuItemOptions, command ) },
   { "-accelerator", GNOCL_STRING, GNOCL_OFFSET( MenuItemOptions, 
         accelerator ) },
   { "-variable", GNOCL_STRING, GNOCL_OFFSET( MenuItemOptions, variable ) },
   { "-onValue", GNOCL_OBJ, GNOCL_OFFSET( MenuItemOptions, onValue ) },
   { "-offValue", GNOCL_OBJ, GNOCL_OFFSET( MenuItemOptions, offValue ) },
   { NULL, 0, 0 }
};

static void freeOpts( MenuItemOptions *p )
{
   GNOCL_STD_OPTIONS_FREE( p );
   GNOCL_FREE_TCL_OPT( p->text );
   GNOCL_FREE_TCL_OPT( p->icon );
   GNOCL_FREE_MEM_OPT( p->command );
   GNOCL_FREE_MEM_OPT( p->accelerator );
   GNOCL_FREE_MEM_OPT( p->variable );
   GNOCL_FREE_TCL_OPT( p->onValue );
   GNOCL_FREE_TCL_OPT( p->offValue );
   GNOCL_FREE_MEM_OPT( p->submenu );
}

static void menuLabelCmdFunc( GtkWidget *widget, gpointer data )
{
   MenuLabelParams *p = (MenuLabelParams *)data;
   if( p->command )
      Tcl_Eval( p->interp, p->command );
}

static int doCommand( MenuCheckParams *para, Tcl_Obj *val )
{
   if( para->command )
   {
      static Gnocl_PercSubst ps[] = {
         { 'w', GNOCL_STRING },  /* widget */
         { 'v', GNOCL_OBJ },     /* value */
         { 0 }
      };
      ps[0].val.str = para->name;
      ps[1].val.obj = val;

      return gnoclPercentSubstAndEval( para->interp, ps, para->command );
   }
   return TCL_OK;
}


static void menuLabelDestroyFunc( GtkWidget *widget, gpointer data )
{
   MenuLabelParams *para = (MenuLabelParams *)data;

   /* untrace and free command and variable */
   gnoclAttacheCommand( &para->command, NULL,
         GTK_OBJECT( para->menuItem ), "activate", menuLabelCmdFunc, para );

   gnoclForgetWidgetFromName( para->name );
   Tcl_DeleteCommand( para->interp, para->name );
   g_free( para->name );

   g_free( para );
}

static char *traceFunc( ClientData data,
      Tcl_Interp *interp, char *name1, char *name2, int flags)
{
   MenuCheckParams *para = (MenuCheckParams *)data;

   if( ( para->inSignal & DONT_GET ) == 0 )
   {
      Tcl_Obj *val = Tcl_GetVar2Ex( interp, name1, name2, flags );
      /* TODO: what to do if val == NULL? */
      if( val != NULL )
      {
         int on = gnoclCheckIsOn( interp, para->onValue, 
               para->offValue, val );

         /* TODO: what to do if on < 0 i.e. an error occured? */
         if( on >= 0 )
         {
            para->inSignal |= DONT_SET|DONT_COMMAND;
            /* toggledFuncRet is only automatically called if the status of
               the check button is changed, i.e. it was not 'on' */
            gtk_check_menu_item_set_active( 
                  GTK_CHECK_MENU_ITEM( para->menuItem ), on );
            para->inSignal &= ~(DONT_SET|DONT_COMMAND);
            doCommand( para, on ? para->onValue : para->offValue );
         }
      }
   }

   return NULL;
}

/* TODO: use same function for checkButton and checkMenuItem? */
static int toggledFuncRet( MenuCheckParams *para )
{
   int val = GTK_CHECK_MENU_ITEM( para->menuItem )->active; 
   Tcl_Obj *valObj = val ? para->onValue : para->offValue;

   int ret = TCL_OK;
   /*
   printf( "DEBGU checkButton in toggle %d onValue: %s %d offValue: %s %d\n", 
      val,
      Tcl_GetString( para->onValue ), Tcl_IsShared( para->onValue ),
      Tcl_GetString( para->offValue ), Tcl_IsShared( para->offValue ) );
   */

   if( ( para->inSignal & DONT_SET ) == 0 && para->variable )
   {
      para->inSignal |= DONT_GET;
      if( Tcl_SetVar2Ex( para->interp, para->variable, NULL, 
            valObj, TCL_GLOBAL_ONLY ) == NULL )
         ret = TCL_ERROR;
      para->inSignal &= ~DONT_GET;
   }
 
   if( ( para->inSignal & DONT_COMMAND ) == 0 )
   {
      if( doCommand( para, valObj ) != TCL_OK )
         ret = TCL_ERROR;
   }

   return ret;
}

static void toggledFunc( GtkWidget *widget, gpointer data )
{
   toggledFuncRet( (MenuCheckParams *)data );
   
}

static void menuCheckDestroyFunc( GtkWidget *widget, gpointer data )
{
   MenuCheckParams *para = (MenuCheckParams *)data;

   /* untrace and free command and variable */
   gnoclAttacheCmdAndVar( 
         1, &para->command, NULL,
         1, &para->variable, NULL,
         "activate", GTK_OBJECT( para->menuItem ), toggledFunc,
         para->interp, traceFunc, para );

   gnoclForgetWidgetFromName( para->name );
   Tcl_DeleteCommand( para->interp, para->name );
   g_free( para->name );

   Tcl_DecrRefCount( para->onValue );
   Tcl_DecrRefCount( para->offValue );
   g_free( para );
}

static int menuLabelConfigure( Tcl_Interp *interp, 
      MenuLabelParams *para, MenuItemOptions *popt )
{
   GNOCL_STD_OPTIONS_HANDLE( popt, GTK_WIDGET( para->menuItem ) );

   if( popt->command.changed )
      gnoclAttacheCommand( &para->command, &popt->command.val,
            GTK_OBJECT( para->menuItem ), "activate", menuLabelCmdFunc, para );

   if( popt->submenu.changed )
   {
      GtkWidget *childWidget = gnoclGetWidgetFromName( popt->submenu.val, 
            interp );
      if( childWidget == NULL )
         return TCL_ERROR;

      if( !GTK_CHECK_TYPE( childWidget, GTK_TYPE_MENU ) )
      {
         Tcl_AppendResult( interp, "submenu window \"", 
               popt->submenu.val, "\" is not a menu item.", (char *)NULL );
         return TCL_ERROR;
      }

      if( gtk_menu_get_attach_widget( GTK_MENU( childWidget ) ) != NULL )
      {
         Tcl_AppendResult( interp, "submenu \"", popt->submenu.val, 
               "\" has already been attached.", (char *)NULL );
         return TCL_ERROR;
      }
      gtk_menu_item_set_submenu( para->menuItem, childWidget );
      /* TODO: if submenu is detachable: set title of submenu to text */
   }

   if( popt->accelerator.changed )
   {
      /*
      GtkWidget *al = gtk_accel_label_new( "Ctl+Q" );
      gtk_accel_label_set_accel_widget( GTK_ACCEL_LABEL( al ),
            GTK_WIDGET( pp->widget ) );
      */
      guint key;
      GdkModifierType mod;

      gtk_accelerator_parse( popt->accelerator.val, &key, &mod );
      /*
      printf( "\"%s\" key: %d mod: %d\n", popt->accelerator.val, key, mod );
      */
      if( key != 0 || mod != 0 )
         gtk_widget_add_accelerator( GTK_WIDGET( para->menuItem ), 
               "activate", gnoclGetAccelGroup(), key, mod, 
               GTK_ACCEL_VISIBLE );
   }

   return TCL_OK; 
}

static int menuCheckConfigure( Tcl_Interp *interp, 
      MenuCheckParams *para, MenuItemOptions *popt )
{
   
   GNOCL_STD_OPTIONS_HANDLE( popt, GTK_WIDGET( para->menuItem ) );

   if( popt->onValue.changed )
   {
      if( para->onValue ) Tcl_DecrRefCount( para->onValue );
      para->onValue = popt->onValue.val;
      Tcl_IncrRefCount( para->onValue );
   }

   if( popt->offValue.changed )
   {
      if( para->offValue ) Tcl_DecrRefCount( para->offValue );
      para->offValue = popt->offValue.val;
      Tcl_IncrRefCount( para->offValue );
   }

   gnoclAttacheCmdAndVar( 
         popt->command.changed, &para->command, &popt->command.val,
         popt->variable.changed, &para->variable, &popt->variable.val,
         "activate", GTK_OBJECT( para->menuItem ), toggledFunc,
         interp, traceFunc, para );

   if( popt->variable.changed && para->variable != NULL )
   {
      /* if variable does not exist -> set it, else set widget state */
      Tcl_Obj *var = Tcl_GetVar2Ex( interp, para->variable, NULL, 
            TCL_GLOBAL_ONLY );
      if( var == NULL )
      {
         int on = GTK_CHECK_MENU_ITEM( para->menuItem )->active; 
         Tcl_Obj *valObj = on ? para->onValue : para->offValue;

         para->inSignal |= DONT_GET;
         Tcl_SetVar2Ex( para->interp, para->variable, NULL, valObj, 
               TCL_GLOBAL_ONLY );
         para->inSignal &= ~DONT_GET;
      }
      else
      {
         if( gnoclCheckIsOn( interp, para->onValue, para->offValue, var ) > 0 )
         {
            para->inSignal |= DONT_SET|DONT_COMMAND;
            gtk_check_menu_item_set_active( 
                  GTK_CHECK_MENU_ITEM( para->menuItem ), 1 );
            /* since the state of checkButton is changed, 
               toggledFuncRet is automatically called here */
            para->inSignal &= ~(DONT_SET|DONT_COMMAND);
         }
      }
      
   }

   if( popt->accelerator.changed )
   {
      guint key;
      GdkModifierType mod;

      gtk_accelerator_parse( popt->accelerator.val, &key, &mod );
      /*
      printf( "\"%s\" key: %d mod: %d\n", popt->accelerator.val, key, mod );
      */
      if( key != 0 || mod != 0 )
         gtk_widget_add_accelerator( GTK_WIDGET( para->menuItem ), 
               "activate", gnoclGetAccelGroup(), key, mod, 
               GTK_ACCEL_VISIBLE );
   }

   return TCL_OK; 
}

static int menuSeparatorFunc( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   static char *cmds[] = { GNOCL_STD_CMD_NAMES, NULL };
   enum cmdIdx { GNOCL_STD_CMD_ENUMS };
   MenuSeparatorParams *para = (MenuSeparatorParams *)data;
   GtkWidget *widget = GTK_WIDGET( para->menuItem ); 
   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 );
   }

   return TCL_OK;
}

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

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

               memset( &opt, 0, sizeof( opt ) );

               if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
                     menuCheckOptions, (char *)&opt ) == TCL_OK )
                  ret = menuCheckConfigure( interp, para, &opt );

               freeOpts( &opt );
               if( ret != TCL_OK )
                  return TCL_ERROR;
            }
            break;
      case InvokeIdx:
            if( GTK_WIDGET_IS_SENSITIVE( widget ) )
               gtk_menu_item_activate( GTK_MENU_ITEM( para->menuItem ) ); 
            break;
      case GetValueIdx:
            {
               int on = GTK_CHECK_MENU_ITEM( para->menuItem )->active; 
               Tcl_Obj *obj;
               if( objc != 2 )
               {
                  Tcl_WrongNumArgs( interp, 2, objv, NULL );
                  return TCL_ERROR;
               }
               obj = on ? para->onValue : para->offValue;
               /* this is not necessary!!? Tcl_IncrRefCount( obj ); */
               Tcl_SetObjResult( interp, obj );
            }
            break;
      case SetValueIdx:
            {
               int on;
               if( objc != 3 )
               {
                  Tcl_WrongNumArgs( interp, 2, objv, "value" );
                  return TCL_ERROR;
               }
               on = gnoclCheckIsOn( interp, para->onValue, para->offValue, 
                     objv[2] );
               if( on >= 0 )
               {
                  para->inSignal |= (DONT_SET | DONT_COMMAND);
                  gtk_check_menu_item_set_active( 
                        GTK_CHECK_MENU_ITEM( para->menuItem ), on );
                  para->inSignal &= ~(DONT_SET | DONT_COMMAND);
                  return toggledFuncRet( para );
               }
               return TCL_ERROR;
            }
            break;
   }

   return TCL_OK;
}


static int menuLabelFunc( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   static char *cmds[] = { GNOCL_STD_CMD_NAMES,
         "configure", "invoke", NULL };
   enum cmdIdx { GNOCL_STD_CMD_ENUMS, ConfigureIdx, InvokeIdx };
   MenuLabelParams *para = (MenuLabelParams *)data;
   GtkWidget *widget = GTK_WIDGET( para->menuItem ); 
   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:
            {
               MenuItemOptions opt;
               int ret = TCL_ERROR;

               memset( &opt, 0, sizeof( opt ) );

               if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
                     menuLabelOptions, (char *)&opt ) == TCL_OK )
                  ret = menuLabelConfigure( interp, para, &opt );

               freeOpts( &opt );
               if( ret != TCL_OK )
                  return TCL_ERROR;
            }
            break;
      case InvokeIdx:
            if( GTK_WIDGET_IS_SENSITIVE( widget ) )
               gtk_menu_item_activate( GTK_MENU_ITEM( para->menuItem ) ); 
            break;
   }

   return TCL_OK;
}

static GtkMenuItem *makeMenuItem( Tcl_Interp *interp, MenuItemOptions *pp, 
      ItemType itemType )
{
   char *text = NULL;
   char *stock = NULL;
   GnoclStringType type = GNOCL_STR_STR;
   GtkMenuItem *widget = NULL;

   /* TODO: error message if command or variable changed */
   if( pp->text.changed )
   {
      type = gnoclGetStringType( pp->text.val );

      if( type & GNOCL_STR_STOCK )
         stock = gnoclGetStockMenuName( NULL, pp->text.val, 
               &text );
      else
         text = gnoclGetString( pp->text.val );
   }
      
   if( stock )
      widget = GTK_MENU_ITEM( gnome_stock_menu_item( stock, text ) );
   else
   {
      if( itemType == CHECK )
         widget = GTK_MENU_ITEM( gtk_check_menu_item_new_with_label( 
               text ? text : "" ) );
      else if( itemType == RADIO )
         widget = GTK_MENU_ITEM( gtk_radio_menu_item_new_with_label( 
               NULL, text ? text : "" ) );
      else
      {
         if( pp->icon.changed )
         {
            GtkWidget *icon;
            GtkWidget *label;
            int ret = gnoclGetPixmap( interp, NULL, pp->icon.val, &icon );
            if( ret != TCL_OK )
               return NULL;

            label = gtk_label_new( text ? text : "" );
            gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );

            gtk_widget_show( label );
            widget = GTK_MENU_ITEM( gtk_pixmap_menu_item_new() );
            gtk_container_add( GTK_CONTAINER( widget ), label );
            if( icon != NULL )
            {
               gtk_widget_show( icon );
               gtk_pixmap_menu_item_set_pixmap( 
                     GTK_PIXMAP_MENU_ITEM( widget ), icon );
            }
         }
         else
            widget = GTK_MENU_ITEM( gtk_menu_item_new_with_label( 
                  text ? text : "" ) );
      }
   }

   if( type & GNOCL_STR_UNDERLINE )
   {
      guint key = gnoclLabelParseULine( 
            GTK_WIDGET( widget ), text );
      if( key != GDK_VoidSymbol )
         gnoclMenu_registerKey( GTK_WIDGET( widget ), key );
   }

   if( stock )
   {
      guchar key; 
      guint8 mod;

      if( gnome_stock_menu_accel( stock, &key, &mod ) )
      {
         gtk_widget_add_accelerator( GTK_WIDGET( widget ), 
            "activate", gnoclGetAccelGroup(), 
            key, mod, GTK_ACCEL_VISIBLE );
      }
   }

   if( type & GNOCL_STR_STOCK )
      g_free( text );
   g_free( stock );

   return widget;
}

static int separatorCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   MenuSeparatorParams *para;

   if( objc != 2 )
   {
      Tcl_WrongNumArgs( interp, 2, objv, "" );
      return TCL_ERROR;
   }

   para = g_new( MenuSeparatorParams, 1 );
   para->interp = interp;
   para->name = gnoclGetAutoWidgetId();
   para->menuItem = GTK_MENU_ITEM( gtk_menu_item_new( ) );
   gtk_widget_set_sensitive( GTK_WIDGET( para->menuItem ), 0 );

   /*
   memset( &opt, 0, sizeof( opt ) );
   para->command = NULL;

   ret = menuItemConfigure( interp, para, &opt );
   freeOpts( &opt );
   if( ret != TCL_OK )
   {
      gtk_widget_destroy( GTK_WIDGET( para->menuItem ) );
      return TCL_ERROR;
   }

   gtk_signal_connect( GTK_OBJECT( para->menuItem ), "destroy", 
         GTK_SIGNAL_FUNC( menuItemDestroyFunc ), para );
   */

   gnoclMemNameAndWidget( para->name, GTK_WIDGET( para->menuItem ) );
   gtk_widget_show( GTK_WIDGET( para->menuItem ) );

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

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

   return TCL_OK;
}

static int checkCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   MenuItemOptions opt;
   MenuCheckParams *para;
   int             ret;
   GtkMenuItem     *menuItem;

   memset( &opt, 0, sizeof( opt ) );

   if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
         menuCheckOptions, (char *)&opt ) != TCL_OK )
      return TCL_ERROR;

   menuItem = makeMenuItem( interp, &opt, CHECK );
   if( menuItem == NULL )
      return TCL_ERROR;

   para = g_new( MenuCheckParams, 1 );
   para->name = gnoclGetAutoWidgetId();
   para->interp = interp;
   para->menuItem = menuItem;
   para->command = NULL;
   para->variable = NULL;
   para->onValue = Tcl_NewIntObj( 1 );
   Tcl_IncrRefCount( para->onValue );
   para->offValue = Tcl_NewIntObj( 0 );
   Tcl_IncrRefCount( para->offValue );
   para->inSignal = 0;

   gtk_check_menu_item_set_show_toggle( 
         GTK_CHECK_MENU_ITEM( para->menuItem ), 1 );

   ret = menuCheckConfigure( interp, para, &opt );
   freeOpts( &opt );
   if( ret != TCL_OK )
   {
      gtk_widget_destroy( GTK_WIDGET( para->menuItem ) );
      return TCL_ERROR;
   }

   gtk_signal_connect( GTK_OBJECT( para->menuItem ), "destroy", 
         GTK_SIGNAL_FUNC( menuCheckDestroyFunc ), para );

   gnoclMemNameAndWidget( para->name, GTK_WIDGET( para->menuItem ) );
   gtk_widget_show( GTK_WIDGET( para->menuItem ) );

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

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

   return TCL_OK;
}

static int radioCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   MenuItemOptions opt;
   MenuCheckParams *para;
   int             ret;
   GtkMenuItem     *menuItem;

   memset( &opt, 0, sizeof( opt ) );

   if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
         menuCheckOptions, (char *)&opt ) != TCL_OK )
      return TCL_ERROR;

   menuItem = makeMenuItem( interp, &opt, RADIO );
   if( menuItem == NULL )
      return TCL_ERROR;

   para = g_new( MenuCheckParams, 1 );
   para->interp = interp;
   para->name = gnoclGetAutoWidgetId();
   para->menuItem = menuItem;
   para->command = NULL;
   para->onValue = Tcl_NewIntObj( 1 );
   Tcl_IncrRefCount( para->onValue );
   para->offValue = Tcl_NewIntObj( 0 );
   Tcl_IncrRefCount( para->offValue );

   gtk_check_menu_item_set_show_toggle( 
         GTK_CHECK_MENU_ITEM( para->menuItem ), 1 );

   ret = menuCheckConfigure( interp, para, &opt );
   freeOpts( &opt );
   if( ret != TCL_OK )
   {
      gtk_widget_destroy( GTK_WIDGET( para->menuItem ) );
      return TCL_ERROR;
   }

   gtk_signal_connect( GTK_OBJECT( para->menuItem ), "destroy", 
         GTK_SIGNAL_FUNC( menuCheckDestroyFunc ), para );

   gnoclMemNameAndWidget( para->name, GTK_WIDGET( para->menuItem ) );
   gtk_widget_show( GTK_WIDGET( para->menuItem ) );

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

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

   return TCL_OK;
}


static int labelCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   MenuItemOptions opt;
   MenuLabelParams *para;
   int             ret;
   GtkMenuItem     *menuItem;

   memset( &opt, 0, sizeof( opt ) );

   if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
         menuLabelOptions, (char *)&opt ) != TCL_OK )
      return TCL_ERROR;

   menuItem = makeMenuItem( interp, &opt, LABEL );
   if( menuItem == NULL )
      return TCL_ERROR;

   para = g_new( MenuLabelParams, 1 );
   para->interp = interp;
   para->name = gnoclGetAutoWidgetId();
   para->menuItem = menuItem;
   para->command = NULL;

   ret = menuLabelConfigure( interp, para, &opt );
   freeOpts( &opt );
   if( ret != TCL_OK )
   {
      gtk_widget_destroy( GTK_WIDGET( para->menuItem ) );
      return TCL_ERROR;
   }

   gtk_signal_connect( GTK_OBJECT( para->menuItem ), "destroy", 
         GTK_SIGNAL_FUNC( menuLabelDestroyFunc ), para );

   gnoclMemNameAndWidget( para->name, GTK_WIDGET( para->menuItem ) );
   gtk_widget_show( GTK_WIDGET( para->menuItem ) );

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

   return TCL_OK;
}

int gnoclMenuItemCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   char *types[] = { "label", "checkButton", "radioButton", "separator", 
         NULL };
   enum typesIdx { LabelIdx, CheckIdx, RadioIdx, SeparatorIdx };
   int type;
   
   if( objc < 2 )
   {
      Tcl_WrongNumArgs( interp, 1, objv, "type ?option val ...?" );
      return TCL_ERROR;
   }
   if( Tcl_GetIndexFromObj( interp, objv[1], types, "type", 0,
         &type ) != TCL_OK )
      return TCL_ERROR;

   switch( type )
   {
      case SeparatorIdx:
            return separatorCmd( data, interp, objc, objv );
      case LabelIdx:
            return labelCmd( data, interp, objc, objv );
      case CheckIdx:
            return checkCmd( data, interp, objc, objv );
      case RadioIdx:
printf( "FIXME: radio menu item is not yet implemented\n" );
            return radioCmd( data, interp, objc, objv );
   }

   return TCL_OK;
}

