blob: 565e80d7aac448921f95ffd46c3f07a3fcaf716f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.ui.tools.internal.viewpoint;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.stream.Collectors;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.sirius.business.api.componentization.ViewpointRegistry;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.business.api.session.SessionListener;
import org.eclipse.sirius.business.internal.metamodel.description.spec.ViewpointSpec;
import org.eclipse.sirius.common.tools.api.util.EqualityHelper;
import org.eclipse.sirius.ui.business.api.viewpoint.ViewpointSelection;
import org.eclipse.sirius.ui.tools.internal.wizards.pages.IViewpointStateListener;
import org.eclipse.sirius.ui.tools.internal.wizards.pages.ViewpointStateChangeEvent;
import org.eclipse.sirius.viewpoint.description.Viewpoint;
import org.eclipse.sirius.viewpoint.provider.Messages;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.PlatformUI;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* Component providing dynamic activation/deactivation behavior for the graphic component
* {@link ViewpointsSelectionGraphicalHandler} and session synchronization. I.e any viewpoint check state change from
* graphical component and triggered by user will be feed through to the session. And in the other way any change of
* viewpoint activation/deactivation or presence change in the session will be feed through to the graphic component to
* update viewpoints available and their check state status.
*
* @author <a href="mailto:pierre.guilet@obeo.fr">Pierre Guilet</a>
*
*/
public class DynamicViewpointsSelectionComponent {
/**
* The session from which viewpoint will be available for activation/deactivation.
*/
private Session session;
/**
* Contains all viewpoints available in the current runtime registry.
*/
private Collection<Viewpoint> availableViewpoints;
/**
* Listen to any change regarding viewpoints or semantic models available to the session and refresh the list of
* viewpoint available in the graphic component with their check status if needed.
*/
private SessionChangeListener sessionChangelistener;
/**
* The graphic component containing checkbox for each viewpoint activable/deactivable and a browser allowing to see
* a description for each.
*/
private ViewpointsSelectionGraphicalHandler viewpointsSelectionGraphicalHandler;
/**
* Listener updating the session accordingly to changes triggered by user when checking /unchecking viewpoint from
* graphical component {@link ViewpointsSelectionGraphicalHandler}.
*/
private DefaultViewpointStateListener viewpointStateListener;
/**
* The last viewpoints activation status for the current editor session.
*/
private Set<Viewpoint> lastSelectedViewpoints;
/**
* Listener delegating to the {@link DefaultViewpointStateListener} listener when a check state change occurs (I.e a
* viewpoint has been checked or unchecked).
*/
private ICheckStateListener checkStateListener;
/**
* Check if selected viewpoint is missing dependencies to be activated. If so it show an error message above the
* viewpoint description browser.
*/
private ISelectionChangedListener selectionListener;
/**
* This listener allows viewpoint activation/deactivation by a double click.
*/
private IDoubleClickListener doubleClickListener;
/**
* Listener updating the session accordingly to changes triggered by user when checking /unchecking viewpoint from
* graphical component {@link ViewpointsSelectionGraphicalHandler}.
*
* @author <a href="mailto:pierre.guilet@obeo.fr">Pierre Guilet</a>
*
*/
private class DefaultViewpointStateListener implements IViewpointStateListener {
@Override
public void viewpointStateChange(ViewpointStateChangeEvent viewpointStateChangeEvent) {
final SortedMap<Viewpoint, Boolean> originalViewpointsMap = Maps.newTreeMap(new ViewpointRegistry.ViewpointComparator());
for (final Viewpoint viewpoint : availableViewpoints) {
boolean selected = false;
for (Viewpoint selectedViewpoint : lastSelectedViewpoints) {
if (EqualityHelper.areEquals(selectedViewpoint, viewpoint)) {
selected = true;
break;
}
}
originalViewpointsMap.put(viewpoint, Boolean.valueOf(selected));
}
SortedMap<Viewpoint, Boolean> newViewpointToSelectionStateMap = Maps.newTreeMap(new ViewpointRegistry.ViewpointComparator());
newViewpointToSelectionStateMap.putAll(originalViewpointsMap);
newViewpointToSelectionStateMap.put(viewpointStateChangeEvent.getViewpoint(), viewpointStateChangeEvent.shouldBeActivated());
// we also deselect viewpoint that will be missing a dependency if such element exists.
Set<Viewpoint> viewpointsMissingDependencies = ViewpointHelper.getViewpointsMissingDependencies(
newViewpointToSelectionStateMap.keySet().stream().filter(viewpoint -> newViewpointToSelectionStateMap.get(viewpoint)).collect(Collectors.toSet()));
for (Viewpoint viewpointsMissingDependency : viewpointsMissingDependencies) {
newViewpointToSelectionStateMap.put(viewpointsMissingDependency, false);
}
ViewpointHelper.applyNewViewpointSelection(originalViewpointsMap, newViewpointToSelectionStateMap, session, true);
// If deselection has been cancelled we update the last selected viewpoints set to reflect this deselection
// in the cache.
boolean viewpointSelected = session.getSelectedViewpoints(true).stream().anyMatch(viewpoint -> EqualityHelper.areEquals(viewpointStateChangeEvent.getViewpoint(), viewpoint));
newViewpointToSelectionStateMap.put(viewpointStateChangeEvent.getViewpoint(), viewpointSelected);
for (Viewpoint viewpointsMissingDependency : viewpointsMissingDependencies) {
viewpointSelected = session.getSelectedViewpoints(true).stream().anyMatch(viewpoint -> EqualityHelper.areEquals(viewpointsMissingDependency, viewpoint));
newViewpointToSelectionStateMap.put(viewpointsMissingDependency, viewpointSelected);
}
lastSelectedViewpoints = new HashSet<Viewpoint>(
newViewpointToSelectionStateMap.keySet().stream().filter(viewpoint -> newViewpointToSelectionStateMap.get(viewpoint)).collect(Collectors.toSet()));
viewpointsSelectionGraphicalHandler.getViewer().setCheckedElements(lastSelectedViewpoints.toArray(new Object[0]));
viewpointsSelectionGraphicalHandler.getViewer().setSelection(new StructuredSelection(viewpointStateChangeEvent.getViewpoint()));
updateGrayedOutElement();
viewpointsSelectionGraphicalHandler.getViewer().refresh();
}
}
/**
* Create an instance of a graphic component allowing to activate/deactivate viewpoint from a session.
*
* @param theSession
* the session from which viewpoint will be available for activation/deactivation.
*/
public DynamicViewpointsSelectionComponent(final Session theSession) {
this.session = theSession;
viewpointsSelectionGraphicalHandler = new ViewpointsSelectionGraphicalHandler();
availableViewpoints = viewpointsSelectionGraphicalHandler.getAvailableViewpoints(theSession);
viewpointStateListener = new DefaultViewpointStateListener();
lastSelectedViewpoints = new HashSet<Viewpoint>();
}
private void updateGrayedOutElement() {
for (Viewpoint viewpoint : availableViewpoints) {
String missingDependencyErrorMessage = getMissingDependencyErrorMessage(viewpoint, lastSelectedViewpoints);
if (missingDependencyErrorMessage != null) {
viewpointsSelectionGraphicalHandler.getViewer().setGrayed(viewpoint, true);
} else {
viewpointsSelectionGraphicalHandler.getViewer().setGrayed(viewpoint, false);
}
}
}
/**
* Listen to any change regarding viewpoints or semantic models available to the session and refresh the list of
* viewpoint available in the graphic component with their check status if needed. Viewpoint can be made unavailable
* for the session when closing the project containing it and model semantic models can be removed for example.
*
* @author <a href="mailto:pierre.guilet@obeo.fr">Pierre Guilet</a>
*
*/
private final class SessionChangeListener implements SessionListener {
@Override
public void notify(int changeKind) {
switch (changeKind) {
case SessionListener.SELECTED_VIEWS_CHANGE_KIND:
case SessionListener.VSM_UPDATED:
case SessionListener.SEMANTIC_CHANGE:
availableViewpoints = viewpointsSelectionGraphicalHandler.getAvailableViewpoints(session);
setViewpoints(getAvailableViewpoints());
break;
default:
break;
}
}
}
/**
* Reinitialize the viewer containing all the checkbox for available viewpoints for the session with their check
* status.
*
* @param theAvailableViewpoints
* all the viewpoints available to the session that need to be made visible for activation/deactivation
* to the user in the graphic component.
*/
private void setViewpoints(Collection<Viewpoint> theAvailableViewpoints) {
PlatformUI.getWorkbench().getDisplay().syncExec(() -> {
lastSelectedViewpoints.clear();
CheckboxTableViewer viewer = viewpointsSelectionGraphicalHandler.getViewer();
viewer.setInput(theAvailableViewpoints);
// case were activated viewpoint are the one corresponding to the session.
Collection<Viewpoint> selectedViewpoints = session.getSelectedViewpoints(false);
for (int i = 0; i < viewer.getTable().getItemCount(); i++) {
Object object = viewer.getElementAt(i);
if (object instanceof Viewpoint) {
Viewpoint viewerViewpoint = (Viewpoint) object;
for (Viewpoint selectedViewpoint : selectedViewpoints) {
if (EqualityHelper.areEquals(viewerViewpoint, selectedViewpoint)) {
lastSelectedViewpoints.add(viewerViewpoint);
}
}
}
}
if (!lastSelectedViewpoints.isEmpty()) {
// Check all the default viewpoints
viewer.setCheckedElements(lastSelectedViewpoints.toArray(new Object[0]));
// Set the focus on the first one
viewer.setSelection(new StructuredSelection(lastSelectedViewpoints.stream().findFirst().get()));
} else {
viewpointsSelectionGraphicalHandler.getBrowser().setText(""); //$NON-NLS-1$
}
// For all no selected viewpoints we check if they have missing dependencies among activated ones and if
// they do we gray out
// those.
for (Viewpoint viewpoint : availableViewpoints) {
Set<Viewpoint> viewpoints = new HashSet<Viewpoint>(lastSelectedViewpoints);
viewpoints.add(viewpoint);
Set<Viewpoint> viewpointsMissingDependencies = ViewpointHelper.getViewpointsMissingDependencies(viewpoints);
if (viewpointsMissingDependencies.contains(viewpoint)) {
viewer.setGrayed(viewpoint, true);
} else {
viewer.setGrayed(viewpoint, false);
}
}
viewpointsSelectionGraphicalHandler.getRootComposite().layout();
});
}
/**
* Returns all registered viewpoints that define editors for metamodel of loaded session's semantic models.
*
* @return all registered viewpoints that define editors for metamodel of loaded session's semantic models.
*/
public Collection<Viewpoint> getAvailableViewpoints() {
return availableViewpoints;
}
/**
* Create the control allowing to select/deselect viewpoints with checkbox and to see in a browser a description of
* each viewpoint.
*
* @param parent
* the parent composite to attached to this graphic component.
*/
public void createControl(final Composite parent) {
viewpointsSelectionGraphicalHandler.createControl(parent, false);
viewpointsSelectionGraphicalHandler.setBrowserMinWidth(200);
viewpointsSelectionGraphicalHandler.setHeight(304);
checkStateListener = event -> {
String errorMessage = null;
if (event.getElement() instanceof Viewpoint) {
Viewpoint viewpointChecked = (Viewpoint) event.getElement();
viewpointsSelectionGraphicalHandler.getViewer().setSelection(new StructuredSelection(viewpointChecked));
if (!lastSelectedViewpoints.isEmpty()) {
errorMessage = getMissingDependencyErrorMessage(viewpointChecked, lastSelectedViewpoints);
}
if (errorMessage == null) {
viewpointsSelectionGraphicalHandler.clearBrowserErrorMessageText();
if (!viewpointsSelectionGraphicalHandler.getViewer().getGrayed(viewpointChecked)) {
if (event.getChecked()) {
viewpointStateListener.viewpointStateChange(new ViewpointStateChangeEvent(viewpointChecked, true));
} else {
viewpointStateListener.viewpointStateChange(new ViewpointStateChangeEvent(viewpointChecked, false));
}
}
} else {
viewpointsSelectionGraphicalHandler.setBrowserErrorMessageText(errorMessage);
}
}
};
viewpointsSelectionGraphicalHandler.getViewer().addCheckStateListener(checkStateListener);
selectionListener = event -> {
if (event.getSelection() instanceof StructuredSelection) {
StructuredSelection selection = (StructuredSelection) event.getSelection();
if (selection.getFirstElement() instanceof Viewpoint) {
Viewpoint viewpoint = (Viewpoint) selection.getFirstElement();
String errorMessage = getMissingDependencyErrorMessage(viewpoint, lastSelectedViewpoints);
if (errorMessage != null) {
viewpointsSelectionGraphicalHandler.setBrowserErrorMessageText(errorMessage);
} else {
viewpointsSelectionGraphicalHandler.clearBrowserErrorMessageText();
}
}
}
};
viewpointsSelectionGraphicalHandler.getViewer().addSelectionChangedListener(selectionListener);
doubleClickListener = (event) -> {
event.getSelection();
if (event.getSelection() instanceof StructuredSelection) {
StructuredSelection selection = (StructuredSelection) event.getSelection();
if (selection.getFirstElement() instanceof ViewpointSpec) {
ViewpointSpec viewpoint = (ViewpointSpec) selection.getFirstElement();
if (!viewpointsSelectionGraphicalHandler.getViewer().getGrayed(viewpoint)) {
viewpointStateListener.viewpointStateChange(new ViewpointStateChangeEvent(viewpoint, !viewpointsSelectionGraphicalHandler.getViewer().getChecked(viewpoint)));
} else {
viewpointsSelectionGraphicalHandler.getViewer().setChecked(viewpoint, !viewpointsSelectionGraphicalHandler.getViewer().getChecked(viewpoint));
}
}
}
};
viewpointsSelectionGraphicalHandler.getViewer().addDoubleClickListener(doubleClickListener);
setViewpoints(availableViewpoints);
sessionChangelistener = new SessionChangeListener();
session.addListener(sessionChangelistener);
}
/**
* Return an error message referencing all missing dependencies for the given viewpoint or null if no missing
* dependencies exists.
*
* @param viewpoint
* the viewpoint from which we check if it has missing dependencies among activated viewpoints.
* @param selectedViewpoints
* the current activated viewpoints.
* @return an error message referencing all missing dependencies for the given viewpoint or null if no missing
* dependencies exists.
*/
protected String getMissingDependencyErrorMessage(Viewpoint viewpoint, Set<Viewpoint> selectedViewpoints) {
Set<Viewpoint> viewpoints = Sets.newHashSet(selectedViewpoints);
viewpoints.add(viewpoint);
Map<String, Collection<String>> missingDependencies = ViewpointSelection.getMissingDependencies(viewpoints);
if (missingDependencies != null && missingDependencies.get(viewpoint.getName()) != null) {
return MessageFormat.format(Messages.DynamicViewpointsSelectionComponent_missingDependencies_requirements, viewpoint.getName(),
missingDependencies.get(viewpoint.getName()).stream().collect(Collectors.joining(", "))); //$NON-NLS-1$
}
return null;
}
/**
* Dispose any listeners added by this instance.
*/
public void dispose() {
if (session != null && sessionChangelistener != null) {
session.removeListener(sessionChangelistener);
}
if (viewpointsSelectionGraphicalHandler != null) {
if (selectionListener != null) {
viewpointsSelectionGraphicalHandler.getViewer().removeSelectionChangedListener(selectionListener);
}
if (checkStateListener != null) {
viewpointsSelectionGraphicalHandler.getViewer().removeCheckStateListener(checkStateListener);
}
if (doubleClickListener != null) {
viewpointsSelectionGraphicalHandler.getViewer().removeDoubleClickListener(doubleClickListener);
}
}
viewpointStateListener = null;
selectionListener = null;
sessionChangelistener = null;
session = null;
}
}