summaryrefslogtreecommitdiffstats
path: root/generic/tkMenu.c
diff options
context:
space:
mode:
authorvincentdarley <vincentdarley>2003-05-30 11:02:59 (GMT)
committervincentdarley <vincentdarley>2003-05-30 11:02:59 (GMT)
commit5482974d6272b18790622e16bd1394c57df0c52c (patch)
tree5bcdc3e30cddc5248bcf66ead0d432266088c277 /generic/tkMenu.c
parent403536467e560a13fc5995ffb19dcf92d9da91e2 (diff)
downloadtk-5482974d6272b18790622e16bd1394c57df0c52c.zip
tk-5482974d6272b18790622e16bd1394c57df0c52c.tar.gz
tk-5482974d6272b18790622e16bd1394c57df0c52c.tar.bz2
fix to menu clone deletion bug
Diffstat (limited to 'generic/tkMenu.c')
-rw-r--r--generic/tkMenu.c198
1 files changed, 138 insertions, 60 deletions
diff --git a/generic/tkMenu.c b/generic/tkMenu.c
index 152e57f..713a288 100644
--- a/generic/tkMenu.c
+++ b/generic/tkMenu.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: tkMenu.c,v 1.20 2003/02/26 02:32:56 hobbs Exp $
+ * RCS: @(#) $Id: tkMenu.c,v 1.21 2003/05/30 11:03:00 vincentdarley Exp $
*/
/*
@@ -496,7 +496,9 @@ MenuCmd(clientData, interp, objc, objv)
}
/*
- * Initialize the data structure for the menu.
+ * Initialize the data structure for the menu. Note that the
+ * menuPtr is eventually freed in 'TkMenuEventProc' in tkMenuDraw.c,
+ * when Tcl_EventuallyFree is called.
*/
menuPtr = (TkMenu *) ckalloc(sizeof(TkMenu));
@@ -1186,7 +1188,9 @@ DestroyMenuInstance(menuPtr)
TkpDestroyMenu(menuPtr);
cascadePtr = menuPtr->menuRefPtr->parentEntryPtr;
menuPtr->menuRefPtr->menuPtr = NULL;
- TkFreeMenuReferences(menuPtr->menuRefPtr);
+ if (TkFreeMenuReferences(menuPtr->menuRefPtr)) {
+ menuPtr->menuRefPtr = NULL;
+ }
for (; cascadePtr != NULL; cascadePtr = nextCascadePtr) {
nextCascadePtr = cascadePtr->nextCascadePtr;
@@ -1252,6 +1256,11 @@ DestroyMenuInstance(menuPtr)
TkMenuFreeDrawOptions(menuPtr);
Tk_FreeConfigOptions((char *) menuPtr,
menuPtr->optionTablesPtr->menuOptionTable, menuPtr->tkwin);
+ if (menuPtr->tkwin != NULL) {
+ Tk_Window tkwin = menuPtr->tkwin;
+ menuPtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+ }
}
/*
@@ -1285,6 +1294,8 @@ TkDestroyMenu(menuPtr)
return;
}
+ Tcl_Preserve(menuPtr);
+
/*
* Now destroy all non-tearoff instances of this menu if this is a
* parent menu. Is this loop safe enough? Are there going to be
@@ -1292,30 +1303,38 @@ TkDestroyMenu(menuPtr)
* we have to do a slightly more complex scheme.
*/
+ menuPtr->menuFlags |= MENU_DELETION_PENDING;
+ if (menuPtr->menuRefPtr != NULL) {
+ /*
+ * If any toplevel widgets have this menu as their menubar,
+ * the geometry of the window may have to be recalculated.
+ */
+ topLevelListPtr = menuPtr->menuRefPtr->topLevelListPtr;
+ while (topLevelListPtr != NULL) {
+ nextTopLevelPtr = topLevelListPtr->nextPtr;
+ TkpSetWindowMenuBar(topLevelListPtr->tkwin, NULL);
+ topLevelListPtr = nextTopLevelPtr;
+ }
+ }
if (menuPtr->masterMenuPtr == menuPtr) {
- menuPtr->menuFlags |= MENU_DELETION_PENDING;
while (menuPtr->nextInstancePtr != NULL) {
menuInstancePtr = menuPtr->nextInstancePtr;
menuPtr->nextInstancePtr = menuInstancePtr->nextInstancePtr;
if (menuInstancePtr->tkwin != NULL) {
- Tk_DestroyWindow(menuInstancePtr->tkwin);
+ Tk_Window tkwin = menuInstancePtr->tkwin;
+ /*
+ * Note: it may be desirable to NULL out the tkwin
+ * field of menuInstancePtr here:
+ * menuInstancePtr->tkwin = NULL;
+ */
+ Tk_DestroyWindow(tkwin);
}
}
- menuPtr->menuFlags &= ~MENU_DELETION_PENDING;
}
- /*
- * If any toplevel widgets have this menu as their menubar,
- * the geometry of the window may have to be recalculated.
- */
-
- topLevelListPtr = menuPtr->menuRefPtr->topLevelListPtr;
- while (topLevelListPtr != NULL) {
- nextTopLevelPtr = topLevelListPtr->nextPtr;
- TkpSetWindowMenuBar(topLevelListPtr->tkwin, NULL);
- topLevelListPtr = nextTopLevelPtr;
- }
DestroyMenuInstance(menuPtr);
+
+ Tcl_Release(menuPtr);
}
/*
@@ -1326,6 +1345,10 @@ TkDestroyMenu(menuPtr)
* This entry is removed from the list of entries that point to the
* cascade menu. This is done in preparation for changing the menu
* that this entry points to.
+ *
+ * At the end of this function, the menu entry no longer contains
+ * a reference to a 'TkMenuReferences' structure, and therefore
+ * no such structure contains a reference to this menu entry either.
*
* Results:
* None
@@ -1352,6 +1375,8 @@ UnhookCascadeEntry(mePtr)
cascadeEntryPtr = menuRefPtr->parentEntryPtr;
if (cascadeEntryPtr == NULL) {
+ TkFreeMenuReferences(menuRefPtr);
+ mePtr->childMenuRefPtr = NULL;
return;
}
@@ -1370,6 +1395,10 @@ UnhookCascadeEntry(mePtr)
*/
menuRefPtr->parentEntryPtr = NULL;
+ /*
+ * The original field is set to zero below, after it is
+ * freed.
+ */
TkFreeMenuReferences(menuRefPtr);
} else {
menuRefPtr->parentEntryPtr = cascadeEntryPtr->nextCascadePtr;
@@ -1388,6 +1417,7 @@ UnhookCascadeEntry(mePtr)
break;
}
}
+ mePtr->nextCascadePtr = NULL;
}
mePtr->childMenuRefPtr = NULL;
}
@@ -1435,7 +1465,40 @@ DestroyMenuEntry(memPtr)
*/
if (mePtr->type == CASCADE_ENTRY) {
- UnhookCascadeEntry(mePtr);
+ if (menuPtr->masterMenuPtr != menuPtr) {
+ TkMenu *destroyThis = NULL;
+ /*
+ * The menu as a whole is a clone. We must delete the clone
+ * of the cascaded menu for the particular entry we are
+ * destroying.
+ */
+ TkMenuReferences *menuRefPtr = mePtr->childMenuRefPtr;
+ if (menuRefPtr != NULL) {
+ destroyThis = menuRefPtr->menuPtr;
+ /*
+ * But only if it is a clone. What can happen is that
+ * we are in the middle of deleting a menu and this
+ * menu pointer has already been reset to point to the
+ * original menu. In that case we have nothing special
+ * to do.
+ */
+ if ((destroyThis != NULL)
+ && (destroyThis->masterMenuPtr == destroyThis)) {
+ destroyThis = NULL;
+ }
+ }
+ UnhookCascadeEntry(mePtr);
+ if (menuRefPtr != NULL) {
+ if (menuRefPtr->menuPtr == destroyThis) {
+ menuRefPtr->menuPtr = NULL;
+ }
+ if (destroyThis != NULL) {
+ TkDestroyMenu(destroyThis);
+ }
+ }
+ } else {
+ UnhookCascadeEntry(mePtr);
+ }
}
if (mePtr->image != NULL) {
Tk_FreeImage(mePtr->image);
@@ -2198,6 +2261,11 @@ MenuCmdDeletedProc(clientData)
*/
if (tkwin != NULL) {
+ /*
+ * Note: it may be desirable to NULL out the tkwin
+ * field of menuPtr here:
+ * menuPtr->tkwin = NULL;
+ */
Tk_DestroyWindow(tkwin);
}
}
@@ -2935,6 +3003,13 @@ RecursivelyDeleteMenu(menuPtr)
int i;
TkMenuEntry *mePtr;
+ /*
+ * It is not 100% clear that this preserve/release pair is
+ * required, but we have added them for safety in this
+ * very complex code.
+ */
+ Tcl_Preserve(menuPtr);
+
for (i = 0; i < menuPtr->numEntries; i++) {
mePtr = menuPtr->entries[i];
if ((mePtr->type == CASCADE_ENTRY)
@@ -2943,7 +3018,11 @@ RecursivelyDeleteMenu(menuPtr)
RecursivelyDeleteMenu(mePtr->childMenuRefPtr->menuPtr);
}
}
- Tk_DestroyWindow(menuPtr->tkwin);
+ if (menuPtr->tkwin != NULL) {
+ Tk_DestroyWindow(menuPtr->tkwin);
+ }
+
+ Tcl_Release(menuPtr);
}
/*
@@ -3095,18 +3174,14 @@ TkSetWindowMenuBar(interp, tkwin, oldMenuName, menuName)
* that reference this menu.
*/
- for (topLevelListPtr = menuRefPtr->topLevelListPtr,
- prevTopLevelPtr = NULL;
- (topLevelListPtr != NULL)
- && (topLevelListPtr->tkwin != tkwin);
- prevTopLevelPtr = topLevelListPtr,
- topLevelListPtr = topLevelListPtr->nextPtr) {
-
- /*
- * Empty loop body.
- */
-
- }
+ topLevelListPtr = menuRefPtr->topLevelListPtr;
+ prevTopLevelPtr = NULL;
+
+ while ((topLevelListPtr != NULL)
+ && (topLevelListPtr->tkwin != tkwin)) {
+ prevTopLevelPtr = topLevelListPtr;
+ topLevelListPtr = topLevelListPtr->nextPtr;
+ }
/*
* Now we have found the toplevel reference that matches the
@@ -3377,7 +3452,8 @@ TkFindMenuReferencesObj(interp, objPtr)
* is cleared. It cleans up the ref if it is now empty.
*
* Results:
- * None.
+ * Returns 1 if the references structure was freed, and 0
+ * otherwise.
*
* Side effects:
* If this is the last field to be cleared, the menu ref is
@@ -3386,7 +3462,7 @@ TkFindMenuReferencesObj(interp, objPtr)
*----------------------------------------------------------------------
*/
-void
+int
TkFreeMenuReferences(menuRefPtr)
TkMenuReferences *menuRefPtr; /* The menu reference to
* free */
@@ -3396,7 +3472,9 @@ TkFreeMenuReferences(menuRefPtr)
&& (menuRefPtr->topLevelListPtr == NULL)) {
Tcl_DeleteHashEntry(menuRefPtr->hashEntryPtr);
ckfree((char *) menuRefPtr);
+ return 1;
}
+ return 0;
}
/*
@@ -3453,28 +3531,28 @@ DeleteMenuCloneEntries(menuPtr, first, last)
}
}
-/*
- *----------------------------------------------------------------------
- *
- * TkMenuCleanup --
- *
- * Resets menusInitialized to allow Tk to be finalized and reused
- * without the DLL being unloaded.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-TkMenuCleanup(ClientData unused)
-{
- menusInitialized = 0;
-}
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMenuCleanup --
+ *
+ * Resets menusInitialized to allow Tk to be finalized and reused
+ * without the DLL being unloaded.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TkMenuCleanup(ClientData unused)
+{
+ menusInitialized = 0;
+}
/*
*----------------------------------------------------------------------
@@ -3498,17 +3576,17 @@ TkMenuInit()
{
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
-
+
if (!menusInitialized) {
Tcl_MutexLock(&menuMutex);
if (!menusInitialized) {
TkpMenuInit();
menusInitialized = 1;
}
- /*
- * Make sure we cleanup on finalize.
- */
- Tcl_CreateExitHandler((Tcl_ExitProc *) TkMenuCleanup, NULL);
+ /*
+ * Make sure we cleanup on finalize.
+ */
+ Tcl_CreateExitHandler((Tcl_ExitProc *) TkMenuCleanup, NULL);
Tcl_MutexUnlock(&menuMutex);
}
if (!tsdPtr->menusInitialized) {