summaryrefslogtreecommitdiff
path: root/libgui/src/tkTableCell.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgui/src/tkTableCell.c')
-rw-r--r--libgui/src/tkTableCell.c563
1 files changed, 563 insertions, 0 deletions
diff --git a/libgui/src/tkTableCell.c b/libgui/src/tkTableCell.c
new file mode 100644
index 00000000000..9d307b7c03e
--- /dev/null
+++ b/libgui/src/tkTableCell.c
@@ -0,0 +1,563 @@
+/*
+ * tkTableCell.c --
+ *
+ * This module implements cell oriented functions for table
+ * widgets.
+ *
+ * Copyright (c) 1998 Jeffrey Hobbs
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#include "tkTable.h"
+
+static int TableSortCompareProc _ANSI_ARGS_((CONST VOID *first,
+ CONST VOID *second));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+TableCellCoords(Table *tablePtr, int row, int col,
+ int *x, int *y, int *width, int *height)
+{
+ if (tablePtr->rows <= 0 || tablePtr->cols <= 0) {
+ *width = *height = *x = *y = 0;
+ return;
+ }
+ /* real coords required, always should be passed acceptable values,
+ * but this is a possible seg fault otherwise */
+ row = MIN(tablePtr->rows-1, MAX(0, row));
+ col = MIN(tablePtr->cols-1, MAX(0, col));
+ *width = tablePtr->colPixels[col];
+ *height = tablePtr->rowPixels[row];
+ *x = tablePtr->highlightWidth + tablePtr->colStarts[col] -
+ ((col < tablePtr->titleCols) ? 0 : tablePtr->colStarts[tablePtr->leftCol]
+ - tablePtr->colStarts[tablePtr->titleCols]);
+ *y = tablePtr->highlightWidth + tablePtr->rowStarts[row] -
+ ((row < tablePtr->titleRows) ? 0 : tablePtr->rowStarts[tablePtr->topRow]
+ - tablePtr->rowStarts[tablePtr->titleRows]);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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)
+{
+ if (tablePtr->tkwin == NULL) return 0;
+
+ if ((row < tablePtr->topRow && row >= tablePtr->titleRows) ||
+ (col < tablePtr->leftCol && col >= tablePtr->titleCols)) {
+ /* hiding in "dead" space between title areas and visible cells */
+ *rx = 0; *ry = 0; *rw = 0; *rh = 0;
+ return 0;
+ } else {
+ int x, y, w, h, w0, h0, hl = tablePtr->highlightWidth;
+ /* Necessary to use separate vars in case dummies are passed in */
+ TableCellCoords(tablePtr, row, col, &x, &y, &w, &h);
+ *rx = x; *ry = y;
+ 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 */
+ *rw = *rh = 0;
+ return 0;
+ } else {
+ if (full) {
+ *rw = w; *rh = h;
+ } else {
+ *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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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, borders = 2, bd = tablePtr->borderWidth;
+ int dbd = 2*bd;
+ x = MAX(0, x); y = MAX(0, y);
+ 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; i <= tablePtr->cols && x+dbd >= tablePtr->colStarts[i]; i++);
+ if (x > tablePtr->colStarts[--i]+bd) {
+ borders--;
+ *col = -1;
+ } else {
+ *col = (--i < tablePtr->leftCol && i >= tablePtr->titleCols) ?
+ tablePtr->titleCols-1 : i;
+ }
+ for (i = 1; i <= tablePtr->rows && y+dbd >= tablePtr->rowStarts[i]; i++);
+ if (y > tablePtr->rowStarts[--i]+bd) {
+ borders--;
+ *row = -1;
+ } else {
+ *row = (--i < tablePtr->topRow && i >= tablePtr->titleRows) ?
+ tablePtr->titleRows-1 : i;
+ }
+ 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 = 1;
+
+ TableMakeArrayIndex(r, c, buf);
+
+ if (tablePtr->caching) {
+ /* if we are caching, let's see if we have the value cached */
+ entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &new);
+ if (!new) {
+ result = (char *) Tcl_GetHashValue(entryPtr);
+ return result?result:"";
+ }
+ }
+ if (tablePtr->command && tablePtr->useCmd) {
+ 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 executed by table)");
+ Tcl_AddErrorInfo(interp, Tcl_DStringValue(&script));
+ Tk_BackgroundError(interp);
+ TableInvalidateAll(tablePtr, 0);
+ } else {
+ result = Tcl_GetStringResult(interp);
+ }
+ Tcl_FreeResult(interp);
+ Tcl_DStringFree(&script);
+ } else if (tablePtr->arrayVar) {
+ result = Tcl_GetVar2(interp, tablePtr->arrayVar, buf, TCL_GLOBAL_ONLY);
+ }
+ if (result == NULL)
+ result = "";
+ 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;
+ val = (char *)ckalloc(strlen(result)+1);
+ strcpy(val, result);
+ Tcl_SetHashValue(entryPtr, val);
+ }
+ return 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)
+{
+ register Tcl_Interp *interp = tablePtr->interp;
+ char buf[INDEX_BUFSIZE];
+ int code = TCL_OK;
+
+ TableMakeArrayIndex(r, c, buf);
+
+ if (tablePtr->state == STATE_DISABLED)
+ return code;
+ if (tablePtr->command && tablePtr->useCmd) {
+ 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)");
+ Tk_BackgroundError(interp);
+ code = TCL_ERROR;
+ }
+ Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
+ Tcl_DStringFree(&script);
+ } else if (tablePtr->arrayVar) {
+ if (value == NULL || *value == '\0') {
+ Tcl_UnsetVar2(interp, tablePtr->arrayVar, buf, TCL_GLOBAL_ONLY);
+ } else if (Tcl_SetVar2(interp, tablePtr->arrayVar, buf, value,
+ TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
+ code = TCL_ERROR;
+ }
+ }
+ if (tablePtr->caching && code == TCL_OK) {
+ Tcl_HashEntry *entryPtr;
+ int new;
+ char *val;
+
+ val = (char *)ckalloc(strlen(value)+1);
+ strcpy(val, value);
+ entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &new);
+ Tcl_SetHashValue(entryPtr, val);
+ }
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableSortCompareProc --
+ * This procedure is invoked by qsort to determine the proper
+ * ordering between two elements.
+ *
+ * Results:
+ * < 0 means first is "smaller" than "second", > 0 means "first"
+ * is larger than "second", and 0 means they should be treated
+ * as equal.
+ *
+ * Side effects:
+ * None, unless a user-defined comparison command does something
+ * weird.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TableSortCompareProc(first, second)
+ CONST VOID *first, *second; /* Elements to be compared. */
+{
+ int r1, c1, r2, c2;
+ char *firstString = *((char **) first);
+ char *secondString = *((char **) second);
+
+ /* This doesn't account for badly formed indices */
+ sscanf(firstString, "%d,%d", &r1, &c1);
+ sscanf(secondString, "%d,%d", &r2, &c2);
+ if (r1 > r2)
+ return 1;
+ else if (r1 < r2)
+ return -1;
+ else if (c1 > c2)
+ return 1;
+ else if (c1 < c2)
+ return -1;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableCellSort --
+ * Sort a list of table cell elements (of form row,col)
+ *
+ * Results:
+ * Returns the sorted list of elements. Because Tcl_Merge allocs
+ * the space for result, it must later be ckfree'd by caller.
+ *
+ * Side effects:
+ * Behaviour undefined for ill-formed input list of elements.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+TableCellSort(Table *tablePtr, char *str)
+{
+ int listArgc;
+ char **listArgv;
+ char *result;
+
+ if (Tcl_SplitList(tablePtr->interp, str, &listArgc, &listArgv) != TCL_OK)
+ return str;
+ qsort((VOID *) listArgv, (size_t) listArgc, sizeof (char *),
+ TableSortCompareProc);
+ result = Tcl_Merge(listArgc, listArgv);
+ ckfree((char *) listArgv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+#if (TK_MINOR_VERSION > 0)
+ len = Tcl_NumUtfChars(tablePtr->activeBuf, strlen(tablePtr->activeBuf));
+#else
+ len = strlen(tablePtr->activeBuf);
+#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;
+ }
+ tmp = MIN(MAX(0, tmp), 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);
+
+ /*
+ * Note that all of these values will be adjusted by row/ColOffset
+ */
+ if (str[0] == '@') { /* @x,y coordinate */
+ int x, y;
+ char *p, *end;
+
+ p = str+1;
+ x = strtol(p, &end, 0);
+ if ((end == p) || (*end != ','))
+ goto IndexError;
+ p = end+1;
+ y = strtol(p, &end, 0);
+ if ((end == p) || (*end != '\0'))
+ goto IndexError;
+ TableWhatCell(tablePtr, x, y, &r, &c);
+ r += tablePtr->rowOffset;
+ c += tablePtr->colOffset;
+ } else if (sscanf(str, "%d,%d", &r,&c) == 2) {
+ char buf[INDEX_BUFSIZE];
+ TableMakeArrayIndex(r, c, buf);
+ /* Make sure it won't work for "2,3extrastuff" */
+ if (strcmp(buf, str))
+ goto IndexError;
+ /* ensure appropriate user index */
+ r = MIN(MAX(tablePtr->rowOffset,r),tablePtr->rows-1+tablePtr->rowOffset);
+ c = MIN(MAX(tablePtr->colOffset,c),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_AppendResult(tablePtr->interp, "no \"active\" cell in table", NULL);
+ 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_AppendResult(tablePtr->interp, "no \"anchor\" cell in table", NULL);
+ 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 */
+ TableGetLastCell(tablePtr, &r, &c);
+ r += tablePtr->rowOffset;
+ c += tablePtr->colOffset;
+ } else {
+ IndexError:
+ Tcl_AppendResult(tablePtr->interp, "bad table index \"",
+ str, "\"", (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;
+}
+