1 /*
2  * tkTableCmds.c --
3  *
4  *	This module implements general commands of a table widget,
5  *	based on the major/minor command structure.
6  *
7  * Copyright (c) 1998-2002 Jeffrey Hobbs
8  *
9  * See the file "license.txt" for information on usage and redistribution
10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  *
12  */
13 
14 #include "tkVMacro.h"
15 #include "tkTable.h"
16 
17 /*
18  *--------------------------------------------------------------
19  *
20  * Table_ActivateCmd --
21  *	This procedure is invoked to process the activate method
22  *	that corresponds to a table widget managed by this module.
23  *	See the user documentation for details on what it does.
24  *
25  * Results:
26  *	A standard Tcl result.
27  *
28  * Side effects:
29  *	See the user documentation.
30  *
31  *--------------------------------------------------------------
32  */
33 int
Table_ActivateCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])34 Table_ActivateCmd(ClientData clientData, register Tcl_Interp *interp,
35 	      int objc, Tcl_Obj *CONST objv[])
36 {
37     register Table *tablePtr = (Table *) clientData;
38     int result = TCL_OK;
39     int row, col;
40 
41     if (objc != 3) {
42 	Tcl_WrongNumArgs(interp, 2, objv, "index");
43 	return TCL_ERROR;
44     } else if (TableGetIndexObj(tablePtr, objv[2], &row, &col) != TCL_OK) {
45 	return TCL_ERROR;
46     } else {
47 	int x, y, w, dummy;
48 	char buf1[INDEX_BUFSIZE], buf2[INDEX_BUFSIZE];
49 
50 	/* convert to valid active index in real coords */
51 	row -= tablePtr->rowOffset;
52 	col -= tablePtr->colOffset;
53 	/* we do this regardless, to avoid cell commit problems */
54 	if ((tablePtr->flags & HAS_ACTIVE) &&
55 	    (tablePtr->flags & TEXT_CHANGED)) {
56 	    tablePtr->flags &= ~TEXT_CHANGED;
57 	    TableSetCellValue(tablePtr,
58 			      tablePtr->activeRow+tablePtr->rowOffset,
59 			      tablePtr->activeCol+tablePtr->colOffset,
60 			      tablePtr->activeBuf);
61 	}
62 	if (row != tablePtr->activeRow || col != tablePtr->activeCol) {
63 	    if (tablePtr->flags & HAS_ACTIVE) {
64 		TableMakeArrayIndex(tablePtr->activeRow+tablePtr->rowOffset,
65 				    tablePtr->activeCol+tablePtr->colOffset,
66 				    buf1);
67 	    } else {
68 		buf1[0] = '\0';
69 	    }
70 	    tablePtr->flags |= HAS_ACTIVE;
71 	    tablePtr->flags &= ~ACTIVE_DISABLED;
72 	    tablePtr->activeRow = row;
73 	    tablePtr->activeCol = col;
74 	    if (tablePtr->activeTagPtr != NULL) {
75 		ckfree((char *) (tablePtr->activeTagPtr));
76 		tablePtr->activeTagPtr = NULL;
77 	    }
78 	    TableAdjustActive(tablePtr);
79 	    TableConfigCursor(tablePtr);
80 	    if (!(tablePtr->flags & BROWSE_CMD) &&
81 		tablePtr->browseCmd != NULL) {
82 		tablePtr->flags |= BROWSE_CMD;
83 		row = tablePtr->activeRow+tablePtr->rowOffset;
84 		col = tablePtr->activeCol+tablePtr->colOffset;
85 		TableMakeArrayIndex(row, col, buf2);
86 
87 		result = LangDoCallback(interp, tablePtr->browseCmd, 1, 2, "%s %s",buf1,buf2);
88 
89 		if (result == TCL_OK || result == TCL_RETURN) {
90 		    Tcl_ResetResult(interp);
91 		}
92 		tablePtr->flags &= ~BROWSE_CMD;
93 	    }
94 	} else {
95 	    char *p = Tcl_GetString(objv[2]);
96 
97 	    if ((tablePtr->activeTagPtr != NULL) && *p == '@' &&
98 		!(tablePtr->flags & ACTIVE_DISABLED) &&
99 		TableCellVCoords(tablePtr, row, col, &x, &y, &w, &dummy, 0)) {
100 		/* we are clicking into the same cell
101 		 * If it was activated with @x,y indexing,
102 		 * find the closest char */
103 		Tk_TextLayout textLayout;
104 		TableTag *tagPtr = tablePtr->activeTagPtr;
105 
106 		/* no error checking because GetIndex did it for us */
107 		p++;
108 		x = strtol(p, &p, 0) - x - tablePtr->activeX;
109 		y = strtol(++p, &p, 0) - y - tablePtr->activeY;
110 
111 		textLayout = Tk_ComputeTextLayout(tagPtr->tkfont,
112 					tablePtr->activeBuf, -1,
113 					(tagPtr->wrap) ? w : 0,
114 					tagPtr->justify, 0, &dummy, &dummy);
115 
116 		tablePtr->icursor = Tk_PointToChar(textLayout, x, y);
117 		Tk_FreeTextLayout(textLayout);
118 		TableRefresh(tablePtr, row, col, CELL|INV_FORCE);
119 	    }
120 	}
121 	tablePtr->flags |= HAS_ACTIVE;
122     }
123     return result;
124 }
125 
126 /*
127  *--------------------------------------------------------------
128  *
129  * Table_AdjustCmd --
130  *	This procedure is invoked to process the width/height method
131  *	that corresponds to a table widget managed by this module.
132  *	See the user documentation for details on what it does.
133  *
134  * Results:
135  *	A standard Tcl result.
136  *
137  * Side effects:
138  *	See the user documentation.
139  *
140  *--------------------------------------------------------------
141  */
142 int
Table_AdjustCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[],int widthType)143 Table_AdjustCmd(ClientData clientData, register Tcl_Interp *interp,
144 		int objc, Tcl_Obj *CONST objv[], int widthType)
145 {
146     /* widthType is a Flag = 1 if this is a col width change command,
147        otherwise assumed to be a row height change command */
148     register Table *tablePtr = (Table *) clientData;
149     Tcl_HashEntry *entryPtr;
150     Tcl_HashSearch search;
151     Tcl_HashTable *hashTablePtr;
152     int i, dummy, value, posn, offset;
153     char buf1[INDEX_BUFSIZE];
154 
155     /* changes the width/height of certain selected columns */
156     if (objc != 3 && (objc & 1)) {
157 	Tcl_WrongNumArgs(interp, 2, objv, widthType ?
158 			 "?col? ?width col width ...?" :
159 			 "?row? ?height row height ...?");
160 	return TCL_ERROR;
161     }
162     if (widthType) {
163 	hashTablePtr = tablePtr->colWidths;
164 	offset = tablePtr->colOffset;
165     } else {
166 	hashTablePtr = tablePtr->rowHeights;
167 	offset = tablePtr->rowOffset;
168     }
169 
170     if (objc == 2) {
171 	/* print out all the preset column widths or row heights */
172 	entryPtr = Tcl_FirstHashEntry(hashTablePtr, &search);
173 	while (entryPtr != NULL) {
174 	    posn = ((int) Tcl_GetHashKey(hashTablePtr, entryPtr)) + offset;
175 	    value = (int) Tcl_GetHashValue(entryPtr);
176 	    sprintf(buf1, "%d %d", posn, value);
177 	    /* OBJECTIFY */
178 	    Tcl_AppendElement(interp, buf1);
179 	    entryPtr = Tcl_NextHashEntry(&search);
180 	}
181     } else if (objc == 3) {
182 	/* get the width/height of a particular row/col */
183 	if (Tcl_GetIntFromObj(interp, objv[2], &posn) != TCL_OK) {
184 	    return TCL_ERROR;
185 	}
186 	/* no range check is done, why bother? */
187 	posn -= offset;
188 	entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn);
189 	if (entryPtr != NULL) {
190 	    Tcl_SetIntObj(Tcl_GetObjResult(interp),
191 			  (int) Tcl_GetHashValue(entryPtr));
192 	} else {
193 	    Tcl_SetIntObj(Tcl_GetObjResult(interp), widthType ?
194 			  tablePtr->defColWidth : tablePtr->defRowHeight);
195 	}
196     } else {
197 	for (i=2; i<objc; i++) {
198 	    /* set new width|height here */
199 	    value = -999999;
200 	    if (Tcl_GetIntFromObj(interp, objv[i++], &posn) != TCL_OK ||
201 		(strcmp(Tcl_GetString(objv[i]), "default") &&
202 		 Tcl_GetIntFromObj(interp, objv[i], &value) != TCL_OK)) {
203 		return TCL_ERROR;
204 	    }
205 	    posn -= offset;
206 	    if (value == -999999) {
207 		/* reset that field */
208 		entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn);
209 		if (entryPtr != NULL) {
210 		    Tcl_DeleteHashEntry(entryPtr);
211 		}
212 	    } else {
213 		entryPtr = Tcl_CreateHashEntry(hashTablePtr,
214 					       (char *) posn, &dummy);
215 		Tcl_SetHashValue(entryPtr, (ClientData) value);
216 	    }
217 	}
218 	TableAdjustParams(tablePtr);
219 	/* rerequest geometry */
220 	TableGeometryRequest(tablePtr);
221 	/*
222 	 * Invalidate the whole window as TableAdjustParams
223 	 * will only check to see if the top left cell has moved
224 	 * FIX: should just move from lowest order visible cell
225 	 * to edge of window
226 	 */
227 	TableInvalidateAll(tablePtr, 0);
228     }
229     return TCL_OK;
230 }
231 
232 /*
233  *--------------------------------------------------------------
234  *
235  * Table_BboxCmd --
236  *	This procedure is invoked to process the bbox method
237  *	that corresponds to a table widget managed by this module.
238  *	See the user documentation for details on what it does.
239  *
240  * Results:
241  *	A standard Tcl result.
242  *
243  * Side effects:
244  *	See the user documentation.
245  *
246  *--------------------------------------------------------------
247  */
248 int
Table_BboxCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])249 Table_BboxCmd(ClientData clientData, register Tcl_Interp *interp,
250 	      int objc, Tcl_Obj *CONST objv[])
251 {
252     register Table *tablePtr = (Table *) clientData;
253     int x, y, w, h, row, col, key;
254     Tcl_Obj *resultPtr;
255 
256     /* Returns bounding box of cell(s) */
257     if (objc < 3 || objc > 4) {
258 	Tcl_WrongNumArgs(interp, 2, objv, "first ?last?");
259 	return TCL_ERROR;
260     } else if (TableGetIndexObj(tablePtr, objv[2], &row, &col) == TCL_ERROR ||
261 	       (objc == 4 &&
262 		TableGetIndexObj(tablePtr, objv[3], &x, &y) == TCL_ERROR)) {
263 	return TCL_ERROR;
264     }
265 
266     resultPtr = Tcl_GetObjResult(interp);
267     if (objc == 3) {
268 	row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
269 	if (TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0)) {
270 	    Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(x));
271 	    Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(y));
272 	    Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(w));
273 	    Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(h));
274 	}
275 	return TCL_OK;
276     } else {
277 	int r1, c1, r2, c2, minX = 99999, minY = 99999, maxX = 0, maxY = 0;
278 
279 	row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
280 	x -= tablePtr->rowOffset; y -= tablePtr->colOffset;
281 	r1 = MIN(row,x); r2 = MAX(row,x);
282 	c1 = MIN(col,y); c2 = MAX(col,y);
283 	key = 0;
284 	for (row = r1; row <= r2; row++) {
285 	    for (col = c1; col <= c2; col++) {
286 		if (TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0)) {
287 		    /* Get max bounding box */
288 		    if (x < minX) minX = x;
289 		    if (y < minY) minY = y;
290 		    if (x+w > maxX) maxX = x+w;
291 		    if (y+h > maxY) maxY = y+h;
292 		    key++;
293 		}
294 	    }
295 	}
296 	if (key) {
297 	    Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(minX));
298 	    Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(minY));
299 	    Tcl_ListObjAppendElement(NULL, resultPtr,
300 				     Tcl_NewIntObj(maxX-minX));
301 	    Tcl_ListObjAppendElement(NULL, resultPtr,
302 				     Tcl_NewIntObj(maxY-minY));
303 	}
304     }
305     return TCL_OK;
306 }
307 
308 static CONST84 char *bdCmdNames[] = {
309     "mark", "dragto", (char *)NULL
310 };
311 enum bdCmd {
312     BD_MARK, BD_DRAGTO
313 };
314 
315 /*
316  *--------------------------------------------------------------
317  *
318  * Table_BorderCmd --
319  *	This procedure is invoked to process the bbox method
320  *	that corresponds to a table widget managed by this module.
321  *	See the user documentation for details on what it does.
322  *
323  * Results:
324  *	A standard Tcl result.
325  *
326  * Side effects:
327  *	See the user documentation.
328  *
329  *--------------------------------------------------------------
330  */
331 int
Table_BorderCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])332 Table_BorderCmd(ClientData clientData, register Tcl_Interp *interp,
333 		int objc, Tcl_Obj *CONST objv[])
334 {
335     register Table *tablePtr = (Table *) clientData;
336     Tcl_HashEntry *entryPtr;
337     int x, y, w, h, row, col, key, dummy, value, cmdIndex;
338     char *rc = NULL;
339     Tcl_Obj *objPtr, *resultPtr;
340 
341     if (objc < 5 || objc > 6) {
342 	Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y ?row|col?");
343 	return TCL_ERROR;
344     }
345     if (Tcl_GetIndexFromObj(interp, objv[2], bdCmdNames,
346 			    "option", 0, &cmdIndex) != TCL_OK ||
347 	Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
348 	Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
349 	return TCL_ERROR;
350     }
351     if (objc == 6) {
352 	rc = Tcl_GetStringFromObj(objv[5], &w);
353 	if ((w < 1) || (strncmp(rc, "row", w) && strncmp(rc, "col", w))) {
354 	    Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y ?row|col?");
355 	    return TCL_ERROR;
356 	}
357     }
358 
359     resultPtr = Tcl_GetObjResult(interp);
360     switch ((enum bdCmd) cmdIndex) {
361     case BD_MARK:
362 	/* Use x && y to determine if we are over a border */
363 	value = TableAtBorder(tablePtr, x, y, &row, &col);
364 	/* Cache the row && col for use in DRAGTO */
365 	tablePtr->scanMarkRow = row;
366 	tablePtr->scanMarkCol = col;
367 	if (!value) {
368 	    return TCL_OK;
369 	}
370 	TableCellCoords(tablePtr, row, col, &x, &y, &dummy, &dummy);
371 	tablePtr->scanMarkX = x;
372 	tablePtr->scanMarkY = y;
373 	if (objc == 5 || *rc == 'r') {
374 	    if (row < 0) {
375 		objPtr = Tcl_NewStringObj("", 0);
376 	    } else {
377 		objPtr = Tcl_NewIntObj(row+tablePtr->rowOffset);
378 	    }
379 	    Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
380 	}
381 	if (objc == 5 || *rc == 'c') {
382 	    if (col < 0) {
383 		objPtr = Tcl_NewStringObj("", 0);
384 	    } else {
385 		objPtr = Tcl_NewIntObj(col+tablePtr->colOffset);
386 	    }
387 	    Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
388 	}
389 	return TCL_OK;	/* BORDER MARK */
390 
391     case BD_DRAGTO:
392 	/* check to see if we want to resize any borders */
393 	if (tablePtr->resize == SEL_NONE) { return TCL_OK; }
394 	row = tablePtr->scanMarkRow;
395 	col = tablePtr->scanMarkCol;
396 	TableCellCoords(tablePtr, row, col, &w, &h, &dummy, &dummy);
397 	key = 0;
398 	if (row >= 0 && (tablePtr->resize & SEL_ROW)) {
399 	    /* row border was active, move it */
400 	    value = y-h;
401 	    if (value < -1) value = -1;
402 	    if (value != tablePtr->scanMarkY) {
403 		entryPtr = Tcl_CreateHashEntry(tablePtr->rowHeights,
404 					       (char *) row, &dummy);
405 		/* -value means rowHeight will be interp'd as pixels, not
406                    lines */
407 		Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
408 		tablePtr->scanMarkY = value;
409 		key++;
410 	    }
411 	}
412 	if (col >= 0 && (tablePtr->resize & SEL_COL)) {
413 	    /* col border was active, move it */
414 	    value = x-w;
415 	    if (value < -1) value = -1;
416 	    if (value != tablePtr->scanMarkX) {
417 		entryPtr = Tcl_CreateHashEntry(tablePtr->colWidths,
418 					       (char *) col, &dummy);
419 		/* -value means colWidth will be interp'd as pixels, not
420                    chars */
421 		Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
422 		tablePtr->scanMarkX = value;
423 		key++;
424 	    }
425 	}
426 	/* Only if something changed do we want to update */
427 	if (key) {
428 	    TableAdjustParams(tablePtr);
429 	    /* Only rerequest geometry if the basis is the #rows &| #cols */
430 	    if (tablePtr->maxReqCols || tablePtr->maxReqRows)
431 		TableGeometryRequest(tablePtr);
432 	    TableInvalidateAll(tablePtr, 0);
433 	}
434 	return TCL_OK;	/* BORDER DRAGTO */
435     }
436     return TCL_OK;
437 }
438 
439 /* clear subcommands */
440 static CONST84 char *clearNames[] = {
441     "all", "cache", "sizes", "tags", (char *)NULL
442 };
443 enum clearCommand {
444     CLEAR_ALL, CLEAR_CACHE, CLEAR_SIZES, CLEAR_TAGS
445 };
446 
447 /*
448  *--------------------------------------------------------------
449  *
450  * Table_ClearCmd --
451  *	This procedure is invoked to process the clear method
452  *	that corresponds to a table widget managed by this module.
453  *	See the user documentation for details on what it does.
454  *
455  * Results:
456  *	Cached info can be lost.  Returns valid Tcl result.
457  *
458  * Side effects:
459  *	Can cause redraw.
460  *	See the user documentation.
461  *
462  *--------------------------------------------------------------
463  */
464 int
Table_ClearCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])465 Table_ClearCmd(ClientData clientData, register Tcl_Interp *interp,
466 		int objc, Tcl_Obj *CONST objv[])
467 {
468     register Table *tablePtr = (Table *) clientData;
469     int cmdIndex, redraw = 0;
470 
471     if (objc < 3 || objc > 5) {
472 	Tcl_WrongNumArgs(interp, 2, objv, "option ?first? ?last?");
473 	return TCL_ERROR;
474     }
475 
476     if (Tcl_GetIndexFromObj(interp, objv[2], clearNames,
477 			    "clear option", 0, &cmdIndex) != TCL_OK) {
478 	return TCL_ERROR;
479     }
480 
481     if (objc == 3) {
482 	if (cmdIndex == CLEAR_TAGS || cmdIndex == CLEAR_ALL) {
483 	    Tcl_DeleteHashTable(tablePtr->rowStyles);
484 	    Tcl_DeleteHashTable(tablePtr->colStyles);
485 	    Tcl_DeleteHashTable(tablePtr->cellStyles);
486 	    Tcl_DeleteHashTable(tablePtr->flashCells);
487 	    Tcl_DeleteHashTable(tablePtr->selCells);
488 
489 	    /* style hash tables */
490 	    Tcl_InitHashTable(tablePtr->rowStyles, TCL_ONE_WORD_KEYS);
491 	    Tcl_InitHashTable(tablePtr->colStyles, TCL_ONE_WORD_KEYS);
492 	    Tcl_InitHashTable(tablePtr->cellStyles, TCL_STRING_KEYS);
493 
494 	    /* special style hash tables */
495 	    Tcl_InitHashTable(tablePtr->flashCells, TCL_STRING_KEYS);
496 	    Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
497 	}
498 
499 	if (cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) {
500 	    Tcl_DeleteHashTable(tablePtr->colWidths);
501 	    Tcl_DeleteHashTable(tablePtr->rowHeights);
502 
503 	    /* style hash tables */
504 	    Tcl_InitHashTable(tablePtr->colWidths, TCL_ONE_WORD_KEYS);
505 	    Tcl_InitHashTable(tablePtr->rowHeights, TCL_ONE_WORD_KEYS);
506 	}
507 
508 	if (cmdIndex == CLEAR_CACHE || cmdIndex == CLEAR_ALL) {
509 	    Table_ClearHashTable(tablePtr->cache);
510 	    Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
511 	    /* If we were caching and we have no other data source,
512 	     * invalidate all the cells */
513 	    if (tablePtr->dataSource == DATA_CACHE) {
514 		TableGetActiveBuf(tablePtr);
515 	    }
516 	}
517 	redraw = 1;
518     } else {
519 	int row, col, r1, r2, c1, c2;
520 	Tcl_HashEntry *entryPtr;
521 	char buf[INDEX_BUFSIZE], *value;
522 
523 	if (TableGetIndexObj(tablePtr, objv[3], &row, &col) != TCL_OK ||
524 	    ((objc == 5) &&
525 	     TableGetIndexObj(tablePtr, objv[4], &r2, &c2) != TCL_OK)) {
526 	    return TCL_ERROR;
527 	}
528 	if (objc == 4) {
529 	    r1 = r2 = row;
530 	    c1 = c2 = col;
531 	} else {
532 	    r1 = MIN(row,r2); r2 = MAX(row,r2);
533 	    c1 = MIN(col,c2); c2 = MAX(col,c2);
534 	}
535 	for (row = r1; row <= r2; row++) {
536 	    /* Note that *Styles entries are user based (no offset)
537 	     * while size entries are 0-based (real) */
538 	    if ((cmdIndex == CLEAR_TAGS || cmdIndex == CLEAR_ALL) &&
539 		(entryPtr = Tcl_FindHashEntry(tablePtr->rowStyles,
540 					      (char *) row))) {
541 		Tcl_DeleteHashEntry(entryPtr);
542 		redraw = 1;
543 	    }
544 
545 	    if ((cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) &&
546 		(entryPtr = Tcl_FindHashEntry(tablePtr->rowHeights,
547 					      (char *) row-tablePtr->rowOffset))) {
548 		Tcl_DeleteHashEntry(entryPtr);
549 		redraw = 1;
550 	    }
551 
552 	    for (col = c1; col <= c2; col++) {
553 		TableMakeArrayIndex(row, col, buf);
554 
555 		if (cmdIndex == CLEAR_TAGS || cmdIndex == CLEAR_ALL) {
556 		    if ((row == r1) &&
557 			(entryPtr = Tcl_FindHashEntry(tablePtr->colStyles,
558 						      (char *) col))) {
559 			Tcl_DeleteHashEntry(entryPtr);
560 			redraw = 1;
561 		    }
562 		    if ((entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles,
563 						      buf))) {
564 			Tcl_DeleteHashEntry(entryPtr);
565 			redraw = 1;
566 		    }
567 		    if ((entryPtr = Tcl_FindHashEntry(tablePtr->flashCells,
568 						      buf))) {
569 			Tcl_DeleteHashEntry(entryPtr);
570 			redraw = 1;
571 		    }
572 		    if ((entryPtr = Tcl_FindHashEntry(tablePtr->selCells,
573 						      buf))) {
574 			Tcl_DeleteHashEntry(entryPtr);
575 			redraw = 1;
576 		    }
577 		}
578 
579 		if ((cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) &&
580 		    row == r1 &&
581 		    (entryPtr = Tcl_FindHashEntry(tablePtr->colWidths, (char *)
582 						  col-tablePtr->colOffset))) {
583 		    Tcl_DeleteHashEntry(entryPtr);
584 		    redraw = 1;
585 		}
586 
587 		if ((cmdIndex == CLEAR_CACHE || cmdIndex == CLEAR_ALL) &&
588 		    (entryPtr = Tcl_FindHashEntry(tablePtr->cache, buf))) {
589 		    value = (char *) Tcl_GetHashValue(entryPtr);
590 		    if (value) { ckfree(value); }
591 		    Tcl_DeleteHashEntry(entryPtr);
592 		    /* if the cache is our data source,
593 		     * we need to invalidate the cells changed */
594 		    if ((tablePtr->dataSource == DATA_CACHE) &&
595 			(row-tablePtr->rowOffset == tablePtr->activeRow &&
596 			 col-tablePtr->colOffset == tablePtr->activeCol))
597 			TableGetActiveBuf(tablePtr);
598 		    redraw = 1;
599 		}
600 	    }
601 	}
602     }
603     /* This could be more sensitive about what it updates,
604      * but that can actually be a lot more costly in some cases */
605     if (redraw) {
606 	if (cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) {
607 	    TableAdjustParams(tablePtr);
608 	    /* rerequest geometry */
609 	    TableGeometryRequest(tablePtr);
610 	}
611 	TableInvalidateAll(tablePtr, 0);
612     }
613     return TCL_OK;
614 }
615 
616 /*
617  *--------------------------------------------------------------
618  *
619  * Table_CurselectionCmd --
620  *	This procedure is invoked to process the bbox method
621  *	that corresponds to a table widget managed by this module.
622  *	See the user documentation for details on what it does.
623  *
624  * Results:
625  *	A standard Tcl result.
626  *
627  * Side effects:
628  *	See the user documentation.
629  *
630  *--------------------------------------------------------------
631  */
632 int
Table_CurselectionCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])633 Table_CurselectionCmd(ClientData clientData, register Tcl_Interp *interp,
634 		      int objc, Tcl_Obj *CONST objv[])
635 {
636     register Table *tablePtr = (Table *) clientData;
637     Tcl_HashEntry *entryPtr;
638     Tcl_HashSearch search;
639     char *value = NULL;
640     int row, col;
641 
642     if (objc > 3) {
643 	Tcl_WrongNumArgs(interp, 2, objv, "?value?");
644 	return TCL_ERROR;
645     }
646     if (objc == 3) {
647 	/* make sure there is a data source to accept a set value */
648 	if ((tablePtr->state == STATE_DISABLED) ||
649 	    (tablePtr->dataSource == DATA_NONE)) {
650 	    return TCL_OK;
651 	}
652 	value = Tcl_GetString(objv[2]);
653 	for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
654 	     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
655 	    TableParseArrayIndex(&row, &col,
656 				 Tcl_GetHashKey(tablePtr->selCells, entryPtr));
657 	    TableSetCellValue(tablePtr, row, col, value);
658 	    row -= tablePtr->rowOffset;
659 	    col -= tablePtr->colOffset;
660 	    if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
661 		TableGetActiveBuf(tablePtr);
662 	    }
663 	    TableRefresh(tablePtr, row, col, CELL);
664 	}
665     } else {
666 	Tcl_Obj *objPtr = Tcl_NewObj();
667 
668 	for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
669 	     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
670 	    value = Tcl_GetHashKey(tablePtr->selCells, entryPtr);
671 	    Tcl_ListObjAppendElement(NULL, objPtr,
672 				     Tcl_NewStringObj(value, -1));
673 	}
674 	Tcl_SetObjResult(interp, TableCellSortObj(interp, objPtr));
675     }
676     return TCL_OK;
677 }
678 
679 /*
680  *--------------------------------------------------------------
681  *
682  * Table_CurvalueCmd --
683  *	This procedure is invoked to process the curvalue method
684  *	that corresponds to a table widget managed by this module.
685  *	See the user documentation for details on what it does.
686  *
687  * Results:
688  *	A standard Tcl result.
689  *
690  * Side effects:
691  *	See the user documentation.
692  *
693  *--------------------------------------------------------------
694  */
695 int
Table_CurvalueCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])696 Table_CurvalueCmd(ClientData clientData, register Tcl_Interp *interp,
697 		  int objc, Tcl_Obj *CONST objv[])
698 {
699     register Table *tablePtr = (Table *) clientData;
700 
701     if (objc > 3) {
702 	Tcl_WrongNumArgs(interp, 2, objv, "?<value>?");
703 	return TCL_ERROR;
704     } else if (!(tablePtr->flags & HAS_ACTIVE)) {
705 	return TCL_OK;
706     }
707 
708     if (objc == 3) {
709 	char *value;
710 	int len;
711 
712 	value = Tcl_GetStringFromObj(objv[2], &len);
713 	if (STREQ(value, tablePtr->activeBuf)) {
714 	    Tcl_SetObjResult(interp, objv[2]);
715 	    return TCL_OK;
716 	}
717 	/* validate potential new active buffer contents
718 	 * only accept if validation returns acceptance. */
719 	if (tablePtr->validate &&
720 	    TableValidateChange(tablePtr,
721 				tablePtr->activeRow+tablePtr->rowOffset,
722 				tablePtr->activeCol+tablePtr->colOffset,
723 				tablePtr->activeBuf,
724 				value, tablePtr->icursor) != TCL_OK) {
725 	    return TCL_OK;
726 	}
727 	tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, len+1);
728 	strcpy(tablePtr->activeBuf, value);
729 	/* mark the text as changed */
730 	tablePtr->flags |= TEXT_CHANGED;
731 	TableSetActiveIndex(tablePtr);
732 	/* check for possible adjustment of icursor */
733 	TableGetIcursor(tablePtr, "insert", (int *)0);
734 	TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol, CELL);
735     }
736 
737     Tcl_SetStringObj(Tcl_GetObjResult(interp), tablePtr->activeBuf, -1);
738     return TCL_OK;
739 }
740 
741 /*
742  *--------------------------------------------------------------
743  *
744  * Table_GetCmd --
745  *	This procedure is invoked to process the bbox method
746  *	that corresponds to a table widget managed by this module.
747  *	See the user documentation for details on what it does.
748  *
749  * Results:
750  *	A standard Tcl result.
751  *
752  * Side effects:
753  *	See the user documentation.
754  *
755  *--------------------------------------------------------------
756  */
757 int
Table_GetCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])758 Table_GetCmd(ClientData clientData, register Tcl_Interp *interp,
759 	     int objc, Tcl_Obj *CONST objv[])
760 {
761     register Table *tablePtr = (Table *) clientData;
762     int result = TCL_OK;
763     int r1, c1, r2, c2, row, col;
764     if (objc < 3 || objc > 4) {
765 	Tcl_WrongNumArgs(interp, 2, objv, "first ?last?");
766 	result = TCL_ERROR;
767     } else if (TableGetIndexObj(tablePtr, objv[2], &row, &col) == TCL_ERROR) {
768 	result = TCL_ERROR;
769     } else if (objc == 3) {
770 	Tcl_SetObjResult(interp,
771 		Tcl_NewStringObj(TableGetCellValue(tablePtr, row, col), -1));
772     } else if (TableGetIndexObj(tablePtr, objv[3], &r2, &c2) == TCL_ERROR) {
773 	result = TCL_ERROR;
774     } else {
775 	Tcl_Obj *objPtr = Tcl_NewObj();
776 
777 	r1 = MIN(row,r2); r2 = MAX(row,r2);
778 	c1 = MIN(col,c2); c2 = MAX(col,c2);
779 	for ( row = r1; row <= r2; row++ ) {
780 	    for ( col = c1; col <= c2; col++ ) {
781 		Tcl_ListObjAppendElement(NULL, objPtr,
782 			Tcl_NewStringObj(TableGetCellValue(tablePtr,
783 				row, col), -1));
784 	    }
785 	}
786 	Tcl_SetObjResult(interp, objPtr);
787     }
788     return result;
789 }
790 
791 /*
792  *--------------------------------------------------------------
793  *
794  * Table_ScanCmd --
795  *	This procedure is invoked to process the scan method
796  *	that corresponds to a table widget managed by this module.
797  *	See the user documentation for details on what it does.
798  *
799  * Results:
800  *	A standard Tcl result.
801  *
802  * Side effects:
803  *	See the user documentation.
804  *
805  *--------------------------------------------------------------
806  */
807 int
Table_ScanCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])808 Table_ScanCmd(ClientData clientData, register Tcl_Interp *interp,
809 	      int objc, Tcl_Obj *CONST objv[])
810 {
811     register Table *tablePtr = (Table *) clientData;
812     int x, y, row, col, cmdIndex;
813 
814     if (objc != 5) {
815 	Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y");
816 	return TCL_ERROR;
817     } else if (Tcl_GetIndexFromObj(interp, objv[2], bdCmdNames,
818 	    "option", 0, &cmdIndex) != TCL_OK ||
819 	    Tcl_GetIntFromObj(interp, objv[3], &x) == TCL_ERROR ||
820 	    Tcl_GetIntFromObj(interp, objv[4], &y) == TCL_ERROR) {
821 	return TCL_ERROR;
822     }
823     switch ((enum bdCmd) cmdIndex) {
824 	case BD_MARK:
825 	    TableWhatCell(tablePtr, x, y, &row, &col);
826 	    tablePtr->scanMarkRow = row-tablePtr->topRow;
827 	    tablePtr->scanMarkCol = col-tablePtr->leftCol;
828 	    tablePtr->scanMarkX = x;
829 	    tablePtr->scanMarkY = y;
830 	    break;
831 
832 	case BD_DRAGTO: {
833 	    int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
834 	    y += (5*(y-tablePtr->scanMarkY));
835 	    x += (5*(x-tablePtr->scanMarkX));
836 
837 	    TableWhatCell(tablePtr, x, y, &row, &col);
838 
839 	    /* maintain appropriate real index */
840 	    tablePtr->topRow  = BETWEEN(row-tablePtr->scanMarkRow,
841 		    tablePtr->titleRows, tablePtr->rows-1);
842 	    tablePtr->leftCol = BETWEEN(col-tablePtr->scanMarkCol,
843 		    tablePtr->titleCols, tablePtr->cols-1);
844 
845 	    /* Adjust the table if new top left */
846 	    if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol) {
847 		TableAdjustParams(tablePtr);
848 	    }
849 	    break;
850 	}
851     }
852     return TCL_OK;
853 }
854 
855 /*
856  *--------------------------------------------------------------
857  *
858  * Table_SelAnchorCmd --
859  *	This procedure is invoked to process the selection anchor method
860  *	that corresponds to a table widget managed by this module.
861  *	See the user documentation for details on what it does.
862  *
863  * Results:
864  *	A standard Tcl result.
865  *
866  * Side effects:
867  *	See the user documentation.
868  *
869  *--------------------------------------------------------------
870  */
871 int
Table_SelAnchorCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])872 Table_SelAnchorCmd(ClientData clientData, register Tcl_Interp *interp,
873 		   int objc, Tcl_Obj *CONST objv[])
874 {
875     register Table *tablePtr = (Table *) clientData;
876     int row, col;
877 
878     if (objc != 4) {
879 	Tcl_WrongNumArgs(interp, 3, objv, "index");
880 	return TCL_ERROR;
881     } else if (TableGetIndexObj(tablePtr, objv[3], &row, &col) != TCL_OK) {
882 	return TCL_ERROR;
883     }
884     tablePtr->flags |= HAS_ANCHOR;
885     /* maintain appropriate real index */
886     if (tablePtr->selectTitles) {
887 	tablePtr->anchorRow = BETWEEN(row-tablePtr->rowOffset,
888 		0, tablePtr->rows-1);
889 	tablePtr->anchorCol = BETWEEN(col-tablePtr->colOffset,
890 		0, tablePtr->cols-1);
891     } else {
892 	tablePtr->anchorRow = BETWEEN(row-tablePtr->rowOffset,
893 		tablePtr->titleRows, tablePtr->rows-1);
894 	tablePtr->anchorCol = BETWEEN(col-tablePtr->colOffset,
895 		tablePtr->titleCols, tablePtr->cols-1);
896     }
897     return TCL_OK;
898 }
899 
900 /*
901  *--------------------------------------------------------------
902  *
903  * Table_SelClearCmd --
904  *	This procedure is invoked to process the selection clear method
905  *	that corresponds to a table widget managed by this module.
906  *	See the user documentation for details on what it does.
907  *
908  * Results:
909  *	A standard Tcl result.
910  *
911  * Side effects:
912  *	See the user documentation.
913  *
914  *--------------------------------------------------------------
915  */
916 int
Table_SelClearCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])917 Table_SelClearCmd(ClientData clientData, register Tcl_Interp *interp,
918 		  int objc, Tcl_Obj *CONST objv[])
919 {
920     register Table *tablePtr = (Table *) clientData;
921     int result = TCL_OK;
922     char buf1[INDEX_BUFSIZE];
923     int row, col, key, clo=0,chi=0,r1,c1,r2,c2;
924     Tcl_HashEntry *entryPtr;
925 
926     if (objc < 4 || objc > 5) {
927 	Tcl_WrongNumArgs(interp, 3, objv, "all|<first> ?<last>?");
928 	return TCL_ERROR;
929     }
930     if (STREQ(Tcl_GetString(objv[3]), "all")) {
931 	Tcl_HashSearch search;
932 	for(entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
933 	    entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
934 	    TableParseArrayIndex(&row, &col,
935 				 Tcl_GetHashKey(tablePtr->selCells,entryPtr));
936 	    Tcl_DeleteHashEntry(entryPtr);
937 	    TableRefresh(tablePtr, row-tablePtr->rowOffset,
938 			 col-tablePtr->colOffset, CELL);
939 	}
940 	return TCL_OK;
941     }
942     if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR ||
943 	(objc==5 &&
944 	 TableGetIndexObj(tablePtr, objv[4], &r2, &c2) == TCL_ERROR)) {
945 	return TCL_ERROR;
946     }
947     key = 0;
948     if (objc == 4) {
949 	r1 = r2 = row;
950 	c1 = c2 = col;
951     } else {
952 	r1 = MIN(row,r2); r2 = MAX(row,r2);
953 	c1 = MIN(col,c2); c2 = MAX(col,c2);
954     }
955     switch (tablePtr->selectType) {
956     case SEL_BOTH:
957 	clo = c1; chi = c2;
958 	c1 = tablePtr->colOffset;
959 	c2 = tablePtr->cols-1+c1;
960 	key = 1;
961 	goto CLEAR_CELLS;
962     CLEAR_BOTH:
963 	key = 0;
964 	c1 = clo; c2 = chi;
965     case SEL_COL:
966 	r1 = tablePtr->rowOffset;
967 	r2 = tablePtr->rows-1+r1;
968 	break;
969     case SEL_ROW:
970 	c1 = tablePtr->colOffset;
971 	c2 = tablePtr->cols-1+c1;
972 	break;
973     }
974     /* row/col are in user index coords */
975 CLEAR_CELLS:
976     for ( row = r1; row <= r2; row++ ) {
977 	for ( col = c1; col <= c2; col++ ) {
978 	    TableMakeArrayIndex(row, col, buf1);
979 	    entryPtr = Tcl_FindHashEntry(tablePtr->selCells, buf1);
980 	    if (entryPtr != NULL) {
981 		Tcl_DeleteHashEntry(entryPtr);
982 		TableRefresh(tablePtr, row-tablePtr->rowOffset,
983 			     col-tablePtr->colOffset, CELL);
984 	    }
985 	}
986     }
987     if (key) goto CLEAR_BOTH;
988     return result;
989 }
990 
991 /*
992  *--------------------------------------------------------------
993  *
994  * Table_SelIncludesCmd --
995  *	This procedure is invoked to process the selection includes method
996  *	that corresponds to a table widget managed by this module.
997  *	See the user documentation for details on what it does.
998  *
999  * Results:
1000  *	A standard Tcl result.
1001  *
1002  * Side effects:
1003  *	See the user documentation.
1004  *
1005  *--------------------------------------------------------------
1006  */
1007 int
Table_SelIncludesCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1008 Table_SelIncludesCmd(ClientData clientData, register Tcl_Interp *interp,
1009 		     int objc, Tcl_Obj *CONST objv[])
1010 {
1011     register Table *tablePtr = (Table *) clientData;
1012     int row, col;
1013 
1014     if (objc != 4) {
1015 	Tcl_WrongNumArgs(interp, 3, objv, "index");
1016 	return TCL_ERROR;
1017     } else if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR) {
1018 	return TCL_ERROR;
1019     } else {
1020 	char buf[INDEX_BUFSIZE];
1021 	TableMakeArrayIndex(row, col, buf);
1022 	Tcl_SetBooleanObj(Tcl_GetObjResult(interp),
1023 			  (Tcl_FindHashEntry(tablePtr->selCells, buf)!=NULL));
1024     }
1025     return TCL_OK;
1026 }
1027 
1028 /*
1029  *--------------------------------------------------------------
1030  *
1031  * Table_SelSetCmd --
1032  *	This procedure is invoked to process the selection set method
1033  *	that corresponds to a table widget managed by this module.
1034  *	See the user documentation for details on what it does.
1035  *
1036  * Results:
1037  *	A standard Tcl result.
1038  *
1039  * Side effects:
1040  *	See the user documentation.
1041  *
1042  *--------------------------------------------------------------
1043  */
1044 int
Table_SelSetCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1045 Table_SelSetCmd(ClientData clientData, register Tcl_Interp *interp,
1046 		int objc, Tcl_Obj *CONST objv[])
1047 {
1048     register Table *tablePtr = (Table *) clientData;
1049     int row, col, dummy, key;
1050     char buf1[INDEX_BUFSIZE];
1051     Tcl_HashSearch search;
1052     Tcl_HashEntry *entryPtr;
1053 
1054     int clo=0, chi=0, r1, c1, r2, c2, firstRow, firstCol, lastRow, lastCol;
1055     if (objc < 4 || objc > 5) {
1056 	Tcl_WrongNumArgs(interp, 3, objv, "first ?last?");
1057 	return TCL_ERROR;
1058     }
1059     if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR ||
1060 	(objc==5 &&
1061 	 TableGetIndexObj(tablePtr, objv[4], &r2, &c2) == TCL_ERROR)) {
1062 	return TCL_ERROR;
1063     }
1064     key = 0;
1065     lastRow = tablePtr->rows-1+tablePtr->rowOffset;
1066     lastCol = tablePtr->cols-1+tablePtr->colOffset;
1067     if (tablePtr->selectTitles) {
1068 	firstRow = tablePtr->rowOffset;
1069 	firstCol = tablePtr->colOffset;
1070     } else {
1071 	firstRow = tablePtr->titleRows+tablePtr->rowOffset;
1072 	firstCol = tablePtr->titleCols+tablePtr->colOffset;
1073     }
1074     /* maintain appropriate user index */
1075     CONSTRAIN(row, firstRow, lastRow);
1076     CONSTRAIN(col, firstCol, lastCol);
1077     if (objc == 4) {
1078 	r1 = r2 = row;
1079 	c1 = c2 = col;
1080     } else {
1081 	CONSTRAIN(r2, firstRow, lastRow);
1082 	CONSTRAIN(c2, firstCol, lastCol);
1083 	r1 = MIN(row,r2); r2 = MAX(row,r2);
1084 	c1 = MIN(col,c2); c2 = MAX(col,c2);
1085     }
1086     switch (tablePtr->selectType) {
1087     case SEL_BOTH:
1088 	if (firstCol > lastCol) c2--; /* No selectable columns in table */
1089 	if (firstRow > lastRow) r2--; /* No selectable rows in table */
1090 	clo = c1; chi = c2;
1091 	c1 = firstCol;
1092 	c2 = lastCol;
1093 	key = 1;
1094 	goto SET_CELLS;
1095     SET_BOTH:
1096 	key = 0;
1097 	c1 = clo; c2 = chi;
1098     case SEL_COL:
1099 	r1 = firstRow;
1100 	r2 = lastRow;
1101 	if (firstCol > lastCol) c2--; /* No selectable columns in table */
1102 	break;
1103     case SEL_ROW:
1104 	c1 = firstCol;
1105 	c2 = lastCol;
1106 	if (firstRow>lastRow) r2--; /* No selectable rows in table */
1107 	break;
1108     }
1109 SET_CELLS:
1110     entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
1111     for ( row = r1; row <= r2; row++ ) {
1112 	for ( col = c1; col <= c2; col++ ) {
1113 	    TableMakeArrayIndex(row, col, buf1);
1114 	    if (Tcl_FindHashEntry(tablePtr->selCells, buf1) == NULL) {
1115 		Tcl_CreateHashEntry(tablePtr->selCells, buf1, &dummy);
1116 		TableRefresh(tablePtr, row-tablePtr->rowOffset,
1117 			     col-tablePtr->colOffset, CELL);
1118 	    }
1119 	}
1120     }
1121     if (key) goto SET_BOTH;
1122 
1123     /* Adjust the table for top left, selection on screen etc */
1124     TableAdjustParams(tablePtr);
1125 
1126     /* If the table was previously empty and we want to export the
1127      * selection, we should grab it now */
1128     if (entryPtr == NULL && tablePtr->exportSelection) {
1129 	Tk_OwnSelection(tablePtr->tkwin, XA_PRIMARY, TableLostSelection,
1130 			(ClientData) tablePtr);
1131     }
1132     return TCL_OK;
1133 }
1134 
1135 /*
1136  *--------------------------------------------------------------
1137  *
1138  * Table_ViewCmd --
1139  *	This procedure is invoked to process the x|yview method
1140  *	that corresponds to a table widget managed by this module.
1141  *	See the user documentation for details on what it does.
1142  *
1143  * Results:
1144  *	A standard Tcl result.
1145  *
1146  * Side effects:
1147  *	See the user documentation.
1148  *
1149  *--------------------------------------------------------------
1150  */
1151 int
Table_ViewCmd(ClientData clientData,register Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1152 Table_ViewCmd(ClientData clientData, register Tcl_Interp *interp,
1153 	      int objc, Tcl_Obj *CONST objv[])
1154 {
1155     register Table *tablePtr = (Table *) clientData;
1156     int row, col, value;
1157     char *xy;
1158 
1159     /* Check xview or yview */
1160     if (objc > 5) {
1161 	Tcl_WrongNumArgs(interp, 2, objv, "?args?");
1162 	return TCL_ERROR;
1163     }
1164     xy = Tcl_GetString(objv[1]);
1165 
1166     if (objc == 2) {
1167 	Tcl_Obj *resultPtr;
1168 	int diff, x, y, w, h;
1169 	double first, last;
1170 
1171 	resultPtr = Tcl_GetObjResult(interp);
1172 	TableGetLastCell(tablePtr, &row, &col);
1173 	TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0);
1174 	if (*xy == 'y') {
1175 	    if (row < tablePtr->titleRows) {
1176 		first = 0;
1177 		last  = 1;
1178 	    } else {
1179 		diff = tablePtr->rowStarts[tablePtr->titleRows];
1180 		last = (double) (tablePtr->rowStarts[tablePtr->rows]-diff);
1181 		first = (tablePtr->rowStarts[tablePtr->topRow]-diff) / last;
1182 		last  = (h+tablePtr->rowStarts[row]-diff) / last;
1183 	    }
1184 	} else {
1185 	    if (col < tablePtr->titleCols) {
1186 		first = 0;
1187 		last  = 1;
1188 	    } else {
1189 		diff = tablePtr->colStarts[tablePtr->titleCols];
1190 		last = (double) (tablePtr->colStarts[tablePtr->cols]-diff);
1191 		first = (tablePtr->colStarts[tablePtr->leftCol]-diff) / last;
1192 		last  = (w+tablePtr->colStarts[col]-diff) / last;
1193 	    }
1194 	}
1195 	Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewDoubleObj(first));
1196 	Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewDoubleObj(last));
1197     } else {
1198 	/* cache old topleft to see if it changes */
1199 	int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
1200 
1201 	if (objc == 3) {
1202 	    if (Tcl_GetIntFromObj(interp, objv[2], &value) != TCL_OK) {
1203 		return TCL_ERROR;
1204 	    }
1205 	    if (*xy == 'y') {
1206 		tablePtr->topRow  = value + tablePtr->titleRows;
1207 	    } else {
1208 		tablePtr->leftCol = value + tablePtr->titleCols;
1209 	    }
1210 	} else {
1211 	    int result;
1212 	    double frac;
1213 #if (TK_MINOR_VERSION > 0) /* 8.1+ */
1214 	    result = Tk_GetScrollInfoObj(interp, objc, objv, &frac, &value);
1215 #else
1216 	    int i;
1217 	    Arg * args = (Arg *) ckalloc((objc + 1) * sizeof(Arg));
1218 	    for (i = 0; i < objc; i++) {
1219 		args[i] = LangStringArg(Tcl_GetString(objv[i]));
1220 	    }
1221 	    args[i] = NULL;
1222 	    result = Tk_GetScrollInfo(interp, objc, args, &frac, &value);
1223 	    ckfree ((char * ) args);
1224 #endif
1225 	    switch (result) {
1226 	    case TK_SCROLL_ERROR:
1227 		return TCL_ERROR;
1228 	    case TK_SCROLL_MOVETO:
1229 		if (frac < 0) frac = 0;
1230 		if (*xy == 'y') {
1231 		    tablePtr->topRow = (int)(frac*tablePtr->rows)
1232 			+tablePtr->titleRows;
1233 		} else {
1234 		    tablePtr->leftCol = (int)(frac*tablePtr->cols)
1235 			+tablePtr->titleCols;
1236 		}
1237 		break;
1238 	    case TK_SCROLL_PAGES:
1239 		TableGetLastCell(tablePtr, &row, &col);
1240 		if (*xy == 'y') {
1241 		    tablePtr->topRow  += value * (row-tablePtr->topRow+1);
1242 		} else {
1243 		    tablePtr->leftCol += value * (col-tablePtr->leftCol+1);
1244 		}
1245 		break;
1246 	    case TK_SCROLL_UNITS:
1247 		if (*xy == 'y') {
1248 		    tablePtr->topRow  += value;
1249 		} else {
1250 		    tablePtr->leftCol += value;
1251 		}
1252 		break;
1253 	    }
1254 	}
1255 	/* maintain appropriate real index */
1256 	CONSTRAIN(tablePtr->topRow, tablePtr->titleRows, tablePtr->rows-1);
1257 	CONSTRAIN(tablePtr->leftCol, tablePtr->titleCols, tablePtr->cols-1);
1258 	/* Do the table adjustment if topRow || leftCol changed */
1259 	if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol) {
1260 	    TableAdjustParams(tablePtr);
1261 	}
1262     }
1263 
1264     return TCL_OK;
1265 }
1266 
1267 #if 0
1268 /*
1269  *--------------------------------------------------------------
1270  *
1271  * Table_Cmd --
1272  *	This procedure is invoked to process the CMD method
1273  *	that corresponds to a table widget managed by this module.
1274  *	See the user documentation for details on what it does.
1275  *
1276  * Results:
1277  *	A standard Tcl result.
1278  *
1279  * Side effects:
1280  *	See the user documentation.
1281  *
1282  *--------------------------------------------------------------
1283  */
1284 int
1285 Table_Cmd(ClientData clientData, register Tcl_Interp *interp,
1286 	  int objc, Tcl_Obj *CONST objv[])
1287 {
1288     register Table *tablePtr = (Table *) clientData;
1289     int result = TCL_OK;
1290 
1291     return result;
1292 }
1293 #endif
1294