1 /*******************************************************************************
2  * Copyright (c) 2000, 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  *     Nikolay Botev - bug 240651
14  *******************************************************************************/
15 
16 package org.eclipse.ui.part;
17 
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.swt.widgets.Composite;
20 import org.eclipse.ui.IEditorInput;
21 import org.eclipse.ui.IEditorPart;
22 import org.eclipse.ui.IEditorReference;
23 import org.eclipse.ui.IEditorSite;
24 import org.eclipse.ui.IPartListener2;
25 import org.eclipse.ui.IWorkbenchPart;
26 import org.eclipse.ui.IWorkbenchPartReference;
27 import org.eclipse.ui.PartInitException;
28 import org.eclipse.ui.internal.e4.compatibility.E4Util;
29 
30 /**
31  * A AbstractMultiEditor is a composite of editors.
32  *
33  * This class is intended to be subclassed.
34  *
35  * @since 3.5
36  */
37 public abstract class AbstractMultiEditor extends EditorPart {
38 
39 	private int activeEditorIndex;
40 
41 	private IEditorPart innerEditors[];
42 
43 	private IPartListener2 propagationListener;
44 
45 	/**
46 	 * Constructs an editor to contain other editors.
47 	 */
AbstractMultiEditor()48 	public AbstractMultiEditor() {
49 		super();
50 	}
51 
52 	/**
53 	 * Handles a property change notification from a nested editor. The default
54 	 * implementation simply forwards the change to listeners on this multi editor
55 	 * by calling <code>firePropertyChange</code> with the same property id. For
56 	 * example, if the dirty state of a nested editor changes (property id
57 	 * <code>ISaveablePart.PROP_DIRTY</code>), this method handles it by firing a
58 	 * property change event for <code>ISaveablePart.PROP_DIRTY</code> to property
59 	 * listeners on this multi editor.
60 	 * <p>
61 	 * Subclasses may extend or reimplement this method.
62 	 * </p>
63 	 *
64 	 * @param propId the id of the property that changed
65 	 * @since 3.6
66 	 */
handlePropertyChange(int propId)67 	protected void handlePropertyChange(int propId) {
68 		firePropertyChange(propId);
69 	}
70 
71 	/*
72 	 * @see IEditorPart#doSave(IProgressMonitor)
73 	 */
74 	@Override
doSave(IProgressMonitor monitor)75 	public void doSave(IProgressMonitor monitor) {
76 		for (IEditorPart e : innerEditors) {
77 			e.doSave(monitor);
78 		}
79 	}
80 
81 	/*
82 	 * @see IEditorPart#doSaveAs()
83 	 */
84 	@Override
doSaveAs()85 	public void doSaveAs() {
86 		// no-op
87 	}
88 
89 	/*
90 	 * @see IEditorPart#init(IEditorSite, IEditorInput)
91 	 */
92 	@Override
init(IEditorSite site, IEditorInput input)93 	public void init(IEditorSite site, IEditorInput input) throws PartInitException {
94 		init(site, (MultiEditorInput) input);
95 	}
96 
97 	/**
98 	 * @param site  the editor site
99 	 * @param input the editor input
100 	 * @exception PartInitException if this editor was not initialized successfully
101 	 *
102 	 * @see IEditorPart#init(IEditorSite, IEditorInput)
103 	 */
init(IEditorSite site, MultiEditorInput input)104 	public void init(IEditorSite site, MultiEditorInput input) throws PartInitException {
105 		setInput(input);
106 		setSite(site);
107 		setPartName(input.getName());
108 		setTitleToolTip(input.getToolTipText());
109 		setupEvents();
110 	}
111 
112 	/*
113 	 * @see IEditorPart#isDirty()
114 	 */
115 	@Override
isDirty()116 	public boolean isDirty() {
117 		for (IEditorPart e : innerEditors) {
118 			if (e.isDirty()) {
119 				return true;
120 			}
121 		}
122 		return false;
123 	}
124 
125 	/*
126 	 * @see IEditorPart#isSaveAsAllowed()
127 	 */
128 	@Override
isSaveAsAllowed()129 	public boolean isSaveAsAllowed() {
130 		return false;
131 	}
132 
133 	/*
134 	 * @see IWorkbenchPart#setFocus()
135 	 */
136 	@Override
setFocus()137 	public void setFocus() {
138 		innerEditors[activeEditorIndex].setFocus();
139 	}
140 
141 	/**
142 	 * Returns the active inner editor.
143 	 *
144 	 * @return the active editor
145 	 */
getActiveEditor()146 	public final IEditorPart getActiveEditor() {
147 		return innerEditors[activeEditorIndex];
148 	}
149 
150 	/**
151 	 * Returns an array with all inner editors.
152 	 *
153 	 * @return the inner editors
154 	 */
getInnerEditors()155 	public final IEditorPart[] getInnerEditors() {
156 		return innerEditors;
157 	}
158 
159 	/**
160 	 * Set the inner editors.
161 	 *
162 	 * Should not be called by clients.
163 	 *
164 	 * @param children the inner editors of this multi editor
165 	 * @noreference This method is not intended to be referenced by clients.
166 	 */
setChildren(IEditorPart[] children)167 	public final void setChildren(IEditorPart[] children) {
168 		innerEditors = children;
169 		activeEditorIndex = 0;
170 
171 		for (IEditorPart child : children) {
172 			child.addPropertyListener((source, propId) -> handlePropertyChange(propId));
173 		}
174 
175 		innerEditorsCreated();
176 	}
177 
178 	/**
179 	 * Called as soon as the inner editors have been created and are available.
180 	 */
innerEditorsCreated()181 	protected abstract void innerEditorsCreated();
182 
183 	/**
184 	 * Activates the given nested editor.
185 	 *
186 	 * @param part the nested editor
187 	 * @since 3.0
188 	 */
activateEditor(IEditorPart part)189 	public void activateEditor(IEditorPart part) {
190 		activeEditorIndex = getIndex(part);
191 		// IEditorPart e = getActiveEditor();
192 		// EditorSite innerSite = (EditorSite) e.getEditorSite();
193 		// ((WorkbenchPage) innerSite.getPage()).requestActivation(e);
194 		E4Util.unsupported("We need to request an activation of this part"); //$NON-NLS-1$
195 	}
196 
197 	/**
198 	 * Returns the index of the given nested editor.
199 	 *
200 	 * @return the index of the nested editor
201 	 * @since 3.0
202 	 */
getIndex(IEditorPart editor)203 	protected int getIndex(IEditorPart editor) {
204 		for (int i = 0; i < innerEditors.length; i++) {
205 			if (innerEditors[i] == editor) {
206 				return i;
207 			}
208 		}
209 		return -1;
210 	}
211 
212 	/**
213 	 * Set up the AbstractMultiEditor to propagate events like partClosed().
214 	 *
215 	 * @since 3.2
216 	 */
setupEvents()217 	private void setupEvents() {
218 		propagationListener = new IPartListener2() {
219 			@Override
220 			public void partActivated(IWorkbenchPartReference partRef) {
221 			}
222 
223 			@Override
224 			public void partBroughtToTop(IWorkbenchPartReference partRef) {
225 			}
226 
227 			@Override
228 			public void partClosed(IWorkbenchPartReference partRef) {
229 				IWorkbenchPart part = partRef.getPart(false);
230 				if (part == AbstractMultiEditor.this && innerEditors != null) {
231 					// propagate the events
232 					E4Util.unsupported("propogate the events needed"); //$NON-NLS-1$
233 				}
234 			}
235 
236 			@Override
237 			public void partDeactivated(IWorkbenchPartReference partRef) {
238 			}
239 
240 			@Override
241 			public void partOpened(IWorkbenchPartReference partRef) {
242 				IWorkbenchPart part = partRef.getPart(false);
243 				if (part == AbstractMultiEditor.this && innerEditors != null) {
244 					// PartService partService = ((WorkbenchPage) getSite()
245 					// .getPage()).getPartService();
246 					// for (int i = 0; i < innerEditors.length; i++) {
247 					// IEditorPart editor = innerEditors[i];
248 					// IWorkbenchPartReference innerRef = ((PartSite) editor
249 					// .getSite()).getPartReference();
250 					// partService.firePartOpened(innerRef);
251 					// }
252 					// propagate the events
253 					E4Util.unsupported("propogate the events needed"); //$NON-NLS-1$
254 				}
255 			}
256 
257 			@Override
258 			public void partHidden(IWorkbenchPartReference partRef) {
259 			}
260 
261 			@Override
262 			public void partVisible(IWorkbenchPartReference partRef) {
263 			}
264 
265 			@Override
266 			public void partInputChanged(IWorkbenchPartReference partRef) {
267 			}
268 		};
269 		getSite().getPage().addPartListener(propagationListener);
270 	}
271 
272 	/**
273 	 * Release the added listener.
274 	 *
275 	 * @since 3.2
276 	 */
277 	@Override
dispose()278 	public void dispose() {
279 		getSite().getPage().removePartListener(propagationListener);
280 		super.dispose();
281 	}
282 
283 	/**
284 	 * This method is called after createPartControl has been executed and should
285 	 * return the container for the given inner editor.
286 	 *
287 	 * @param innerEditorReference a reference to the inner editor that is being
288 	 *                             created.
289 	 * @return the container in which the inner editor's pane and part controls are
290 	 *         to be created.
291 	 */
getInnerEditorContainer(IEditorReference innerEditorReference)292 	public abstract Composite getInnerEditorContainer(IEditorReference innerEditorReference);
293 
294 }
295