Files
aspn/tktable/generic/tkTableCell.c
baloan d19378fbab tktable added
--HG--
branch : aspn
2011-03-14 23:41:59 +01:00

1421 lines
42 KiB
C

/*
* tkTableCell.c --
*
* This module implements cell oriented functions for table
* widgets.
*
* Copyright (c) 1998-2000 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: tkTableCell.c,v 1.12 2008/11/14 21:10:12 hobbs Exp $
*/
#include "tkTable.h"
/*
*----------------------------------------------------------------------
*
* TableTrueCell --
* Takes a row,col pair in user coords and returns the true
* cell that it relates to, either dimension bounded, or a
* span cell if it was hidden.
*
* Results:
* The true row, col in user coords are placed in the pointers.
* If the value changed for some reasons, 0 is returned (it was not
* the /true/ cell).
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TableTrueCell(Table *tablePtr, int r, int c, int *row, int *col)
{
*row = r; *col = c;
/*
* We check spans before constraints, because we don't want to
* constrain and then think we ended up in a span
*/
if (tablePtr->spanAffTbl && !(tablePtr->flags & AVOID_SPANS)) {
char buf[INDEX_BUFSIZE];
Tcl_HashEntry *entryPtr;
TableMakeArrayIndex(r, c, buf);
entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
if ((entryPtr != NULL) &&
((char *)Tcl_GetHashValue(entryPtr) != NULL)) {
/*
* This cell is covered by another spanning cell.
* We need to return the coords for that spanning cell.
*/
TableParseArrayIndex(row, col, (char *)Tcl_GetHashValue(entryPtr));
return 0;
}
}
*row = BETWEEN(r, tablePtr->rowOffset,
tablePtr->rows-1+tablePtr->rowOffset);
*col = BETWEEN(c, tablePtr->colOffset,
tablePtr->cols-1+tablePtr->colOffset);
return ((*row == r) && (*col == c));
}
/*
*----------------------------------------------------------------------
*
* TableCellCoords --
* Takes a row,col pair in real coords and finds it position
* on the virtual screen.
*
* Results:
* The virtual x, y, width, and height of the cell
* are placed in the pointers.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TableCellCoords(Table *tablePtr, int row, int col,
int *x, int *y, int *w, int *h)
{
register int hl = tablePtr->highlightWidth;
int result = CELL_OK;
if (tablePtr->rows <= 0 || tablePtr->cols <= 0) {
*w = *h = *x = *y = 0;
return CELL_BAD;
}
/*
* Real coords required, always should be passed acceptable values,
* but this is a possible seg fault otherwise
*/
CONSTRAIN(row, 0, tablePtr->rows-1);
CONSTRAIN(col, 0, tablePtr->cols-1);
*w = tablePtr->colPixels[col];
*h = tablePtr->rowPixels[row];
/*
* Adjust for sizes of spanning cells
* and ensure that this cell isn't "hidden"
*/
if (tablePtr->spanAffTbl && !(tablePtr->flags & AVOID_SPANS)) {
char buf[INDEX_BUFSIZE];
Tcl_HashEntry *entryPtr;
TableMakeArrayIndex(row+tablePtr->rowOffset,
col+tablePtr->colOffset, buf);
entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
if (entryPtr != NULL) {
int rs, cs;
char *cell;
cell = (char *) Tcl_GetHashValue(entryPtr);
if (cell != NULL) {
/* This cell is covered by another spanning cell */
/* We need to return the coords for that cell */
TableParseArrayIndex(&rs, &cs, cell);
*w = rs;
*h = cs;
result = CELL_HIDDEN;
goto setxy;
}
/* Get the actual span values out of spanTbl */
entryPtr = Tcl_FindHashEntry(tablePtr->spanTbl, buf);
cell = (char *) Tcl_GetHashValue(entryPtr);
TableParseArrayIndex(&rs, &cs, cell);
if (rs > 0) {
/*
* Make sure we don't overflow our space
*/
if (row < tablePtr->titleRows) {
rs = MIN(tablePtr->titleRows-1, row+rs);
} else {
rs = MIN(tablePtr->rows-1, row+rs);
}
*h = tablePtr->rowStarts[rs+1]-tablePtr->rowStarts[row];
result = CELL_SPAN;
} else if (rs <= 0) {
/* currently negative spans are not supported */
}
if (cs > 0) {
/*
* Make sure we don't overflow our space
*/
if (col < tablePtr->titleCols) {
cs = MIN(tablePtr->titleCols-1, col+cs);
} else {
cs = MIN(tablePtr->cols-1, col+cs);
}
*w = tablePtr->colStarts[cs+1]-tablePtr->colStarts[col];
result = CELL_SPAN;
} else if (cs <= 0) {
/* currently negative spans are not supported */
}
}
}
setxy:
*x = hl + tablePtr->colStarts[col];
if (col >= tablePtr->titleCols) {
*x -= tablePtr->colStarts[tablePtr->leftCol]
- tablePtr->colStarts[tablePtr->titleCols];
}
*y = hl + tablePtr->rowStarts[row];
if (row >= tablePtr->titleRows) {
*y -= tablePtr->rowStarts[tablePtr->topRow]
- tablePtr->rowStarts[tablePtr->titleRows];
}
return result;
}
/*
*----------------------------------------------------------------------
*
* TableCellVCoords --
* Takes a row,col pair in real coords and finds it position
* on the actual screen. The full arg specifies whether
* only 100% visible cells should be considered visible.
*
* Results:
* The x, y, width, and height of the cell are placed in the pointers,
* depending upon visibility of the cell.
* Returns 0 for hidden and 1 for visible cells.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TableCellVCoords(Table *tablePtr, int row, int col,
int *rx, int *ry, int *rw, int *rh, int full)
{
int x, y, w, h, w0, h0, cellType, hl = tablePtr->highlightWidth;
if (tablePtr->tkwin == NULL) return 0;
/*
* Necessary to use separate vars in case dummies are passed in
*/
cellType = TableCellCoords(tablePtr, row, col, &x, &y, &w, &h);
*rx = x; *ry = y; *rw = w; *rh = h;
if (cellType == CELL_OK) {
if ((row < tablePtr->topRow && row >= tablePtr->titleRows) ||
(col < tablePtr->leftCol && col >= tablePtr->titleCols)) {
/*
* A non-spanning cell hiding in "dead" space
* between title areas and visible cells
*/
return 0;
}
} else if (cellType == CELL_SPAN) {
/*
* we might need to treat full better is CELL_SPAN but primary
* cell is visible
*/
int topX = tablePtr->colStarts[tablePtr->titleCols]+hl;
int topY = tablePtr->rowStarts[tablePtr->titleRows]+hl;
if ((col < tablePtr->leftCol) && (col >= tablePtr->titleCols)) {
if (full || (x+w < topX)) {
return 0;
} else {
w -= topX-x;
x = topX;
}
}
if ((row < tablePtr->topRow) && (row >= tablePtr->titleRows)) {
if (full || (y+h < topY)) {
return 0;
} else {
h -= topY-y;
y = topY;
}
}
/*
* re-set these according to changed coords
*/
*rx = x; *ry = y; *rw = w; *rh = h;
} else {
/*
* If it is a hidden cell, then w,h is the row,col in user coords
* of the cell that spans over this one
*/
return 0;
}
/*
* At this point, we know it is on the screen,
* but not if we can see 100% of it (if we care)
*/
if (full) {
w0 = w; h0 = h;
} else {
/*
* if we don't care about seeing the whole thing, then
* make sure we at least see a pixel worth
*/
w0 = h0 = 1;
}
/*
* Is the cell visible?
*/
if ((x < hl) || (y < hl) || ((x+w0) > (Tk_Width(tablePtr->tkwin)-hl))
|| ((y+h0) > (Tk_Height(tablePtr->tkwin)-hl))) {
/* definitely off the screen */
return 0;
} else {
/* if it was full, then w,h are already be properly constrained */
if (!full) {
*rw = MIN(w, Tk_Width(tablePtr->tkwin)-hl-x);
*rh = MIN(h, Tk_Height(tablePtr->tkwin)-hl-y);
}
return 1;
}
}
/*
*----------------------------------------------------------------------
*
* TableWhatCell --
* Takes a x,y screen coordinate and determines what cell contains.
* that point. This will return cells that are beyond the right/bottom
* edge of the viewable screen.
*
* Results:
* The row,col of the cell are placed in the pointers.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
TableWhatCell(register Table *tablePtr, int x, int y, int *row, int *col)
{
int i;
x = MAX(0, x); y = MAX(0, y);
/* Adjust for table's global highlightthickness border */
x -= tablePtr->highlightWidth;
y -= tablePtr->highlightWidth;
/* Adjust the x coord if not in the column titles to change display coords
* into internal coords */
x += (x < tablePtr->colStarts[tablePtr->titleCols]) ? 0 :
tablePtr->colStarts[tablePtr->leftCol] -
tablePtr->colStarts[tablePtr->titleCols];
y += (y < tablePtr->rowStarts[tablePtr->titleRows]) ? 0 :
tablePtr->rowStarts[tablePtr->topRow] -
tablePtr->rowStarts[tablePtr->titleRows];
x = MIN(x, tablePtr->maxWidth-1);
y = MIN(y, tablePtr->maxHeight-1);
for (i = 1; x >= tablePtr->colStarts[i]; i++);
*col = i - 1;
for (i = 1; y >= tablePtr->rowStarts[i]; i++);
*row = i - 1;
if (tablePtr->spanAffTbl && !(tablePtr->flags & AVOID_SPANS)) {
char buf[INDEX_BUFSIZE];
Tcl_HashEntry *entryPtr;
/* We now correct the returned cell if this was "hidden" */
TableMakeArrayIndex(*row+tablePtr->rowOffset,
*col+tablePtr->colOffset, buf);
entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
if ((entryPtr != NULL) &&
/* We have to make sure this was not already hidden
* that's an error */
((char *)Tcl_GetHashValue(entryPtr) != NULL)) {
/* this is a "hidden" cell */
TableParseArrayIndex(row, col, (char *)Tcl_GetHashValue(entryPtr));
*row -= tablePtr->rowOffset;
*col -= tablePtr->colOffset;
}
}
}
/*
*----------------------------------------------------------------------
*
* TableAtBorder --
* Takes a x,y screen coordinate and determines if that point is
* over a border.
*
* Results:
* The left/top row,col corresponding to that point are placed in
* the pointers. The number of borders (+1 for row, +1 for col)
* hit is returned.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TableAtBorder(Table * tablePtr, int x, int y, int *row, int *col)
{
int i, brow, bcol, borders = 2, bd[6];
TableGetTagBorders(&(tablePtr->defaultTag),
&bd[0], &bd[1], &bd[2], &bd[3]);
bd[4] = (bd[0] + bd[1])/2;
bd[5] = (bd[2] + bd[3])/2;
/*
* Constrain x && y appropriately, and adjust x if it is not in the
* column titles to change display coords into internal coords.
*/
x = MAX(0, x); y = MAX(0, y);
x -= tablePtr->highlightWidth; y -= tablePtr->highlightWidth;
x += (x < tablePtr->colStarts[tablePtr->titleCols]) ? 0 :
tablePtr->colStarts[tablePtr->leftCol] -
tablePtr->colStarts[tablePtr->titleCols];
x = MIN(x, tablePtr->maxWidth - 1);
for (i = 1; (i <= tablePtr->cols) &&
(x + (bd[0] + bd[1])) >= tablePtr->colStarts[i]; i++);
if (x > tablePtr->colStarts[--i] + bd[4]) {
borders--;
*col = -1;
bcol = (i < tablePtr->leftCol && i >= tablePtr->titleCols) ?
tablePtr->titleCols-1 : i-1;
} else {
bcol = *col = (i < tablePtr->leftCol && i >= tablePtr->titleCols) ?
tablePtr->titleCols-1 : i-1;
}
y += (y < tablePtr->rowStarts[tablePtr->titleRows]) ? 0 :
tablePtr->rowStarts[tablePtr->topRow] -
tablePtr->rowStarts[tablePtr->titleRows];
y = MIN(y, tablePtr->maxHeight - 1);
for (i = 1; i <= tablePtr->rows &&
(y + (bd[2] + bd[3])) >= tablePtr->rowStarts[i]; i++);
if (y > tablePtr->rowStarts[--i]+bd[5]) {
borders--;
*row = -1;
brow = (i < tablePtr->topRow && i >= tablePtr->titleRows) ?
tablePtr->titleRows-1 : i-1;
} else {
brow = *row = (i < tablePtr->topRow && i >= tablePtr->titleRows) ?
tablePtr->titleRows-1 : i-1;
}
/*
* We have to account for spanning cells, which may hide cells.
* In that case, we have to decrement our border count.
*/
if (tablePtr->spanAffTbl && !(tablePtr->flags & AVOID_SPANS) && borders) {
Tcl_HashEntry *entryPtr1, *entryPtr2 ;
char buf1[INDEX_BUFSIZE], buf2[INDEX_BUFSIZE];
char *val;
if (*row != -1) {
TableMakeArrayIndex(brow+tablePtr->rowOffset,
bcol+tablePtr->colOffset+1, buf1);
TableMakeArrayIndex(brow+tablePtr->rowOffset+1,
bcol+tablePtr->colOffset+1, buf2);
entryPtr1 = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf1);
entryPtr2 = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf2);
if (entryPtr1 != NULL && entryPtr2 != NULL) {
if ((val = (char *) Tcl_GetHashValue(entryPtr1)) != NULL) {
strcpy(buf1, val);
}
if ((val = (char *) Tcl_GetHashValue(entryPtr2)) != NULL) {
strcpy(buf2, val);
}
if (strcmp(buf1, buf2) == 0) {
borders--;
*row = -1;
}
}
}
if (*col != -1) {
TableMakeArrayIndex(brow+tablePtr->rowOffset+1,
bcol+tablePtr->colOffset, buf1);
TableMakeArrayIndex(brow+tablePtr->rowOffset+1,
bcol+tablePtr->colOffset+1, buf2);
entryPtr1 = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf1);
entryPtr2 = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf2);
if (entryPtr1 != NULL && entryPtr2 != NULL) {
if ((val = (char *) Tcl_GetHashValue(entryPtr1)) != NULL) {
strcpy(buf1, val);
}
if ((val = (char *) Tcl_GetHashValue(entryPtr2)) != NULL) {
strcpy(buf2, val);
}
if (strcmp(buf1, buf2) == 0) {
borders--;
*col = -1;
}
}
}
}
return borders;
}
/*
*----------------------------------------------------------------------
*
* TableGetCellValue --
* Takes a row,col pair in user coords and returns the value for
* that cell. This varies depending on what data source the
* user has selected.
*
* Results:
* The value of the cell is returned. The return value is VOLATILE
* (do not free).
*
* Side effects:
* The value will be cached if caching is turned on.
*
*----------------------------------------------------------------------
*/
char *
TableGetCellValue(Table *tablePtr, int r, int c)
{
register Tcl_Interp *interp = tablePtr->interp;
char *result = NULL;
char buf[INDEX_BUFSIZE];
Tcl_HashEntry *entryPtr = NULL;
int new;
TableMakeArrayIndex(r, c, buf);
if (tablePtr->dataSource == DATA_CACHE) {
/*
* only cache as data source - just rely on cache
*/
entryPtr = Tcl_FindHashEntry(tablePtr->cache, buf);
if (entryPtr) {
result = (char *) Tcl_GetHashValue(entryPtr);
}
goto VALUE;
}
if (tablePtr->caching) {
/*
* If we are caching, let's see if we have the value cached.
* If so, use it, otherwise it will be cached after retrieving
* from the other data source.
*/
entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &new);
if (!new) {
result = (char *) Tcl_GetHashValue(entryPtr);
goto VALUE;
}
}
if (tablePtr->dataSource & DATA_COMMAND) {
Tcl_DString script;
Tcl_DStringInit(&script);
ExpandPercents(tablePtr, tablePtr->command, r, c, "", (char *)NULL,
0, &script, 0);
if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) == TCL_ERROR) {
tablePtr->useCmd = 0;
tablePtr->dataSource &= ~DATA_COMMAND;
if (tablePtr->arrayVar)
tablePtr->dataSource |= DATA_ARRAY;
Tcl_AddErrorInfo(interp, "\n\t(in -command evaled by table)");
Tcl_AddErrorInfo(interp, Tcl_DStringValue(&script));
Tcl_BackgroundError(interp);
TableInvalidateAll(tablePtr, 0);
} else {
result = (char *) Tcl_GetStringResult(interp);
}
Tcl_DStringFree(&script);
}
if (tablePtr->dataSource & DATA_ARRAY) {
result = (char *) Tcl_GetVar2(interp, tablePtr->arrayVar, buf,
TCL_GLOBAL_ONLY);
}
if (tablePtr->caching && entryPtr != NULL) {
/*
* If we are caching, make sure we cache the returned value
*
* entryPtr will have been set from above, but check to make sure
* someone didn't change caching during -command evaluation.
*/
char *val = NULL;
if (result) {
val = (char *)ckalloc(strlen(result)+1);
strcpy(val, result);
}
Tcl_SetHashValue(entryPtr, val);
}
VALUE:
#ifdef PROCS
if (result != NULL) {
/* Do we have procs, are we showing their value, is this a proc? */
if (tablePtr->hasProcs && !tablePtr->showProcs && *result == '=' &&
!(r-tablePtr->rowOffset == tablePtr->activeRow &&
c-tablePtr->colOffset == tablePtr->activeCol)) {
Tcl_DString script;
/* provides a rough mutex on preventing proc loops */
entryPtr = Tcl_CreateHashEntry(tablePtr->inProc, buf, &new);
if (!new) {
Tcl_SetHashValue(entryPtr, 1);
Tcl_AddErrorInfo(interp, "\n\t(loop hit in proc evaled by table)");
return result;
}
Tcl_SetHashValue(entryPtr, 0);
Tcl_DStringInit(&script);
ExpandPercents(tablePtr, result+1, r, c, result+1, (char *)NULL,
0, &script, 0);
if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) != TCL_OK ||
Tcl_GetHashValue(entryPtr) == 1) {
Tcl_AddErrorInfo(interp, "\n\tin proc evaled by table:\n");
Tcl_AddErrorInfo(interp, Tcl_DStringValue(&script));
Tcl_BackgroundError(interp);
} else {
result = Tcl_GetStringResult(interp);
}
/*
* XXX FIX: Can't free result that we still need.
* Use ref-counted objects instead.
*/
Tcl_FreeResult(interp);
Tcl_DStringFree(&script);
Tcl_DeleteHashEntry(entryPtr);
}
}
#endif
return (result?result:"");
}
/*
*----------------------------------------------------------------------
*
* TableSetCellValue --
* Takes a row,col pair in user coords and saves the given value for
* that cell. This varies depending on what data source the
* user has selected.
*
* Results:
* Returns TCL_ERROR or TCL_OK, depending on whether an error
* occured during set (ie: during evaluation of -command).
*
* Side effects:
* If the value is NULL (empty string), it will be unset from
* an array rather than set to the empty string.
*
*----------------------------------------------------------------------
*/
int
TableSetCellValue(Table *tablePtr, int r, int c, char *value)
{
char buf[INDEX_BUFSIZE];
int code = TCL_OK, flash = 0;
Tcl_Interp *interp = tablePtr->interp;
TableMakeArrayIndex(r, c, buf);
if (tablePtr->state == STATE_DISABLED) {
return TCL_OK;
}
if (tablePtr->dataSource & DATA_COMMAND) {
Tcl_DString script;
Tcl_DStringInit(&script);
ExpandPercents(tablePtr, tablePtr->command, r, c, value, (char *)NULL,
1, &script, 0);
if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) == TCL_ERROR) {
/* An error resulted. Prevent further triggering of the command
* and set up the error message. */
tablePtr->useCmd = 0;
tablePtr->dataSource &= ~DATA_COMMAND;
if (tablePtr->arrayVar)
tablePtr->dataSource |= DATA_ARRAY;
Tcl_AddErrorInfo(interp, "\n\t(in command executed by table)");
Tcl_BackgroundError(interp);
code = TCL_ERROR;
} else {
flash = 1;
}
Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
Tcl_DStringFree(&script);
}
if (tablePtr->dataSource & DATA_ARRAY) {
/* Warning: checking for \0 as the first char could invalidate
* allowing it as a valid first char, but only with incorrect utf-8
*/
if ((value == NULL || *value == '\0') && tablePtr->sparse) {
Tcl_UnsetVar2(interp, tablePtr->arrayVar, buf, TCL_GLOBAL_ONLY);
value = NULL;
} else if (Tcl_SetVar2(interp, tablePtr->arrayVar, buf, value,
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
code = TCL_ERROR;
}
}
if (code == TCL_ERROR) {
return TCL_ERROR;
}
/*
* This would be repetitive if we are using the array (which traces).
*/
if (tablePtr->caching && !(tablePtr->dataSource & DATA_ARRAY)) {
Tcl_HashEntry *entryPtr;
int new;
char *val = NULL;
entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &new);
if (!new) {
val = (char *) Tcl_GetHashValue(entryPtr);
if (val) ckfree(val);
}
if (value) {
val = (char *)ckalloc(strlen(value)+1);
strcpy(val, value);
}
Tcl_SetHashValue(entryPtr, val);
flash = 1;
}
/* We do this conditionally because the var array already has
* it's own check to flash */
if (flash && tablePtr->flashMode) {
r -= tablePtr->rowOffset;
c -= tablePtr->colOffset;
TableAddFlash(tablePtr, r, c);
TableRefresh(tablePtr, r, c, CELL);
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TableMoveCellValue --
* To move cells faster on delete/insert line or col when cache is on
* and variable, command is off.
* To avoid another call to TableMakeArrayIndex(r, c, buf),
* we optionally provide the buffers.
* outOfBounds means we will just set the cell value to ""
*
* Results:
* Returns TCL_ERROR or TCL_OK, depending on whether an error
* occured during set (ie: during evaluation of -command).
*
* Side effects:
* If the value is NULL (empty string), it will be unset from
* an array rather than set to the empty string.
*
*----------------------------------------------------------------------
*/
int
TableMoveCellValue(Table *tablePtr, int fromr, int fromc, char *frombuf,
int tor, int toc, char *tobuf, int outOfBounds)
{
if (outOfBounds) {
return TableSetCellValue(tablePtr, tor, toc, "");
}
if (tablePtr->dataSource == DATA_CACHE) {
char *val;
char *result = NULL;
Tcl_HashEntry *entryPtr;
/*
* Let's see if we have the from value cached. If so, copy
* that to the to cell. The to cell entry value will be
* deleted from the cache, and recreated only if from value
* was not NULL.
* We can be liberal removing our internal cached cells when
* DATA_CACHE is our only data source.
*/
entryPtr = Tcl_FindHashEntry(tablePtr->cache, frombuf);
if (entryPtr) {
result = (char *) Tcl_GetHashValue(entryPtr);
/*
* we set tho old value to NULL
*/
Tcl_DeleteHashEntry(entryPtr);
}
if (result) {
int new;
/*
* We enter here when there was a from value.
* set 'to' to the 'from' value without new mallocing.
*/
entryPtr = Tcl_CreateHashEntry(tablePtr->cache, tobuf, &new);
/*
* free old value
*/
if (!new) {
val = (char *) Tcl_GetHashValue(entryPtr);
if (val) ckfree(val);
}
Tcl_SetHashValue(entryPtr, result);
} else {
entryPtr = Tcl_FindHashEntry(tablePtr->cache, tobuf);
if (entryPtr) {
val = (char *) Tcl_GetHashValue(entryPtr);
if (val) ckfree(val);
Tcl_DeleteHashEntry(entryPtr);
}
}
return TCL_OK;
}
/*
* We have to do it the old way
*/
return TableSetCellValue(tablePtr, tor, toc,
TableGetCellValue(tablePtr, fromr, fromc));
}
/*
*----------------------------------------------------------------------
*
* TableGetIcursor --
* Parses the argument as an index into the active cell string.
* Recognises 'end', 'insert' or an integer. Constrains it to the
* size of the buffer. This acts like a "SetIcursor" when *posn is NULL.
*
* Results:
* If (posn != NULL), then it gets the cursor position.
*
* Side effects:
* Can move cursor position.
*
*----------------------------------------------------------------------
*/
int
TableGetIcursor(Table *tablePtr, char *arg, int *posn)
{
int tmp, len;
len = strlen(tablePtr->activeBuf);
#ifdef TCL_UTF_MAX
/* Need to base it off strlen to account for \x00 (Unicode null) */
len = Tcl_NumUtfChars(tablePtr->activeBuf, len);
#endif
/* ensure icursor didn't get out of sync */
if (tablePtr->icursor > len) tablePtr->icursor = len;
/* is this end */
if (strcmp(arg, "end") == 0) {
tmp = len;
} else if (strcmp(arg, "insert") == 0) {
tmp = tablePtr->icursor;
} else {
if (Tcl_GetInt(tablePtr->interp, arg, &tmp) != TCL_OK) {
return TCL_ERROR;
}
CONSTRAIN(tmp, 0, len);
}
if (posn) {
*posn = tmp;
} else {
tablePtr->icursor = tmp;
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* TableGetIndex --
* Parse an index into a table and return either its value
* or an error.
*
* Results:
* A standard Tcl result. If all went well, then *row,*col is
* filled in with the index corresponding to string. If an
* error occurs then an error message is left in interp result.
* The index returned is in user coords.
*
* Side effects:
* Sets row,col index to an appropriately constrained user index.
*
*--------------------------------------------------------------
*/
int
TableGetIndex(tablePtr, str, row_p, col_p)
register Table *tablePtr; /* Table for which the index is being
* specified. */
char *str; /* Symbolic specification of cell in table. */
int *row_p; /* Where to store converted row. */
int *col_p; /* Where to store converted col. */
{
int r, c, len = strlen(str);
char dummy;
/*
* Note that all of these values will be adjusted by row/ColOffset
*/
if (str[0] == '@') { /* @x,y coordinate */
int x, y;
if (sscanf(str+1, "%d,%d%c", &x, &y, &dummy) != 2) {
/* Make sure it won't work for "2,3extrastuff" */
goto IndexError;
}
TableWhatCell(tablePtr, x, y, &r, &c);
r += tablePtr->rowOffset;
c += tablePtr->colOffset;
} else if (*str == '-' || isdigit(str[0])) {
if (sscanf(str, "%d,%d%c", &r, &c, &dummy) != 2) {
/* Make sure it won't work for "2,3extrastuff" */
goto IndexError;
}
/* ensure appropriate user index */
CONSTRAIN(r, tablePtr->rowOffset,
tablePtr->rows-1+tablePtr->rowOffset);
CONSTRAIN(c, tablePtr->colOffset,
tablePtr->cols-1+tablePtr->colOffset);
} else if (len > 1 && strncmp(str, "active", len) == 0 ) { /* active */
if (tablePtr->flags & HAS_ACTIVE) {
r = tablePtr->activeRow+tablePtr->rowOffset;
c = tablePtr->activeCol+tablePtr->colOffset;
} else {
Tcl_SetObjResult(tablePtr->interp,
Tcl_NewStringObj("no \"active\" cell in table", -1));
return TCL_ERROR;
}
} else if (len > 1 && strncmp(str, "anchor", len) == 0) { /* anchor */
if (tablePtr->flags & HAS_ANCHOR) {
r = tablePtr->anchorRow+tablePtr->rowOffset;
c = tablePtr->anchorCol+tablePtr->colOffset;
} else {
Tcl_SetObjResult(tablePtr->interp,
Tcl_NewStringObj("no \"anchor\" cell in table", -1));
return TCL_ERROR;
}
} else if (strncmp(str, "end", len) == 0) { /* end */
r = tablePtr->rows-1+tablePtr->rowOffset;
c = tablePtr->cols-1+tablePtr->colOffset;
} else if (strncmp(str, "origin", len) == 0) { /* origin */
r = tablePtr->titleRows+tablePtr->rowOffset;
c = tablePtr->titleCols+tablePtr->colOffset;
} else if (strncmp(str, "topleft", len) == 0) { /* topleft */
r = tablePtr->topRow+tablePtr->rowOffset;
c = tablePtr->leftCol+tablePtr->colOffset;
} else if (strncmp(str, "bottomright", len) == 0) { /* bottomright */
/*
* FIX: Should this avoid spans, or consider them in the bottomright?
tablePtr->flags |= AVOID_SPANS;
tablePtr->flags &= ~AVOID_SPANS;
*/
TableGetLastCell(tablePtr, &r, &c);
r += tablePtr->rowOffset;
c += tablePtr->colOffset;
} else {
IndexError:
Tcl_AppendStringsToObj(Tcl_GetObjResult(tablePtr->interp),
"bad table index \"", str, "\": must be active, anchor, end, ",
"origin, topleft, bottomright, @x,y, or <row>,<col>",
(char *)NULL);
return TCL_ERROR;
}
/* Note: values are expected to be properly constrained
* as a user index by this point */
if (row_p) *row_p = r;
if (col_p) *col_p = c;
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_SetCmd --
* This procedure is invoked to process the set 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_SetCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *)clientData;
int row, col, len, i, j, max;
char *str;
/* sets any number of tags/indices to a given value */
if (objc < 3) {
CMD_SET_USAGE:
Tcl_WrongNumArgs(interp, 2, objv,
"?row|col? index ?value? ?index value ...?");
return TCL_ERROR;
}
/* make sure there is a data source to accept set */
if (tablePtr->dataSource == DATA_NONE) {
return TCL_OK;
}
str = Tcl_GetStringFromObj(objv[2], &len);
if (strncmp(str, "row", len) == 0 || strncmp(str, "col", len) == 0) {
Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
/* set row index list ?index list ...? */
if (objc < 4) {
goto CMD_SET_USAGE;
} else if (objc == 4) {
if (TableGetIndexObj(tablePtr, objv[3],
&row, &col) != TCL_OK) {
return TCL_ERROR;
}
if (*str == 'r') {
max = tablePtr->cols+tablePtr->colOffset;
for (i=col; i<max; i++) {
str = TableGetCellValue(tablePtr, row, i);
Tcl_ListObjAppendElement(NULL, resultPtr,
Tcl_NewStringObj(str, -1));
}
} else {
max = tablePtr->rows+tablePtr->rowOffset;
for (i=row; i<max; i++) {
str = TableGetCellValue(tablePtr, i, col);
Tcl_ListObjAppendElement(NULL, resultPtr,
Tcl_NewStringObj(str, -1));
}
}
} else if (tablePtr->state == STATE_NORMAL) {
int listc;
Tcl_Obj **listv;
/* make sure there are an even number of index/list pairs */
if (objc & 0) {
goto CMD_SET_USAGE;
}
for (i = 3; i < objc-1; i += 2) {
if ((TableGetIndexObj(tablePtr, objv[i],
&row, &col) != TCL_OK) ||
(Tcl_ListObjGetElements(interp, objv[i+1],
&listc, &listv) != TCL_OK)) {
return TCL_ERROR;
}
if (*str == 'r') {
max = col+MIN(tablePtr->cols+tablePtr->colOffset-col,
listc);
for (j = col; j < max; j++) {
if (TableSetCellValue(tablePtr, row, j,
Tcl_GetString(listv[j-col]))
!= TCL_OK) {
return TCL_ERROR;
}
if (row-tablePtr->rowOffset == tablePtr->activeRow &&
j-tablePtr->colOffset == tablePtr->activeCol) {
TableGetActiveBuf(tablePtr);
}
TableRefresh(tablePtr, row-tablePtr->rowOffset,
j-tablePtr->colOffset, CELL);
}
} else {
max = row+MIN(tablePtr->rows+tablePtr->rowOffset-row,
listc);
for (j = row; j < max; j++) {
if (TableSetCellValue(tablePtr, j, col,
Tcl_GetString(listv[j-row]))
!= TCL_OK) {
return TCL_ERROR;
}
if (j-tablePtr->rowOffset == tablePtr->activeRow &&
col-tablePtr->colOffset == tablePtr->activeCol) {
TableGetActiveBuf(tablePtr);
}
TableRefresh(tablePtr, j-tablePtr->rowOffset,
col-tablePtr->colOffset, CELL);
}
}
}
}
} else if (objc == 3) {
/* set index */
if (TableGetIndexObj(tablePtr, objv[2], &row, &col) != TCL_OK) {
return TCL_ERROR;
} else {
/*
* Cannot use Tcl_GetObjResult here because TableGetCellValue
* can corrupt the resultPtr.
*/
Tcl_SetObjResult(interp, Tcl_NewStringObj(
TableGetCellValue(tablePtr, row, col),-1));
}
} else {
/* set index val ?index val ...? */
/* make sure there are an even number of index/value pairs */
if (objc & 1) {
goto CMD_SET_USAGE;
}
for (i = 2; i < objc-1; i += 2) {
if ((TableGetIndexObj(tablePtr, objv[i], &row, &col) != TCL_OK) ||
(TableSetCellValue(tablePtr, row, col,
Tcl_GetString(objv[i+1])) != TCL_OK)) {
return TCL_ERROR;
}
row -= tablePtr->rowOffset;
col -= tablePtr->colOffset;
if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
TableGetActiveBuf(tablePtr);
}
TableRefresh(tablePtr, row, col, CELL);
}
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_SpanSet --
* Takes row,col in user coords and sets a span on the
* cell if possible
*
* Results:
* A standard Tcl result
*
* Side effects:
* The span can be constrained
*
*--------------------------------------------------------------
*/
static int
Table_SpanSet(register Table *tablePtr, int urow, int ucol, int rs, int cs)
{
Tcl_Interp *interp = tablePtr->interp;
int i, j, new, ors, ocs, result = TCL_OK;
int row, col;
Tcl_HashEntry *entryPtr;
Tcl_HashSearch search;
char *dbuf, buf[INDEX_BUFSIZE], cell[INDEX_BUFSIZE], span[INDEX_BUFSIZE];
row = urow - tablePtr->rowOffset;
col = ucol - tablePtr->colOffset;
TableMakeArrayIndex(urow, ucol, cell);
if (tablePtr->spanTbl == NULL) {
tablePtr->spanTbl = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(tablePtr->spanTbl, TCL_STRING_KEYS);
tablePtr->spanAffTbl = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(tablePtr->spanAffTbl, TCL_STRING_KEYS);
}
/* first check in the affected cells table */
if ((entryPtr=Tcl_FindHashEntry(tablePtr->spanAffTbl, cell)) != NULL) {
/* We have to make sure this was not already hidden
* that's an error */
if ((char *)Tcl_GetHashValue(entryPtr) != NULL) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"cannot set spanning on hidden cell ",
cell, (char *) NULL);
return TCL_ERROR;
}
}
/* do constraints on the spans
* title cells must not expand beyond the titles
* other cells can't expand negatively into title area
*/
if ((row < tablePtr->titleRows) &&
(row + rs >= tablePtr->titleRows)) {
rs = tablePtr->titleRows - row - 1;
}
if ((col < tablePtr->titleCols) &&
(col + cs >= tablePtr->titleCols)) {
cs = tablePtr->titleCols - col - 1;
}
rs = MAX(0, rs);
cs = MAX(0, cs);
/* then work in the span cells table */
if ((entryPtr = Tcl_FindHashEntry(tablePtr->spanTbl, cell)) != NULL) {
/* We have to readjust for what was there first */
TableParseArrayIndex(&ors, &ocs, (char *)Tcl_GetHashValue(entryPtr));
ckfree((char *) Tcl_GetHashValue(entryPtr));
Tcl_DeleteHashEntry(entryPtr);
for (i = urow; i <= urow+ors; i++) {
for (j = ucol; j <= ucol+ocs; j++) {
TableMakeArrayIndex(i, j, buf);
entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
if (entryPtr != NULL) {
Tcl_DeleteHashEntry(entryPtr);
}
TableRefresh(tablePtr, i-tablePtr->rowOffset,
j-tablePtr->colOffset, CELL);
}
}
} else {
ors = ocs = 0;
}
/* calc to make sure that span is OK */
for (i = urow; i <= urow+rs; i++) {
for (j = ucol; j <= ucol+cs; j++) {
TableMakeArrayIndex(i, j, buf);
entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
if (entryPtr != NULL) {
/* Something already spans here */
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"cannot overlap already spanned cell ",
buf, (char *) NULL);
result = TCL_ERROR;
rs = ors;
cs = ocs;
break;
}
}
if (result == TCL_ERROR)
break;
}
/* 0,0 span means set to unspanned again */
if (rs == 0 && cs == 0) {
entryPtr = Tcl_FindHashEntry(tablePtr->spanTbl, cell);
if (entryPtr != NULL) {
ckfree((char *) Tcl_GetHashValue(entryPtr));
Tcl_DeleteHashEntry(entryPtr);
}
entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, cell);
if (entryPtr != NULL) {
Tcl_DeleteHashEntry(entryPtr);
}
if (Tcl_FirstHashEntry(tablePtr->spanTbl, &search) == NULL) {
/* There are no more spans, so delete tables to improve
* performance of TableCellCoords */
Tcl_DeleteHashTable(tablePtr->spanTbl);
ckfree((char *) (tablePtr->spanTbl));
Tcl_DeleteHashTable(tablePtr->spanAffTbl);
ckfree((char *) (tablePtr->spanAffTbl));
tablePtr->spanTbl = NULL;
tablePtr->spanAffTbl = NULL;
}
return result;
}
/* Make sure there is no extra stuff */
TableMakeArrayIndex(rs, cs, span);
/* Set affected cell table to a NULL value */
entryPtr = Tcl_CreateHashEntry(tablePtr->spanAffTbl, cell, &new);
Tcl_SetHashValue(entryPtr, (char *) NULL);
/* set the spanning cells table with span value */
entryPtr = Tcl_CreateHashEntry(tablePtr->spanTbl, cell, &new);
dbuf = (char *)ckalloc(strlen(span)+1);
strcpy(dbuf, span);
Tcl_SetHashValue(entryPtr, dbuf);
dbuf = Tcl_GetHashKey(tablePtr->spanTbl, entryPtr);
/* Set other affected cells */
EmbWinUnmap(tablePtr, row, row + rs, col, col + cs);
for (i = urow; i <= urow+rs; i++) {
for (j = ucol; j <= ucol+cs; j++) {
TableMakeArrayIndex(i, j, buf);
entryPtr = Tcl_CreateHashEntry(tablePtr->spanAffTbl, buf, &new);
if (!(i == urow && j == ucol)) {
Tcl_SetHashValue(entryPtr, (char *) dbuf);
}
}
}
TableRefresh(tablePtr, row, col, CELL);
return result;
}
/*
*--------------------------------------------------------------
*
* Table_SpanCmd --
* This procedure is invoked to process the span 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_SpanCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int rs, cs, row, col, i;
Tcl_HashEntry *entryPtr;
if (objc < 2 || (objc > 4 && (objc&1))) {
Tcl_WrongNumArgs(interp, 2, objv,
"?index? ?rows,cols index rows,cols ...?");
return TCL_ERROR;
}
if (objc == 2) {
if (tablePtr->spanTbl) {
Tcl_HashSearch search;
Tcl_Obj *objPtr, *resultPtr = Tcl_NewObj();
for (entryPtr = Tcl_FirstHashEntry(tablePtr->spanTbl, &search);
entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
objPtr = Tcl_NewStringObj(Tcl_GetHashKey(tablePtr->spanTbl,
entryPtr), -1);
Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
objPtr = Tcl_NewStringObj((char *) Tcl_GetHashValue(entryPtr),
-1);
Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
}
Tcl_SetObjResult(interp, resultPtr);
}
return TCL_OK;
} else if (objc == 3) {
if (TableGetIndexObj(tablePtr, objv[2], &row, &col) == TCL_ERROR) {
return TCL_ERROR;
}
/* Just return the spanning values of the one cell */
if (tablePtr->spanTbl &&
(entryPtr = Tcl_FindHashEntry(tablePtr->spanTbl,
Tcl_GetString(objv[2]))) != NULL) {
Tcl_SetObjResult(interp,
Tcl_NewStringObj((char *)Tcl_GetHashValue(entryPtr), -1));
}
return TCL_OK;
} else {
for (i = 2; i < objc-1; i += 2) {
if (TableGetIndexObj(tablePtr, objv[i], &row, &col) == TCL_ERROR ||
(TableParseArrayIndex(&rs, &cs,
Tcl_GetString(objv[i+1])) != 2) ||
Table_SpanSet(tablePtr, row, col, rs, cs) == TCL_ERROR) {
return TCL_ERROR;
}
}
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_HiddenCmd --
* This procedure is invoked to process the hidden 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_HiddenCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int i, row, col;
Tcl_HashEntry *entryPtr;
char *span;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 2, objv, "?index? ?index ...?");
return TCL_ERROR;
}
if (tablePtr->spanTbl == NULL) {
/* Avoid the whole thing if we have no spans */
if (objc > 3) {
Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0);
}
return TCL_OK;
}
if (objc == 2) {
/* return all "hidden" cells */
Tcl_HashSearch search;
Tcl_Obj *objPtr = Tcl_NewObj();
for (entryPtr = Tcl_FirstHashEntry(tablePtr->spanAffTbl, &search);
entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
if ((span = (char *) Tcl_GetHashValue(entryPtr)) == NULL) {
/* this is actually a spanning cell */
continue;
}
Tcl_ListObjAppendElement(NULL, objPtr,
Tcl_NewStringObj(Tcl_GetHashKey(tablePtr->spanAffTbl,
entryPtr), -1));
}
Tcl_SetObjResult(interp, TableCellSortObj(interp, objPtr));
return TCL_OK;
}
if (objc == 3) {
if (TableGetIndexObj(tablePtr, objv[2], &row, &col) != TCL_OK) {
return TCL_ERROR;
}
/* Just return the spanning values of the one cell */
entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl,
Tcl_GetString(objv[2]));
if (entryPtr != NULL &&
(span = (char *)Tcl_GetHashValue(entryPtr)) != NULL) {
/* this is a hidden cell */
Tcl_SetObjResult(interp, Tcl_NewStringObj(span, -1));
}
return TCL_OK;
}
for (i = 2; i < objc; i++) {
if (TableGetIndexObj(tablePtr, objv[i], &row, &col) == TCL_ERROR) {
return TCL_ERROR;
}
entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl,
Tcl_GetString(objv[i]));
if (entryPtr != NULL &&
(char *)Tcl_GetHashValue(entryPtr) != NULL) {
/* this is a hidden cell */
continue;
}
/* We only reach here if it doesn't satisfy "hidden" criteria */
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0));
return TCL_OK;
}
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1));
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* TableSpanSanCheck --
* This procedure is invoked by TableConfigure to make sure
* that spans are kept sane according to the docs.
* See the user documentation for details on what it does.
*
* Results:
* void.
*
* Side effects:
* Spans in title areas can be reconstrained.
*
*--------------------------------------------------------------
*/
void
TableSpanSanCheck(register Table *tablePtr)
{
int rs, cs, row, col, reset;
Tcl_HashEntry *entryPtr;
Tcl_HashSearch search;
if (tablePtr->spanTbl == NULL) {
return;
}
for (entryPtr = Tcl_FirstHashEntry(tablePtr->spanTbl, &search);
entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
reset = 0;
TableParseArrayIndex(&row, &col,
Tcl_GetHashKey(tablePtr->spanTbl, entryPtr));
TableParseArrayIndex(&rs, &cs,
(char *) Tcl_GetHashValue(entryPtr));
if ((row-tablePtr->rowOffset < tablePtr->titleRows) &&
(row-tablePtr->rowOffset+rs >= tablePtr->titleRows)) {
rs = tablePtr->titleRows-(row-tablePtr->rowOffset)-1;
reset = 1;
}
if ((col-tablePtr->colOffset < tablePtr->titleCols) &&
(col-tablePtr->colOffset+cs >= tablePtr->titleCols)) {
cs = tablePtr->titleCols-(col-tablePtr->colOffset)-1;
reset = 1;
}
if (reset) {
Table_SpanSet(tablePtr, row, col, rs, cs);
}
}
}