1 /*******************************************************************************
2  * Copyright (c) 2010, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.e4.ui.internal.workbench.swt;
15 
16 import org.eclipse.core.runtime.ISafeRunnable;
17 import org.eclipse.core.runtime.SafeRunner;
18 import org.eclipse.e4.core.contexts.IEclipseContext;
19 import org.eclipse.e4.ui.bindings.EBindingService;
20 import org.eclipse.e4.ui.internal.workbench.E4Workbench;
21 import org.eclipse.e4.ui.internal.workbench.Policy;
22 import org.eclipse.e4.ui.model.application.MApplication;
23 import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
24 import org.eclipse.e4.ui.services.EContextService;
25 import org.eclipse.swt.SWT;
26 import org.eclipse.swt.widgets.Event;
27 import org.eclipse.swt.widgets.Listener;
28 import org.eclipse.swt.widgets.Shell;
29 
30 /**
31  * An SWT listener for listening for activation events of shells that aren't
32  * associated with an MWindow.
33  */
34 public class ShellActivationListener implements Listener {
35 
36 	/**
37 	 * A string key for use with a shell's keyed data to determine whether
38 	 * activation events of that shell should be ignored by this listener. The
39 	 * retrieved data of this string key must either be <code>null</code> or be
40 	 * of type <code>Boolean</code>.
41 	 */
42 	public static final String DIALOG_IGNORE_KEY = "org.eclipse.e4.ui.ignoreDialog"; //$NON-NLS-1$
43 
44 	/**
45 	 * A string key for use with a shell's keyed data to retrieve the top level
46 	 * eclipse context that the shell is supposed to represent.
47 	 */
48 	private static final String ECLIPSE_CONTEXT_SHELL_CONTEXT = "org.eclipse.e4.ui.shellContext"; //$NON-NLS-1$
49 
50 	private MApplication application;
51 
ShellActivationListener(MApplication application)52 	ShellActivationListener(MApplication application) {
53 		this.application = application;
54 	}
55 
56 	@Override
handleEvent(Event event)57 	public void handleEvent(Event event) {
58 		if (!(event.widget instanceof Shell)) {
59 			return;
60 		}
61 
62 		Shell shell = (Shell) event.widget;
63 		Object obj = shell.getData(AbstractPartRenderer.OWNING_ME);
64 		if (obj instanceof MWindow) {
65 			processWindow(event, shell, (MWindow) obj);
66 			return;
67 		}
68 
69 		obj = shell.getData(DIALOG_IGNORE_KEY);
70 		if (obj instanceof Boolean && ((Boolean) obj).booleanValue()) {
71 			return;
72 		}
73 
74 		switch (event.type) {
75 		case SWT.Activate:
76 			activate(shell);
77 			break;
78 		case SWT.Deactivate:
79 			deactivate(shell);
80 			break;
81 		}
82 	}
83 
processWindow(Event event, Shell shell, MWindow window)84 	private void processWindow(Event event, Shell shell, MWindow window) {
85 		switch (event.type) {
86 		case SWT.Activate:
87 			final IEclipseContext local = window.getContext();
88 			if (Policy.DEBUG_WORKBENCH) {
89 				WorkbenchSWTActivator.trace(Policy.DEBUG_WORKBENCH_FLAG,
90 						"setting mwindow context " + local, null);
91 			}
92 			// record this shell's context
93 			shell.setData(ECLIPSE_CONTEXT_SHELL_CONTEXT, local);
94 
95 			SafeRunner.run(new ISafeRunnable() {
96 				@Override
97 				public void run() throws Exception {
98 					// reconstruct the active chain for this mwindow
99 					local.activateBranch();
100 				}
101 
102 				@Override
103 				public void handleException(Throwable exception) {
104 					WorkbenchSWTActivator.trace("/trace/workbench",
105 							"failed correcting context chain", exception);
106 				}
107 			});
108 			break;
109 		case SWT.Deactivate:
110 			Object context = window.getContext();
111 			if(Policy.DEBUG_WORKBENCH) {
112 				WorkbenchSWTActivator.trace(Policy.DEBUG_WORKBENCH_FLAG,
113 						"setting mwindow context " + context, null);
114 			}
115 			// record this shell's context
116 			shell.setData(ECLIPSE_CONTEXT_SHELL_CONTEXT, context);
117 			break;
118 		}
119 	}
120 
activate(Shell shell)121 	private void activate(Shell shell) {
122 		final IEclipseContext parentContext = application.getContext();
123 		final IEclipseContext shellContext = getShellContext(shell,
124 				parentContext);
125 
126 		SafeRunner.run(new ISafeRunnable() {
127 			@Override
128 			public void run() throws Exception {
129 				// activate this shell
130 				shellContext.activate();
131 			}
132 
133 			@Override
134 			public void handleException(Throwable exception) {
135 				WorkbenchSWTActivator.trace("/trace/workbench",
136 						"failed setting dialog child", exception);
137 			}
138 		});
139 
140 	}
141 
deactivate(Shell shell)142 	private void deactivate(Shell shell) {
143 		// bug 412001. Cannot assume anything about a non-modelled Shell's
144 		// deactivation. It could be:
145 		// * some other application got activated
146 		// * some dialog we cannot see (IE's Find dialog in 412001) get's
147 		// activated
148 		// * another unmodelled shell is about to be activated
149 		// * a modelled shell is about to be activated.
150 		// No matter what, the time to do things is on activation
151 
152 	}
153 
154 	/**
155 	 * Retrieves the eclipse context for the specified shell. If one cannot be
156 	 * found, a child context will be created off of the provided parent
157 	 * context.
158 	 *
159 	 * @param shell
160 	 *            the shell of interest, must not be <code>null</code>
161 	 * @param parentContext
162 	 *            the parent context that the shell's context should be created
163 	 *            off of if it doesn't have one, must not be <code>null</code>
164 	 * @return the shell's eclipse context
165 	 */
getShellContext(final Shell shell, IEclipseContext parentContext)166 	private IEclipseContext getShellContext(final Shell shell,
167 			IEclipseContext parentContext) {
168 		IEclipseContext shellContext = (IEclipseContext) shell
169 				.getData(ECLIPSE_CONTEXT_SHELL_CONTEXT);
170 		if (shellContext != null) {
171 			return shellContext;
172 		}
173 		final IEclipseContext context = parentContext
174 				.createChild(EBindingService.DIALOG_CONTEXT_ID);
175 
176 		context.set(E4Workbench.LOCAL_ACTIVE_SHELL, shell);
177 
178 		// set the context into the widget for future retrieval
179 		shell.setData(ECLIPSE_CONTEXT_SHELL_CONTEXT, context);
180 
181 		EContextService contextService = context.get(EContextService.class);
182 		contextService.activateContext(EBindingService.DIALOG_CONTEXT_ID);
183 
184 		shell.addDisposeListener(e -> {
185 			deactivate(shell);
186 			context.dispose();
187 		});
188 
189 		return context;
190 	}
191 }
192