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

1307 lines
38 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* tkTableCmds.c --
*
* This module implements general commands of a table widget,
* based on the major/minor command structure.
*
* Copyright (c) 1998-2002 Jeffrey Hobbs
*
* See the file "license.txt" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#include "tkTable.h"
/*
*--------------------------------------------------------------
*
* Table_ActivateCmd --
* This procedure is invoked to process the activate method
* that corresponds to a table 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_ActivateCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int result = TCL_OK;
int row, col, templen;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "index");
return TCL_ERROR;
} else if (Tcl_GetStringFromObj(objv[2], &templen), templen == 0) {
/*
* Test implementation to clear active cell (becroft)
*/
tablePtr->flags &= ~HAS_ACTIVE;
tablePtr->flags |= ACTIVE_DISABLED;
tablePtr->activeRow = -1;
tablePtr->activeCol = -1;
TableAdjustActive(tablePtr);
TableConfigCursor(tablePtr);
} else if (TableGetIndexObj(tablePtr, objv[2], &row, &col) != TCL_OK) {
return TCL_ERROR;
} else {
int x, y, w, dummy;
char buf1[INDEX_BUFSIZE], buf2[INDEX_BUFSIZE];
/* convert to valid active index in real coords */
row -= tablePtr->rowOffset;
col -= tablePtr->colOffset;
/* we do this regardless, to avoid cell commit problems */
if ((tablePtr->flags & HAS_ACTIVE) &&
(tablePtr->flags & TEXT_CHANGED)) {
tablePtr->flags &= ~TEXT_CHANGED;
TableSetCellValue(tablePtr,
tablePtr->activeRow+tablePtr->rowOffset,
tablePtr->activeCol+tablePtr->colOffset,
tablePtr->activeBuf);
}
if (row != tablePtr->activeRow || col != tablePtr->activeCol) {
if (tablePtr->flags & HAS_ACTIVE) {
TableMakeArrayIndex(tablePtr->activeRow+tablePtr->rowOffset,
tablePtr->activeCol+tablePtr->colOffset,
buf1);
} else {
buf1[0] = '\0';
}
tablePtr->flags |= HAS_ACTIVE;
tablePtr->flags &= ~ACTIVE_DISABLED;
tablePtr->activeRow = row;
tablePtr->activeCol = col;
if (tablePtr->activeTagPtr != NULL) {
ckfree((char *) (tablePtr->activeTagPtr));
tablePtr->activeTagPtr = NULL;
}
TableAdjustActive(tablePtr);
TableConfigCursor(tablePtr);
if (!(tablePtr->flags & BROWSE_CMD) &&
tablePtr->browseCmd != NULL) {
Tcl_DString script;
tablePtr->flags |= BROWSE_CMD;
row = tablePtr->activeRow+tablePtr->rowOffset;
col = tablePtr->activeCol+tablePtr->colOffset;
TableMakeArrayIndex(row, col, buf2);
Tcl_DStringInit(&script);
ExpandPercents(tablePtr, tablePtr->browseCmd, row, col,
buf1, buf2, tablePtr->icursor, &script, 0);
result = Tcl_GlobalEval(interp, Tcl_DStringValue(&script));
if (result == TCL_OK || result == TCL_RETURN) {
Tcl_ResetResult(interp);
}
Tcl_DStringFree(&script);
tablePtr->flags &= ~BROWSE_CMD;
}
} else {
char *p = Tcl_GetString(objv[2]);
if ((tablePtr->activeTagPtr != NULL) && *p == '@' &&
!(tablePtr->flags & ACTIVE_DISABLED) &&
TableCellVCoords(tablePtr, row, col, &x, &y, &w, &dummy, 0)) {
/* we are clicking into the same cell
* If it was activated with @x,y indexing,
* find the closest char */
Tk_TextLayout textLayout;
TableTag *tagPtr = tablePtr->activeTagPtr;
/* no error checking because GetIndex did it for us */
p++;
x = strtol(p, &p, 0) - x - tablePtr->activeX;
p++;
y = strtol(p, &p, 0) - y - tablePtr->activeY;
textLayout = Tk_ComputeTextLayout(tagPtr->tkfont,
tablePtr->activeBuf, -1,
(tagPtr->wrap) ? w : 0,
tagPtr->justify, 0, &dummy, &dummy);
tablePtr->icursor = Tk_PointToChar(textLayout, x, y);
Tk_FreeTextLayout(textLayout);
TableRefresh(tablePtr, row, col, CELL|INV_FORCE);
}
}
tablePtr->flags |= HAS_ACTIVE;
}
return result;
}
/*
*--------------------------------------------------------------
*
* Table_AdjustCmd --
* This procedure is invoked to process the width/height method
* that corresponds to a table 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_AdjustCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
Tcl_HashEntry *entryPtr;
Tcl_HashSearch search;
Tcl_HashTable *hashTablePtr;
int i, widthType, dummy, value, posn, offset;
char buf1[INDEX_BUFSIZE];
widthType = (*(Tcl_GetString(objv[1])) == 'w');
/* changes the width/height of certain selected columns */
if (objc != 3 && (objc & 1)) {
Tcl_WrongNumArgs(interp, 2, objv, widthType ?
"?col? ?width col width ...?" :
"?row? ?height row height ...?");
return TCL_ERROR;
}
if (widthType) {
hashTablePtr = tablePtr->colWidths;
offset = tablePtr->colOffset;
} else {
hashTablePtr = tablePtr->rowHeights;
offset = tablePtr->rowOffset;
}
if (objc == 2) {
/* print out all the preset column widths or row heights */
entryPtr = Tcl_FirstHashEntry(hashTablePtr, &search);
while (entryPtr != NULL) {
posn = ((int) Tcl_GetHashKey(hashTablePtr, entryPtr)) + offset;
value = (int) Tcl_GetHashValue(entryPtr);
sprintf(buf1, "%d %d", posn, value);
/* OBJECTIFY */
Tcl_AppendElement(interp, buf1);
entryPtr = Tcl_NextHashEntry(&search);
}
} else if (objc == 3) {
/* get the width/height of a particular row/col */
if (Tcl_GetIntFromObj(interp, objv[2], &posn) != TCL_OK) {
return TCL_ERROR;
}
/* no range check is done, why bother? */
posn -= offset;
entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn);
if (entryPtr != NULL) {
Tcl_SetIntObj(Tcl_GetObjResult(interp),
(int) Tcl_GetHashValue(entryPtr));
} else {
Tcl_SetIntObj(Tcl_GetObjResult(interp), widthType ?
tablePtr->defColWidth : tablePtr->defRowHeight);
}
} else {
for (i=2; i<objc; i++) {
/* set new width|height here */
value = -999999;
if (Tcl_GetIntFromObj(interp, objv[i++], &posn) != TCL_OK ||
(strcmp(Tcl_GetString(objv[i]), "default") &&
Tcl_GetIntFromObj(interp, objv[i], &value) != TCL_OK)) {
return TCL_ERROR;
}
posn -= offset;
if (value == -999999) {
/* reset that field */
entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn);
if (entryPtr != NULL) {
Tcl_DeleteHashEntry(entryPtr);
}
} else {
entryPtr = Tcl_CreateHashEntry(hashTablePtr,
(char *) posn, &dummy);
Tcl_SetHashValue(entryPtr, (ClientData) value);
}
}
TableAdjustParams(tablePtr);
/* rerequest geometry */
TableGeometryRequest(tablePtr);
/*
* Invalidate the whole window as TableAdjustParams
* will only check to see if the top left cell has moved
* FIX: should just move from lowest order visible cell
* to edge of window
*/
TableInvalidateAll(tablePtr, 0);
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_BboxCmd --
* This procedure is invoked to process the bbox method
* that corresponds to a table 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_BboxCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int x, y, w, h, row, col, key;
Tcl_Obj *resultPtr;
/* Returns bounding box of cell(s) */
if (objc < 3 || objc > 4) {
Tcl_WrongNumArgs(interp, 2, objv, "first ?last?");
return TCL_ERROR;
} else if (TableGetIndexObj(tablePtr, objv[2], &row, &col) == TCL_ERROR ||
(objc == 4 &&
TableGetIndexObj(tablePtr, objv[3], &x, &y) == TCL_ERROR)) {
return TCL_ERROR;
}
resultPtr = Tcl_GetObjResult(interp);
if (objc == 3) {
row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
if (TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0)) {
Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(x));
Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(y));
Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(w));
Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(h));
}
return TCL_OK;
} else {
int r1, c1, r2, c2, minX = 99999, minY = 99999, maxX = 0, maxY = 0;
row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
x -= tablePtr->rowOffset; y -= tablePtr->colOffset;
r1 = MIN(row,x); r2 = MAX(row,x);
c1 = MIN(col,y); c2 = MAX(col,y);
key = 0;
for (row = r1; row <= r2; row++) {
for (col = c1; col <= c2; col++) {
if (TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0)) {
/* Get max bounding box */
if (x < minX) minX = x;
if (y < minY) minY = y;
if (x+w > maxX) maxX = x+w;
if (y+h > maxY) maxY = y+h;
key++;
}
}
}
if (key) {
Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(minX));
Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(minY));
Tcl_ListObjAppendElement(NULL, resultPtr,
Tcl_NewIntObj(maxX-minX));
Tcl_ListObjAppendElement(NULL, resultPtr,
Tcl_NewIntObj(maxY-minY));
}
}
return TCL_OK;
}
static CONST84 char *bdCmdNames[] = {
"mark", "dragto", (char *)NULL
};
enum bdCmd {
BD_MARK, BD_DRAGTO
};
/*
*--------------------------------------------------------------
*
* Table_BorderCmd --
* This procedure is invoked to process the bbox method
* that corresponds to a table 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_BorderCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
Tcl_HashEntry *entryPtr;
int x, y, w, h, row, col, key, dummy, value, cmdIndex;
char *rc = NULL;
Tcl_Obj *objPtr, *resultPtr;
if (objc < 5 || objc > 6) {
Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y ?row|col?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[2], bdCmdNames,
"option", 0, &cmdIndex) != TCL_OK ||
Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
return TCL_ERROR;
}
if (objc == 6) {
rc = Tcl_GetStringFromObj(objv[5], &w);
if ((w < 1) || (strncmp(rc, "row", w) && strncmp(rc, "col", w))) {
Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y ?row|col?");
return TCL_ERROR;
}
}
resultPtr = Tcl_GetObjResult(interp);
switch ((enum bdCmd) cmdIndex) {
case BD_MARK:
/* Use x && y to determine if we are over a border */
value = TableAtBorder(tablePtr, x, y, &row, &col);
/* Cache the row && col for use in DRAGTO */
tablePtr->scanMarkRow = row;
tablePtr->scanMarkCol = col;
if (!value) {
return TCL_OK;
}
TableCellCoords(tablePtr, row, col, &x, &y, &dummy, &dummy);
tablePtr->scanMarkX = x;
tablePtr->scanMarkY = y;
if (objc == 5 || *rc == 'r') {
if (row < 0) {
objPtr = Tcl_NewStringObj("", 0);
} else {
objPtr = Tcl_NewIntObj(row+tablePtr->rowOffset);
}
Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
}
if (objc == 5 || *rc == 'c') {
if (col < 0) {
objPtr = Tcl_NewStringObj("", 0);
} else {
objPtr = Tcl_NewIntObj(col+tablePtr->colOffset);
}
Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
}
return TCL_OK; /* BORDER MARK */
case BD_DRAGTO:
/* check to see if we want to resize any borders */
if (tablePtr->resize == SEL_NONE) { return TCL_OK; }
row = tablePtr->scanMarkRow;
col = tablePtr->scanMarkCol;
TableCellCoords(tablePtr, row, col, &w, &h, &dummy, &dummy);
key = 0;
if (row >= 0 && (tablePtr->resize & SEL_ROW)) {
/* row border was active, move it */
value = y-h;
if (value < -1) value = -1;
if (value != tablePtr->scanMarkY) {
entryPtr = Tcl_CreateHashEntry(tablePtr->rowHeights,
(char *) row, &dummy);
/* -value means rowHeight will be interp'd as pixels, not
lines */
Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
tablePtr->scanMarkY = value;
key++;
}
}
if (col >= 0 && (tablePtr->resize & SEL_COL)) {
/* col border was active, move it */
value = x-w;
if (value < -1) value = -1;
if (value != tablePtr->scanMarkX) {
entryPtr = Tcl_CreateHashEntry(tablePtr->colWidths,
(char *) col, &dummy);
/* -value means colWidth will be interp'd as pixels, not
chars */
Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
tablePtr->scanMarkX = value;
key++;
}
}
/* Only if something changed do we want to update */
if (key) {
TableAdjustParams(tablePtr);
/* Only rerequest geometry if the basis is the #rows &| #cols */
if (tablePtr->maxReqCols || tablePtr->maxReqRows)
TableGeometryRequest(tablePtr);
TableInvalidateAll(tablePtr, 0);
}
return TCL_OK; /* BORDER DRAGTO */
}
return TCL_OK;
}
/* clear subcommands */
static CONST84 char *clearNames[] = {
"all", "cache", "sizes", "tags", (char *)NULL
};
enum clearCommand {
CLEAR_ALL, CLEAR_CACHE, CLEAR_SIZES, CLEAR_TAGS
};
/*
*--------------------------------------------------------------
*
* Table_ClearCmd --
* This procedure is invoked to process the clear method
* that corresponds to a table widget managed by this module.
* See the user documentation for details on what it does.
*
* Results:
* Cached info can be lost. Returns valid Tcl result.
*
* Side effects:
* Can cause redraw.
* See the user documentation.
*
*--------------------------------------------------------------
*/
int
Table_ClearCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int cmdIndex, redraw = 0;
if (objc < 3 || objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv, "option ?first? ?last?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[2], clearNames,
"clear option", 0, &cmdIndex) != TCL_OK) {
return TCL_ERROR;
}
if (objc == 3) {
if (cmdIndex == CLEAR_TAGS || cmdIndex == CLEAR_ALL) {
Tcl_DeleteHashTable(tablePtr->rowStyles);
Tcl_DeleteHashTable(tablePtr->colStyles);
Tcl_DeleteHashTable(tablePtr->cellStyles);
Tcl_DeleteHashTable(tablePtr->flashCells);
Tcl_DeleteHashTable(tablePtr->selCells);
/* style hash tables */
Tcl_InitHashTable(tablePtr->rowStyles, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(tablePtr->colStyles, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(tablePtr->cellStyles, TCL_STRING_KEYS);
/* special style hash tables */
Tcl_InitHashTable(tablePtr->flashCells, TCL_STRING_KEYS);
Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
}
if (cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) {
Tcl_DeleteHashTable(tablePtr->colWidths);
Tcl_DeleteHashTable(tablePtr->rowHeights);
/* style hash tables */
Tcl_InitHashTable(tablePtr->colWidths, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(tablePtr->rowHeights, TCL_ONE_WORD_KEYS);
}
if (cmdIndex == CLEAR_CACHE || cmdIndex == CLEAR_ALL) {
Table_ClearHashTable(tablePtr->cache);
Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
/* If we were caching and we have no other data source,
* invalidate all the cells */
if (tablePtr->dataSource == DATA_CACHE) {
TableGetActiveBuf(tablePtr);
}
}
redraw = 1;
} else {
int row, col, r1, r2, c1, c2;
Tcl_HashEntry *entryPtr;
char buf[INDEX_BUFSIZE], *value;
if (TableGetIndexObj(tablePtr, objv[3], &row, &col) != TCL_OK ||
((objc == 5) &&
TableGetIndexObj(tablePtr, objv[4], &r2, &c2) != TCL_OK)) {
return TCL_ERROR;
}
if (objc == 4) {
r1 = r2 = row;
c1 = c2 = col;
} else {
r1 = MIN(row,r2); r2 = MAX(row,r2);
c1 = MIN(col,c2); c2 = MAX(col,c2);
}
for (row = r1; row <= r2; row++) {
/* Note that *Styles entries are user based (no offset)
* while size entries are 0-based (real) */
if ((cmdIndex == CLEAR_TAGS || cmdIndex == CLEAR_ALL) &&
(entryPtr = Tcl_FindHashEntry(tablePtr->rowStyles,
(char *) row))) {
Tcl_DeleteHashEntry(entryPtr);
redraw = 1;
}
if ((cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) &&
(entryPtr = Tcl_FindHashEntry(tablePtr->rowHeights,
(char *) row-tablePtr->rowOffset))) {
Tcl_DeleteHashEntry(entryPtr);
redraw = 1;
}
for (col = c1; col <= c2; col++) {
TableMakeArrayIndex(row, col, buf);
if (cmdIndex == CLEAR_TAGS || cmdIndex == CLEAR_ALL) {
if ((row == r1) &&
(entryPtr = Tcl_FindHashEntry(tablePtr->colStyles,
(char *) col))) {
Tcl_DeleteHashEntry(entryPtr);
redraw = 1;
}
if ((entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles,
buf))) {
Tcl_DeleteHashEntry(entryPtr);
redraw = 1;
}
if ((entryPtr = Tcl_FindHashEntry(tablePtr->flashCells,
buf))) {
Tcl_DeleteHashEntry(entryPtr);
redraw = 1;
}
if ((entryPtr = Tcl_FindHashEntry(tablePtr->selCells,
buf))) {
Tcl_DeleteHashEntry(entryPtr);
redraw = 1;
}
}
if ((cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) &&
row == r1 &&
(entryPtr = Tcl_FindHashEntry(tablePtr->colWidths, (char *)
col-tablePtr->colOffset))) {
Tcl_DeleteHashEntry(entryPtr);
redraw = 1;
}
if ((cmdIndex == CLEAR_CACHE || cmdIndex == CLEAR_ALL) &&
(entryPtr = Tcl_FindHashEntry(tablePtr->cache, buf))) {
value = (char *) Tcl_GetHashValue(entryPtr);
if (value) { ckfree(value); }
Tcl_DeleteHashEntry(entryPtr);
/* if the cache is our data source,
* we need to invalidate the cells changed */
if ((tablePtr->dataSource == DATA_CACHE) &&
(row-tablePtr->rowOffset == tablePtr->activeRow &&
col-tablePtr->colOffset == tablePtr->activeCol))
TableGetActiveBuf(tablePtr);
redraw = 1;
}
}
}
}
/* This could be more sensitive about what it updates,
* but that can actually be a lot more costly in some cases */
if (redraw) {
if (cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) {
TableAdjustParams(tablePtr);
/* rerequest geometry */
TableGeometryRequest(tablePtr);
}
TableInvalidateAll(tablePtr, 0);
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_CurselectionCmd --
* This procedure is invoked to process the bbox method
* that corresponds to a table 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_CurselectionCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
Tcl_HashEntry *entryPtr;
Tcl_HashSearch search;
char *value = NULL;
int row, col;
if (objc > 3) {
Tcl_WrongNumArgs(interp, 2, objv, "?value?");
return TCL_ERROR;
}
if (objc == 3) {
/* make sure there is a data source to accept a set value */
if ((tablePtr->state == STATE_DISABLED) ||
(tablePtr->dataSource == DATA_NONE)) {
return TCL_OK;
}
value = Tcl_GetString(objv[2]);
for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
TableParseArrayIndex(&row, &col,
Tcl_GetHashKey(tablePtr->selCells, entryPtr));
TableSetCellValue(tablePtr, row, col, value);
row -= tablePtr->rowOffset;
col -= tablePtr->colOffset;
if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
TableGetActiveBuf(tablePtr);
}
TableRefresh(tablePtr, row, col, CELL);
}
} else {
Tcl_Obj *objPtr = Tcl_NewObj();
for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
value = Tcl_GetHashKey(tablePtr->selCells, entryPtr);
Tcl_ListObjAppendElement(NULL, objPtr,
Tcl_NewStringObj(value, -1));
}
Tcl_SetObjResult(interp, TableCellSortObj(interp, objPtr));
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_CurvalueCmd --
* This procedure is invoked to process the curvalue method
* that corresponds to a table 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_CurvalueCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
if (objc > 3) {
Tcl_WrongNumArgs(interp, 2, objv, "?<value>?");
return TCL_ERROR;
} else if (!(tablePtr->flags & HAS_ACTIVE)) {
return TCL_OK;
}
if (objc == 3) {
char *value;
int len;
value = Tcl_GetStringFromObj(objv[2], &len);
if (STREQ(value, tablePtr->activeBuf)) {
Tcl_SetObjResult(interp, objv[2]);
return TCL_OK;
}
/* validate potential new active buffer contents
* only accept if validation returns acceptance. */
if (tablePtr->validate &&
TableValidateChange(tablePtr,
tablePtr->activeRow+tablePtr->rowOffset,
tablePtr->activeCol+tablePtr->colOffset,
tablePtr->activeBuf,
value, tablePtr->icursor) != TCL_OK) {
return TCL_OK;
}
tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, len+1);
strcpy(tablePtr->activeBuf, value);
/* mark the text as changed */
tablePtr->flags |= TEXT_CHANGED;
TableSetActiveIndex(tablePtr);
/* check for possible adjustment of icursor */
TableGetIcursor(tablePtr, "insert", (int *)0);
TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol, CELL);
}
Tcl_SetObjResult(interp, Tcl_NewStringObj(tablePtr->activeBuf, -1));
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_GetCmd --
* This procedure is invoked to process the bbox method
* that corresponds to a table 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_GetCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int result = TCL_OK;
int r1, c1, r2, c2, row, col;
if (objc < 3 || objc > 4) {
Tcl_WrongNumArgs(interp, 2, objv, "first ?last?");
result = TCL_ERROR;
} else if (TableGetIndexObj(tablePtr, objv[2], &row, &col) == TCL_ERROR) {
result = TCL_ERROR;
} else if (objc == 3) {
Tcl_SetObjResult(interp,
Tcl_NewStringObj(TableGetCellValue(tablePtr, row, col), -1));
} else if (TableGetIndexObj(tablePtr, objv[3], &r2, &c2) == TCL_ERROR) {
result = TCL_ERROR;
} else {
Tcl_Obj *objPtr = Tcl_NewObj();
r1 = MIN(row,r2); r2 = MAX(row,r2);
c1 = MIN(col,c2); c2 = MAX(col,c2);
for ( row = r1; row <= r2; row++ ) {
for ( col = c1; col <= c2; col++ ) {
Tcl_ListObjAppendElement(NULL, objPtr,
Tcl_NewStringObj(TableGetCellValue(tablePtr,
row, col), -1));
}
}
Tcl_SetObjResult(interp, objPtr);
}
return result;
}
/*
*--------------------------------------------------------------
*
* Table_ScanCmd --
* This procedure is invoked to process the scan method
* that corresponds to a table 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_ScanCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int x, y, row, col, cmdIndex;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y");
return TCL_ERROR;
} else if (Tcl_GetIndexFromObj(interp, objv[2], bdCmdNames,
"option", 0, &cmdIndex) != TCL_OK ||
Tcl_GetIntFromObj(interp, objv[3], &x) == TCL_ERROR ||
Tcl_GetIntFromObj(interp, objv[4], &y) == TCL_ERROR) {
return TCL_ERROR;
}
switch ((enum bdCmd) cmdIndex) {
case BD_MARK:
TableWhatCell(tablePtr, x, y, &row, &col);
tablePtr->scanMarkRow = row-tablePtr->topRow;
tablePtr->scanMarkCol = col-tablePtr->leftCol;
tablePtr->scanMarkX = x;
tablePtr->scanMarkY = y;
break;
case BD_DRAGTO: {
int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
y += (5*(y-tablePtr->scanMarkY));
x += (5*(x-tablePtr->scanMarkX));
TableWhatCell(tablePtr, x, y, &row, &col);
/* maintain appropriate real index */
tablePtr->topRow = BETWEEN(row-tablePtr->scanMarkRow,
tablePtr->titleRows, tablePtr->rows-1);
tablePtr->leftCol = BETWEEN(col-tablePtr->scanMarkCol,
tablePtr->titleCols, tablePtr->cols-1);
/* Adjust the table if new top left */
if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol) {
TableAdjustParams(tablePtr);
}
break;
}
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_SelAnchorCmd --
* This procedure is invoked to process the selection anchor method
* that corresponds to a table 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_SelAnchorCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int row, col;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 3, objv, "index");
return TCL_ERROR;
} else if (TableGetIndexObj(tablePtr, objv[3], &row, &col) != TCL_OK) {
return TCL_ERROR;
}
tablePtr->flags |= HAS_ANCHOR;
/* maintain appropriate real index */
if (tablePtr->selectTitles) {
tablePtr->anchorRow = BETWEEN(row-tablePtr->rowOffset,
0, tablePtr->rows-1);
tablePtr->anchorCol = BETWEEN(col-tablePtr->colOffset,
0, tablePtr->cols-1);
} else {
tablePtr->anchorRow = BETWEEN(row-tablePtr->rowOffset,
tablePtr->titleRows, tablePtr->rows-1);
tablePtr->anchorCol = BETWEEN(col-tablePtr->colOffset,
tablePtr->titleCols, tablePtr->cols-1);
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_SelClearCmd --
* This procedure is invoked to process the selection clear method
* that corresponds to a table 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_SelClearCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int result = TCL_OK;
char buf1[INDEX_BUFSIZE];
int row, col, key, clo=0,chi=0,r1,c1,r2,c2;
Tcl_HashEntry *entryPtr;
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 3, objv, "all|<first> ?<last>?");
return TCL_ERROR;
}
if (STREQ(Tcl_GetString(objv[3]), "all")) {
Tcl_HashSearch search;
for(entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
TableParseArrayIndex(&row, &col,
Tcl_GetHashKey(tablePtr->selCells,entryPtr));
Tcl_DeleteHashEntry(entryPtr);
TableRefresh(tablePtr, row-tablePtr->rowOffset,
col-tablePtr->colOffset, CELL);
}
return TCL_OK;
}
if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR ||
(objc==5 &&
TableGetIndexObj(tablePtr, objv[4], &r2, &c2) == TCL_ERROR)) {
return TCL_ERROR;
}
key = 0;
if (objc == 4) {
r1 = r2 = row;
c1 = c2 = col;
} else {
r1 = MIN(row,r2); r2 = MAX(row,r2);
c1 = MIN(col,c2); c2 = MAX(col,c2);
}
switch (tablePtr->selectType) {
case SEL_BOTH:
clo = c1; chi = c2;
c1 = tablePtr->colOffset;
c2 = tablePtr->cols-1+c1;
key = 1;
goto CLEAR_CELLS;
CLEAR_BOTH:
key = 0;
c1 = clo; c2 = chi;
case SEL_COL:
r1 = tablePtr->rowOffset;
r2 = tablePtr->rows-1+r1;
break;
case SEL_ROW:
c1 = tablePtr->colOffset;
c2 = tablePtr->cols-1+c1;
break;
}
/* row/col are in user index coords */
CLEAR_CELLS:
for ( row = r1; row <= r2; row++ ) {
for ( col = c1; col <= c2; col++ ) {
TableMakeArrayIndex(row, col, buf1);
entryPtr = Tcl_FindHashEntry(tablePtr->selCells, buf1);
if (entryPtr != NULL) {
Tcl_DeleteHashEntry(entryPtr);
TableRefresh(tablePtr, row-tablePtr->rowOffset,
col-tablePtr->colOffset, CELL);
}
}
}
if (key) goto CLEAR_BOTH;
return result;
}
/*
*--------------------------------------------------------------
*
* Table_SelIncludesCmd --
* This procedure is invoked to process the selection includes method
* that corresponds to a table 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_SelIncludesCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int row, col;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 3, objv, "index");
return TCL_ERROR;
} else if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR) {
return TCL_ERROR;
} else {
char buf[INDEX_BUFSIZE];
TableMakeArrayIndex(row, col, buf);
Tcl_SetBooleanObj(Tcl_GetObjResult(interp),
(Tcl_FindHashEntry(tablePtr->selCells, buf)!=NULL));
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_SelSetCmd --
* This procedure is invoked to process the selection set method
* that corresponds to a table 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_SelSetCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int row, col, dummy, key;
char buf1[INDEX_BUFSIZE];
Tcl_HashSearch search;
Tcl_HashEntry *entryPtr;
int clo=0, chi=0, r1, c1, r2, c2, firstRow, firstCol, lastRow, lastCol;
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 3, objv, "first ?last?");
return TCL_ERROR;
}
if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR ||
(objc==5 &&
TableGetIndexObj(tablePtr, objv[4], &r2, &c2) == TCL_ERROR)) {
return TCL_ERROR;
}
key = 0;
lastRow = tablePtr->rows-1+tablePtr->rowOffset;
lastCol = tablePtr->cols-1+tablePtr->colOffset;
if (tablePtr->selectTitles) {
firstRow = tablePtr->rowOffset;
firstCol = tablePtr->colOffset;
} else {
firstRow = tablePtr->titleRows+tablePtr->rowOffset;
firstCol = tablePtr->titleCols+tablePtr->colOffset;
}
/* maintain appropriate user index */
CONSTRAIN(row, firstRow, lastRow);
CONSTRAIN(col, firstCol, lastCol);
if (objc == 4) {
r1 = r2 = row;
c1 = c2 = col;
} else {
CONSTRAIN(r2, firstRow, lastRow);
CONSTRAIN(c2, firstCol, lastCol);
r1 = MIN(row,r2); r2 = MAX(row,r2);
c1 = MIN(col,c2); c2 = MAX(col,c2);
}
switch (tablePtr->selectType) {
case SEL_BOTH:
if (firstCol > lastCol) c2--; /* No selectable columns in table */
if (firstRow > lastRow) r2--; /* No selectable rows in table */
clo = c1; chi = c2;
c1 = firstCol;
c2 = lastCol;
key = 1;
goto SET_CELLS;
SET_BOTH:
key = 0;
c1 = clo; c2 = chi;
case SEL_COL:
r1 = firstRow;
r2 = lastRow;
if (firstCol > lastCol) c2--; /* No selectable columns in table */
break;
case SEL_ROW:
c1 = firstCol;
c2 = lastCol;
if (firstRow>lastRow) r2--; /* No selectable rows in table */
break;
}
SET_CELLS:
entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
for ( row = r1; row <= r2; row++ ) {
for ( col = c1; col <= c2; col++ ) {
TableMakeArrayIndex(row, col, buf1);
if (Tcl_FindHashEntry(tablePtr->selCells, buf1) == NULL) {
Tcl_CreateHashEntry(tablePtr->selCells, buf1, &dummy);
TableRefresh(tablePtr, row-tablePtr->rowOffset,
col-tablePtr->colOffset, CELL);
}
}
}
if (key) goto SET_BOTH;
/* Adjust the table for top left, selection on screen etc */
TableAdjustParams(tablePtr);
/* If the table was previously empty and we want to export the
* selection, we should grab it now */
if (entryPtr == NULL && tablePtr->exportSelection) {
Tk_OwnSelection(tablePtr->tkwin, XA_PRIMARY, TableLostSelection,
(ClientData) tablePtr);
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Table_ViewCmd --
* This procedure is invoked to process the x|yview method
* that corresponds to a table 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_ViewCmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int row, col, value;
char *xy;
/* Check xview or yview */
if (objc > 5) {
Tcl_WrongNumArgs(interp, 2, objv, "?args?");
return TCL_ERROR;
}
xy = Tcl_GetString(objv[1]);
if (objc == 2) {
Tcl_Obj *resultPtr;
int diff, x, y, w, h;
double first, last;
resultPtr = Tcl_GetObjResult(interp);
TableGetLastCell(tablePtr, &row, &col);
TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0);
if (*xy == 'y') {
if (row < tablePtr->titleRows) {
first = 0;
last = 1;
} else {
diff = tablePtr->rowStarts[tablePtr->titleRows];
last = (double) (tablePtr->rowStarts[tablePtr->rows]-diff);
first = (tablePtr->rowStarts[tablePtr->topRow]-diff) / last;
last = (h+tablePtr->rowStarts[row]-diff) / last;
}
} else {
if (col < tablePtr->titleCols) {
first = 0;
last = 1;
} else {
diff = tablePtr->colStarts[tablePtr->titleCols];
last = (double) (tablePtr->colStarts[tablePtr->cols]-diff);
first = (tablePtr->colStarts[tablePtr->leftCol]-diff) / last;
last = (w+tablePtr->colStarts[col]-diff) / last;
}
}
Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewDoubleObj(first));
Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewDoubleObj(last));
} else {
/* cache old topleft to see if it changes */
int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
if (objc == 3) {
if (Tcl_GetIntFromObj(interp, objv[2], &value) != TCL_OK) {
return TCL_ERROR;
}
if (*xy == 'y') {
tablePtr->topRow = value + tablePtr->titleRows;
} else {
tablePtr->leftCol = value + tablePtr->titleCols;
}
} else {
int result;
double frac;
#if (TK_MINOR_VERSION > 0) /* 8.1+ */
result = Tk_GetScrollInfoObj(interp, objc, objv, &frac, &value);
#else
int i;
char **argv = (char **) ckalloc((objc + 1) * sizeof(char *));
for (i = 0; i < objc; i++) {
argv[i] = Tcl_GetString(objv[i]);
}
argv[i] = NULL;
result = Tk_GetScrollInfo(interp, objc, argv, &frac, &value);
ckfree ((char *) argv);
#endif
switch (result) {
case TK_SCROLL_ERROR:
return TCL_ERROR;
case TK_SCROLL_MOVETO:
if (frac < 0) frac = 0;
if (*xy == 'y') {
tablePtr->topRow = (int)(frac*tablePtr->rows)
+tablePtr->titleRows;
} else {
tablePtr->leftCol = (int)(frac*tablePtr->cols)
+tablePtr->titleCols;
}
break;
case TK_SCROLL_PAGES:
TableGetLastCell(tablePtr, &row, &col);
if (*xy == 'y') {
tablePtr->topRow += value * (row-tablePtr->topRow+1);
} else {
tablePtr->leftCol += value * (col-tablePtr->leftCol+1);
}
break;
case TK_SCROLL_UNITS:
if (*xy == 'y') {
tablePtr->topRow += value;
} else {
tablePtr->leftCol += value;
}
break;
}
}
/* maintain appropriate real index */
CONSTRAIN(tablePtr->topRow, tablePtr->titleRows, tablePtr->rows-1);
CONSTRAIN(tablePtr->leftCol, tablePtr->titleCols, tablePtr->cols-1);
/* Do the table adjustment if topRow || leftCol changed */
if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol) {
TableAdjustParams(tablePtr);
}
}
return TCL_OK;
}
#if 0
/*
*--------------------------------------------------------------
*
* Table_Cmd --
* This procedure is invoked to process the CMD method
* that corresponds to a table 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_Cmd(ClientData clientData, register Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
register Table *tablePtr = (Table *) clientData;
int result = TCL_OK;
return result;
}
#endif