Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: b575055e05dfb4b46ba7bff2764df5d8f6a531ce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal;

import java.util.Iterator;
import java.util.LinkedList;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;

/**
 * Manages a pool of shells. This can be used instead of creating and destroying
 * shells. By reusing shells, they will never be disposed until the pool goes away.
 * This is useful in situations where client code may have cached pointers to the
 * shells to use as a parent for dialogs. It also works around bug 86226 (SWT menus
 * cannot be reparented).
 *
 * @since 3.1
 */
public class ShellPool {

    private int flags;

    /**
     * Parent shell (or null if none)
     */
    private Shell parentShell;

    private LinkedList availableShells = new LinkedList();

    private static final String CLOSE_LISTENER = "close listener"; //$NON-NLS-1$

    private boolean isDisposed = false;

    private DisposeListener disposeListener = new DisposeListener() {
        @Override
		public void widgetDisposed(DisposeEvent e) {
            WorkbenchPlugin.log(new RuntimeException("Widget disposed too early!")); //$NON-NLS-1$
        }
    };

    private ShellListener closeListener = new ShellAdapter() {

        @Override
		public void shellClosed(ShellEvent e) {
                if (isDisposed) {
                    return;
                }

                if (e.doit) {
                    Shell s = (Shell)e.widget;
                    ShellListener l = (ShellListener)s.getData(CLOSE_LISTENER);

                    if (l != null) {
                        s.setData(CLOSE_LISTENER, null);
                        l.shellClosed(e);

                        // The shell can 'cancel' the close by setting
                        // the 'doit' to false...if so, do nothing
                        if (e.doit) {
						for (Control control : s.getChildren()) {
	                            control.dispose();
	                        }
	                        availableShells.add(s);
	                        s.setVisible(false);
                        }
                        else {
                        	// Restore the listener
                            s.setData(CLOSE_LISTENER, l);
                        }
                    }
                }
                e.doit = false;
         }
    };

    /**
     * Creates a shell pool that allocates shells that are children of the
     * given parent and are created with the given flags.
     *
     * @param parentShell parent shell (may be null, indicating that this pool creates
     * top-level shells)
     * @param childFlags flags for all child shells
     */
    public ShellPool(Shell parentShell, int childFlags) {
        this.parentShell = parentShell;
        this.flags = childFlags;
    }

    /**
     * Returns a new shell. The shell must not be disposed directly, but it may be closed.
     * Once the shell is closed, it will be returned to the shell pool. Note: callers must
     * remove all listeners from the shell before closing it.
     */
    public Shell allocateShell(ShellListener closeListener) {
        Shell result;
        if (!availableShells.isEmpty()) {
            result = (Shell)availableShells.removeFirst();
        } else {
            result = new Shell(parentShell, flags);
            result.addShellListener(this.closeListener);
            result.addDisposeListener(disposeListener);
        }

        result.setData(CLOSE_LISTENER, closeListener);
        return result;
    }

    /**
     * Disposes this pool. Any unused shells in the pool are disposed immediately,
     * and any shells in use will be disposed once they are closed.
     *
     * @since 3.1
     */
    public void dispose() {
        for (Iterator iter = availableShells.iterator(); iter.hasNext();) {
            Shell next = (Shell) iter.next();
            next.removeDisposeListener(disposeListener);

            next.dispose();
        }

        availableShells.clear();
        isDisposed = true;
    }
}

Back to the top