From 5c2ad0a34a4a78a7879cd6dc7e4523f789aa3d0e Mon Sep 17 00:00:00 2001 From: hobbs Date: Tue, 3 Apr 2001 04:40:50 +0000 Subject: * tests/entry.test: added tests entry-20.*, delete during widget activity * generic/tkEntry.c (DestroyEntry, EntryEventProc): fixed the entry widget to survive deletion while processing scrollbar updates and validation. --- generic/tkEntry.c | 64 +++++++++++++++++++++++++++++++++++++++++++++---------- tests/entry.test | 56 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 104 insertions(+), 16 deletions(-) diff --git a/generic/tkEntry.c b/generic/tkEntry.c index 4a01d12..5555b64 100644 --- a/generic/tkEntry.c +++ b/generic/tkEntry.c @@ -12,7 +12,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.18 2000/11/22 01:49:37 ericm Exp $ + * RCS: @(#) $Id: tkEntry.c,v 1.19 2001/04/03 04:40:50 hobbs Exp $ */ #include "tkInt.h" @@ -851,7 +851,7 @@ Tk_EntryObjCmd(clientData, interp, objc, objv) Tk_DestroyWindow(entryPtr->tkwin); return TCL_ERROR; } - + Tcl_SetResult(interp, Tk_PathName(entryPtr->tkwin), TCL_STATIC); return TCL_OK; } @@ -1323,12 +1323,6 @@ DestroyEntry(memPtr) char *memPtr; /* Info about entry widget. */ { Entry *entryPtr = (Entry *) memPtr; - entryPtr->flags |= ENTRY_DELETED; - - Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd); - if (entryPtr->flags & REDRAW_PENDING) { - Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr); - } /* * Free up all the stuff that requires special handling, then @@ -1367,6 +1361,14 @@ DestroyEntry(memPtr) Tk_FreeConfigOptions((char *) entryPtr, entryPtr->optionTable, entryPtr->tkwin); entryPtr->tkwin = NULL; + + /* + * Tcl_EventuallyFree should be used here or better yet in the + * DestroyNotify branch of EntryEventProc. However, that can lead + * complications in Tk_FreeConfigOptions where the display for the + * entry has been deleted by Tk_DestroyWindow, which is needed + * when freeing the cursor option. + */ ckfree((char *) entryPtr); } @@ -1794,7 +1796,7 @@ DisplayEntry(clientData) Tk_3DBorder border; entryPtr->flags &= ~REDRAW_PENDING; - if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { + if ((entryPtr->flags & ENTRY_DELETED) || !Tk_IsMapped(tkwin)) { return; } @@ -1810,6 +1812,15 @@ DisplayEntry(clientData) } /* + * We do this check twice because updating the scrollbar can have + * the side-effect of destroying or unmapping the entry widget. + */ + + if ((entryPtr->flags & ENTRY_DELETED) || !Tk_IsMapped(tkwin)) { + return; + } + + /* * In order to avoid screen flashes, this procedure redraws the * textual area of the entry into off-screen memory, then copies * it back on-screen in a single operation. This means there's @@ -2606,6 +2617,7 @@ EntryEventProc(clientData, eventPtr) Tk_UndefineCursor(entryPtr->tkwin); } } + return; } switch (eventPtr->type) { @@ -2614,7 +2626,15 @@ EntryEventProc(clientData, eventPtr) entryPtr->flags |= BORDER_NEEDED; break; case DestroyNotify: - DestroyEntry((char *) clientData); + if (!(entryPtr->flags & ENTRY_DELETED)) { + entryPtr->flags |= (ENTRY_DELETED | VALIDATE_ABORT); + Tcl_DeleteCommandFromToken(entryPtr->interp, + entryPtr->widgetCmd); + if (entryPtr->flags & REDRAW_PENDING) { + Tcl_CancelIdleCall(DisplayEntry, clientData); + } + DestroyEntry((char *) entryPtr); + } break; case ConfigureNotify: Tcl_Preserve((ClientData) entryPtr); @@ -3027,7 +3047,7 @@ static void EventuallyRedraw(entryPtr) Entry *entryPtr; /* Information about widget. */ { - if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) { + if ((entryPtr->flags & ENTRY_DELETED) || !Tk_IsMapped(entryPtr->tkwin)) { return; } @@ -3424,15 +3444,27 @@ EntryValidateChange(entryPtr, change, new, index, type) * it means that a loop condition almost occured. Do not allow * this validation result to finish. */ + if (entryPtr->validate == VALIDATE_NONE || (!varValidate && (entryPtr->flags & VALIDATE_VAR))) { code = TCL_ERROR; } + + /* + * It's possible that the user deleted the entry during validation. + * In that case, abort future validation and return an error. + */ + + if (entryPtr->flags & ENTRY_DELETED) { + return TCL_ERROR; + } + /* * If validate will return ERROR, then disallow further validations * Otherwise, if it didn't accept the new string (returned TCL_BREAK) * then eval the invalidCmd (if it's set) */ + if (code == TCL_ERROR) { entryPtr->validate = VALIDATE_NONE; } else if (code == TCL_BREAK) { @@ -3444,6 +3476,7 @@ EntryValidateChange(entryPtr, change, new, index, type) * 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) { @@ -3461,6 +3494,15 @@ EntryValidateChange(entryPtr, change, new, index, type) entryPtr->validate = VALIDATE_NONE; } Tcl_DStringFree(&script); + + /* + * It's possible that the user deleted the entry during validation. + * In that case, abort future validation and return an error. + */ + + if (entryPtr->flags & ENTRY_DELETED) { + return TCL_ERROR; + } } } diff --git a/tests/entry.test b/tests/entry.test index ecaf4f3..fd4d7e3 100644 --- a/tests/entry.test +++ b/tests/entry.test @@ -6,7 +6,7 @@ # Copyright (c) 1998-1999 by Scriptics Corporation. # All rights reserved. # -# RCS: @(#) $Id: entry.test,v 1.10 2000/05/29 01:43:15 hobbs Exp $ +# RCS: @(#) $Id: entry.test,v 1.11 2001/04/03 04:40:50 hobbs Exp $ if {[lsearch [namespace children] ::tcltest] == -1} { source [file join [pwd] [file dirname [info script]] defs.tcl] @@ -1506,9 +1506,9 @@ proc doval {W d i P s S v V} { set ::vVals [list $W $d $i $P $s $S $v $V] return 0 } -.e configure -validate all test entry-19.18 {entry widget validation} { + .e configure -validate all set ::e nextdata list [.e cget -validate] $::vVals } {none {.e -1 -1 nextdata newdata {} all forced}} @@ -1518,23 +1518,22 @@ proc doval {W d i P s S v V} { set ::e mydata return 1 } -.e configure -validate all ## This sets validate to none because it shows that we prevent a possible ## loop condition in the validation, when the entry textvar is also set test entry-19.19 {entry widget validation} { + .e configure -validate all .e validate list [.e cget -validate] [.e get] $::vVals } {none mydata {.e -1 -1 nextdata nextdata {} all forced}} -.e configure -validate all - ## This leaves validate alone because we trigger validation through the ## textvar (a write trace), and the write during validation triggers ## nothing (by definition of avoiding loops on var traces). This is ## one of those "dangerous" conditions where the user will have a ## different value in the entry widget shown as is in the textvar. test entry-19.20 {entry widget validation} { + .e configure -validate all set ::e testdata list [.e cget -validate] [.e get] $::e $::vVals } {all testdata mydata {.e -1 -1 testdata mydata {} all forced}} @@ -1546,6 +1545,53 @@ catch {unset ::e ::vVals} ## End validation tests ## +test entry-20.1 {widget deletion while active} { + destroy .e + entry .e -validate all \ + -validatecommand { destroy %W ; return 1 } \ + -invalidcommand bell + update + .e insert 0 abc + winfo exists .e +} 0 +test entry-20.2 {widget deletion while active} { + destroy .e + entry .e -validate all \ + -validatecommand { return 0 } \ + -invalidcommand { destroy %W } + .e insert 0 abc + winfo exists .e +} 0 +test entry-20.3 {widget deletion while active} { + destroy .e + entry .e -validate all \ + -validatecommand { rename .e {} ; return 1 } + .e insert 0 abc + winfo exists .e +} 0 +test entry-20.4 {widget deletion while active} { + destroy .e + entry .e -validate all \ + -validatecommand { return 0 } \ + -invalidcommand { rename .e {} } + .e insert 0 abc + winfo exists .e +} 0 +test entry-20.5 {widget deletion while active} { + destroy .e + entry .e -validatecommand { destroy .e ; return 0 } + .e validate + winfo exists .e +} 0 +test entry-20.6 {widget deletion while active} { + destroy .e + pack [entry .e] + update + .e config -xscrollcommand { destroy .e } + update idle + winfo exists .e +} 0 + # XXX Still need to write tests for EntryBlinkProc, EntryFocusProc, # and EntryTextVarProc. -- cgit v0.12