
/*
 * clist.c --
 *
 * This file implements the clist 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:
        11: renamed list to clist
            new commands sort, getCellText
            new option columnClicked
            removed option titleActive: now coupled with columnClicked
   2001-07: Begin of developement
 */

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

typedef struct
{
   GNOCL_STD_OPTIONS_VARS;

   Gnocl_IntBool    columns;
   Gnocl_ObjBool    selectionMode;
   Gnocl_ObjBool    shadow;
   Gnocl_ListBool   children;
   Gnocl_ListBool   titles;
   Gnocl_ListBool   justifications;
   Gnocl_BoolBool   titlesShow;
   Gnocl_StringBool selectionChanged;
   Gnocl_StringBool columnClicked;
   Gnocl_ObjBool    scrollbar;
} ClistOptions;

static const GnoclWidgetOptions widgetOptions[] =
{   
   GNOCL_STD_OPTIONS_NAMES( ClistOptions ),
   
   { "-columns", GNOCL_INT, GNOCL_OFFSET( ClistOptions, columns ) },
   { "-selectionMode", GNOCL_OBJ, 
         GNOCL_OFFSET( ClistOptions, selectionMode ) },
   { "-shadow", GNOCL_OBJ, GNOCL_OFFSET( ClistOptions, shadow ) },
   { "-children", GNOCL_LIST, GNOCL_OFFSET( ClistOptions, children ) },
   { "-titles", GNOCL_LIST, GNOCL_OFFSET( ClistOptions, titles ) },
   { "-justifications", GNOCL_LIST, 
         GNOCL_OFFSET( ClistOptions, justifications ) },
   { "-titlesShow", GNOCL_BOOL, GNOCL_OFFSET( ClistOptions, titlesShow ) },
   { "-selectionChanged", GNOCL_STRING, 
         GNOCL_OFFSET( ClistOptions, selectionChanged ) },
   { "-columnClicked", GNOCL_STRING, 
         GNOCL_OFFSET( ClistOptions, columnClicked ) },
   { "-scrollbar", GNOCL_OBJ, GNOCL_OFFSET( ClistOptions, scrollbar ) },
   { 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;
   GtkCList   *clist;
   GtkScrolledWindow *scrollWin;
   char       *selectionChanged;
   char       *columnClicked;
   Tcl_Obj    *sortFunc;
   int        sortColumn;
} ClistParams;

static void freeOpts( ClistOptions *p )
{
   GNOCL_STD_OPTIONS_FREE( p );

   GNOCL_FREE_TCL_OPT( p->selectionMode );
   GNOCL_FREE_TCL_OPT( p->shadow );
   GNOCL_FREE_TCL_OPT( p->children );
   GNOCL_FREE_TCL_OPT( p->titles );
   GNOCL_FREE_TCL_OPT( p->justifications );
   GNOCL_FREE_TCL_OPT( p->scrollbar );
}

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

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

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

static void columnClickedFunc( GtkCList *clist, gint column, gpointer data )
{
   static Gnocl_PercSubst ps[] = { 
      { 'w', GNOCL_STRING },  /* widget */
      { 'c', GNOCL_INT },     /* col */
      { 0 }
   };
   ClistParams *para = (ClistParams *)data;
   
   ps[0].val.str = para->name;
   ps[1].val.i = column;

   gnoclPercentSubstAndEval( para->interp, ps, para->columnClicked );
}

static void selectionChangedFunc( GtkCList *clist, ClistParams *para,
   int col, int row, GdkEventButton *event, int selected )
{
   static Gnocl_PercSubst ps[] = { 
      { 'w', GNOCL_STRING },  /* widget */
      { 's', GNOCL_BOOL },    /* selected */
      { 'r', GNOCL_INT },     /* row */
      { 'c', GNOCL_INT },     /* col */
      { 'b', GNOCL_INT },     /* button */
      { 't', GNOCL_STRING },  /* event type */
      { 0 }
   };
   
   if( para->selectionChanged != NULL )
   {
      ps[0].val.str = para->name;
      ps[1].val.b = selected;
      ps[2].val.i = row;
      ps[3].val.i = col;
      ps[4].val.i = event ? event->button : 0;
      ps[5].val.str = event ? gnoclEventToString( event->type ) : NULL;

      gnoclPercentSubstAndEval( para->interp, ps, para->selectionChanged );
   }
}

static void selectRowFunc( GtkCList *clist, gint row, gint column,
      GdkEventButton *event, gpointer data )
{
   selectionChangedFunc( clist, (ClistParams *)data, column, row, 
         event, 1 );
}
static void unselectRowFunc( GtkCList *clist, gint row, gint column,
      GdkEventButton *event, gpointer data )
{
   selectionChangedFunc( clist, (ClistParams *)data, column, row, 
         event, 0 );
}

static int getNoColumns( GtkCList *clist )
{
   GtkArg arg;
   arg.name = "n_columns";
   gtk_object_getv( GTK_OBJECT( clist ), 1, &arg );
   return GTK_VALUE_INT( arg );
}

static int getValidColumn( GtkCList *clist, Tcl_Interp *interp, 
      Tcl_Obj *obj, int *column )
{
   /* TODO? allow "end-x", range "1-4", clist "1 3 4" */
   if( Tcl_GetIntFromObj( interp, obj, column ) != TCL_OK )
      return TCL_ERROR;
   if( *column < 0 || *column >= getNoColumns( clist ) )
   {
      Tcl_SetResult( interp, "column not in a valid range", TCL_STATIC );
      return TCL_ERROR;
   }
   return TCL_OK;
}


static int addChildren( GtkCList *clist, Tcl_Interp *interp, 
      Tcl_Obj *children, int begin )
{
   int noColumns;
   int n, noChilds;
   typedef char *pchar;
   pchar *texts;

   if( Tcl_ListObjLength( interp, children, &noChilds ) != TCL_OK  )
   {
      Tcl_SetResult( interp, "row-clist must be proper clist", 
            TCL_STATIC );
      return TCL_ERROR;
   }
   /* FIXME: if noColumns == 1 && noChilds > 1 */

   noColumns =  getNoColumns( clist );

   texts = g_new( pchar, noColumns );
   for( n = 0; n < noChilds; ++n )
   {
      Tcl_Obj *tp;
      if( Tcl_ListObjIndex( interp, children, n, &tp ) != TCL_OK )
         return TCL_ERROR;

      if( noColumns == 1 )
         texts[0] = (char *)gnoclGetString( tp );
      else
      {
         int     k, nCol;
         Tcl_Obj *tpp;
         if( Tcl_ListObjLength( interp, tp, &nCol ) != TCL_OK  )
         {
            Tcl_SetResult( interp, "row entry must be proper clist", 
                  TCL_STATIC );
            return TCL_ERROR;
         }
         if( nCol > noColumns )
         {
            char buffer[1024];
            sprintf( buffer,  "too many columns (%d) in row entry, max is %d", 
                  nCol, noColumns );
            Tcl_SetResult( interp, buffer, TCL_VOLATILE );
            return TCL_ERROR;
         }
         for( k = 0; k < nCol; ++k )
         {
            if( Tcl_ListObjIndex( interp, tp, k, &tpp ) != TCL_OK )
               return TCL_ERROR;
            texts[k] = (char *)gnoclGetString( tpp );
         }
         for( ; k < noColumns; ++k )
            texts[k] = (char *)"";
      }

      if( begin )
         gtk_clist_prepend( clist, texts );
      else
         gtk_clist_append( clist, texts );
   }
   g_free( texts );

   /* set optimal column width */
   for( n = 0; n < noColumns; ++n )
      gtk_clist_set_column_width( clist, n, 
            gtk_clist_optimal_column_width( clist, n ) );

   return TCL_OK;
}


/*
   test, if params are valid
   set params and show window with new settings
*/
static int clistConfigure( Tcl_Interp *interp, ClistParams *para, 
      ClistOptions *popt )
{
   int noColumns = getNoColumns( para->clist );

   GNOCL_STD_OPTIONS_HANDLE( popt, GTK_WIDGET( para->clist ) );

   if( popt->selectionMode.changed )
   {
      GtkSelectionMode mode;
      if( gnoclGetSelectionMode( interp, popt->selectionMode.val, &mode )
            != TCL_OK )
         return TCL_ERROR;
      gtk_clist_set_selection_mode( para->clist, mode );
   }

   if( popt->shadow.changed )
   {
      GtkShadowType type;
      if( gnoclGetShadowType( interp, popt->shadow.val, &type )
            != TCL_OK )
         return TCL_ERROR;
      gtk_clist_set_shadow_type( para->clist, type );
   }

   if( popt->children.changed )
   {
      /* first delete all current children */
      gtk_clist_clear( para->clist );
      if( addChildren( para->clist, interp, popt->children.val, 0 ) 
            != TCL_OK )
         return TCL_ERROR;
   }

   if( popt->titlesShow.changed )
   {
      if( popt->titlesShow.val )
         gtk_clist_column_titles_show( para->clist );
      else
         gtk_clist_column_titles_hide( para->clist );
   }

   if( popt->titles.changed )
   {
      int nCol, n;

      Tcl_ListObjLength( interp, popt->titles.val, &nCol );
      if( nCol > noColumns )
      {
         char buffer[1024];
         sprintf( buffer,  "too many titles (%d), max is %d", 
               nCol, noColumns );
         Tcl_SetResult( interp, buffer, TCL_VOLATILE );
         return TCL_ERROR;
      }

      for( n = 0; n < nCol; ++n )
      {
         Tcl_Obj *tp;
         if( Tcl_ListObjIndex( interp, popt->titles.val, n, &tp ) != TCL_OK )
            return TCL_ERROR;

         gtk_clist_set_column_title( para->clist, n, gnoclGetString( tp ) );
      }
   }

   if( popt->justifications.changed )
   {
      int nCol, n;

      Tcl_ListObjLength( interp, popt->justifications.val, &nCol );
      if( nCol > noColumns )
      {
         char buffer[1024];
         sprintf( buffer,  "too many justifications (%d), max is %d", 
               nCol, noColumns );
         Tcl_SetResult( interp, buffer, TCL_VOLATILE );
         return TCL_ERROR;
      }

      for( n = 0; n < nCol; ++n )
      {
         Tcl_Obj *tp;
         GtkJustification justify;

         if( Tcl_ListObjIndex( interp, popt->justifications.val, n, &tp ) 
               != TCL_OK )
            return TCL_ERROR;
         if( gnoclGetJustification( interp, tp, &justify ) != TCL_OK )
            return TCL_ERROR;

         gtk_clist_set_column_justification( para->clist, n, justify );
      }
   }

   if( popt->selectionChanged.changed )
   {
      g_free( para->selectionChanged );
      if( *popt->selectionChanged.val == 0 )
         para->selectionChanged = NULL;
      else
      {
         para->selectionChanged = popt->selectionChanged.val;
         popt->selectionChanged.val = NULL;
      }
   }

   if( popt->columnClicked.changed )
   {
      printf( "in columnClicked\n" );
      gnoclAttacheCommand( &para->columnClicked, &popt->columnClicked.val,
            GTK_OBJECT( para->clist ), "click-column", 
            (GtkSignalFunc)columnClickedFunc, para );
      if( para->columnClicked == NULL )
         gtk_clist_column_titles_passive( para->clist );
      else
         gtk_clist_column_titles_active( para->clist );
   }

   if( popt->scrollbar.changed )
   {
      GtkPolicyType hor, vert;
      if( gnoclGetScrollbarPolicy( interp, popt->scrollbar.val, 
            &hor, &vert ) != TCL_OK )
         return TCL_ERROR;

      gtk_scrolled_window_set_policy( para->scrollWin, hor, vert );
   }

   return TCL_OK;
}

static int getSelection( ClistParams *pp, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   Tcl_Obj *resList;
   GList   *lp;

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

   /* FIXME: how to do this in a documented way? */
   for( lp = g_list_first( pp->clist->selection ); lp != NULL; 
         lp = g_list_next( lp ) )
      Tcl_ListObjAppendElement( interp, resList, 
            Tcl_NewIntObj( GPOINTER_TO_INT( lp->data ) ) );

   Tcl_SetObjResult( interp, resList );

   return TCL_OK;
}

static int setSelection( ClistParams *pp, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   int k, no;

   if( objc != 3 || 
         Tcl_ListObjLength( interp, objv[2], &no ) != TCL_OK )
   {
      Tcl_WrongNumArgs( interp, 2, objv, "clist-of-rows" );
      return TCL_ERROR;
   }
   for( k = 0; k < no; ++k )
   {
      Tcl_Obj *tp;
      int n;

      if( Tcl_ListObjIndex( interp, objv[2], k, &tp ) != TCL_OK 
            || Tcl_GetIntFromObj( interp, tp, &n ) != TCL_OK )
         return TCL_ERROR;

      gtk_clist_select_row( pp->clist, n, -1 );
   }

   return TCL_OK;
}

static const char *getCellText( GtkCListRow *row, int col )
{
   switch (row->cell[col].type)
   {
      case GTK_CELL_TEXT:
         return GTK_CELL_TEXT( row->cell[col] )->text;
      case GTK_CELL_PIXTEXT:
         return GTK_CELL_PIXTEXT( row->cell[col] )->text;
      default:
   }
   return NULL;
}

static int compareFunc( GtkCList *clist, gconstpointer ptr1, 
      gconstpointer  ptr2 )
{
   ClistParams *para = (ClistParams *)gtk_object_get_user_data( 
         GTK_OBJECT( clist ) );
   GtkCListRow *row1 = (GtkCListRow *) ptr1;
   GtkCListRow *row2 = (GtkCListRow *) ptr2;
   const char *txt1 = getCellText( row1, para->sortColumn );
   const char *txt2 = getCellText( row2, para->sortColumn );
   Tcl_Obj *objv[3] = { para->sortFunc, 
         Tcl_NewStringObj( txt1, -1 ), Tcl_NewStringObj( txt2, -1 ) };

   int ret = Tcl_EvalObjv( para->interp, 3, objv, 
         TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT );
   /*
   Tcl_DecrRefCount( objv[1] );
   Tcl_DecrRefCount( objv[2] );
   */
   if( ret == TCL_OK )
   {
      Tcl_Obj *obj = Tcl_GetObjResult( para->interp );
      int val;
      if( Tcl_GetIntFromObj( para->interp, obj, &val ) == TCL_OK )
         return val;
   }
   return -1;
}


static int doSort( ClistParams *para, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   typedef struct 
   {
      Gnocl_BoolBool reverse;
      Gnocl_ObjBool  command;
   } SortOptions;

   GnoclWidgetOptions options[] = 
   {
      { "-reverse", GNOCL_BOOL, GNOCL_OFFSET( SortOptions, reverse ) },
      { "-command", GNOCL_OBJ, GNOCL_OFFSET( SortOptions, command ) },
      { NULL, 0, 0 }
   };

   SortOptions params;
   int column;

   if( objc < 3 || objc % 2 == 0 )
   {
      Tcl_WrongNumArgs( interp, 2, objv, "column ?option val ...?" );
      return TCL_ERROR;
   }

   if( getValidColumn( para->clist, interp, objv[2], &column ) != TCL_OK )
      return TCL_ERROR;

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

   if( params.reverse.changed && params.reverse.val )
      gtk_clist_set_sort_type( para->clist, GTK_SORT_DESCENDING );
   else
      gtk_clist_set_sort_type( para->clist, GTK_SORT_ASCENDING );

   if( params.command.changed && params.command.val )
   {
      para->sortFunc = params.command.val;
      params.command.val = NULL;
      para->sortColumn = column;
      gtk_clist_set_compare_func( para->clist, compareFunc );
   }
   else
   {
      gtk_clist_set_compare_func( para->clist, NULL );
      para->sortFunc = NULL;
   }
   
   gtk_clist_set_sort_column( para->clist, column );
   gtk_clist_sort( para->clist );

   if( para->sortFunc ) Tcl_DecrRefCount( para->sortFunc );

   return TCL_OK;
}


static int columnConfigure( ClistParams *pp, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   typedef struct 
   {
      Gnocl_BoolBool   titleActive;
      Gnocl_StringBool title;
      Gnocl_ObjBool    justification;
      Gnocl_BoolBool   visible;
      Gnocl_BoolBool   resizeable;
      Gnocl_BoolBool   autoResize;
      Gnocl_IntBool    width;
      Gnocl_IntBool    minWidth;
      Gnocl_IntBool    maxWidth;
   } ConfOptions;

   GnoclWidgetOptions options[] = 
   {
      { "-titleActive", GNOCL_BOOL, GNOCL_OFFSET( ConfOptions, titleActive ) },
      { "-title", GNOCL_STRING, GNOCL_OFFSET( ConfOptions, title ) },
      { "-justification", GNOCL_OBJ, 
            GNOCL_OFFSET( ConfOptions, justification ) },
      { "-visible", GNOCL_BOOL, GNOCL_OFFSET( ConfOptions, visible ) },
      { "-resizeable", GNOCL_BOOL, GNOCL_OFFSET( ConfOptions, resizeable ) },
      { "-autoResize", GNOCL_BOOL, GNOCL_OFFSET( ConfOptions, autoResize ) },
      { "-width", GNOCL_INT, GNOCL_OFFSET( ConfOptions, width ) },
      { "-minWidth", GNOCL_INT, GNOCL_OFFSET( ConfOptions, minWidth ) },
      { "-maxWidth", GNOCL_INT, GNOCL_OFFSET( ConfOptions, maxWidth ) },
      { NULL, 0, 0 }
   };

   ConfOptions params;
   int column;

   if( objc < 3 || objc % 2 == 0 )
   {
      Tcl_WrongNumArgs( interp, 2, objv, 
            "column ?option val ...?" );
      return TCL_ERROR;
   }

   if( getValidColumn( pp->clist, interp, objv[3], &column ) != TCL_OK )
      return TCL_ERROR;

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



   if( params.titleActive.changed )
   {
      if( params.titleActive.val )
         gtk_clist_column_title_active( pp->clist, column );
      else
         gtk_clist_column_title_passive( pp->clist, column );
   }

   if( params.title.changed )
      gtk_clist_set_column_title( pp->clist, column, params.title.val );

   if( params.justification.changed )
   {
      GtkJustification justify;

      if( gnoclGetJustification( interp, params.justification.val, 
            &justify ) != TCL_OK )
         return TCL_ERROR;
      gtk_clist_set_column_justification( pp->clist, column, justify );
   }

   if( params.visible.changed )
      gtk_clist_set_column_visibility( pp->clist, column, params.visible.val );

   if( params.resizeable.changed )
      gtk_clist_set_column_resizeable( pp->clist, column, 
            params.resizeable.val );

   if( params.autoResize.changed )
      gtk_clist_set_column_auto_resize( pp->clist, column, 
            params.autoResize.val );

   if( params.width.changed )
      gtk_clist_set_column_width( pp->clist, column, params.width.val );

   if( params.minWidth.changed )
      gtk_clist_set_column_min_width( pp->clist, column, 
            params.minWidth.val );

   if( params.maxWidth.changed )
      gtk_clist_set_column_max_width( pp->clist, column, 
            params.maxWidth.val );

   return TCL_OK;
}

static int clistFunc( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   /* TODO: getOptimalColumnWidth */
   static char *cmds[] = {  GNOCL_STD_CMD_NAMES,
         "configure", "add", "addBegin", "addEnd", 
         "getSelection", "select",
         "rowConfigure", "columnConfigure", "cellConfigure",
         "getCellText", "sort",
         NULL };
   enum cmdIdx { GNOCL_STD_CMD_ENUMS,
         ConfigureIdx, AddIdx, BeginIdx, EndIdx,
         GetSelectionIdx, SelectIdx,
         RowConfigureIdx, ColumnConfigureIdx, CellConfigureIdx,
         GetCellTextIdx, SortIdx };

   ClistParams *para = (ClistParams *)data;
   int idx;
   /* select: [clist 1 2 4 "5 to 10"] all end end-5 "" */

   GtkWidget *widget = GTK_WIDGET( para->clist );

   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:
            {
               ClistOptions opt;
               int ret = TCL_ERROR;
               memset( &opt, 0, sizeof( opt ) );

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

               freeOpts( &opt );
               if( ret != TCL_OK )
                  return TCL_ERROR;
            }
            break;
      case AddIdx:
      case BeginIdx:
      case EndIdx:
            if( objc != 3 )
            {
               Tcl_WrongNumArgs( interp, 2, objv, "row-list" );
               return TCL_ERROR;
            }
            return addChildren( para->clist, interp, objv[2], idx == BeginIdx );
      case GetSelectionIdx:
            return getSelection( para, interp, objc, objv );
      case SelectIdx:
            return setSelection( para, interp, objc, objv );
      case ColumnConfigureIdx:
            return columnConfigure( para, interp, objc, objv );
      case RowConfigureIdx:
      case CellConfigureIdx:
                           /* TODO */
                           break;
      case GetCellTextIdx:
            {
               char *text;
               int row, column;
               if( objc != 4 )
               {
                  Tcl_WrongNumArgs( interp, 2, objv, "row column" );
                  return TCL_ERROR;
               }
               if( Tcl_GetIntFromObj( interp, objv[2], &row ) )
                  return TCL_ERROR;
               if( Tcl_GetIntFromObj( interp, objv[3], &column ) )
                  return TCL_ERROR;
               if( gtk_clist_get_text( para->clist, row, column, &text ) == 0 )
               {
                  Tcl_SetResult( interp, "text could not be retrieved.",
                        TCL_STATIC );
                  return TCL_ERROR;
               }

               Tcl_SetObjResult( interp, Tcl_NewStringObj( text, -1 ) );
               return TCL_OK;
            }
      case SortIdx:
            return doSort( para, interp, objc, objv );
   }
   return TCL_OK;
}

int gnoclClistCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   ClistOptions opt;
   ClistParams *para;
   int ret;
   GtkWidget *widget;

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

   if( opt.columns.val < 1 )
   {
      Tcl_SetResult( interp, "number of columns must be greater 0", 
            TCL_STATIC );
      return TCL_ERROR;
   }


   para = g_new( ClistParams, 1 );
   #if 0
   /* TODO: test, if label != "" and shadow != "flat" ? */
   if( opt.label.val || opt.shadow.val )
      para->frame = GTK_FRAME( gtk_frame_new( NULL ) );
   else
      para->frame = NULL;
   #endif
   para->name = gnoclGetAutoWidgetId( );
   para->interp = interp;
   para->clist = GTK_CLIST( gtk_clist_new( opt.columns.val ) );
   para->selectionChanged = NULL;
   para->columnClicked = NULL;

   para->scrollWin = GTK_SCROLLED_WINDOW( 
         gtk_scrolled_window_new( NULL, NULL ) );
   gtk_scrolled_window_set_policy( para->scrollWin, 
         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
   gtk_container_add( GTK_CONTAINER( para->scrollWin ), 
         GTK_WIDGET( para->clist ) );

#if 0
   if( para->frame )
   {
      widget = GTK_WIDGET( para->frame );
      gtk_container_add( GTK_CONTAINER( para->frame ), 
            GTK_WIDGET( para->clist ) );
   }
   else
      widget = GTK_WIDGET( para->clist );
#endif   
   /* widget = GTK_WIDGET( para->clist ); */
   widget = GTK_WIDGET( para->scrollWin );

   ret = clistConfigure( interp, para, &opt );
   freeOpts( &opt );
   if( ret != TCL_OK )
   {
      /*
      if( para->frame )
         gtk_widget_destroy( GTK_WIDGET( para->frame ) );
      */
      gtk_widget_destroy( GTK_WIDGET( para->clist ) );
      return TCL_ERROR;
   }

   /* used for compare function */
   gtk_object_set_user_data( GTK_OBJECT( para->clist ), para );

   /* set default values */
   if( opt.titles.changed && !opt.titlesShow.changed )
   {
      gtk_clist_column_titles_show( para->clist );
   }
   if( !opt.columnClicked.changed )
      gtk_clist_column_titles_passive( para->clist );

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

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

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

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

   gtk_signal_connect( GTK_OBJECT( para->clist ), "select-row", 
         GTK_SIGNAL_FUNC( selectRowFunc ), para );
   gtk_signal_connect( GTK_OBJECT( para->clist ), "unselect-row", 
         GTK_SIGNAL_FUNC( unselectRowFunc ), para );

 
   return TCL_OK;
}

