blob: f3c0a9a7607e4e3a7ef47d8ea52ae4ea599210ec [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 Obeo
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.diagram.tools.api.management;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.sirius.business.api.helper.ViewpointUtil;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.business.api.session.SessionManager;
import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter;
import org.eclipse.sirius.diagram.DDiagram;
import org.eclipse.sirius.diagram.DSemanticDiagram;
import org.eclipse.sirius.diagram.business.api.componentization.DiagramComponentizationManager;
import org.eclipse.sirius.diagram.business.api.helper.SiriusDiagramUtil;
import org.eclipse.sirius.diagram.business.api.helper.layers.LayerService;
import org.eclipse.sirius.diagram.business.api.query.DDiagramQuery;
import org.eclipse.sirius.diagram.description.DiagramDescription;
import org.eclipse.sirius.diagram.description.Layer;
import org.eclipse.sirius.diagram.description.tool.ToolGroup;
import org.eclipse.sirius.diagram.description.tool.ToolGroupExtension;
import org.eclipse.sirius.diagram.description.tool.ToolSection;
import org.eclipse.sirius.diagram.tools.internal.management.ToolFilterDescriptionListenersManager;
import org.eclipse.sirius.diagram.tools.internal.management.ToolManagementRegistry;
import org.eclipse.sirius.viewpoint.ToolGroupInstance;
import org.eclipse.sirius.viewpoint.ToolInstance;
import org.eclipse.sirius.viewpoint.ToolSectionInstance;
import org.eclipse.sirius.viewpoint.UIState;
import org.eclipse.sirius.viewpoint.ViewpointFactory;
import org.eclipse.sirius.viewpoint.description.Environment;
import org.eclipse.sirius.viewpoint.description.tool.AbstractToolDescription;
import org.eclipse.sirius.viewpoint.description.tool.ToolEntry;
import org.eclipse.sirius.viewpoint.description.tool.ToolFilterDescription;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* This component maintains the {@link ToolInstance} available in the {@link UIState} of a given {@link DDiagram}
* regarding layers activated, tool filters status.
*
* @author <a href="mailto:pierre.guilet@obeo.fr">Pierre Guilet</a>
*
*/
public class ToolManagement {
/**
* Tool filters installed for the specific diagram managed by this instance.
*/
private Set<ToolFilter> filters = new LinkedHashSet<ToolFilter>();
/**
* The currently activated layers.
*/
private List<Layer> activatedLayersOfSelectedViewpoints;
/**
* The currently deactivated layers.
*/
private List<Layer> deactivatedLayersAndAllLayersOfDeselectedViewpoints;
/**
* The diagram from which this component handles available tools.
*/
private DDiagram dDiagram;
/**
* The GMF diagram from which this component handles available tools.
*/
/**
* Listener manager to update toolS wHen their filter condition changes.
*/
private ToolFilterDescriptionListenersManager listenersManager;
/**
* A map of {@link DDiagram} to their registered {@link ToolChangeListener}.
*/
private Set<ToolChangeListener> toolChangeListeners;
/**
* Initialize this tool management from given diagram.
*
* @param dDiagram
* the diagram from which tool will be handled by this component.
*/
public ToolManagement(DDiagram dDiagram) {
this.dDiagram = dDiagram;
listenersManager = new ToolFilterDescriptionListenersManager();
listenersManager.init(dDiagram);
toolChangeListeners = new HashSet<>();
}
/**
* Add a listener to be aware of any tool change}.
*
* @param toolChangeListener
* the {@link ToolChangeListener} to add.
*/
public void addToolChangeListener(ToolChangeListener toolChangeListener) {
toolChangeListeners.add(toolChangeListener);
}
/**
* Returns the listeners interested by tool changes.
*
* @return the listeners interested by tool changes.
*/
public Set<ToolChangeListener> getToolListeners() {
return toolChangeListeners;
}
/**
* Remove the given {@link ToolChangeListener}.
*
* @param toolChangeListener
* the {@link ToolChangeListener} to remove.
* @return true if the given {@link ToolChangeListener} has been removed. False otherwise.
*/
public boolean removeToolChangeListener(ToolChangeListener toolChangeListener) {
return toolChangeListeners.remove(toolChangeListener);
}
/**
* Returns the currently activated layers of selected viewpoints.
*
* @return the currently activated layers of selected viewpoints.
*/
public List<Layer> getActivatedLayersOfSelectedViewpoints() {
return activatedLayersOfSelectedViewpoints;
}
/**
* Notifies diagram listeners that related tools have been updated because a reload of the VSM has been done.
*
*/
public void notifyToolChangeAfterVSMReload() {
Set<ToolChangeListener> toolListeners = getToolListeners();
for (ToolChangeListener toolChangeListener : toolListeners) {
toolChangeListener.notifyToolChange(ToolChangeListener.ChangeKind.VSM_UPDATE);
}
}
/**
* Notifies diagram listeners that related tools have been updated.
*
*/
public void notifyToolChange() {
Set<ToolChangeListener> toolListeners = getToolListeners();
for (ToolChangeListener toolChangeListener : toolListeners) {
toolChangeListener.notifyToolChange(ToolChangeListener.ChangeKind.OTHER_UPDATE);
}
}
/**
* Returns the currently deactivated layers and all layers of deselected viewpoints.
*
* @return the currently deactivated layers and all layers of deselected viewpoints.
*/
public List<Layer> getDeactivatedLayersAndAllLayersOfDeselectedViewpoints() {
return deactivatedLayersAndAllLayersOfDeselectedViewpoints;
}
/**
* This method update the tools available for the current diagram state and returns those. Tools are filtered by
* filters that are installed specifically for the diagram managed by this instance, i.e. those added via
* {@link #addToolFilter(ToolFilter)}, and by filters that are installed globally via the toolManagement extension
* point.
*
* @param updateFilters
* true if filters should be updated.
*
* @return the updated tools associated to the attached diagram.
*/
public List<ToolSectionInstance> updateTools(boolean updateFilters) {
Session session = null;
if (updateFilters) {
clearFilters();
}
if (dDiagram instanceof DSemanticDiagram) {
session = SessionManager.INSTANCE.getSession(((DSemanticDiagram) dDiagram).getTarget());
}
return computeAvailableTools(session, updateFilters);
}
/**
* Clear diagrams filters.
*
*/
public void clearFilters() {
listenersManager.init(dDiagram);
Collection<ToolFilter> filtersCopy = Lists.newArrayList(filters);
for (final ToolFilter filter : filtersCopy) {
if (filter instanceof ToolFilterFromDescription) {
removeToolFilter(filter);
}
}
}
/**
* Remove a filter to hide a tool.
*
* @param toolFilter
* the tool filter to remove.
*/
public void removeToolFilter(ToolFilter toolFilter) {
filters.remove(toolFilter);
}
/**
* Compute all tools available for the attached {@link DDiagram}.
*
* @param session
* used to get viewpoint information and to execute update command.
* @param updateFilters
*
* @return all tools available for the attached {@link DDiagram}.
*/
private List<ToolSectionInstance> computeAvailableTools(Session session, boolean updateFilters) {
dDiagram.getUiState().getToolSections().clear();
ToolSectionInstance defaultToolSection = ViewpointFactory.eINSTANCE.createToolSectionInstance();
dDiagram.getUiState().getToolSections().add(defaultToolSection);
defaultToolSection.setId(ToolConstants.DEFAULT_SECTION_ID);
// add gef tools manually for the moment.
addToolById("selectionTool", defaultToolSection); //$NON-NLS-1$
addToolById("zoomInTool", defaultToolSection); //$NON-NLS-1$
addToolById("zoomOutTool", defaultToolSection); //$NON-NLS-1$
addToolById("noteTool", defaultToolSection); //$NON-NLS-1$
addToolById("textTool", defaultToolSection); //$NON-NLS-1$
addToolById(ToolConstants.TOOL_NOTEATTACHMENT, defaultToolSection);
// add generic tool
addToolById(ToolConstants.TOOL_GENERIC_CONNECTION_CREATION, defaultToolSection);
// add VSM tools
List<ToolEntry> defaultTools = getDefaultTools(TransactionUtil.getEditingDomain(dDiagram).getResourceSet());
for (ToolEntry toolEntry : defaultTools) {
addNewTool(defaultToolSection, toolEntry, true);
}
addVSMTools(session, updateFilters);
return dDiagram.getUiState().getToolSections();
}
private void addToolById(String id, ToolSectionInstance defaultToolSection) {
ToolInstance tool = ViewpointFactory.eINSTANCE.createToolInstance();
tool.setId(id);
tool.setVisible(true);
tool.setEnabled(true);
defaultToolSection.getTools().add(tool);
}
/**
* Adds a new {@link ToolInstance} referencing the given {@link ToolEntry} to the list of available tools for
* attached diagram.
*
* @param toolSectionParent
* the {@link ToolSectionInstance} where to put the new {@link ToolInstance}.
*
* @param toolEntry
* the {@link ToolEntry} associated to the new {@link ToolInstance}
* @param useNameAsId
* true if the name of the {@link ToolEntry} should be used as id. False if the id should be based on its
* URI.
* @return Returns the newly created ToolInstance.
*/
private ToolInstance addNewTool(ToolSectionInstance toolSectionParent, ToolEntry toolEntry, boolean useNameAsId) {
ToolInstance newToolInstance = createNewTool(toolEntry, useNameAsId);
toolSectionParent.getTools().add(newToolInstance);
return newToolInstance;
}
/**
* Adds a new {@link ToolInstance} referencing the given {@link ToolEntry} to the list of available tools for
* attached diagram.
*
* @param toolSectionParent
* the {@link ToolSectionInstance} where to put the new {@link ToolInstance}.
*
* @param toolEntry
* the {@link ToolEntry} associated to the new {@link ToolInstance}
* @param useNameAsId
* true if the name of the {@link ToolEntry} should be used as id. False if the id should be based on its
* URI.
* @return Returns the newly created ToolInstance.
*/
private ToolInstance createNewTool(ToolEntry toolEntry, boolean useNameAsId) {
ToolInstance newToolInstance = ViewpointFactory.eINSTANCE.createToolInstance();
if (useNameAsId) {
newToolInstance.setId(toolEntry.getName());
} else {
newToolInstance.setId(getId(toolEntry));
}
newToolInstance.setVisible(true);
newToolInstance.setEnabled(true);
newToolInstance.setToolEntry(toolEntry);
if (toolEntry instanceof AbstractToolDescription && isFiltered((AbstractToolDescription) toolEntry)) {
newToolInstance.setFiltered(true);
}
return newToolInstance;
}
/**
* Add a tool filter.
*
* @param toolFilter
* the tool filter to add.
*/
public void addToolFilter(ToolFilter toolFilter) {
filters.add(toolFilter);
}
/**
* Returns the id of a {@link ToolEntry}.
*
* @param entry
* the entry from which the id should be returned.
* @return the id of a {@link ToolEntry}.
*/
public static String getId(final EObject entry) {
return EcoreUtil.getURI(entry).toString();
}
/**
* Adds VSM tools for a diagram that has no layer.
*
* @param session
* The {@session} containing the {@link DDiagram}.
* @param updateFilters
*/
private void addVSMToolsForDiagramWithoutLayer(Session session, boolean updateFilters) {
// Update the filters
DiagramDescription diagramDescription = dDiagram.getDescription();
if (updateFilters) {
updateFilters(session, diagramDescription.getAllTools());
}
// Owned tools
ToolSection toolSection = diagramDescription.getToolSection();
if (toolSection != null) {
initToolSection(toolSection);
}
}
private void initToolSection(ToolSection toolSection) {
ToolSectionInstance newToolSectionInstance = ViewpointFactory.eINSTANCE.createToolSectionInstance();
newToolSectionInstance.setId(ToolManagement.getId(toolSection));
newToolSectionInstance.setSection(newToolSectionInstance);
dDiagram.getUiState().getToolSections().add(newToolSectionInstance);
newToolSectionInstance.getTools().addAll(toolSection.getOwnedTools().stream().map(ts -> {
return createNewTool(ts, false);
}).collect(Collectors.toSet()));
newToolSectionInstance.getTools().addAll(toolSection.getReusedTools().stream().map(ts -> {
return createNewTool(ts, false);
}).collect(Collectors.toSet()));
EList<ToolGroupExtension> groupExtensions = toolSection.getGroupExtensions();
for (ToolGroupExtension toolGroupExtension : groupExtensions) {
ToolGroup group = toolGroupExtension.getGroup();
ToolGroupInstance newToolGroupInstance = ViewpointFactory.eINSTANCE.createToolGroupInstance();
newToolGroupInstance.setId(group.getName());
newToolGroupInstance.setGroup(newToolGroupInstance);
newToolGroupInstance.getTools().addAll(group.getTools().stream().map(ts -> {
return createNewTool(ts, false);
}).collect(Collectors.toSet()));
newToolSectionInstance.getTools().add(newToolGroupInstance);
}
EList<ToolSection> subSections = toolSection.getSubSections();
for (ToolSection subToolSection : subSections) {
initToolSection(subToolSection);
}
}
/**
* Adds VSM tools for a diagram that has layer(s), at least a default one.
*
* @param session
* The {@session} containing the {@link DDiagram}.
* @param updateFilters
*/
private void addVSMToolsForDiagramWithLayer(Session session, boolean updateFilters) {
Map<String, List<ToolInstance>> idToTool = new HashMap<>();
// Copy of all layers of selected viewpoints
HashSet<Layer> layersInActivatedViewpoints = new HashSet<Layer>(new DiagramComponentizationManager().getAllLayers(session.getSelectedViewpoints(false), dDiagram.getDescription()));
// Copy of diagram activated layers (in all Viewpoints: activated or
// not)
Set<Layer> activatedLayers = new HashSet<Layer>(new DDiagramQuery(dDiagram).getAllActivatedLayers());
// Get the list of activated layers (of selected viewpoints)
activatedLayersOfSelectedViewpoints = Lists.newArrayList(Sets.intersection(layersInActivatedViewpoints, activatedLayers));
// Get the list of deactivated layers (deactivated layers of selected
// viewpoints and all layers of deselected viewpoints)
deactivatedLayersAndAllLayersOfDeselectedViewpoints = Lists.newArrayList(Sets.symmetricDifference(layersInActivatedViewpoints, activatedLayers));
Map<String, ToolSectionInstance> idToToolSection = new HashMap<>();
// Update the filters
for (final ToolSection section : new DiagramComponentizationManager().getRootPaletteSections(session.getSelectedViewpoints(false), dDiagram.getDescription())) {
if (updateFilters) {
updateFilters(session, new DiagramComponentizationManager().getAllToolEntries(session.getSelectedViewpoints(false), section));
}
ToolSectionInstance newToolSectionInstance = ViewpointFactory.eINSTANCE.createToolSectionInstance();
String id = ToolManagement.getId(section);
newToolSectionInstance.setId(id);
idToToolSection.put(id, newToolSectionInstance);
newToolSectionInstance.setSection(section);
dDiagram.getUiState().getToolSections().add(newToolSectionInstance);
List<ToolEntry> allToolEntries = new DiagramComponentizationManager().getAllToolEntries(session.getSelectedViewpoints(false), section);
for (ToolEntry toolEntry : allToolEntries) {
if (toolEntry instanceof ToolGroup) {
ToolGroupInstance newToolGroupInstance = ViewpointFactory.eINSTANCE.createToolGroupInstance();
newToolGroupInstance.setId(getId(toolEntry));
newToolGroupInstance.setGroup(newToolGroupInstance);
newToolGroupInstance.setToolEntry(toolEntry);
// get extension tools for a group
newToolGroupInstance.getTools().addAll(new DiagramComponentizationManager().getTools(session.getSelectedViewpoints(false), (ToolGroup) toolEntry).stream().map(ts -> {
ToolInstance tool = createNewTool(ts, false);
return tool;
}).collect(Collectors.toCollection(LinkedHashSet::new)));
newToolSectionInstance.getTools().add(newToolGroupInstance);
} else {
ToolInstance newToolInstance = createNewTool(toolEntry, false);
newToolSectionInstance.getTools().add(newToolInstance);
List<ToolInstance> tools = idToTool.get(newToolInstance.getId());
if (tools == null) {
tools = new ArrayList<>();
idToTool.put(newToolInstance.getId(), tools);
}
tools.add(newToolInstance);
}
}
}
for (final Layer layer : Lists.newArrayList(deactivatedLayersAndAllLayersOfDeselectedViewpoints)) {
EList<ToolSection> toolSections = layer.getToolSections();
for (ToolSection toolSection : toolSections) {
ToolSectionInstance toolSectionInstance = idToToolSection.get(ToolManagement.getId(toolSection));
if (toolSectionInstance != null) {
toolSectionInstance.setVisible(false);
}
}
}
}
/**
* Adds VSM tools to the given list of {@link ToolInstance}.
*
* @param session
* The {@session} containing the {@link DDiagram}.
* @param updateFilters
*/
private void addVSMTools(Session session, boolean updateFilters) {
if (LayerService.withoutLayersMode(dDiagram.getDescription())) {
addVSMToolsForDiagramWithoutLayer(session, updateFilters);
} else {
addVSMToolsForDiagramWithLayer(session, updateFilters);
}
}
private Iterator<ToolFilter> getAllToolFilters() {
return Iterators.concat(filters.iterator(), ToolManagementRegistry.getInstance().getProvidedToolFilters().iterator());
}
private boolean isFiltered(AbstractToolDescription toolDescription) {
for (Iterator<ToolFilter> it = getAllToolFilters(); it.hasNext(); /**/) {
if (it.next().filter(dDiagram, toolDescription)) {
return true;
}
}
return false;
}
/**
* Update tool filters. If the session is null, nothing will be done.
*
* @param session
* the session
* @param toolEntries
* the list of entry of tools to add.
*/
private void updateFilters(final Session session, final List<? extends ToolEntry> toolEntries) {
if (session != null) {
final IInterpreter interpreter = session.getInterpreter();
if (interpreter != null) {
for (final ToolEntry toolEntry : toolEntries) {
if (toolEntry instanceof AbstractToolDescription) {
/* create filters from description */
for (final ToolFilterDescription filterDescription : ((AbstractToolDescription) toolEntry).getFilters()) {
ToolFilter filter = new ToolFilterFromDescription(interpreter, filterDescription);
filters.add(filter);
}
listenersManager.addListenersForFilters(interpreter, ((AbstractToolDescription) toolEntry).getFilters());
} else if (toolEntry instanceof ToolGroup) {
updateFilters(session, new DiagramComponentizationManager().getTools(session.getSelectedViewpoints(false), (ToolGroup) toolEntry));
}
}
}
}
}
private List<ToolEntry> getDefaultTools(final ResourceSet context) {
final Resource coreEnvResource = context.getResource(URI.createURI(ViewpointUtil.VIEWPOINT_ENVIRONMENT_RESOURCE_URI, true), true);
final Environment coreEnv = (Environment) coreEnvResource.getContents().get(0);
final Resource diagramEnvResource = context.getResource(URI.createURI(SiriusDiagramUtil.DIAGRAM_ENVIRONMENT_RESOURCE_URI, true), true);
final Environment diagramEnv = (Environment) diagramEnvResource.getContents().get(0);
List<ToolEntry> defaultTools = new ArrayList<>();
defaultTools.addAll(coreEnv.getDefaultTools());
defaultTools.addAll(diagramEnv.getDefaultTools());
return defaultTools;
}
}