summaryrefslogtreecommitdiffstats
path: root/generic/ttk/ttkWidget.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/ttk/ttkWidget.c')
-rw-r--r--generic/ttk/ttkWidget.c190
1 files changed, 103 insertions, 87 deletions
diff --git a/generic/ttk/ttkWidget.c b/generic/ttk/ttkWidget.c
index 66ab44c..c2cb2b6 100644
--- a/generic/ttk/ttkWidget.c
+++ b/generic/ttk/ttkWidget.c
@@ -1,4 +1,4 @@
-/* $Id: ttkWidget.c,v 1.17 2008/11/11 23:39:30 jenglish Exp $
+/* $Id: ttkWidget.c,v 1.18 2008/11/13 01:13:54 jenglish Exp $
* Copyright (c) 2003, Joe English
*
* Core widget utilities.
@@ -170,30 +170,48 @@ int TtkWidgetEnsembleCommand(
return commands[index].command(interp, objc, objv, clientData);
}
-/*
- * WidgetInstanceObjCmd --
+/* WidgetInstanceObjCmd --
* Widget instance command implementation.
*/
static int
WidgetInstanceObjCmd(
- ClientData clientData, /* Widget record pointer */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
WidgetCore *corePtr = clientData;
const WidgetCommandSpec *commands = corePtr->widgetSpec->commands;
- int status = TCL_OK;
+ int status;
Tcl_Preserve(clientData);
status = TtkWidgetEnsembleCommand(commands,1, interp,objc,objv,clientData);
+ if (WidgetDestroyed(corePtr)) {
+ status = TCL_ERROR;
+ Tcl_SetResult(interp, "Widget has been destroyed", TCL_STATIC);
+ }
Tcl_Release(clientData);
return status;
}
-/*
- * Command deletion callback for widget instance commands.
+/*------------------------------------------------------------------------
+ * +++ Widget destruction.
+ *
+ * A widget can be destroyed when the application explicitly
+ * destroys the window or one of its ancestors via [destroy]
+ * or Tk_DestroyWindow(); when the application deletes the widget
+ * instance command; when there is an error in the widget constructor;
+ * or when another application calls XDestroyWindow on the window ID.
+ *
+ * The window receives a <DestroyNotify> event in all cases,
+ * so we do the bulk of the cleanup there. See [#2207435] for
+ * further notes (esp. re: Tk_FreeConfigOptions).
+ *
+ * Widget code that reenters the interp should only do so
+ * when the widtget is Tcl_Preserve()d, and should check
+ * the WIDGET_DESTROYED flag bit upon return.
+ */
+
+/* WidgetInstanceObjCmdDeleted --
+ * Widget instance command deletion callback.
*/
static void
WidgetInstanceObjCmdDeleted(ClientData clientData)
@@ -204,20 +222,46 @@ WidgetInstanceObjCmdDeleted(ClientData clientData)
Tk_DestroyWindow(corePtr->tkwin);
}
-/*
- * WidgetCleanup --
- * Final cleanup for widget.
- *
- * @@@ TODO: check all code paths leading to widget destruction,
- * @@@ describe here.
- * @@@ Call widget-specific cleanup routine at an appropriate point.
+/* FreeWidget --
+ * Final cleanup for widget; called via Tcl_EventuallyFree().
*/
static void
-WidgetCleanup(char *memPtr)
+FreeWidget(char *memPtr)
{
ckfree(memPtr);
}
+/* DestroyWidget --
+ * Main widget destructor; called from <DestroyNotify> event handler.
+ */
+static void
+DestroyWidget(WidgetCore *corePtr)
+{
+ corePtr->flags |= WIDGET_DESTROYED;
+
+ corePtr->widgetSpec->cleanupProc(corePtr);
+
+ Tk_FreeConfigOptions(
+ (ClientData)corePtr, corePtr->optionTable, corePtr->tkwin);
+
+ if (corePtr->layout) {
+ Ttk_FreeLayout(corePtr->layout);
+ }
+
+ if (corePtr->flags & REDISPLAY_PENDING) {
+ Tcl_CancelIdleCall(DrawWidget, corePtr);
+ }
+
+ corePtr->tkwin = NULL;
+ if (corePtr->widgetCmd) {
+ Tcl_Command cmd = corePtr->widgetCmd;
+ corePtr->widgetCmd = 0;
+ /* NB: this can reenter the interpreter via a command traces */
+ Tcl_DeleteCommandFromToken(corePtr->interp, cmd);
+ }
+ Tcl_EventuallyFree(corePtr, FreeWidget);
+}
+
/*
* CoreEventProc --
* Event handler for basic events.
@@ -258,34 +302,10 @@ static void CoreEventProc(ClientData clientData, XEvent *eventPtr)
}
break;
case DestroyNotify :
- corePtr->flags |= WIDGET_DESTROYED;
-
- Tk_DeleteEventHandler(corePtr->tkwin,
- CoreEventMask,CoreEventProc,clientData);
-
- if (corePtr->flags & REDISPLAY_PENDING) {
- Tcl_CancelIdleCall(DrawWidget, clientData);
- }
-
- corePtr->widgetSpec->cleanupProc(corePtr);
-
- Tk_FreeConfigOptions(
- clientData, corePtr->optionTable, corePtr->tkwin);
- corePtr->tkwin = NULL;
-
- if (corePtr->layout) {
- Ttk_FreeLayout(corePtr->layout);
- }
-
- /* NB: this can reenter the interpreter via a command traces */
- if (corePtr->widgetCmd) {
- Tcl_Command cmd = corePtr->widgetCmd;
- corePtr->widgetCmd = 0;
- Tcl_DeleteCommandFromToken(corePtr->interp, cmd);
- }
- Tcl_EventuallyFree(clientData, WidgetCleanup);
+ Tk_DeleteEventHandler(
+ corePtr->tkwin, CoreEventMask,CoreEventProc,clientData);
+ DestroyWidget(corePtr);
break;
-
case FocusIn:
case FocusOut:
/* Don't process "virtual crossing" events */
@@ -348,24 +368,20 @@ int TtkWidgetConstructorObjCmd(
{
WidgetSpec *widgetSpec = clientData;
const char *className = widgetSpec->className;
- WidgetCore *corePtr;
- void *recordPtr;
+ Tk_OptionTable optionTable =
+ Tk_CreateOptionTable(interp, widgetSpec->optionSpecs);
Tk_Window tkwin;
- Tk_OptionTable optionTable;
+ void *recordPtr;
+ WidgetCore *corePtr;
+ Tk_SavedOptions savedOptions;
int i;
- if (objc < 2 || objc % 1 == 1) {
+ if (objc < 2 || objc % 2 == 1) {
Tcl_WrongNumArgs(interp, 1, objv, "pathName ?-option value ...?");
return TCL_ERROR;
}
- tkwin = Tk_CreateWindowFromPath(
- interp, Tk_MainWindow(interp), Tcl_GetString(objv[1]), NULL);
- if (tkwin == NULL)
- return TCL_ERROR;
-
- /*
- * Check if a -class resource has been specified:
+ /* Check if a -class option has been specified.
* We have to do this before the InitOptions() call,
* since InitOptions() is affected by the widget class.
*/
@@ -376,15 +392,10 @@ int TtkWidgetConstructorObjCmd(
}
}
- Tk_SetClass(tkwin, className);
-
- /*
- * Set the BackgroundPixmap to ParentRelative here, so
- * subclasses don't need to worry about setting the background.
- */
- Tk_SetWindowBackgroundPixmap(tkwin, ParentRelative);
-
- optionTable = Tk_CreateOptionTable(interp, widgetSpec->optionSpecs);
+ tkwin = Tk_CreateWindowFromPath(
+ interp, Tk_MainWindow(interp), Tcl_GetString(objv[1]), NULL);
+ if (tkwin == NULL)
+ return TCL_ERROR;
/*
* Allocate and initialize the widget record.
@@ -399,52 +410,57 @@ int TtkWidgetConstructorObjCmd(
corePtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(tkwin),
WidgetInstanceObjCmd, recordPtr, WidgetInstanceObjCmdDeleted);
corePtr->optionTable = optionTable;
+ corePtr->layout = NULL;
+ corePtr->flags = 0;
+ corePtr->state = 0;
+ Tk_SetClass(tkwin, className);
Tk_SetClassProcs(tkwin, &widgetClassProcs, recordPtr);
-
- if (Tk_InitOptions(interp, recordPtr, optionTable, tkwin) != TCL_OK)
- goto error_nocleanup;
+ Tk_SetWindowBackgroundPixmap(tkwin, ParentRelative);
widgetSpec->initializeProc(interp, recordPtr);
- if (Tk_SetOptions(interp, recordPtr, optionTable, objc - 2,
- objv + 2, tkwin, NULL/*savePtr*/, NULL/*maskPtr*/) != TCL_OK)
+ Tk_CreateEventHandler(tkwin, CoreEventMask, CoreEventProc, recordPtr);
+
+ /*
+ * Initial configuration.
+ */
+
+ Tcl_Preserve(corePtr);
+ if (Tk_InitOptions(interp, recordPtr, optionTable, tkwin) != TCL_OK) {
goto error;
+ }
+ if (Tk_SetOptions(interp, recordPtr, optionTable,
+ objc - 2, objv + 2, tkwin, &savedOptions, NULL) != TCL_OK) {
+ Tk_RestoreSavedOptions(&savedOptions);
+ goto error;
+ } else {
+ Tk_FreeSavedOptions(&savedOptions);
+ }
if (widgetSpec->configureProc(interp, recordPtr, ~0) != TCL_OK)
goto error;
-
if (widgetSpec->postConfigureProc(interp, recordPtr, ~0) != TCL_OK)
goto error;
if (WidgetDestroyed(corePtr))
goto error;
- if (UpdateLayout(interp, corePtr) != TCL_OK)
- goto error;
+ Tcl_Release(corePtr);
SizeChanged(corePtr);
- Tk_CreateEventHandler(tkwin, CoreEventMask, CoreEventProc, recordPtr);
-
Tk_MakeWindowExist(tkwin);
Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(tkwin), -1));
-
return TCL_OK;
error:
- widgetSpec->cleanupProc(recordPtr);
-error_nocleanup:
- if (corePtr->layout) {
- Ttk_FreeLayout(corePtr->layout);
- corePtr->layout = 0;
+ if (WidgetDestroyed(corePtr)) {
+ Tcl_SetResult(interp, "Widget has been destroyed", TCL_STATIC);
+ } else {
+ Tk_DestroyWindow(tkwin);
}
- Tk_UndefineCursor(tkwin); /* @@@ TEMP: workaround for #2207435 */
- Tk_FreeConfigOptions(recordPtr, optionTable, tkwin);
- Tk_DestroyWindow(tkwin);
- corePtr->tkwin = 0;
- Tcl_DeleteCommandFromToken(interp, corePtr->widgetCmd);
- ckfree(recordPtr);
+ Tcl_Release(corePtr);
return TCL_ERROR;
}