From 2c8a777381c7c653bf9304fd50a526a8a81ce293 Mon Sep 17 00:00:00 2001 From: hobbs Date: Thu, 2 Mar 2000 21:52:40 +0000 Subject: * 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. --- generic/tkEntry.c | 44 +++++++++++++++++++++++++++++++++++--------- generic/tkListbox.c | 40 +++++++++++++++++++--------------------- 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); } } -- cgit v0.12