summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhobbs <hobbs>2001-04-03 04:40:50 (GMT)
committerhobbs <hobbs>2001-04-03 04:40:50 (GMT)
commit5c2ad0a34a4a78a7879cd6dc7e4523f789aa3d0e (patch)
treea525aba7f40d33313e28afdfe008bcd9322aae02
parent9eb72b9d79626c34c27d968c36568bce309c741f (diff)
downloadtk-5c2ad0a34a4a78a7879cd6dc7e4523f789aa3d0e.zip
tk-5c2ad0a34a4a78a7879cd6dc7e4523f789aa3d0e.tar.gz
tk-5c2ad0a34a4a78a7879cd6dc7e4523f789aa3d0e.tar.bz2
* 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.
-rw-r--r--generic/tkEntry.c64
-rw-r--r--tests/entry.test56
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.