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