Destroy.c revision 0568f49b
1/***********************************************************
2Copyright (c) 1993, Oracle and/or its affiliates. All rights reserved.
3
4Permission is hereby granted, free of charge, to any person obtaining a
5copy of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation
7the rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the
9Software is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice (including the next
12paragraph) shall be included in all copies or substantial portions of the
13Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21DEALINGS IN THE SOFTWARE.
22
23Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
24
25                        All Rights Reserved
26
27Permission to use, copy, modify, and distribute this software and its
28documentation for any purpose and without fee is hereby granted,
29provided that the above copyright notice appear in all copies and that
30both that copyright notice and this permission notice appear in
31supporting documentation, and that the name of Digital not be
32used in advertising or publicity pertaining to distribution of the
33software without specific, written prior permission.
34
35DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
41SOFTWARE.
42
43******************************************************************/
44
45/*
46
47Copyright 1987, 1988, 1994, 1998  The Open Group
48
49Permission to use, copy, modify, distribute, and sell this software and its
50documentation for any purpose is hereby granted without fee, provided that
51the above copyright notice appear in all copies and that both that
52copyright notice and this permission notice appear in supporting
53documentation.
54
55The above copyright notice and this permission notice shall be included in
56all copies or substantial portions of the Software.
57
58THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
61OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
62AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
63CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64
65Except as contained in this notice, the name of The Open Group shall not be
66used in advertising or otherwise to promote the sale, use or other dealings
67in this Software without prior written authorization from The Open Group.
68
69*/
70
71#ifdef HAVE_CONFIG_H
72#include <config.h>
73#endif
74#include "IntrinsicI.h"
75
76struct _DestroyRec {
77    int dispatch_level;
78    Widget widget;
79};
80
81static void Recursive(Widget widget, XtWidgetProc proc)
82{
83    register Cardinal i;
84
85    /* Recurse down normal children */
86    if (XtIsComposite(widget)) {
87	CompositePart *cwp = &(((CompositeWidget) widget)->composite);
88	for (i = 0; i < cwp->num_children; i++) {
89	    Recursive(cwp->children[i], proc);
90	}
91    }
92
93    /* Recurse down popup children */
94    if (XtIsWidget(widget)) {
95	for (i = 0; i < widget->core.num_popups; i++) {
96	    Recursive(widget->core.popup_list[i], proc);
97	}
98    }
99
100    /* Finally, apply procedure to this widget */
101    (*proc) (widget);
102} /* Recursive */
103
104static void Phase1Destroy (Widget widget)
105{
106    Widget hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
107
108    widget->core.being_destroyed = TRUE;
109    if (XtHasCallbacks(hookobj, XtNdestroyHook) == XtCallbackHasSome) {
110	XtDestroyHookDataRec call_data;
111
112	call_data.type = XtHdestroy;
113	call_data.widget = widget;
114	XtCallCallbackList(hookobj,
115		((HookObject)hookobj)->hooks.destroyhook_callbacks,
116		(XtPointer) &call_data);
117    }
118} /* Phase1Destroy */
119
120static void Phase2Callbacks(Widget widget)
121{
122    if (widget->core.destroy_callbacks != NULL) {
123	XtCallCallbackList(widget,
124			   widget->core.destroy_callbacks, (XtPointer) NULL);
125    }
126} /* Phase2Callbacks */
127
128static void Phase2Destroy(register Widget widget)
129{
130    register WidgetClass	    class;
131    register ConstraintWidgetClass  cwClass;
132    ObjectClassExtension	    ext;
133
134    /* Call constraint destroy procedures */
135    if (XtParent(widget) != NULL && !XtIsShell(widget) && XtIsConstraint(XtParent(widget))) {
136	LOCK_PROCESS;
137	cwClass = (ConstraintWidgetClass)XtParent(widget)->core.widget_class;
138	UNLOCK_PROCESS;
139	for (;;) {
140	    XtWidgetProc destroy;
141
142	    LOCK_PROCESS;
143	    destroy = cwClass->constraint_class.destroy;
144	    UNLOCK_PROCESS;
145	    if (destroy)
146		(*destroy) (widget);
147            if (cwClass == (ConstraintWidgetClass)constraintWidgetClass) break;
148	    LOCK_PROCESS;
149	    cwClass = (ConstraintWidgetClass) cwClass->core_class.superclass;
150	    UNLOCK_PROCESS;
151	}
152    }
153
154    /* Call widget destroy procedures */
155    LOCK_PROCESS;
156    for (class = widget->core.widget_class;
157	 class != NULL;
158	 class = class->core_class.superclass) {
159	XtWidgetProc destroy;
160
161	destroy = class->core_class.destroy;
162	UNLOCK_PROCESS;
163	if (destroy)
164	    (*destroy) (widget);
165	LOCK_PROCESS;
166    }
167
168    /* Call widget deallocate procedure */
169    ext = (ObjectClassExtension)
170	XtGetClassExtension(widget->core.widget_class,
171			    XtOffsetOf(CoreClassPart, extension),
172			    NULLQUARK, XtObjectExtensionVersion,
173			    sizeof(ObjectClassExtensionRec));
174    if (ext && ext->deallocate) {
175	XtDeallocateProc deallocate;
176	deallocate = ext->deallocate;
177	UNLOCK_PROCESS;
178	(*deallocate)(widget, NULL);
179    } else {
180	UNLOCK_PROCESS;
181	XtFree((char *)widget);
182    }
183} /* Phase2Destroy */
184
185static Boolean IsDescendant(Widget widget, Widget root)
186{
187    while ((widget = XtParent(widget)) != root) {
188	if (widget == NULL) return False;
189    }
190    return True;
191}
192
193static void XtPhase2Destroy (Widget widget)
194{
195    Display	    *display = NULL;
196    Window	    window;
197    Widget          parent;
198    XtAppContext    app = XtWidgetToApplicationContext(widget);
199    Widget	    outerInPhase2Destroy = app->in_phase2_destroy;
200    int		    starting_count = app->destroy_count;
201    Boolean	    isPopup = False;
202
203    /* invalidate focus trace cache for this display */
204    _XtGetPerDisplay(XtDisplayOfObject(widget))->pdi.traceDepth = 0;
205
206    parent = widget->core.parent;
207
208    if (parent && XtIsWidget(parent) && parent->core.num_popups) {
209	Cardinal i;
210	for (i = 0; i < parent->core.num_popups; i++) {
211	    if (parent->core.popup_list[i] == widget) {
212		isPopup = True;
213		break;
214	    }
215	}
216    }
217
218    if (!isPopup && parent && XtIsComposite(parent)) {
219	XtWidgetProc delete_child;
220
221	LOCK_PROCESS;
222	delete_child =
223	    ((CompositeWidgetClass) parent->core.widget_class)->
224		composite_class.delete_child;
225	UNLOCK_PROCESS;
226        if (XtIsRectObj(widget)) {
227       	    XtUnmanageChild(widget);
228        }
229	if (delete_child == NULL) {
230	    String param;
231	    Cardinal num_params = 1;
232
233	    LOCK_PROCESS;
234	    param = parent->core.widget_class->core_class.class_name;
235	    UNLOCK_PROCESS;
236	    XtAppWarningMsg(XtWidgetToApplicationContext(widget),
237		"invalidProcedure","deleteChild",XtCXtToolkitError,
238		"null delete_child procedure for class %s in XtDestroy",
239		&param, &num_params);
240	} else {
241	    (*delete_child) (widget);
242	}
243    }
244
245    /* widget is freed in Phase2Destroy, so retrieve window now.
246     * Shells destroy their own windows, to prevent window leaks in
247     * popups; this test is practical only when XtIsShell() is cheap.
248     */
249    if (XtIsShell(widget) || !XtIsWidget(widget)) {
250	window = 0;
251    }
252    else {
253	display = XtDisplay(widget);
254	window = widget->core.window;
255    }
256
257    Recursive(widget, Phase2Callbacks);
258    if (app->destroy_count > starting_count) {
259	int i = starting_count;
260	while (i < app->destroy_count) {
261
262	    DestroyRec * dr = app->destroy_list + i;
263	    if (IsDescendant(dr->widget, widget)) {
264		Widget descendant = dr->widget;
265		register int j;
266		app->destroy_count--;
267		for (j = app->destroy_count - i; --j >= 0; dr++)
268		    *dr = *(dr + 1);
269		XtPhase2Destroy(descendant);
270	    }
271	    else i++;
272	}
273    }
274
275    app->in_phase2_destroy = widget;
276    Recursive(widget, Phase2Destroy);
277    app->in_phase2_destroy = outerInPhase2Destroy;
278
279    if (isPopup) {
280	Cardinal i;
281	for (i = 0; i < parent->core.num_popups; i++)
282	    if (parent->core.popup_list[i] == widget) {
283		parent->core.num_popups--;
284		while (i < parent->core.num_popups) {
285		    parent->core.popup_list[i] = parent->core.popup_list[i+1];
286		    i++;
287		}
288		break;
289	    }
290    }
291
292    /* %%% the following parent test hides a more serious problem,
293       but it avoids breaking those who depended on the old bug
294       until we have time to fix it properly. */
295
296    if (window && (parent == NULL || !parent->core.being_destroyed))
297	XDestroyWindow(display, window);
298} /* XtPhase2Destroy */
299
300
301void _XtDoPhase2Destroy(XtAppContext app, int dispatch_level)
302{
303    /* Phase 2 must occur in fifo order.  List is not necessarily
304     * contiguous in dispatch_level.
305     */
306
307    int i = 0;
308    while (i < app->destroy_count) {
309
310	/* XtPhase2Destroy can result in calls to XtDestroyWidget,
311	 * and these could cause app->destroy_list to be reallocated.
312	 */
313
314	DestroyRec* dr = app->destroy_list + i;
315	if (dr->dispatch_level >= dispatch_level)  {
316	    Widget w = dr->widget;
317	    register int j;
318	    app->destroy_count--;
319	    for (j = app->destroy_count - i; --j >=0; dr++)
320		*dr = *(dr + 1);
321	    XtPhase2Destroy(w);
322	}
323	else i++;
324    }
325}
326
327
328void XtDestroyWidget (Widget widget)
329{
330    XtAppContext app;
331    DestroyRec *dr;
332
333    app = XtWidgetToApplicationContext(widget);
334    LOCK_APP(app);
335    if (widget->core.being_destroyed) {
336	UNLOCK_APP(app);
337	return;
338    }
339    Recursive(widget, Phase1Destroy);
340
341    if (app->in_phase2_destroy &&
342	IsDescendant(widget, app->in_phase2_destroy))
343    {
344	XtPhase2Destroy(widget);
345	UNLOCK_APP(app);
346	return;
347    }
348
349    if (app->destroy_count == app->destroy_list_size) {
350	app->destroy_list_size += 10;
351	app->destroy_list = (DestroyRec*)
352	    XtRealloc( (char*)app->destroy_list,
353		       (Cardinal)(sizeof(DestroyRec) * (size_t)app->destroy_list_size)
354		      );
355    }
356    dr = app->destroy_list + app->destroy_count++;
357    dr->dispatch_level = app->dispatch_level;
358    dr->widget = widget;
359
360    if (app->dispatch_level > 1) {
361	int i;
362	for (i = app->destroy_count - 1; i;) {
363	    /* this handles only one case of nesting difficulties */
364 	    dr = app->destroy_list + (--i);
365 	    if (dr->dispatch_level < app->dispatch_level &&
366 		IsDescendant(dr->widget, widget)) {
367		DestroyRec *dr2 = app->destroy_list + (app->destroy_count-1);
368 		dr2->dispatch_level = dr->dispatch_level;
369  		break;
370  	    }
371  	}
372    }
373
374    if (_XtSafeToDestroy(app)) {
375	app->dispatch_level = 1; /* avoid nested _XtDoPhase2Destroy */
376	_XtDoPhase2Destroy(app, 0);
377	app->dispatch_level = 0;
378    }
379    UNLOCK_APP(app);
380
381} /* XtDestroyWidget */
382