blob: 1308904db8cfd3091a678b6814f0993e3ea48a2b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 429728, 441150, 444410, 472654
* Simon Scholz <simon.scholz@vogella.com> - Bug 429729, 506306
* Mike Leneweit <mike-le@web.de> - Bug 444410
*******************************************************************************/
package org.eclipse.e4.ui.workbench.renderers.swt;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.ui.css.core.engine.CSSEngine;
import org.eclipse.e4.ui.css.core.resources.IResourcesRegistry;
import org.eclipse.e4.ui.css.swt.dom.WidgetElement;
import org.eclipse.e4.ui.css.swt.resources.ResourceByDefinitionKey;
import org.eclipse.e4.ui.css.swt.resources.SWTResourcesRegistry;
import org.eclipse.e4.ui.di.UIEventTopic;
import org.eclipse.e4.ui.internal.workbench.E4Workbench;
import org.eclipse.e4.ui.internal.workbench.PartServiceSaveHandler;
import org.eclipse.e4.ui.internal.workbench.renderers.swt.SWTRenderersMessages;
import org.eclipse.e4.ui.internal.workbench.swt.CSSConstants;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.MContext;
import org.eclipse.e4.ui.model.application.ui.MElementContainer;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.MUILabel;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.basic.MPartStack;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimBar;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.services.IServiceConstants;
import org.eclipse.e4.ui.services.IStylingEngine;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.e4.ui.workbench.modeling.ISaveHandler;
import org.eclipse.e4.ui.workbench.modeling.IWindowCloseHandler;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.util.Geometry;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Resource;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
import org.osgi.service.event.Event;
/**
* Default SWT renderer responsible for an instance of MWindow. See
* {@link WorkbenchRendererFactory}
*/
public class WBWRenderer extends SWTPartRenderer {
private static String ShellMinimizedTag = "shellMinimized"; //$NON-NLS-1$
private static String ShellMaximizedTag = "shellMaximized"; //$NON-NLS-1$
private class WindowSizeUpdateJob implements Runnable {
public List<MWindow> windowsToUpdate = new ArrayList<>();
@Override
public void run() {
boundsJob = null;
while (!windowsToUpdate.isEmpty()) {
MWindow window = windowsToUpdate.remove(0);
Shell shell = (Shell) window.getWidget();
if (shell == null || shell.isDisposed()) {
continue;
}
shell.setBounds(window.getX(), window.getY(),
window.getWidth(), window.getHeight());
}
}
}
WindowSizeUpdateJob boundsJob;
boolean ignoreSizeChanges = false;
@Inject
Logger logger;
@Inject
private IEclipseContext context;
@Inject
private IPresentationEngine engine;
private ThemeDefinitionChangedHandler themeDefinitionChanged;
@Inject
private EModelService modelService;
@Inject
private Display display;
@Inject
@Optional
private void subscribeTopicSelectedElementChanged(
@UIEventTopic(UIEvents.ElementContainer.TOPIC_SELECTEDELEMENT) Event event) {
// Ensure that this event is for a MApplication
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MApplication)) {
return;
}
MWindow win = (MWindow) event.getProperty(UIEvents.EventTags.NEW_VALUE);
if ((win == null) || !win.getTags().contains("topLevel")) { //$NON-NLS-1$
return;
}
win.setToBeRendered(true);
if (!(win.getRenderer() == WBWRenderer.this)) {
return;
}
Shell shell = (Shell) win.getWidget();
if (shell.getMinimized()) {
shell.setMinimized(false);
}
shell.setActive();
shell.moveAbove(null);
}
@Inject
@Optional
private void subscribeTopicLabelChanged(@UIEventTopic(UIEvents.UILabel.TOPIC_ALL) Event event) {
Object objElement = event.getProperty(UIEvents.EventTags.ELEMENT);
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MWindow)) {
return;
}
// Is this listener interested ?
MWindow windowModel = (MWindow) objElement;
if (windowModel.getRenderer() != WBWRenderer.this) {
return;
}
// No widget == nothing to update
Shell theShell = (Shell) windowModel.getWidget();
if (theShell == null) {
return;
}
String attName = (String) event.getProperty(UIEvents.EventTags.ATTNAME);
if (UIEvents.UILabel.LABEL.equals(attName) || UIEvents.UILabel.LOCALIZED_LABEL.equals(attName)) {
String newTitle = (String) event.getProperty(UIEvents.EventTags.NEW_VALUE);
theShell.setText(newTitle);
} else if (UIEvents.UILabel.ICONURI.equals(attName)) {
theShell.setImage(getImage(windowModel));
// child windows may take their shell icon from the parent
for (MWindow child : windowModel.getWindows()) {
if (child.getRenderer() instanceof WBWRenderer) {
((WBWRenderer) child.getRenderer()).handleParentChange(child);
}
}
} else if (UIEvents.UILabel.TOOLTIP.equals(attName) || UIEvents.UILabel.LOCALIZED_TOOLTIP.equals(attName)) {
String newTTip = (String) event.getProperty(UIEvents.EventTags.NEW_VALUE);
theShell.setToolTipText(newTTip);
}
}
@Inject
@Optional
private void subscribeTopicWindowChanged(@UIEventTopic(UIEvents.Window.TOPIC_ALL) Event event) {
if (ignoreSizeChanges) {
return;
}
// Ensure that this event is for a MMenuItem
Object objElement = event.getProperty(UIEvents.EventTags.ELEMENT);
if (!(objElement instanceof MWindow)) {
return;
}
// Is this listener interested ?
MWindow windowModel = (MWindow) objElement;
if (windowModel.getRenderer() != WBWRenderer.this) {
return;
}
// No widget == nothing to update
Shell theShell = (Shell) windowModel.getWidget();
if (theShell == null) {
return;
}
String attName = (String) event.getProperty(UIEvents.EventTags.ATTNAME);
if (UIEvents.Window.X.equals(attName) || UIEvents.Window.Y.equals(attName)
|| UIEvents.Window.WIDTH.equals(attName) || UIEvents.Window.HEIGHT.equals(attName)) {
if (boundsJob == null) {
boundsJob = new WindowSizeUpdateJob();
boundsJob.windowsToUpdate.add(windowModel);
theShell.getDisplay().asyncExec(boundsJob);
} else {
if (!boundsJob.windowsToUpdate.contains(windowModel)) {
boundsJob.windowsToUpdate.add(windowModel);
}
}
}
}
@Inject
@Optional
private void subscribeTopicVisibleChanged(@UIEventTopic(UIEvents.UIElement.TOPIC_VISIBLE) Event event) {
// Ensure that this event is for a MMenuItem
Object objElement = event.getProperty(UIEvents.EventTags.ELEMENT);
if (!(objElement instanceof MWindow)) {
return;
}
// Is this listener interested ?
MWindow windowModel = (MWindow) objElement;
if (windowModel.getRenderer() != WBWRenderer.this) {
return;
}
// No widget == nothing to update
Shell theShell = (Shell) windowModel.getWidget();
if (theShell == null) {
return;
}
String attName = (String) event.getProperty(UIEvents.EventTags.ATTNAME);
if (UIEvents.UIElement.VISIBLE.equals(attName)) {
boolean isVisible = (Boolean) event.getProperty(UIEvents.EventTags.NEW_VALUE);
theShell.setVisible(isVisible);
}
}
@Inject
@Optional
private void subscribeThemeDefinitionChanged(
@UIEventTopic(UIEvents.UILifeCycle.THEME_DEFINITION_CHANGED) Event event) {
themeDefinitionChanged.handleEvent(event);
}
@Inject
@Optional
private void subscribeTopicDetachedChanged(@UIEventTopic(UIEvents.Window.TOPIC_WINDOWS) Event event) {
/*
* Handle any changes required for parent changes on detached windows.
* This isn't quite straightforward as we don't see TOPIC_PARENT events
* parent changes are only described as ADD and REMOVE on the
* Window.TOPIC_WINDOWS and Application.TOPIC_CHILDREN.
*/
if (!(event.getProperty(UIEvents.EventTags.ELEMENT) instanceof MWindow)) {
return;
}
if (UIEvents.isREMOVE(event)) {
for (Object removed : UIEvents.asIterable(event, UIEvents.EventTags.OLD_VALUE)) {
if (removed instanceof MWindow && ((MWindow) removed).getRenderer() instanceof WBWRenderer) {
MWindow window = (MWindow) removed;
((WBWRenderer) window.getRenderer()).handleParentChange(window);
}
}
} else if (UIEvents.isADD(event)) {
for (Object removed : UIEvents.asIterable(event, UIEvents.EventTags.NEW_VALUE)) {
if (removed instanceof MWindow && ((MWindow) removed).getRenderer() instanceof WBWRenderer) {
MWindow window = (MWindow) removed;
((WBWRenderer) window.getRenderer()).handleParentChange(window);
}
}
}
}
/**
* Update this child window with any values that may have been obtained from
* the parent.
*
* @param child
* the child window (may now be orphaned)
*/
private void handleParentChange(MWindow child) {
// No widget == nothing to update
Shell theShell = (Shell) child.getWidget();
if (theShell == null) {
return;
}
// Detached windows may take their shell icon from the parent window
theShell.setImage(getImage(child));
}
/**
* Closes the provided detached window.
*
* @param window
* the detached window to close
* @return <code>true</code> if the window should be closed,
* <code>false</code> otherwise
*/
private boolean closeDetachedWindow(MWindow window) {
EPartService partService = window.getContext().get(EPartService.class);
List<MPart> parts = modelService.findElements(window, null, MPart.class, null);
// this saves one part at a time, not ideal but better than not saving
// at all
for (MPart part : parts) {
if (!partService.savePart(part, true)) {
// user cancelled the operation, return false
return false;
}
}
// hide every part individually, following 3.x behaviour
for (MPart part : parts) {
partService.hidePart(part);
}
return true;
}
@PostConstruct
protected void init() {
themeDefinitionChanged = new ThemeDefinitionChangedHandler();
}
@Override
public Object createWidget(MUIElement element, Object parent) {
final Widget newWidget;
if (!(element instanceof MWindow) || (parent != null && !(parent instanceof Control))) {
return null;
}
MWindow wbwModel = (MWindow) element;
MApplication appModel = wbwModel.getContext().get(MApplication.class);
Boolean rtlMode = (Boolean) appModel.getTransientData().get(E4Workbench.RTL_MODE);
int rtlStyle = (rtlMode != null && rtlMode.booleanValue()) ? SWT.RIGHT_TO_LEFT : 0;
Shell parentShell = parent == null ? null : ((Control) parent).getShell();
final Shell wbwShell;
int styleOverride = getStyleOverride(wbwModel) | rtlStyle;
if (parentShell == null) {
int style = styleOverride == -1 ? SWT.SHELL_TRIM | rtlStyle : styleOverride;
wbwShell = new Shell(display, style);
wbwModel.getTags().add("topLevel"); //$NON-NLS-1$
} else {
int style = SWT.TITLE | SWT.RESIZE | SWT.MAX | SWT.CLOSE | rtlStyle;
style = styleOverride == -1 ? style : styleOverride;
if (wbwModel.getTags().contains(IPresentationEngine.WINDOW_TOP_LEVEL)) {
wbwShell = new Shell(display, style);
} else {
wbwShell = new Shell(parentShell, style);
}
// Prevent ESC from closing the DW
wbwShell.addTraverseListener(e -> {
if (e.detail == SWT.TRAVERSE_ESCAPE) {
e.doit = false;
}
});
}
wbwShell.setBackgroundMode(SWT.INHERIT_DEFAULT);
Rectangle modelBounds = wbwShell.getBounds();
if (wbwModel instanceof EObject) {
EObject wbw = (EObject) wbwModel;
EClass wbwclass = wbw.eClass();
// use eIsSet rather than embed sentinel values
if (wbw.eIsSet(wbwclass.getEStructuralFeature("x"))) { //$NON-NLS-1$
modelBounds.x = wbwModel.getX();
}
if (wbw.eIsSet(wbwclass.getEStructuralFeature("y"))) { //$NON-NLS-1$
modelBounds.y = wbwModel.getY();
}
if (wbw.eIsSet(wbwclass.getEStructuralFeature("height"))) { //$NON-NLS-1$
modelBounds.height = wbwModel.getHeight();
}
if (wbw.eIsSet(wbwclass.getEStructuralFeature("width"))) { //$NON-NLS-1$
modelBounds.width = wbwModel.getWidth();
}
}
// Force the shell onto the display if it would be invisible otherwise
Display display = Display.getCurrent();
Monitor closestMonitor = getClosestMonitor(display, Geometry.centerPoint(modelBounds));
Rectangle displayBounds = closestMonitor.getClientArea();
if (!modelBounds.intersects(displayBounds)) {
Geometry.moveInside(modelBounds, displayBounds);
}
wbwShell.setBounds(modelBounds);
setCSSInfo(wbwModel, wbwShell);
// set up context
IEclipseContext localContext = getContext(wbwModel);
// We need to retrieve specific CSS properties for our layout.
CSSEngineHelper helper = new CSSEngineHelper(localContext, wbwShell);
TrimmedPartLayout tl = new TrimmedPartLayout(wbwShell);
tl.gutterTop = helper.getMarginTop(0);
tl.gutterBottom = helper.getMarginBottom(0);
tl.gutterLeft = helper.getMarginLeft(0);
tl.gutterRight = helper.getMarginRight(0);
wbwShell.setLayout(tl);
newWidget = wbwShell;
bindWidget(element, newWidget);
// Add the shell into the WBW's context
localContext.set(Shell.class, wbwShell);
localContext.set(E4Workbench.LOCAL_ACTIVE_SHELL, wbwShell);
setCloseHandler(wbwModel);
localContext.set(IShellProvider.class, () -> wbwShell);
final PartServiceSaveHandler saveHandler = new PartServiceSaveHandler() {
@Override
public Save promptToSave(MPart dirtyPart) {
Shell shell = (Shell) context.get(IServiceConstants.ACTIVE_SHELL);
Object[] elements = promptForSave(shell, Collections.singleton(dirtyPart));
if (elements == null) {
return Save.CANCEL;
}
return elements.length == 0 ? Save.NO : Save.YES;
}
@Override
public Save[] promptToSave(Collection<MPart> dirtyParts) {
List<MPart> parts = new ArrayList<>(dirtyParts);
Shell shell = (Shell) context
.get(IServiceConstants.ACTIVE_SHELL);
Save[] response = new Save[dirtyParts.size()];
Object[] elements = promptForSave(shell, parts);
if (elements == null) {
Arrays.fill(response, Save.CANCEL);
} else {
Arrays.fill(response, Save.NO);
for (int i = 0; i < elements.length; i++) {
response[parts.indexOf(elements[i])] = Save.YES;
}
}
return response;
}
};
saveHandler.logger = logger;
localContext.set(ISaveHandler.class, saveHandler);
if (wbwModel.getLabel() != null) {
wbwShell.setText(wbwModel.getLocalizedLabel());
}
Image windowImage = getImage(wbwModel);
if (windowImage != null) {
wbwShell.setImage(windowImage);
} else {
// TODO: This should be added to the model, see bug 308494
// it allows for a range of icon sizes that the platform gets to
// choose from
wbwShell.setImages(Window.getDefaultImages());
}
return newWidget;
}
/**
* TODO: Create an API for this method and delete this version. See bug
* 491273
*
* Returns the monitor whose client area contains the given point. If no
* monitor contains the point, returns the monitor that is closest to the
* point. If this is ever made public, it should be moved into a separate
* utility class.
*
* @param toSearch
* point to find (display coordinates)
* @param toFind
* point to find (display coordinates)
* @return the montor closest to the given point
*/
private static Monitor getClosestMonitor(Display toSearch, Point toFind) {
int closest = Integer.MAX_VALUE;
Monitor[] monitors = toSearch.getMonitors();
Monitor result = monitors[0];
for (int idx = 0; idx < monitors.length; idx++) {
Monitor current = monitors[idx];
Rectangle clientArea = current.getClientArea();
if (clientArea.contains(toFind)) {
return current;
}
int distance = Geometry.distanceSquared(Geometry.centerPoint(clientArea), toFind);
if (distance < closest) {
closest = distance;
result = current;
}
}
return result;
}
private void setCloseHandler(MWindow window) {
IEclipseContext context = window.getContext();
// no direct model parent, must be a detached window
if (window.getParent() == null) {
context.set(IWindowCloseHandler.class,
window1 -> closeDetachedWindow(window1));
} else {
context.set(IWindowCloseHandler.class,
window1 -> {
EPartService partService = window1.getContext().get(EPartService.class);
return partService.saveAll(true);
});
}
}
@Override
public Image getImage(MUILabel element) {
Image image = super.getImage(element);
if (image == null && element instanceof MWindow) {
// Detached windows should take their image from parent window
MWindow parent = modelService.getTopLevelWindowFor((MWindow) element);
if (parent != null && parent != element) {
image = getImage(parent);
}
}
return image;
}
@Override
public void hookControllerLogic(MUIElement me) {
super.hookControllerLogic(me);
Widget widget = (Widget) me.getWidget();
if (widget instanceof Shell && me instanceof MWindow) {
final Shell shell = (Shell) widget;
final MWindow w = (MWindow) me;
shell.addControlListener(new ControlListener() {
@Override
public void controlResized(ControlEvent e) {
// Don't store the maximized size in the model
if (shell.getMaximized()) {
return;
}
try {
ignoreSizeChanges = true;
w.setWidth(shell.getSize().x);
w.setHeight(shell.getSize().y);
} finally {
ignoreSizeChanges = false;
}
}
@Override
public void controlMoved(ControlEvent e) {
// Don't store the maximized size in the model
if (shell.getMaximized()) {
return;
}
try {
ignoreSizeChanges = true;
w.setX(shell.getLocation().x);
w.setY(shell.getLocation().y);
} finally {
ignoreSizeChanges = false;
}
}
});
shell.addShellListener(new ShellAdapter() {
@Override
public void shellClosed(ShellEvent e) {
// override the shell close event
e.doit = false;
MWindow window = (MWindow) e.widget.getData(OWNING_ME);
IWindowCloseHandler closeHandler = window.getContext().get(IWindowCloseHandler.class);
// if there's no handler or the handler permits the close
// request, clean-up as necessary
if (closeHandler == null || closeHandler.close(window)) {
cleanUp(window);
}
}
});
shell.addListener(SWT.Activate, event -> {
MUIElement parentME = w.getParent();
if (parentME instanceof MApplication) {
MApplication app = (MApplication) parentME;
app.setSelectedElement(w);
w.getContext().activate();
} else if (parentME == null) {
parentME = modelService.getContainer(w);
if (parentME instanceof MContext) {
w.getContext().activate();
}
}
updateNonFocusState(SWT.Activate, w);
});
shell.addListener(SWT.Deactivate, event -> updateNonFocusState(SWT.Deactivate, w));
}
}
private void updateNonFocusState(int event, MWindow win) {
MPerspective perspective = modelService.getActivePerspective(win);
if (perspective == null) {
return;
}
List<MPartStack> stacks = modelService.findElements(perspective, null,
MPartStack.class, Arrays.asList(CSSConstants.CSS_ACTIVE_CLASS));
if (stacks.isEmpty()) {
return;
}
MPartStack stack = stacks.get(0);
int tagsCount = stack.getTags().size();
boolean hasNonFocusTag = stack.getTags().contains(
CSSConstants.CSS_NO_FOCUS_CLASS);
if (event == SWT.Activate && hasNonFocusTag) {
stack.getTags().remove(CSSConstants.CSS_NO_FOCUS_CLASS);
} else if (event == SWT.Deactivate && !hasNonFocusTag) {
stack.getTags().add(CSSConstants.CSS_NO_FOCUS_CLASS);
}
if (tagsCount != stack.getTags().size()) {
setCSSInfo(stack, stack.getWidget());
}
}
private void cleanUp(MWindow window) {
MUIElement parent = modelService.getContainer(window);
if (parent instanceof MApplication) {
MApplication application = (MApplication) parent;
List<MWindow> children = application.getChildren();
if (children.size() > 1) {
// not the last window, destroy and remove
window.setToBeRendered(false);
children.remove(window);
} else {
// last window, just destroy without changing the model
engine.removeGui(window);
}
} else if (parent != null) {
window.setToBeRendered(false);
// this is a detached window, check for parts
if (modelService.findElements(window, null, MPart.class, null)
.isEmpty()) {
// if no parts, remove it
if (parent instanceof MWindow) {
((MWindow) parent).getWindows().remove(window);
} else if (parent instanceof MPerspective) {
((MPerspective) parent).getWindows().remove(window);
}
}
}
}
/*
* Processing the contents of a Workbench window has to take into account
* that there may be trim elements contained in its child list. Since the
*/
@Override
public void processContents(MElementContainer<MUIElement> me) {
if (!(((MUIElement) me) instanceof MWindow)) {
return;
}
MWindow wbwModel = (MWindow) ((MUIElement) me);
super.processContents(me);
// Populate the main menu
IPresentationEngine renderer = context.get(IPresentationEngine.class);
if (wbwModel.getMainMenu() != null) {
renderer.createGui(wbwModel.getMainMenu(), me.getWidget(), null);
Shell shell = (Shell) me.getWidget();
shell.setMenuBar((Menu) wbwModel.getMainMenu().getWidget());
}
// create Detached Windows
for (MWindow dw : wbwModel.getWindows()) {
renderer.createGui(dw, me.getWidget(), wbwModel.getContext());
}
// Populate the trim (if any)
if (wbwModel instanceof MTrimmedWindow) {
Shell shell = (Shell) wbwModel.getWidget();
MTrimmedWindow tWindow = (MTrimmedWindow) wbwModel;
List<MTrimBar> trimBars = new ArrayList<>(
tWindow.getTrimBars());
for (MTrimBar trimBar : trimBars) {
renderer.createGui(trimBar, shell, wbwModel.getContext());
// bug 387161: hack around that createGui(e, parent, context)
// does not reparent the element widget to the
// limbo shell wheb visible=false
if (!trimBar.isVisible()) {
trimBar.setVisible(true);
trimBar.setVisible(false);
}
}
}
}
@Override
public Object getUIContainer(MUIElement element) {
MUIElement parent = element.getParent();
if (parent == null) {
// might be a detached window
parent = modelService.getContainer(element);
return parent == null ? null : parent.getWidget();
}
Composite shellComp = (Composite) element.getParent().getWidget();
TrimmedPartLayout tpl = (TrimmedPartLayout) shellComp.getLayout();
return tpl.clientArea;
}
@Override
public void postProcess(MUIElement shellME) {
super.postProcess(shellME);
Shell shell = (Shell) shellME.getWidget();
// Capture the max/min state
final MUIElement disposeME = shellME;
shell.addDisposeListener(e -> {
Shell shell1 = (Shell) e.widget;
if (disposeME != null) {
disposeME.getTags().remove(ShellMinimizedTag);
disposeME.getTags().remove(ShellMaximizedTag);
if (shell1.getMinimized()) {
disposeME.getTags().add(ShellMinimizedTag);
}
if (shell1.getMaximized()) {
disposeME.getTags().add(ShellMaximizedTag);
}
}
});
// Apply the correct shell state
if (shellME.getTags().contains(ShellMaximizedTag)) {
shell.setMaximized(true);
} else if (shellME.getTags().contains(ShellMinimizedTag)) {
shell.setMinimized(true);
}
shell.layout(true);
forceLayout(shell);
if (shellME.isVisible()) {
shell.open();
} else {
shell.setVisible(false);
}
}
private Object[] promptForSave(Shell parentShell,
Collection<MPart> saveableParts) {
SaveablePartPromptDialog dialog = new SaveablePartPromptDialog(
parentShell, saveableParts);
if (dialog.open() == Window.CANCEL) {
return null;
}
return dialog.getCheckedElements();
}
private void applyDialogStyles(Control control) {
IStylingEngine engine = (IStylingEngine) context
.get(IStylingEngine.SERVICE_NAME);
if (engine != null) {
Shell shell = control.getShell();
if (shell.getBackgroundMode() == SWT.INHERIT_NONE) {
shell.setBackgroundMode(SWT.INHERIT_DEFAULT);
}
engine.style(shell);
}
}
class SaveablePartPromptDialog extends Dialog {
private Collection<MPart> collection;
private CheckboxTableViewer tableViewer;
private Object[] checkedElements = new Object[0];
SaveablePartPromptDialog(Shell shell, Collection<MPart> collection) {
super(shell);
this.collection = collection;
}
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText(SWTRenderersMessages.choosePartsToSaveTitle);
}
@Override
protected Control createDialogArea(Composite parent) {
parent = (Composite) super.createDialogArea(parent);
Label label = new Label(parent, SWT.LEAD);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
label.setText(SWTRenderersMessages.choosePartsToSave);
tableViewer = CheckboxTableViewer.newCheckList(parent, SWT.SINGLE | SWT.BORDER);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
data.heightHint = 250;
data.widthHint = 300;
tableViewer.getControl().setLayoutData(data);
tableViewer.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
return ((MPart) element).getLocalizedLabel();
}
});
tableViewer.setContentProvider(ArrayContentProvider.getInstance());
tableViewer.setInput(collection);
tableViewer.setAllChecked(true);
return parent;
}
@Override
public void create() {
super.create();
applyDialogStyles(getShell());
}
@Override
protected void okPressed() {
checkedElements = tableViewer.getCheckedElements();
super.okPressed();
}
public Object[] getCheckedElements() {
return checkedElements;
}
@Override
protected boolean isResizable() {
return true;
}
}
protected static class ThemeDefinitionChangedHandler {
protected Set<Resource> unusedResources = new HashSet<>();
public void handleEvent(Event event) {
Object element = event.getProperty(IEventBroker.DATA);
if (!(element instanceof MApplication)) {
return;
}
Set<CSSEngine> engines = new HashSet<>();
// In theory we can have multiple engines since API allows it.
// It doesn't hurt to be prepared for such case
for (MWindow window : ((MApplication) element).getChildren()) {
CSSEngine engine = getEngine(window);
if (engine != null) {
engines.add(engine);
}
}
for (CSSEngine engine : engines) {
for (Object resource : removeResources(engine.getResourcesRegistry())) {
if (resource instanceof Resource && !((Resource) resource).isDisposed()) {
unusedResources.add((Resource) resource);
}
}
engine.reapply();
}
}
protected CSSEngine getEngine(MWindow window) {
return WidgetElement.getEngine((Widget) window.getWidget());
}
protected List<Object> removeResources(IResourcesRegistry registry) {
if (registry instanceof SWTResourcesRegistry) {
return ((SWTResourcesRegistry) registry)
.removeResourcesByKeyTypeAndType(
ResourceByDefinitionKey.class, Font.class,
Color.class);
}
return Collections.emptyList();
}
public void dispose() {
for (Resource resource : unusedResources) {
if (!resource.isDisposed()) {
resource.dispose();
}
}
unusedResources.clear();
}
}
private void forceLayout(Shell shell) {
int i = 0;
while(shell.isLayoutDeferred()) {
shell.setLayoutDeferred(false);
i++;
}
while(i > 0) {
shell.setLayoutDeferred(true);
i--;
}
}
}