/* 
 * tkTableUtil.c --
 *
 *	This module contains utility functions for table widgets.
 *
 * Copyright (c) 2000-2002 Jeffrey Hobbs
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tkTableUtil.c,v 1.5 2004/02/08 03:09:46 cerney Exp $
 */

#include "tkTable.h"

static char *	Cmd_GetName _ANSI_ARGS_((const Cmd_Struct *cmds, int val));
static int	Cmd_GetValue _ANSI_ARGS_((const Cmd_Struct *cmds,
			Arg arg));
static void	Cmd_GetError _ANSI_ARGS_((Tcl_Interp *interp,
			const Cmd_Struct *cmds, Arg arg));

/*
 *--------------------------------------------------------------
 *
 * Table_ClearHashTable --
 *	This procedure is invoked to clear a STRING_KEY hash table,
 *	freeing the string entries and then deleting the hash table.
 *	The hash table cannot be used after calling this, except to
 *	be freed or reinitialized.
 *
 * Results:
 *	Cached info will be lost.
 *
 * Side effects:
 *	Can cause redraw.
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */
void
Table_ClearHashTable(Tcl_HashTable *hashTblPtr)
{
    Tcl_HashEntry *entryPtr;
    Tcl_HashSearch search;
    char *value;

    for (entryPtr = Tcl_FirstHashEntry(hashTblPtr, &search);
	 entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
	value = (char *) Tcl_GetHashValue(entryPtr);
	if (value != NULL) ckfree(value);
    }

    Tcl_DeleteHashTable(hashTblPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TableOptionBdSet --
 *
 *	This routine configures the borderwidth value for a tag.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	It may adjust the tag struct values of bd[0..4] and borders.
 *
 *----------------------------------------------------------------------
 */

int
TableOptionBdSet(clientData, interp, tkwin, value, widgRec, offset)
    ClientData clientData;		/* Type of struct being set. */
    Tcl_Interp *interp;			/* Used for reporting errors. */
    Tk_Window tkwin;			/* Window containing table widget. */
    Arg  value;				/* Value of option. */
    char *widgRec;			/* Pointer to record for item. */
    int offset;				/* Offset into item. */
{
    char **borderStr;
    int *bordersPtr, *bdPtr;
    int type	= (int) clientData;
    int result	= TCL_OK;
    int argc;
    Arg *args;


    if ((type == BD_TABLE) && (STREQ(Tcl_GetString(value),"") )) {
	/*
	 * NULL strings aren't allowed for the table global -bd
	 */
	Tcl_AppendResult(interp, "borderwidth value may not be empty",
		         NULL);
	return TCL_ERROR;
    }

    if ((type == BD_TABLE) || (type == BD_TABLE_TAG)) {
	TableTag *tagPtr = (TableTag *) (widgRec + offset);
	borderStr	= &(tagPtr->borderStr);
	bordersPtr	= &(tagPtr->borders);
	bdPtr		= tagPtr->bd;
    } else if (type == BD_TABLE_WIN) {
	TableEmbWindow *tagPtr = (TableEmbWindow *) widgRec;
	borderStr	= &(tagPtr->borderStr);
	bordersPtr	= &(tagPtr->borders);
	bdPtr		= tagPtr->bd;
    } else {
	Tcl_Panic("invalid type given to TableOptionBdSet\n");
	return TCL_ERROR; /* lint */
    }

    result = Tcl_ListObjGetElements(interp, value, &argc, &args);
    if (result == TCL_OK) {
	int i, bd[4];

	if (((type == BD_TABLE) && (argc == 0)) || (argc == 3) || (argc > 4)) {
	    Tcl_AppendResult(interp,
		    "1, 2 or 4 values must be specified for borderwidth",
		             NULL);
	    result = TCL_ERROR;
	} else {
	    /*
	     * We use the shadow bd array first, in case we have an error
	     * parsing arguments half way through.
	     */
	    for (i = 0; i < argc; i++) {
		if (Tk_GetPixels(interp, tkwin, Tcl_GetString(args[i]), &(bd[i])) != TCL_OK) {
		    result = TCL_ERROR;
		    break;
		}
	    }
	    /*
	     * If everything is OK, store the parsed and given values for
	     * easy retrieval.
	     */
	    if (result == TCL_OK) {
		for (i = 0; i < argc; i++) {
		    bdPtr[i] = MAX(0, bd[i]);
		}
		if (*borderStr) {
		    ckfree(*borderStr);
		}
		if (value) {
		    *borderStr	= (char *) ckalloc( strlen( Tcl_GetString(value) ) + 1);
		    strcpy(*borderStr, Tcl_GetString(value));
		} else {
		    *borderStr	= NULL;
		}
		*bordersPtr	= argc;
	    }
	}
	/*ckfree ((char *) objv);*/
    }

    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * TableOptionBdGet --
 *
 * Results:
 *	Value of the -bd option.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Arg
TableOptionBdGet(clientData, tkwin, widgRec, offset, freeProcPtr)
    ClientData clientData;		/* Type of struct being set. */
    Tk_Window tkwin;			/* Window containing canvas widget. */
    char *widgRec;			/* Pointer to record for item. */
    int offset;				/* Offset into item. */
    Tcl_FreeProc **freeProcPtr;		/* Pointer to variable to fill in with
					 * information about how to reclaim
					 * storage for return string. */
{
    register int type	= (int) clientData;

    if (type == BD_TABLE) {
	return LangStringArg( ((TableTag *) (widgRec + offset))->borderStr);
    } else if (type == BD_TABLE_TAG) {
	return LangStringArg( ((TableTag *) widgRec)->borderStr);
    } else if (type == BD_TABLE_WIN) {
	return LangStringArg( ((TableEmbWindow *) widgRec)->borderStr);
    } else {
	Tcl_Panic("invalid type given to TableOptionBdSet\n");
	return NULL; /* lint */
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TableTagConfigureBd --
 *	This routine configures the border values based on a tag.
 *	The previous value of the bd string (oldValue) is assumed to
 *	be a valid value for this tag.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	It may adjust the value used by -bd.
 *
 *----------------------------------------------------------------------
 */

int
TableTagConfigureBd(Table *tablePtr, TableTag *tagPtr,
	Arg oldValue, int nullOK)
{
    int i, argc, result = TCL_OK;
    Arg *args;

    /*
     * First check to see if the value really changed.
     */
    if (strcmp(tagPtr->borderStr ? tagPtr->borderStr : "",
	    Tcl_GetString(oldValue) ? Tcl_GetString(oldValue) : "") == 0) {
	return TCL_OK;
    }

    tagPtr->borders = 0;
    if (!nullOK && ((tagPtr->borderStr == NULL)
	    || (*(tagPtr->borderStr) == '\0'))) {
	/*
	 * NULL strings aren't allowed for this tag
	 */
	result = TCL_ERROR;
    } else if (tagPtr->borderStr) {
        result = Tcl_ListObjGetElements(tablePtr->interp, LangStringArg(tagPtr->borderStr), &argc, &args);
	if (result == TCL_OK) {
	    if ((!nullOK && (argc == 0)) || (argc == 3) || (argc > 4)) {
		Tcl_SetResult(tablePtr->interp, "1, 2 or 4 values must be specified to -borderwidth", TCL_STATIC);


		result = TCL_ERROR;
	    } else {
		for (i = 0; i < argc; i++) {
		    if (Tk_GetPixels(tablePtr->interp, tablePtr->tkwin,
			    Tcl_GetString(args[i]), &(tagPtr->bd[i])) != TCL_OK) {
			result = TCL_ERROR;
			break;
		    }
		    tagPtr->bd[i] = MAX(0, tagPtr->bd[i]);
		}
		tagPtr->borders = argc;
	    }
	    /* ckfree ((char *) objv); */
	}
    }

    if (result != TCL_OK) {
	if (tagPtr->borderStr) {
	    ckfree ((char *) tagPtr->borderStr);
	}
	if (oldValue != NULL) {
	    size_t length = strlen(Tcl_GetString(oldValue)) + 1;
	    /*
	     * We are making the assumption that oldValue is correct.
	     * We have to reparse in case the bad new value had a couple
	     * of correct args before failing on a bad pixel value.
	     */
    	    Tcl_ListObjGetElements(tablePtr->interp, oldValue, &argc, &args);
	    for (i = 0; i < argc; i++) {
		Tk_GetPixels(tablePtr->interp, tablePtr->tkwin,
			Tcl_GetString(args[i]), &(tagPtr->bd[i]));
	    }
	    /* ckfree ((char *) objv); */
	    tagPtr->borders	= argc;
	    tagPtr->borderStr	= (char *) ckalloc(length);
	    memcpy(tagPtr->borderStr, Tcl_GetString(oldValue), length);
	} else {
	    tagPtr->borders	= 0;
	    tagPtr->borderStr	=          NULL;
	}
    }

    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * Cmd_OptionSet --
 *
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
Cmd_OptionSet(ClientData clientData, Tcl_Interp *interp,
	Tk_Window unused, Arg value, char *widgRec, int offset)
{
  Cmd_Struct *p = (Cmd_Struct *)clientData;
  int mode = Cmd_GetValue(p,value);
  if (!mode) {
    Cmd_GetError(interp,p,value);
    return TCL_ERROR;
  }
  *((int*)(widgRec+offset)) = mode;
  return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Cmd_OptionGet --
 *
 *
 * Results:
 *	Value of the option.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Arg
Cmd_OptionGet(ClientData clientData, Tk_Window unused,
	      char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
{
  Cmd_Struct *p = (Cmd_Struct *)clientData;
  int mode = *((int*)(widgRec+offset));
  return LangStringArg(Cmd_GetName(p,mode));
}

/*
 * simple Cmd_Struct lookup functions
 */

char *
Cmd_GetName(const Cmd_Struct *cmds, int val)
{
  for(;cmds->name && cmds->name[0];cmds++) {
    if (cmds->value==val) return cmds->name;
  }
  return NULL;
}

int
Cmd_GetValue(const Cmd_Struct *cmds, Arg arg)
{
  unsigned int len = strlen(Tcl_GetString(arg));
  for(;cmds->name && cmds->name[0];cmds++) {
    if (!strncmp(cmds->name, Tcl_GetString(arg), len)) return cmds->value;
  }
  return 0;
}

void
Cmd_GetError(Tcl_Interp *interp, const Cmd_Struct *cmds, Arg arg)
{
  int i;
  char *argstring = Tcl_GetString(arg);
  Tcl_AppendResult(interp, "bad option \"", argstring, "\" must be ", (char *) 0);
  for(i=0;cmds->name && cmds->name[0];cmds++,i++) {
    Tcl_AppendResult(interp, (i?", ":""), cmds->name, (char *) 0);
  }
}