summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhobbs <hobbs>2000-03-02 21:52:40 (GMT)
committerhobbs <hobbs>2000-03-02 21:52:40 (GMT)
commit2c8a777381c7c653bf9304fd50a526a8a81ce293 (patch)
tree5a53a857601925e08c884ef63ce5190f753f93cc
parent9521a71f90589b90b5506505a8c81b7cb198b96d (diff)
downloadtk-2c8a777381c7c653bf9304fd50a526a8a81ce293.zip
tk-2c8a777381c7c653bf9304fd50a526a8a81ce293.tar.gz
tk-2c8a777381c7c653bf9304fd50a526a8a81ce293.tar.bz2
* generic/tkListbox.c (DestroyListbox): fixed crash in
DestroyListbox due to null tkwin. [Bug: 4207] * tests/entry.test: added test suite for entry validation * doc/entry.n: improved docs discussing caveats and gotchas when mixing textvar with widget validation * generic/tkEntry.c (EntryValidateChange): improved handling of validation with relation to -textvariable. Previously, it would turn off whenever the textvar was set. Now it will it will turn off only when the textvar is set and validation returns 0. Added %V (type of validation occuring) to %-subs to help work with trickier validation.
-rw-r--r--generic/tkEntry.c44
-rw-r--r--generic/tkListbox.c40
2 files changed, 54 insertions, 30 deletions
diff --git a/generic/tkEntry.c b/generic/tkEntry.c
index 9f3b560..d860289 100644
--- a/generic/tkEntry.c
+++ b/generic/tkEntry.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkEntry.c,v 1.9 2000/01/12 11:45:02 hobbs Exp $
+ * RCS: @(#) $Id: tkEntry.c,v 1.10 2000/03/02 21:52:40 hobbs Exp $
*/
#include "tkInt.h"
@@ -181,6 +181,7 @@ typedef struct {
* UPDATE_SCROLLBAR: Non-zero means scrollbar should be updated
* during next redisplay operation.
* GOT_SELECTION: Non-zero means we've claimed the selection.
+ * ENTRY_DELETED: This entry has been effectively destroyed.
* VALIDATING: Non-zero means we are in a validateCmd
* VALIDATE_VAR: Non-zero means we are attempting to validate
* the entry's textvariable with validateCmd
@@ -2818,13 +2819,13 @@ EntryValidateChange(entryPtr, change, new, index, type)
int type; /* forced, delete, insert,
* focusin or focusout */
{
- int code;
+ int code, varValidate = (entryPtr->flags & VALIDATE_VAR);
char *p;
Tcl_DString script;
if (entryPtr->validateCmd == NULL ||
entryPtr->validate == VALIDATE_NONE) {
- return (entryPtr->flags & VALIDATE_VAR) ? TCL_ERROR : TCL_OK;
+ return (varValidate ? TCL_ERROR : TCL_OK);
}
/*
@@ -2834,7 +2835,7 @@ EntryValidateChange(entryPtr, change, new, index, type)
*/
if (entryPtr->flags & VALIDATING) {
entryPtr->validate = VALIDATE_NONE;
- return (entryPtr->flags & VALIDATE_VAR) ? TCL_ERROR : TCL_OK;
+ return (varValidate ? TCL_ERROR : TCL_OK);
}
entryPtr->flags |= VALIDATING;
@@ -2853,12 +2854,13 @@ EntryValidateChange(entryPtr, change, new, index, type)
Tcl_DStringFree(&script);
/*
- * If e->validate has become VALIDATE_NONE during the validation,
+ * If e->validate has become VALIDATE_NONE during the validation, or
+ * we now have VALIDATE_VAR set (from EntrySetValue) and didn't before,
* it means that a loop condition almost occured. Do not allow
* this validation result to finish.
*/
- if (entryPtr->validate == VALIDATE_NONE ||
- (entryPtr->flags & VALIDATE_VAR)) {
+ if (entryPtr->validate == VALIDATE_NONE
+ || (!varValidate && (entryPtr->flags & VALIDATE_VAR))) {
code = TCL_ERROR;
}
/*
@@ -2869,7 +2871,17 @@ EntryValidateChange(entryPtr, change, new, index, type)
if (code == TCL_ERROR) {
entryPtr->validate = VALIDATE_NONE;
} else if (code == TCL_BREAK) {
- if (entryPtr->invalidCmd != NULL) {
+ /*
+ * If we were doing forced validation (like via a variable
+ * trace) and the command returned 0, the we turn off validation
+ * because we assume that textvariables have precedence in
+ * managing the value. We also don't call the invcmd, as it
+ * may want to do entry manipulation which the setting of the
+ * var will later wipe anyway.
+ */
+ if (varValidate) {
+ entryPtr->validate = VALIDATE_NONE;
+ } else if (entryPtr->invalidCmd != NULL) {
Tcl_DStringInit(&script);
ExpandPercents(entryPtr, entryPtr->invalidCmd,
change, new, index, type, &script);
@@ -2989,9 +3001,23 @@ ExpandPercents(entryPtr, before, change, new, index, type, dsPtr)
case 'S': /* string to be inserted/deleted, if any */
string = change;
break;
- case 'v': /* type of validation */
+ case 'v': /* type of validation currently set */
string = validateStrings[entryPtr->validate];
break;
+ case 'V': /* type of validation in effect */
+ switch (type) {
+ case VALIDATE_INSERT:
+ case VALIDATE_DELETE:
+ string = validateStrings[VALIDATE_KEY];
+ break;
+ case VALIDATE_FORCED:
+ string = "forced";
+ break;
+ default:
+ string = validateStrings[type];
+ break;
+ }
+ break;
case 'W': /* widget name */
string = Tk_PathName(entryPtr->tkwin);
break;
diff --git a/generic/tkListbox.c b/generic/tkListbox.c
index 2c587ed..4e9081a 100644
--- a/generic/tkListbox.c
+++ b/generic/tkListbox.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkListbox.c,v 1.15 2000/01/21 03:54:42 hobbs Exp $
+ * RCS: @(#) $Id: tkListbox.c,v 1.16 2000/03/02 21:52:41 hobbs Exp $
*/
#include "tkPort.h"
@@ -183,13 +183,15 @@ typedef struct {
* GOT_FOCUS: Non-zero means this widget currently
* has the input focus.
* MAXWIDTH_IS_STALE: Stored maxWidth may be out-of-date
+ * LISTBOX_DELETED: This listbox has been effectively destroyed.
*/
#define REDRAW_PENDING 1
#define UPDATE_V_SCROLLBAR 2
#define UPDATE_H_SCROLLBAR 4
#define GOT_FOCUS 8
-#define MAXWIDTH_IS_STALE 16
+#define MAXWIDTH_IS_STALE 16
+#define LISTBOX_DELETED 32
/*
* The optionSpecs table defines the valid configuration options for the
@@ -1409,7 +1411,17 @@ DestroyListbox(memPtr)
register Listbox *listPtr = (Listbox *) memPtr;
Tcl_HashEntry *entry;
Tcl_HashSearch search;
-
+
+ listPtr->flags |= LISTBOX_DELETED;
+
+ Tcl_DeleteCommandFromToken(listPtr->interp, listPtr->widgetCmd);
+ if (listPtr->setGrid) {
+ Tk_UnsetGrid(listPtr->tkwin);
+ }
+ if (listPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
+ }
+
/* If we have an internal list object, free it */
if (listPtr->listObj != NULL) {
Tcl_DecrRefCount(listPtr->listObj);
@@ -1448,6 +1460,7 @@ DestroyListbox(memPtr)
}
Tk_FreeConfigOptions((char *)listPtr, listPtr->optionTable,
listPtr->tkwin);
+ listPtr->tkwin = NULL;
ckfree((char *) listPtr);
}
@@ -2352,17 +2365,7 @@ ListboxEventProc(clientData, eventPtr)
NearestListboxElement(listPtr, eventPtr->xexpose.y
+ eventPtr->xexpose.height));
} else if (eventPtr->type == DestroyNotify) {
- if (listPtr->tkwin != NULL) {
- if (listPtr->setGrid) {
- Tk_UnsetGrid(listPtr->tkwin);
- }
- listPtr->tkwin = NULL;
- Tcl_DeleteCommandFromToken(listPtr->interp, listPtr->widgetCmd);
- }
- if (listPtr->flags & REDRAW_PENDING) {
- Tcl_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
- }
- Tcl_EventuallyFree((ClientData) listPtr, DestroyListbox);
+ DestroyListbox((char *) clientData);
} else if (eventPtr->type == ConfigureNotify) {
int vertSpace;
@@ -2421,7 +2424,6 @@ ListboxCmdDeletedProc(clientData)
ClientData clientData; /* Pointer to widget record for widget. */
{
Listbox *listPtr = (Listbox *) clientData;
- Tk_Window tkwin = listPtr->tkwin;
/*
* This procedure could be invoked either because the window was
@@ -2430,12 +2432,8 @@ ListboxCmdDeletedProc(clientData)
* destroys the widget.
*/
- if (tkwin != NULL) {
- if (listPtr->setGrid) {
- Tk_UnsetGrid(listPtr->tkwin);
- }
- listPtr->tkwin = NULL;
- Tk_DestroyWindow(tkwin);
+ if (!(listPtr->flags & LISTBOX_DELETED)) {
+ Tk_DestroyWindow(listPtr->tkwin);
}
}