Destroy.c revision a3bd7f05
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
82Recursive(Widget widget, XtWidgetProc proc)
83{
84    register Cardinal i;
85
86    /* Recurse down normal children */
87    if (XtIsComposite(widget)) {
88        CompositePart *cwp = &(((CompositeWidget) widget)->composite);
89
90        for (i = 0; i < cwp->num_children; i++) {
91            Recursive(cwp->children[i], proc);
92        }
93    }
94
95    /* Recurse down popup children */
96    if (XtIsWidget(widget)) {
97        for (i = 0; i < widget->core.num_popups; i++) {
98            Recursive(widget->core.popup_list[i], proc);
99        }
100    }
101
102    /* Finally, apply procedure to this widget */
103    (*proc) (widget);
104}                               /* Recursive */
105
106static void
107Phase1Destroy(Widget widget)
108{
109    Widget hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
110
111    widget->core.being_destroyed = TRUE;
112    if (XtHasCallbacks(hookobj, XtNdestroyHook) == XtCallbackHasSome) {
113        XtDestroyHookDataRec call_data;
114
115        call_data.type = XtHdestroy;
116        call_data.widget = widget;
117        XtCallCallbackList(hookobj,
118                           ((HookObject) hookobj)->hooks.destroyhook_callbacks,
119                           (XtPointer) &call_data);
120    }
121}                               /* Phase1Destroy */
122
123static void
124Phase2Callbacks(Widget widget)
125{
126    if (widget->core.destroy_callbacks != NULL) {
127        XtCallCallbackList(widget,
128                           widget->core.destroy_callbacks, (XtPointer) NULL);
129    }
130}                               /* Phase2Callbacks */
131
132static void
133Phase2Destroy(register Widget widget)
134{
135    register WidgetClass class;
136    register ConstraintWidgetClass cwClass;
137    ObjectClassExtension ext;
138
139    /* Call constraint destroy procedures */
140    if (XtParent(widget) != NULL && !XtIsShell(widget) &&
141        XtIsConstraint(XtParent(widget))) {
142        LOCK_PROCESS;
143        cwClass = (ConstraintWidgetClass) XtParent(widget)->core.widget_class;
144        UNLOCK_PROCESS;
145        for (;;) {
146            XtWidgetProc destroy;
147
148            LOCK_PROCESS;
149            destroy = cwClass->constraint_class.destroy;
150            UNLOCK_PROCESS;
151            if (destroy)
152                (*destroy) (widget);
153            if (cwClass == (ConstraintWidgetClass) constraintWidgetClass)
154                break;
155            LOCK_PROCESS;
156            cwClass = (ConstraintWidgetClass) cwClass->core_class.superclass;
157            UNLOCK_PROCESS;
158        }
159    }
160
161    /* Call widget destroy procedures */
162    LOCK_PROCESS;
163    for (class = widget->core.widget_class;
164         class != NULL; class = class->core_class.superclass) {
165        XtWidgetProc destroy;
166
167        destroy = class->core_class.destroy;
168        UNLOCK_PROCESS;
169        if (destroy)
170            (*destroy) (widget);
171        LOCK_PROCESS;
172    }
173
174    /* Call widget deallocate procedure */
175    ext = (ObjectClassExtension) XtGetClassExtension(widget->core.widget_class,
176                                                     XtOffsetOf(CoreClassPart,
177                                                                extension),
178                                                     NULLQUARK,
179                                                     XtObjectExtensionVersion,
180                                                     sizeof
181                                                     (ObjectClassExtensionRec));
182    if (ext && ext->deallocate) {
183        XtDeallocateProc deallocate;
184
185        deallocate = ext->deallocate;
186        UNLOCK_PROCESS;
187        (*deallocate) (widget, NULL);
188    }
189    else {
190        UNLOCK_PROCESS;
191        XtFree((char *) widget);
192    }
193}                               /* Phase2Destroy */
194
195static Boolean
196IsDescendant(Widget widget, Widget root)
197{
198    while ((widget = XtParent(widget)) != root) {
199        if (widget == NULL)
200            return False;
201    }
202    return True;
203}
204
205static void
206XtPhase2Destroy(Widget widget)
207{
208    Display *display = NULL;
209    Window window;
210    Widget parent;
211    XtAppContext app = XtWidgetToApplicationContext(widget);
212    Widget outerInPhase2Destroy = app->in_phase2_destroy;
213    int starting_count = app->destroy_count;
214    Boolean isPopup = False;
215
216    /* invalidate focus trace cache for this display */
217    _XtGetPerDisplay(XtDisplayOfObject(widget))->pdi.traceDepth = 0;
218
219    parent = widget->core.parent;
220
221    if (parent && XtIsWidget(parent) && parent->core.num_popups) {
222        Cardinal i;
223
224        for (i = 0; i < parent->core.num_popups; i++) {
225            if (parent->core.popup_list[i] == widget) {
226                isPopup = True;
227                break;
228            }
229        }
230    }
231
232    if (!isPopup && parent && XtIsComposite(parent)) {
233        XtWidgetProc delete_child;
234
235        LOCK_PROCESS;
236        delete_child =
237            ((CompositeWidgetClass) parent->core.widget_class)->composite_class.
238            delete_child;
239        UNLOCK_PROCESS;
240        if (XtIsRectObj(widget)) {
241            XtUnmanageChild(widget);
242        }
243        if (delete_child == NULL) {
244            String param;
245            Cardinal num_params = 1;
246
247            LOCK_PROCESS;
248            param = parent->core.widget_class->core_class.class_name;
249            UNLOCK_PROCESS;
250            XtAppWarningMsg(XtWidgetToApplicationContext(widget),
251                            "invalidProcedure", "deleteChild",
252                            XtCXtToolkitError,
253                            "null delete_child procedure for class %s in XtDestroy",
254                            &param, &num_params);
255        }
256        else {
257            (*delete_child) (widget);
258        }
259    }
260
261    /* widget is freed in Phase2Destroy, so retrieve window now.
262     * Shells destroy their own windows, to prevent window leaks in
263     * popups; this test is practical only when XtIsShell() is cheap.
264     */
265    if (XtIsShell(widget) || !XtIsWidget(widget)) {
266        window = 0;
267    }
268    else {
269        display = XtDisplay(widget);
270
271        window = widget->core.window;
272    }
273
274    Recursive(widget, Phase2Callbacks);
275    if (app->destroy_count > starting_count) {
276        int i = starting_count;
277
278        while (i < app->destroy_count) {
279
280            DestroyRec *dr = app->destroy_list + i;
281
282            if (IsDescendant(dr->widget, widget)) {
283                Widget descendant = dr->widget;
284                register int j;
285
286                app->destroy_count--;
287                for (j = app->destroy_count - i; --j >= 0; dr++)
288                    *dr = *(dr + 1);
289                XtPhase2Destroy(descendant);
290            }
291            else
292                i++;
293        }
294    }
295
296    app->in_phase2_destroy = widget;
297    Recursive(widget, Phase2Destroy);
298    app->in_phase2_destroy = outerInPhase2Destroy;
299
300    if (isPopup) {
301        Cardinal i;
302
303        for (i = 0; i < parent->core.num_popups; i++)
304            if (parent->core.popup_list[i] == widget) {
305                parent->core.num_popups--;
306                while (i < parent->core.num_popups) {
307                    parent->core.popup_list[i] = parent->core.popup_list[i + 1];
308                    i++;
309                }
310                break;
311            }
312    }
313
314    /* %%% the following parent test hides a more serious problem,
315       but it avoids breaking those who depended on the old bug
316       until we have time to fix it properly. */
317
318    if (window && (parent == NULL || !parent->core.being_destroyed))
319        XDestroyWindow(display, window);
320}                               /* XtPhase2Destroy */
321
322void
323_XtDoPhase2Destroy(XtAppContext app, int dispatch_level)
324{
325    /* Phase 2 must occur in fifo order.  List is not necessarily
326     * contiguous in dispatch_level.
327     */
328
329    int i = 0;
330
331    while (i < app->destroy_count) {
332
333        /* XtPhase2Destroy can result in calls to XtDestroyWidget,
334         * and these could cause app->destroy_list to be reallocated.
335         */
336
337        DestroyRec *dr = app->destroy_list + i;
338
339        if (dr->dispatch_level >= dispatch_level) {
340            Widget w = dr->widget;
341            register int j;
342
343            app->destroy_count--;
344            for (j = app->destroy_count - i; --j >= 0; dr++)
345                *dr = *(dr + 1);
346            XtPhase2Destroy(w);
347        }
348        else
349            i++;
350    }
351}
352
353void
354XtDestroyWidget(Widget widget)
355{
356    XtAppContext app;
357    DestroyRec *dr;
358
359    app = XtWidgetToApplicationContext(widget);
360    LOCK_APP(app);
361    if (widget->core.being_destroyed) {
362        UNLOCK_APP(app);
363        return;
364    }
365    Recursive(widget, Phase1Destroy);
366
367    if (app->in_phase2_destroy && IsDescendant(widget, app->in_phase2_destroy)) {
368        XtPhase2Destroy(widget);
369        UNLOCK_APP(app);
370        return;
371    }
372
373    if (app->destroy_count == app->destroy_list_size) {
374        app->destroy_list_size += 10;
375        app->destroy_list = (DestroyRec *)
376            XtRealloc((char *) app->destroy_list,
377                      (Cardinal) (sizeof(DestroyRec) *
378                                  (size_t) app->destroy_list_size)
379            );
380    }
381    dr = app->destroy_list + app->destroy_count++;
382    dr->dispatch_level = app->dispatch_level;
383    dr->widget = widget;
384
385    if (app->dispatch_level > 1) {
386        int i;
387
388        for (i = app->destroy_count - 1; i;) {
389            /* this handles only one case of nesting difficulties */
390            dr = app->destroy_list + (--i);
391            if (dr->dispatch_level < app->dispatch_level &&
392                IsDescendant(dr->widget, widget)) {
393                DestroyRec *dr2 = app->destroy_list + (app->destroy_count - 1);
394
395                dr2->dispatch_level = dr->dispatch_level;
396                break;
397            }
398        }
399    }
400
401    if (_XtSafeToDestroy(app)) {
402        app->dispatch_level = 1;        /* avoid nested _XtDoPhase2Destroy */
403        _XtDoPhase2Destroy(app, 0);
404        app->dispatch_level = 0;
405    }
406    UNLOCK_APP(app);
407
408}                               /* XtDestroyWidget */
409