/* * tkTableTag.c -- * * This module implements tags for table widgets. * * Copyright (c) 1998-2001 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: tkTableTag.c,v 1.10 2001/07/01 01:33:17 hobbs Exp $ */ #include "tkTable.h" static TableTag *TableTagGetEntry _ANSI_ARGS_((Table *tablePtr, char *name, int objc, char **argv)); static unsigned int TableTagGetPriority _ANSI_ARGS_((Table *tablePtr, TableTag *tagPtr)); static void TableImageProc _ANSI_ARGS_((ClientData clientData, int x, int y, int width, int height, int imageWidth, int imageHeight)); static char *tagCmdNames[] = { "celltag", "cget", "coltag", "configure", "delete", "exists", "includes", "lower", "names", "raise", "rowtag", (char *) NULL }; enum tagCmd { TAG_CELLTAG, TAG_CGET, TAG_COLTAG, TAG_CONFIGURE, TAG_DELETE, TAG_EXISTS, TAG_INCLUDES, TAG_LOWER, TAG_NAMES, TAG_RAISE, TAG_ROWTAG }; static Cmd_Struct tagState_vals[]= { {"unknown", STATE_UNKNOWN}, {"normal", STATE_NORMAL}, {"disabled", STATE_DISABLED}, {"", 0 } }; static Tk_CustomOption tagStateOpt = { Cmd_OptionSet, Cmd_OptionGet, (ClientData)(&tagState_vals) }; static Tk_CustomOption tagBdOpt = { TableOptionBdSet, TableOptionBdGet, (ClientData) BD_TABLE_TAG }; /* * The default specification for configuring tags * Done like this to make the command line parsing easy */ static Tk_ConfigSpec tagConfig[] = { {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", "center", Tk_Offset(TableTag, anchor), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK }, {TK_CONFIG_BORDER, "-background", "background", "Background", NULL, Tk_Offset(TableTag, bg), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK }, {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth", "", 0 /* no offset */, TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK, &tagBdOpt }, {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", NULL, Tk_Offset(TableTag, fg), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK }, {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_FONT, "-font", "font", "Font", NULL, Tk_Offset(TableTag, tkfont), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK }, {TK_CONFIG_STRING, "-image", "image", "Image", NULL, Tk_Offset(TableTag, imageStr), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK }, {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify", "left", Tk_Offset(TableTag, justify), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK }, {TK_CONFIG_INT, "-multiline", "multiline", "Multiline", "-1", Tk_Offset(TableTag, multiline), TK_CONFIG_DONT_SET_DEFAULT }, {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", "flat", Tk_Offset(TableTag, relief), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK }, {TK_CONFIG_INT, "-showtext", "showText", "ShowText", "-1", Tk_Offset(TableTag, showtext), TK_CONFIG_DONT_SET_DEFAULT }, {TK_CONFIG_CUSTOM, "-state", "state", "State", "unknown", Tk_Offset(TableTag, state), TK_CONFIG_DONT_SET_DEFAULT, &tagStateOpt }, {TK_CONFIG_INT, "-wrap", "wrap", "Wrap", "-1", Tk_Offset(TableTag, wrap), TK_CONFIG_DONT_SET_DEFAULT }, {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0} }; /* * The join tag structure is used to create a combined tag, so it * keeps priority info. */ typedef struct { TableTag tag; /* must be first */ unsigned int magic; unsigned int pbg, pfg, pborders, prelief, ptkfont, panchor, pimage; unsigned int pstate, pjustify, pmultiline, pwrap, pshowtext; } TableJoinTag; /* *---------------------------------------------------------------------- * * TableImageProc -- * Called when an image associated with a tag is changed. * * Results: * None. * * Side effects: * Invalidates the whole table. * This should only invalidate affected cells, but that info * is not managed... * *---------------------------------------------------------------------- */ static void TableImageProc(ClientData clientData, int x, int y, int width, int height, int imageWidth, int imageHeight) { TableInvalidateAll((Table *)clientData, 0); } /* *---------------------------------------------------------------------- * * TableNewTag -- * ckallocs space for a new tag structure and inits the structure. * * Results: * Returns a pointer to the new structure. Must be freed later. * * Side effects: * None. * *---------------------------------------------------------------------- */ TableTag * TableNewTag(Table *tablePtr) { TableTag *tagPtr; /* * If tablePtr is NULL, make a regular tag, otherwise make a join tag. */ if (tablePtr == NULL) { tagPtr = (TableTag *) ckalloc(sizeof(TableTag)); memset((VOID *) tagPtr, 0, sizeof(TableTag)); /* * Set the values that aren't 0/NULL by default */ tagPtr->anchor = (Tk_Anchor)-1; tagPtr->justify = (Tk_Justify)-1; tagPtr->multiline = -1; tagPtr->relief = -1; tagPtr->showtext = -1; tagPtr->state = STATE_UNKNOWN; tagPtr->wrap = -1; } else { TableJoinTag *jtagPtr = (TableJoinTag *) ckalloc(sizeof(TableJoinTag)); memset((VOID *) jtagPtr, 0, sizeof(TableJoinTag)); tagPtr = (TableTag *) jtagPtr; tagPtr->anchor = (Tk_Anchor)-1; tagPtr->justify = (Tk_Justify)-1; tagPtr->multiline = -1; tagPtr->relief = -1; tagPtr->showtext = -1; tagPtr->state = STATE_UNKNOWN; tagPtr->wrap = -1; jtagPtr->magic = 0x99ABCDEF; jtagPtr->pbg = -1; jtagPtr->pfg = -1; jtagPtr->pborders = -1; jtagPtr->prelief = -1; jtagPtr->ptkfont = -1; jtagPtr->panchor = -1; jtagPtr->pimage = -1; jtagPtr->pstate = -1; jtagPtr->pjustify = -1; jtagPtr->pmultiline = -1; jtagPtr->pwrap = -1; jtagPtr->pshowtext = -1; } return (TableTag *) tagPtr; } /* *---------------------------------------------------------------------- * * TableResetTag -- * This routine resets a given tag to the table defaults. * * Results: * Tag will have values changed. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TableResetTag(Table *tablePtr, TableTag *tagPtr) { TableJoinTag *jtagPtr = (TableJoinTag *) tagPtr; if (jtagPtr->magic != 0x99ABCDEF) { panic("bad mojo in TableResetTag"); } memset((VOID *) jtagPtr, 0, sizeof(TableJoinTag)); tagPtr->anchor = (Tk_Anchor)-1; tagPtr->justify = (Tk_Justify)-1; tagPtr->multiline = -1; tagPtr->relief = -1; tagPtr->showtext = -1; tagPtr->state = STATE_UNKNOWN; tagPtr->wrap = -1; jtagPtr->magic = 0x99ABCDEF; jtagPtr->pbg = -1; jtagPtr->pfg = -1; jtagPtr->pborders = -1; jtagPtr->prelief = -1; jtagPtr->ptkfont = -1; jtagPtr->panchor = -1; jtagPtr->pimage = -1; jtagPtr->pstate = -1; jtagPtr->pjustify = -1; jtagPtr->pmultiline = -1; jtagPtr->pwrap = -1; jtagPtr->pshowtext = -1; /* * Merge in the default tag. */ memcpy((VOID *) jtagPtr, (VOID *) &(tablePtr->defaultTag), sizeof(TableTag)); } /* *---------------------------------------------------------------------- * * TableMergeTag -- * This routine merges two tags by adding any fields from the addTag * that are set to the baseTag. * * Results: * baseTag will inherit all set characteristics of addTag * (addTag thus has the priority). * * Side effects: * None. * *---------------------------------------------------------------------- */ void TableMergeTag(Table *tablePtr, TableTag *baseTag, TableTag *addTag) { TableJoinTag *jtagPtr = (TableJoinTag *) baseTag; unsigned int prio; if (jtagPtr->magic != 0x99ABCDEF) { panic("bad mojo in TableMergeTag"); } #ifndef NO_TAG_PRIORITIES /* * Find priority for the tag to merge */ prio = TableTagGetPriority(tablePtr, addTag); if ((addTag->anchor != -1) && (prio < jtagPtr->panchor)) { baseTag->anchor = addTag->anchor; jtagPtr->panchor = prio; } if ((addTag->bg != NULL) && (prio < jtagPtr->pbg)) { baseTag->bg = addTag->bg; jtagPtr->pbg = prio; } if ((addTag->fg != NULL) && (prio < jtagPtr->pfg)) { baseTag->fg = addTag->fg; jtagPtr->pfg = prio; } if ((addTag->tkfont != NULL) && (prio < jtagPtr->ptkfont)) { baseTag->tkfont = addTag->tkfont; jtagPtr->ptkfont = prio; } if ((addTag->imageStr != NULL) && (prio < jtagPtr->pimage)) { baseTag->imageStr = addTag->imageStr; baseTag->image = addTag->image; jtagPtr->pimage = prio; } if ((addTag->multiline >= 0) && (prio < jtagPtr->pmultiline)) { baseTag->multiline = addTag->multiline; jtagPtr->pmultiline = prio; } if ((addTag->relief != -1) && (prio < jtagPtr->prelief)) { baseTag->relief = addTag->relief; jtagPtr->prelief = prio; } if ((addTag->showtext >= 0) && (prio < jtagPtr->pshowtext)) { baseTag->showtext = addTag->showtext; jtagPtr->pshowtext = prio; } if ((addTag->state != STATE_UNKNOWN) && (prio < jtagPtr->pstate)) { baseTag->state = addTag->state; jtagPtr->pstate = prio; } if ((addTag->justify != -1) && (prio < jtagPtr->pjustify)) { baseTag->justify = addTag->justify; jtagPtr->pjustify = prio; } if ((addTag->wrap >= 0) && (prio < jtagPtr->pwrap)) { baseTag->wrap = addTag->wrap; jtagPtr->pwrap = prio; } if ((addTag->borders) && (prio < jtagPtr->pborders)) { baseTag->borderStr = addTag->borderStr; baseTag->borders = addTag->borders; baseTag->bd[0] = addTag->bd[0]; baseTag->bd[1] = addTag->bd[1]; baseTag->bd[2] = addTag->bd[2]; baseTag->bd[3] = addTag->bd[3]; jtagPtr->pborders = prio; } #else if (addTag->anchor != -1) baseTag->anchor = addTag->anchor; if (addTag->bg != NULL) baseTag->bg = addTag->bg; if (addTag->fg != NULL) baseTag->fg = addTag->fg; if (addTag->tkfont != NULL) baseTag->tkfont = addTag->tkfont; if (addTag->imageStr != NULL) { baseTag->imageStr = addTag->imageStr; baseTag->image = addTag->image; } if (addTag->multiline >= 0) baseTag->multiline = addTag->multiline; if (addTag->relief != -1) baseTag->relief = addTag->relief; if (addTag->showtext >= 0) baseTag->showtext = addTag->showtext; if (addTag->state != STATE_UNKNOWN) baseTag->state = addTag->state; if (addTag->justify != -1) baseTag->justify = addTag->justify; if (addTag->wrap >= 0) baseTag->wrap = addTag->wrap; if (addTag->borders) { baseTag->borderStr = addTag->borderStr; baseTag->borders = addTag->borders; baseTag->bd[0] = addTag->bd[0]; baseTag->bd[1] = addTag->bd[1]; baseTag->bd[2] = addTag->bd[2]; baseTag->bd[3] = addTag->bd[3]; } #endif } /* *---------------------------------------------------------------------- * * TableInvertTag -- * This routine swaps background and foreground for the selected tag. * * Results: * Inverts fg and bg of tag. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TableInvertTag(TableTag *baseTag) { Tk_3DBorder tmpBg; tmpBg = baseTag->fg; baseTag->fg = baseTag->bg; baseTag->bg = tmpBg; } /* *---------------------------------------------------------------------- * * TableGetTagBorders -- * This routine gets the border values based on a tag. * * Results: * It returns the values in the int*'s (if not NULL), and the * total number of defined borders as a result. * * Side effects: * None. * *---------------------------------------------------------------------- */ int TableGetTagBorders(TableTag *tagPtr, int *left, int *right, int *top, int *bottom) { switch (tagPtr->borders) { case 0: if (left) { *left = 0; } if (right) { *right = 0; } if (top) { *top = 0; } if (bottom) { *bottom = 0; } break; case 1: if (left) { *left = tagPtr->bd[0]; } if (right) { *right = tagPtr->bd[0]; } if (top) { *top = tagPtr->bd[0]; } if (bottom) { *bottom = tagPtr->bd[0]; } break; case 2: if (left) { *left = tagPtr->bd[0]; } if (right) { *right = tagPtr->bd[1]; } if (top) { *top = 0; } if (bottom) { *bottom = 0; } break; case 4: if (left) { *left = tagPtr->bd[0]; } if (right) { *right = tagPtr->bd[1]; } if (top) { *top = tagPtr->bd[2]; } if (bottom) { *bottom = tagPtr->bd[3]; } break; default: panic("invalid border value '%d'\n", tagPtr->borders); break; } return tagPtr->borders; } /* *---------------------------------------------------------------------- * * TableTagGetEntry -- * Takes a name and optional args and creates a tag entry in the * table's tag table. * * Results: * A new tag entry will be created and returned. * * Side effects: * None. * *---------------------------------------------------------------------- */ static TableTag * TableTagGetEntry(Table *tablePtr, char *name, int objc, char **argv) { Tcl_HashEntry *entryPtr; TableTag *tagPtr = NULL; int new; entryPtr = Tcl_CreateHashEntry(tablePtr->tagTable, name, &new); if (new) { tagPtr = TableNewTag(NULL); Tcl_SetHashValue(entryPtr, (ClientData) tagPtr); if (tablePtr->tagPrioSize >= tablePtr->tagPrioMax) { int i; /* * Increase the priority list size in blocks of 10 */ tablePtr->tagPrioMax += 10; tablePtr->tagPrioNames = (char **) ckrealloc( (char *) tablePtr->tagPrioNames, sizeof(TableTag *) * tablePtr->tagPrioMax); tablePtr->tagPrios = (TableTag **) ckrealloc( (char *) tablePtr->tagPrios, sizeof(TableTag *) * tablePtr->tagPrioMax); for (i = tablePtr->tagPrioSize; i < tablePtr->tagPrioMax; i++) { tablePtr->tagPrioNames[i] = (char *) NULL; tablePtr->tagPrios[i] = (TableTag *) NULL; } } tablePtr->tagPrioNames[tablePtr->tagPrioSize] = (char *) Tcl_GetHashKey(tablePtr->tagTable, entryPtr); tablePtr->tagPrios[tablePtr->tagPrioSize] = tagPtr; tablePtr->tagPrioSize++; } else { tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr); } if (objc) { Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin, tagConfig, objc, argv, (char *)tagPtr, TK_CONFIG_ARGV_ONLY); } return tagPtr; } /* *---------------------------------------------------------------------- * * TableTagGetPriority -- * Get the priority value for a tag. * * Results: * returns the priority. * * Side effects: * None. * *---------------------------------------------------------------------- */ static unsigned int TableTagGetPriority(Table *tablePtr, TableTag *tagPtr) { unsigned int prio = 0; while (tagPtr != tablePtr->tagPrios[prio]) { prio++; } return prio; } /* *---------------------------------------------------------------------- * * TableInitTags -- * Creates the static table tags. * * Results: * active, sel, title and flash are created as tags. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TableInitTags(Table *tablePtr) { static char *activeArgs[] = {"-bg", ACTIVE_BG, "-relief", "flat" }; static char *selArgs[] = {"-bg", SELECT_BG, "-fg", SELECT_FG, "-relief", "sunken" }; static char *titleArgs[] = {"-bg", DISABLED, "-fg", "white", "-relief", "flat", "-state", "disabled" }; static char *flashArgs[] = {"-bg", "red" }; /* * The order of creation is important to priority. */ TableTagGetEntry(tablePtr, "flash", ARSIZE(flashArgs), flashArgs); TableTagGetEntry(tablePtr, "active", ARSIZE(activeArgs), activeArgs); TableTagGetEntry(tablePtr, "sel", ARSIZE(selArgs), selArgs); TableTagGetEntry(tablePtr, "title", ARSIZE(titleArgs), titleArgs); } /* *---------------------------------------------------------------------- * * FindRowColTag -- * Finds a row/col tag based on the row/col styles and tagCommand. * * Results: * Returns tag associated with row/col cell, if any. * * Side effects: * Possible side effects from eval of tagCommand. * IMPORTANT: This plays with the interp result object, * so use of resultPtr in prior command may be invalid after * calling this function. * *---------------------------------------------------------------------- */ TableTag * FindRowColTag(Table *tablePtr, int cell, int mode) { Tcl_HashEntry *entryPtr; TableTag *tagPtr = NULL; entryPtr = Tcl_FindHashEntry((mode == ROW) ? tablePtr->rowStyles : tablePtr->colStyles, (char *) cell); if (entryPtr == NULL) { char *cmd = (mode == ROW) ? tablePtr->rowTagCmd : tablePtr->colTagCmd; if (cmd) { register Tcl_Interp *interp = tablePtr->interp; char buf[INDEX_BUFSIZE]; /* * Since no specific row/col tag exists, eval the given command * with row/col appended */ sprintf(buf, " %d", cell); Tcl_Preserve((ClientData) interp); if (Tcl_VarEval(interp, cmd, buf, (char *)NULL) == TCL_OK) { char *name = Tcl_GetStringResult(interp); if (name && *name) { /* * If a result was returned, check to see if it is * a valid tag. */ entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, name); } } Tcl_Release((ClientData) interp); Tcl_ResetResult(interp); } } if (entryPtr != NULL) { /* * This can be either the one in row|colStyles, * or that returned by eval'ing the row|colTagCmd */ tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr); } return tagPtr; } /* *---------------------------------------------------------------------- * * TableCleanupTag -- * Releases the resources used by a tag before it is freed up. * * Results: * None. * * Side effects: * The tag is no longer valid. * *---------------------------------------------------------------------- */ void TableCleanupTag(Table *tablePtr, TableTag *tagPtr) { /* * Free resources that the optionSpec doesn't specifically know about */ if (tagPtr->image) { Tk_FreeImage(tagPtr->image); } Tk_FreeOptions(tagConfig, (char *) tagPtr, tablePtr->display, 0); } /* *-------------------------------------------------------------- * * Table_TagCmd -- * This procedure is invoked to process the tag method * that corresponds to a widget managed by this module. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *-------------------------------------------------------------- */ int Table_TagCmd(ClientData clientData, register Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { register Table *tablePtr = (Table *)clientData; int result = TCL_OK, cmdIndex, i, newEntry, value, len; int row, col, tagPrio, refresh = 0; TableTag *tagPtr, *tag2Ptr; Tcl_HashEntry *entryPtr, *scanPtr; Tcl_HashTable *hashTblPtr; Tcl_HashSearch search; Tk_Image image; Tcl_Obj *objPtr, *resultPtr; char buf[INDEX_BUFSIZE], *keybuf, *tagname; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?"); return TCL_ERROR; } result = Tcl_GetIndexFromObj(interp, objv[2], tagCmdNames, "tag option", 0, &cmdIndex); if (result != TCL_OK) { return result; } /* * Before using this object, make sure there aren't any calls that * could have changed the interp result, thus freeing the object. */ resultPtr = Tcl_GetObjResult(interp); switch ((enum tagCmd) cmdIndex) { case TAG_CELLTAG: /* add named tag to a (group of) cell(s) */ if (objc < 4) { Tcl_WrongNumArgs(interp, 3, objv, "tag ?arg arg ...?"); return TCL_ERROR; } tagname = Tcl_GetStringFromObj(objv[3], &len); if (len == 0) { /* * An empty string was specified, so just delete the tag. */ tagPtr = NULL; } else { /* * Get the pointer to the tag structure. If it doesn't * exist, it will be created. */ tagPtr = TableTagGetEntry(tablePtr, tagname, 0, NULL); } if (objc == 4) { /* * The user just wants the cells with this tag returned. * Handle specially tags named: active, flash, sel, title */ if ((tablePtr->flags & HAS_ACTIVE) && STREQ(tagname, "active")) { TableMakeArrayIndex( tablePtr->activeRow+tablePtr->rowOffset, tablePtr->activeCol+tablePtr->colOffset, buf); Tcl_SetStringObj(resultPtr, buf, -1); } else if ((tablePtr->flashMode && STREQ(tagname, "flash")) || STREQ(tagname, "sel")) { hashTblPtr = (*tagname == 's') ? tablePtr->selCells : tablePtr->flashCells; for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search); scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) { keybuf = (char *) Tcl_GetHashKey(hashTblPtr, scanPtr); Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewStringObj(keybuf, -1)); } } else if (STREQ(tagname, "title") && (tablePtr->titleRows || tablePtr->titleCols)) { for (row = tablePtr->rowOffset; row < tablePtr->rowOffset+tablePtr->rows; row++) { for (col = tablePtr->colOffset; col < tablePtr->colOffset+tablePtr->titleCols; col++) { TableMakeArrayIndex(row, col, buf); Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewStringObj(buf, -1)); } } for (row = tablePtr->rowOffset; row < tablePtr->rowOffset+tablePtr->titleRows; row++) { for (col = tablePtr->colOffset+tablePtr->titleCols; col < tablePtr->colOffset+tablePtr->cols; col++) { TableMakeArrayIndex(row, col, buf); Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewStringObj(buf, -1)); } } } else { /* * Check this tag pointer amongst all tagged cells */ for (scanPtr = Tcl_FirstHashEntry(tablePtr->cellStyles, &search); scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) { if ((TableTag *) Tcl_GetHashValue(scanPtr) == tagPtr) { keybuf = (char *) Tcl_GetHashKey( tablePtr->cellStyles, scanPtr); Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewStringObj(keybuf, -1)); } } } return TCL_OK; } /* * Loop through the arguments and fill in the hash table */ for (i = 4; i < objc; i++) { /* * Try and parse the index */ if (TableGetIndexObj(tablePtr, objv[i], &row, &col) != TCL_OK) { return TCL_ERROR; } /* * Get the hash key ready */ TableMakeArrayIndex(row, col, buf); if (tagPtr == NULL) { /* * This is a deletion */ entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf); if (entryPtr != NULL) { Tcl_DeleteHashEntry(entryPtr); refresh = 1; } } else { /* * Add a key to the hash table and set it to point to the * Tag structure if it wasn't the same as an existing one */ entryPtr = Tcl_CreateHashEntry(tablePtr->cellStyles, buf, &newEntry); if (newEntry || (tagPtr != (TableTag *) Tcl_GetHashValue(entryPtr))) { Tcl_SetHashValue(entryPtr, (ClientData) tagPtr); refresh = 1; } } /* * Now invalidate this cell for redraw */ if (refresh) { TableRefresh(tablePtr, row-tablePtr->rowOffset, col-tablePtr->colOffset, CELL); } } return TCL_OK; case TAG_COLTAG: case TAG_ROWTAG: { /* tag a row or a column */ int forRows = (cmdIndex == TAG_ROWTAG); if (objc < 4) { Tcl_WrongNumArgs(interp, 3, objv, "tag ?arg arg ..?"); return TCL_ERROR; } tagname = Tcl_GetStringFromObj(objv[3], &len); if (len == 0) { /* * Empty string, so we want to delete this element */ tagPtr = NULL; } else { /* * Get the pointer to the tag structure. If it doesn't * exist, it will be created. */ tagPtr = TableTagGetEntry(tablePtr, tagname, 0, NULL); } /* * Choose the correct hash table based on args */ hashTblPtr = forRows ? tablePtr->rowStyles : tablePtr->colStyles; if (objc == 4) { /* the user just wants the tagged cells to be returned */ /* Special handling for tags: active, flash, sel, title */ if ((tablePtr->flags & HAS_ACTIVE) && strcmp(tagname, "active") == 0) { Tcl_SetIntObj(resultPtr, (forRows ? tablePtr->activeRow+tablePtr->rowOffset : tablePtr->activeCol+tablePtr->colOffset)); } else if ((tablePtr->flashMode && STREQ(tagname, "flash")) || STREQ(tagname, "sel")) { Tcl_HashTable *cacheTblPtr; cacheTblPtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(cacheTblPtr, TCL_ONE_WORD_KEYS); hashTblPtr = (*tagname == 's') ? tablePtr->selCells : tablePtr->flashCells; for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search); scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) { TableParseArrayIndex(&row, &col, Tcl_GetHashKey(hashTblPtr, scanPtr)); value = forRows ? row : col; entryPtr = Tcl_CreateHashEntry(cacheTblPtr, (char *)value, &newEntry); if (newEntry) { Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(value)); } } Tcl_DeleteHashTable(cacheTblPtr); ckfree((char *) (cacheTblPtr)); } else if (STREQ(tagname, "title") && (forRows?tablePtr->titleRows:tablePtr->titleCols)) { if (forRows) { for (row = tablePtr->rowOffset; row < tablePtr->rowOffset+tablePtr->titleRows; row++) { Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(row)); } } else { for (col = tablePtr->colOffset; col < tablePtr->colOffset+tablePtr->titleCols; col++) { Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(col)); } } } else { for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search); scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) { /* is this the tag pointer on this row */ if ((TableTag *) Tcl_GetHashValue(scanPtr) == tagPtr) { objPtr = Tcl_NewIntObj( (int) Tcl_GetHashKey(hashTblPtr, scanPtr)); Tcl_ListObjAppendElement(NULL, resultPtr, objPtr); } } } return TCL_OK; } /* * Loop through the arguments and fill in the hash table */ for (i = 4; i < objc; i++) { /* * Try and parse the index */ if (Tcl_GetIntFromObj(interp, objv[i], &value) != TCL_OK) { return TCL_ERROR; } if (tagPtr == NULL) { /* * This is a deletion */ entryPtr = Tcl_FindHashEntry(hashTblPtr, (char *)value); if (entryPtr != NULL) { Tcl_DeleteHashEntry(entryPtr); refresh = 1; } } else { /* * Add a key to the hash table and set it to point to the * Tag structure if it wasn't the same as an existing one */ entryPtr = Tcl_CreateHashEntry(hashTblPtr, (char *) value, &newEntry); if (newEntry || (tagPtr != (TableTag *) Tcl_GetHashValue(entryPtr))) { Tcl_SetHashValue(entryPtr, (ClientData) tagPtr); refresh = 1; } } /* and invalidate the row or column affected */ if (refresh) { if (cmdIndex == TAG_ROWTAG) { TableRefresh(tablePtr, value-tablePtr->rowOffset, 0, ROW); } else { TableRefresh(tablePtr, 0, value-tablePtr->colOffset, COL); } } } return TCL_OK; /* COLTAG && ROWTAG */ } case TAG_CGET: if (objc != 5) { Tcl_WrongNumArgs(interp, 3, objv, "tagName option"); return TCL_ERROR; } tagname = Tcl_GetString(objv[3]); entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname); if (entryPtr == NULL) { goto invalidtag; } else { tagPtr = (TableTag *) Tcl_GetHashValue (entryPtr); result = Tk_ConfigureValue(interp, tablePtr->tkwin, tagConfig, (char *) tagPtr, Tcl_GetString(objv[4]), 0); } return result; /* CGET */ case TAG_CONFIGURE: if (objc < 4) { Tcl_WrongNumArgs(interp, 3, objv, "tagName ?arg arg ...?"); return TCL_ERROR; } /* * Get the pointer to the tag structure. If it doesn't * exist, it will be created. */ tagPtr = TableTagGetEntry(tablePtr, Tcl_GetString(objv[3]), 0, NULL); /* * If there were less than 6 args, we return the configuration * (for all or just one option), even for new tags */ if (objc < 6) { result = Tk_ConfigureInfo(interp, tablePtr->tkwin, tagConfig, (char *) tagPtr, (objc == 5) ? Tcl_GetString(objv[4]) : NULL, 0); } else { char **argv; /* Stringify */ argv = (char **) ckalloc((objc + 1) * sizeof(char *)); for (i = 0; i < objc; i++) argv[i] = Tcl_GetString(objv[i]); argv[objc] = NULL; result = Tk_ConfigureWidget(interp, tablePtr->tkwin, tagConfig, objc-4, argv+4, (char *) tagPtr, TK_CONFIG_ARGV_ONLY); ckfree((char *) argv); if (result == TCL_ERROR) { return TCL_ERROR; } /* * Handle change of image name */ if (tagPtr->imageStr) { image = Tk_GetImage(interp, tablePtr->tkwin, tagPtr->imageStr, TableImageProc, (ClientData)tablePtr); if (image == NULL) { result = TCL_ERROR; } } else { image = NULL; } if (tagPtr->image) { Tk_FreeImage(tagPtr->image); } tagPtr->image = image; /* * We reconfigured, so invalidate the table to redraw */ TableInvalidateAll(tablePtr, 0); } return result; case TAG_DELETE: /* delete a tag */ if (objc < 4) { Tcl_WrongNumArgs(interp, 3, objv, "tagName ?tagName ...?"); return TCL_ERROR; } /* run through the remaining arguments */ for (i = 3; i < objc; i++) { tagname = Tcl_GetString(objv[i]); /* cannot delete the title tag */ if (STREQ(tagname, "title") || STREQ(tagname, "sel") || STREQ(tagname, "flash") || STREQ(tagname, "active")) { Tcl_AppendStringsToObj(resultPtr, "cannot delete ", tagname, " tag", (char *) NULL); return TCL_ERROR; } entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname); if (entryPtr != NULL) { /* get the tag pointer */ tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr); /* delete all references to this tag in rows */ scanPtr = Tcl_FirstHashEntry(tablePtr->rowStyles, &search); for (; scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) { if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr) { Tcl_DeleteHashEntry(scanPtr); refresh = 1; } } /* delete all references to this tag in cols */ scanPtr = Tcl_FirstHashEntry(tablePtr->colStyles, &search); for (; scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) { if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr) { Tcl_DeleteHashEntry(scanPtr); refresh = 1; } } /* delete all references to this tag in cells */ scanPtr = Tcl_FirstHashEntry(tablePtr->cellStyles, &search); for (; scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) { if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr) { Tcl_DeleteHashEntry(scanPtr); refresh = 1; } } /* * Remove the tag from the prio list and collapse * the rest of the tags. We could check for shrinking * the prio list as well. */ for (i = 0; i < tablePtr->tagPrioSize; i++) { if (tablePtr->tagPrios[i] == tagPtr) break; } for ( ; i < tablePtr->tagPrioSize; i++) { tablePtr->tagPrioNames[i] = tablePtr->tagPrioNames[i+1]; tablePtr->tagPrios[i] = tablePtr->tagPrios[i+1]; } tablePtr->tagPrioSize--; /* Release the tag structure */ TableCleanupTag(tablePtr, tagPtr); ckfree((char *) tagPtr); /* And free the hash table entry */ Tcl_DeleteHashEntry(entryPtr); } } /* since we deleted a tag, redraw the screen */ if (refresh) { TableInvalidateAll(tablePtr, 0); } return result; case TAG_EXISTS: if (objc != 4) { Tcl_WrongNumArgs(interp, 3, objv, "tagName"); return TCL_ERROR; } Tcl_SetBooleanObj(resultPtr, (Tcl_FindHashEntry(tablePtr->tagTable, Tcl_GetString(objv[3])) != NULL)); return TCL_OK; case TAG_INCLUDES: /* does a tag contain a index ? */ if (objc != 5) { Tcl_WrongNumArgs(interp, 3, objv, "tag index"); return TCL_ERROR; } tagname = Tcl_GetString(objv[3]); /* check to see if the tag actually exists */ entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname); if (entryPtr == NULL) { /* Unknown tag, just return 0 */ Tcl_SetBooleanObj(resultPtr, 0); return TCL_OK; } /* parse index */ if (TableGetIndexObj(tablePtr, objv[4], &row, &col) != TCL_OK) { return TCL_ERROR; } /* create hash key */ TableMakeArrayIndex(row, col, buf); if (STREQ(tagname, "active")) { result = (tablePtr->activeRow+tablePtr->rowOffset==row && tablePtr->activeCol+tablePtr->colOffset==col); } else if (STREQ(tagname, "flash")) { result = (tablePtr->flashMode && (Tcl_FindHashEntry(tablePtr->flashCells, buf) != NULL)); } else if (STREQ(tagname, "sel")) { result = (Tcl_FindHashEntry(tablePtr->selCells, buf) != NULL); } else if (STREQ(tagname, "title")) { result = (row < tablePtr->titleRows+tablePtr->rowOffset || col < tablePtr->titleCols+tablePtr->colOffset); } else { /* get the pointer to the tag structure */ tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr); scanPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf); /* * Look to see if there is a cell, row, or col tag * for this cell */ result = ((scanPtr && (tagPtr == (TableTag *) Tcl_GetHashValue(scanPtr))) || (tagPtr == FindRowColTag(tablePtr, row, ROW)) || (tagPtr == FindRowColTag(tablePtr, col, COL))); } /* * Because we may call FindRowColTag above, we can't use * the resultPtr, but this is almost equivalent, and is SAFE */ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); return TCL_OK; case TAG_NAMES: /* * Print out the tag names in priority order */ if (objc < 3 || objc > 4) { Tcl_WrongNumArgs(interp, 3, objv, "?pattern?"); return TCL_ERROR; } tagname = (objc == 4) ? Tcl_GetString(objv[3]) : NULL; for (i = 0; i < tablePtr->tagPrioSize; i++) { keybuf = tablePtr->tagPrioNames[i]; if (objc == 3 || Tcl_StringMatch(keybuf, tagname)) { objPtr = Tcl_NewStringObj(keybuf, -1); Tcl_ListObjAppendElement(NULL, resultPtr, objPtr); } } return TCL_OK; case TAG_LOWER: case TAG_RAISE: /* * Change priority of the named tag */ if (objc != 4 && objc != 5) { Tcl_WrongNumArgs(interp, 3, objv, (cmdIndex == TAG_LOWER) ? "tagName ?belowThis?" : "tagName ?aboveThis?"); return TCL_ERROR; } tagname = Tcl_GetString(objv[3]); /* check to see if the tag actually exists */ entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname); if (entryPtr == NULL) { goto invalidtag; } tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr); tagPrio = TableTagGetPriority(tablePtr, tagPtr); keybuf = tablePtr->tagPrioNames[tagPrio]; /* * In the RAISE case, the priority is one higher (-1) because * we want the named tag to move above the other in priority. */ if (objc == 5) { tagname = Tcl_GetString(objv[4]); entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname); if (entryPtr == NULL) { goto invalidtag; } tag2Ptr = (TableTag *) Tcl_GetHashValue(entryPtr); if (cmdIndex == TAG_LOWER) { value = TableTagGetPriority(tablePtr, tag2Ptr); } else { value = TableTagGetPriority(tablePtr, tag2Ptr) - 1; } } else { if (cmdIndex == TAG_LOWER) { /* * Lower this tag's priority to the bottom. */ value = tablePtr->tagPrioSize - 1; } else { /* * Raise this tag's priority to the top. */ value = -1; } } if (value < tagPrio) { /* * Move tag up in priority. */ for (i = tagPrio; i && i > value; i--) { tablePtr->tagPrioNames[i] = tablePtr->tagPrioNames[i-1]; tablePtr->tagPrios[i] = tablePtr->tagPrios[i-1]; } i++; tablePtr->tagPrioNames[i] = keybuf; tablePtr->tagPrios[i] = tagPtr; refresh = 1; } else if (value > tagPrio) { /* * Move tag down in priority. */ for (i = tagPrio; i < value; i++) { tablePtr->tagPrioNames[i] = tablePtr->tagPrioNames[i+1]; tablePtr->tagPrios[i] = tablePtr->tagPrios[i+1]; } tablePtr->tagPrioNames[i] = keybuf; tablePtr->tagPrios[i] = tagPtr; refresh = 1; } /* since we deleted a tag, redraw the screen */ if (refresh) { TableInvalidateAll(tablePtr, 0); } return TCL_OK; } return TCL_OK; invalidtag: /* * When jumping here, ensure the invalid 'tagname' is set already. */ Tcl_AppendStringsToObj(resultPtr, "invalid tag name \"", tagname, "\"", (char *) NULL); return TCL_ERROR; }