1 /*******************************************************************************
2  * Copyright (c) 2009, 2016 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  *     Lars Vogel <Lars.Vogel@vogella.com> - Bug 441150, 441120
14  *******************************************************************************/
15 package org.eclipse.e4.ui.workbench.renderers.swt;
16 
17 import javax.inject.Inject;
18 import org.eclipse.core.runtime.Platform;
19 import org.eclipse.e4.core.di.annotations.Optional;
20 import org.eclipse.e4.ui.di.UIEventTopic;
21 import org.eclipse.e4.ui.model.application.ui.MElementContainer;
22 import org.eclipse.e4.ui.model.application.ui.MUIElement;
23 import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainer;
24 import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainerElement;
25 import org.eclipse.e4.ui.workbench.UIEvents;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.graphics.Rectangle;
28 import org.eclipse.swt.widgets.Composite;
29 import org.eclipse.swt.widgets.Layout;
30 import org.osgi.service.event.Event;
31 
32 /**
33  * Default SWT renderer responsible for a MPartSashContainer. See
34  * {@link WorkbenchRendererFactory}
35  */
36 public class SashRenderer extends SWTPartRenderer {
37 
38 	private static final int DEFAULT_WEIGHT = 5000;
39 
40 	private int processedContent = 0;
41 
42 	@SuppressWarnings("unchecked")
43 	@Inject
44 	@Optional
subscribeTopicOrientationChanged(@IEventTopicUIEvents.GenericTile.TOPIC_HORIZONTAL) Event event)45 	private void subscribeTopicOrientationChanged(@UIEventTopic(UIEvents.GenericTile.TOPIC_HORIZONTAL) Event event) {
46 		// Ensure that this event is for a MPartSashContainer
47 		MUIElement element = (MUIElement) event.getProperty(UIEvents.EventTags.ELEMENT);
48 		if (element.getRenderer() != SashRenderer.this) {
49 			return;
50 		}
51 		forceLayout((MElementContainer<MUIElement>) element);
52 	}
53 
54 	@Inject
55 	@Optional
subscribeTopicSashWeightChanged(@IEventTopicUIEvents.UIElement.TOPIC_CONTAINERDATA) Event event)56 	private void subscribeTopicSashWeightChanged(@UIEventTopic(UIEvents.UIElement.TOPIC_CONTAINERDATA) Event event) {
57 		// Ensure that this event is for a child of a MPartSashContainer
58 		MUIElement element = (MUIElement) event.getProperty(UIEvents.EventTags.ELEMENT);
59 		MElementContainer<MUIElement> parent = element.getParent();
60 		if (parent.getRenderer() != SashRenderer.this) {
61 			return;
62 		}
63 		forceLayout(parent);
64 	}
65 
66 	/**
67 	 * @param pscModel
68 	 */
forceLayout(MElementContainer<MUIElement> pscModel)69 	protected void forceLayout(MElementContainer<MUIElement> pscModel) {
70 		if (processedContent != 0) {
71 			return;
72 		}
73 		// layout the containing Composite
74 		while (!(pscModel.getWidget() instanceof Composite)) {
75 			pscModel = pscModel.getParent();
76 		}
77 
78 		Composite s = (Composite) pscModel.getWidget();
79 
80 		// FIXME SWT Win requires a synchronous layout call to update the UI
81 		// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=558392
82 		// once this is fixed, the requestLayout call should be sufficient
83 		if (Platform.getOS().equals(Platform.OS_WIN32)) {
84 			Layout layout = s.getLayout();
85 			if (layout instanceof SashLayout) {
86 				if (((SashLayout) layout).layoutUpdateInProgress) {
87 					return;
88 				}
89 			}
90 			s.layout(true, true);
91 		} else {
92 			s.requestLayout();
93 		}
94 	}
95 
96 	@Override
createWidget(final MUIElement element, Object parent)97 	public Object createWidget(final MUIElement element, Object parent) {
98 		MUIElement elementParent = element.getParent();
99 		if (elementParent == null && element.getCurSharedRef() != null)
100 			elementParent = element.getCurSharedRef();
101 
102 		if (elementParent != null && elementParent.getRenderer() == this) {
103 			Rectangle newRect = new Rectangle(0, 0, 0, 0);
104 
105 			// If my layout's container gets disposed 'unbind' the sash elements
106 			if (parent instanceof Composite) {
107 				((Composite) parent).addDisposeListener(e -> {
108 					element.setWidget(null);
109 					element.setRenderer(null);
110 				});
111 			}
112 			return newRect;
113 		}
114 
115 		// Check to see if this is a new PSC added 'above' the previous 'root'
116 		// sash
117 		Composite sashComposite = null;
118 		MPartSashContainer psc = (MPartSashContainer) element;
119 		for (MPartSashContainerElement psce : psc.getChildren()) {
120 			if (psce instanceof MPartSashContainer && psce.getWidget() instanceof Composite) {
121 				// 'Adopt' the previous root's layout / composite
122 				sashComposite = (Composite) psce.getWidget();
123 				bindWidget(psce, new Rectangle(0, 0, 0, 0));
124 			}
125 		}
126 		// This is a 'root' sash container, create a composite
127 		if (sashComposite == null) {
128 			sashComposite = new Composite((Composite) parent, SWT.NONE);
129 		}
130 		sashComposite.setLayout(new SashLayout(sashComposite, element));
131 
132 		return sashComposite;
133 	}
134 
135 	@Override
childRendered(MElementContainer<MUIElement> parentElement, MUIElement element)136 	public void childRendered(MElementContainer<MUIElement> parentElement, MUIElement element) {
137 		super.childRendered(parentElement, element);
138 
139 		// Ensure that the element's 'containerInfo' is initialized
140 		ensureLayoutWeight(element);
141 		forceLayout(parentElement);
142 	}
143 
144 	@Override
processContents(MElementContainer<MUIElement> container)145 	public void processContents(MElementContainer<MUIElement> container) {
146 		try {
147 			processedContent++;
148 			super.processContents(container);
149 		} finally {
150 			processedContent--;
151 			if (processedContent == 0) {
152 				forceLayout(container);
153 			}
154 		}
155 	}
156 
157 	@Override
hideChild(MElementContainer<MUIElement> parentElement, MUIElement child)158 	public void hideChild(MElementContainer<MUIElement> parentElement, MUIElement child) {
159 		super.hideChild(parentElement, child);
160 
161 		forceLayout(parentElement);
162 	}
163 
164 	@Override
getUIContainer(MUIElement element)165 	public Object getUIContainer(MUIElement element) {
166 		// OK, find the 'root' of the sash container
167 		MUIElement parentElement = element.getParent();
168 		while (parentElement.getRenderer() == this && !(parentElement.getWidget() instanceof Composite)) {
169 			parentElement = parentElement.getParent();
170 		}
171 
172 		if (parentElement.getWidget() instanceof Composite) {
173 			return parentElement.getWidget();
174 		}
175 		return null;
176 	}
177 
178 	/*
179 	 * Container data is used by the SashLayout to determine the size of the control
180 	 */
ensureLayoutWeight(MUIElement element)181 	private static void ensureLayoutWeight(MUIElement element) {
182 		int weight = DEFAULT_WEIGHT;
183 
184 		String info = element.getContainerData();
185 		if (info != null && info.length() > 0) {
186 			try {
187 				int value = Integer.parseInt(info);
188 				weight = value;
189 			} catch (NumberFormatException e) {
190 				// continue to use the default value
191 			}
192 		}
193 		element.setContainerData(Integer.toString(weight));
194 	}
195 }
196