blob: 3c042c7ab4f9065c00e47fd6b0d48cfa90ba9fd4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2017 THALES GLOBAL SERVICES 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.tree.ui.tools.internal.editor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.TreeViewerEditor;
import org.eclipse.jface.viewers.ViewerColumn;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.business.api.session.SessionManager;
import org.eclipse.sirius.common.tools.DslCommonPlugin;
import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.ext.base.Options;
import org.eclipse.sirius.tools.api.command.ICommandFactory;
import org.eclipse.sirius.tools.api.profiler.SiriusTasksKey;
import org.eclipse.sirius.tree.DTree;
import org.eclipse.sirius.tree.DTreeItem;
import org.eclipse.sirius.tree.business.api.command.ITreeCommandFactory;
import org.eclipse.sirius.tree.business.internal.helper.TreeHelper;
import org.eclipse.sirius.tree.description.TreeDescription;
import org.eclipse.sirius.tree.description.TreeItemCreationTool;
import org.eclipse.sirius.tree.description.TreeItemMapping;
import org.eclipse.sirius.tree.description.TreeMapping;
import org.eclipse.sirius.tree.ui.provider.TreeUIPlugin;
import org.eclipse.sirius.tree.ui.tools.internal.editor.actions.AbstractToolAction;
import org.eclipse.sirius.tree.ui.tools.internal.editor.actions.CreateToolItemAction;
import org.eclipse.sirius.tree.ui.tools.internal.editor.actions.DeleteTreeItemsAction;
import org.eclipse.sirius.tree.ui.tools.internal.editor.actions.EditorCreateTreeItemMenuAction;
import org.eclipse.sirius.tree.ui.tools.internal.editor.listeners.TreeItemExpansionManager;
import org.eclipse.sirius.tree.ui.tools.internal.editor.provider.DTreeContentProvider;
import org.eclipse.sirius.tree.ui.tools.internal.editor.provider.DTreeDecoratingLabelProvider;
import org.eclipse.sirius.tree.ui.tools.internal.editor.provider.DTreeItemDropListener;
import org.eclipse.sirius.tree.ui.tools.internal.editor.provider.DTreeItemEditingSupport;
import org.eclipse.sirius.tree.ui.tools.internal.editor.provider.TreeUIUpdater;
import org.eclipse.sirius.ui.tools.internal.editor.AbstractDTableViewerManager;
import org.eclipse.sirius.ui.tools.internal.editor.AbstractDTreeEditor;
import org.eclipse.sirius.ui.tools.internal.editor.DTableColumnViewerEditorActivationStrategy;
import org.eclipse.sirius.ui.tools.internal.editor.DTableTreeFocusListener;
import org.eclipse.sirius.ui.tools.internal.editor.DescriptionFileChangedNotifier;
import org.eclipse.sirius.ui.tools.internal.editor.SelectDRepresentationElementsListener;
import org.eclipse.sirius.ui.tools.internal.views.common.navigator.adapters.ModelDragTargetAdapter;
import org.eclipse.sirius.viewpoint.DRepresentation;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.ByteArrayTransfer;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.PlatformUI;
import com.google.common.collect.Lists;
/**
* This class manages the tree viewer for display the DTree.
*
* @author nlepine
*/
public class DTreeViewerManager extends AbstractDTableViewerManager {
/** The key for the image which represents a delete action. */
public static final String REFRESH_IMG = "tree/refresh"; //$NON-NLS-1$
/** The key for the image which represents a delete action. */
public static final String DELETE_IMG = "tree/delete"; //$NON-NLS-1$
/** The key for the image which represents a delete action. */
public static final String CREATE_TREE_ITEM_IMG = "tree/newTreeItem"; //$NON-NLS-1$
static {
imageRegistry.put(REFRESH_IMG, ImageDescriptor.createFromURL((URL) TreeUIPlugin.INSTANCE.getImage(REFRESH_IMG)));
imageRegistry.put(DELETE_IMG, ImageDescriptor.createFromURL((URL) TreeUIPlugin.INSTANCE.getImage(DELETE_IMG)));
imageRegistry.put(CREATE_TREE_ITEM_IMG, ImageDescriptor.createFromURL((URL) TreeUIPlugin.INSTANCE.getImage(CREATE_TREE_ITEM_IMG)));
}
private final ITreeCommandFactory treeCommandFactory;
private TreeUIUpdater treeUIUpdater;
private DescriptionFileChangedNotifier descriptionFileChangedNotifier;
private DTreeContentProvider dTreeContentProvider;
private DTreeMenuListener actualMenuListener;
private final EditorCreateTreeItemMenuAction createTreeItemMenu = new EditorCreateTreeItemMenuAction();
private SelectDRepresentationElementsListener selectTableElementsListener;
private TreeColumnLayout treeLayout;
private Composite composite;
/**
* The constructor.
*
* @param parent
* The parent composite
* @param input
* The input DTable
* @param editingDomain
* The transactional editing domain of this viewer
* @param accessor
* The accessor for the model
* @param tableCommandFactory
* The EMF command factory
* @param treeEditor
* The associated editor
*/
public DTreeViewerManager(final Composite parent, final DTree input, final TransactionalEditingDomain editingDomain, final ModelAccessor accessor, final ICommandFactory tableCommandFactory,
final AbstractDTreeEditor treeEditor) {
super(parent, input, editingDomain, accessor, tableCommandFactory, treeEditor);
this.treeCommandFactory = (ITreeCommandFactory) tableCommandFactory;
this.createTreeViewer(parent);
}
public static ImageRegistry getImageRegistry() {
return imageRegistry;
}
/**
* Creates the TreeViewer.
*
* Problem for action on column header
*
* @param composite
* the Container of the TreeViewer to create
*/
@Override
protected void createTreeViewer(final Composite theComposite) {
this.composite = theComposite;
// Create a composite to hold the children
final GridData gridData = new GridData(GridData.FILL_BOTH);
composite.setLayoutData(gridData);
treeLayout = new TreeColumnLayout();
composite.setLayout(treeLayout);
// Create and setup the TreeViewer
final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI;
DTreeViewer dTreeViewer = new DTreeViewer(composite, style, getAccessor().getPermissionAuthority());
treeViewer = dTreeViewer;
new TreeItemExpansionManager(dTreeViewer.getTree(), getSession());
// Add a focus listener to deactivate the EMF actions on the Tree
treeViewer.getTree().addFocusListener(new DTableTreeFocusListener(treeEditor, treeViewer.getTree()));
treeViewer.setUseHashlookup(true);
// tableViewer.setSorter(new
// ExampleTaskSorter(ExampleTaskSorter.DESCRIPTION));
// TreeUIUpdater must be called before {@link
// SelectDRepresentationElementsListener} to have TreeItem created
treeUIUpdater = new TreeUIUpdater(dTreeViewer, dRepresentation);
selectTableElementsListener = new SelectDRepresentationElementsListener(treeEditor, true);
descriptionFileChangedNotifier = new DescriptionFileChangedNotifier(this);
dTreeContentProvider = new DTreeContentProvider();
treeViewer.setContentProvider(dTreeContentProvider);
// Wrap the LabelProvider in a DecoratingLabelProvider
ILabelDecorator decorator = PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator();
AdapterFactory adapterFactory = TreeUIPlugin.getPlugin().getItemProvidersAdapterFactory();
DTreeDecoratingLabelProvider labelProvider = new DTreeDecoratingLabelProvider(adapterFactory, decorator);
treeViewer.setLabelProvider(labelProvider);
fillMenu();
initializeEditingSupport();
initializeDragAndDropSupport();
initializeKeyBindingSupport();
ColumnViewerToolTipSupport.enableFor(treeViewer);
// Create a TreeViewerEditor
TreeViewerEditor.create(treeViewer, new DTableColumnViewerEditorActivationStrategy(treeViewer),
ColumnViewerEditor.TABBING_HORIZONTAL | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR | ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION);
// The input for the table viewer is the instance of DTable
treeViewer.setInput(dRepresentation);
// Expands the line according to the model
treeViewer.setExpandedElements(TreeHelper.getExpandedItems((DTree) dRepresentation).toArray());
}
private void initializeKeyBindingSupport() {
treeViewer.getTree().addKeyListener(new KeyListener() {
@Override
public void keyPressed(final KeyEvent e) {
if (e.keyCode == SWT.DEL) {
DeleteTreeItemsAction deleteItemsAction = new DeleteTreeItemsAction(getEditingDomain(), getTreeCommandFactory());
deleteItemsAction.setItems(getSelectedItems());
if (deleteItemsAction.canExecute()) {
deleteItemsAction.run();
}
}
}
@Override
public void keyReleased(final KeyEvent e) {
};
});
}
private Session getSession() {
return SessionManager.INSTANCE.getSession(((DTree) dRepresentation).getTarget());
}
/**
* Initializes the Editing Support (allowing direct Edit) to associate to
* the current Tree.
*/
protected void initializeEditingSupport() {
// Step 1 : get the TreeViewerColumn corresponding to this Tree
DslCommonPlugin.PROFILER.startWork(SiriusTasksKey.ADD_SWT_COLUMN_KEY);
Option<ViewerColumn> optionViewerColumn = getViewerColumn();
if (optionViewerColumn.some()) {
ViewerColumn treeColumn = optionViewerColumn.get();
// Step 2 : set the Editing support
treeColumn.setEditingSupport(new DTreeItemEditingSupport(treeViewer, getEditingDomain(), getAccessor(), getTreeCommandFactory(), this.getEditor()));
}
DslCommonPlugin.PROFILER.stopWork(SiriusTasksKey.ADD_SWT_COLUMN_KEY);
}
/**
* Initializes Drag and Drop support for the current DTree.
*/
protected void initializeDragAndDropSupport() {
// If copy/paste operations should be supported, may add
// " | DND.DROP_COPY" to the supported operations.
int supportedOperations = DND.DROP_MOVE | DND.DROP_LINK;
ISelectionProvider selectionProvider = this.treeViewer;
this.treeViewer.addDragSupport(supportedOperations, new ByteArrayTransfer[] { LocalSelectionTransfer.getTransfer() }, new ModelDragTargetAdapter(selectionProvider));
this.treeViewer.addDropSupport(supportedOperations, new ByteArrayTransfer[] { LocalSelectionTransfer.getTransfer() },
new DTreeItemDropListener(this.treeViewer, this.editingDomain, this.treeCommandFactory, this.accessor));
}
/**
* Returns the viewer column associated to the treeViewer.
*
* @return the viewer column of the treeViewer, or Options.newNone() if to
* viewer column found.
*/
private Option<ViewerColumn> getViewerColumn() {
Option<ViewerColumn> viewerColumn = Options.newNone();
// We use reflection to access to the ColumnViewer.getViewerColumn(int)
// method (package visibility)
Class<?> columnViewerClass = ColumnViewer.class;
Method method;
try {
method = columnViewerClass.getDeclaredMethod("getViewerColumn", Integer.TYPE); //$NON-NLS-1$
method.setAccessible(true);
Object invoke = method.invoke(treeViewer, 0);
if (invoke instanceof ViewerColumn) {
viewerColumn = Options.newSome((ViewerColumn) invoke);
}
} catch (SecurityException e) {
// Nothing to do, method will return Options.NONE
} catch (NoSuchMethodException e) {
// Nothing to do, method will return Options.NONE
} catch (IllegalArgumentException e) {
// Nothing to do, method will return Options.NONE
} catch (InvocationTargetException e) {
// Nothing to do, method will return Options.NONE
} catch (IllegalAccessException e) {
// Nothing to do, method will return Options.NONE
}
return viewerColumn;
}
/**
* Initialize a cache and add, if needed, the contextual menu for the table.
* <BR>
* Cached the actions of creation and deletion in order to increase
* performance and not calculate it on each contextual menu.<BR>
*/
@Override
public void fillMenu() {
if (descriptionFileChanged) {
descriptionFileChanged = false;
final Map<TreeMapping, List<AbstractToolAction>> mappingToCreateActions = new HashMap<TreeMapping, List<AbstractToolAction>>();
final List<AbstractToolAction> createActionsForTree = new ArrayList<AbstractToolAction>();
calculateAvailableMenus(mappingToCreateActions, createActionsForTree);
mgr.setRemoveAllWhenShown(true);
if (actualMenuListener != null) {
mgr.removeAll();
actualMenuListener.setMappingToCreateActions(mappingToCreateActions);
actualMenuListener.setCreateActionsForTree(createActionsForTree);
} else {
actualMenuListener = new DTreeMenuListener((DTree) dRepresentation, this, mappingToCreateActions, createActionsForTree);
mgr.addMenuListener(actualMenuListener);
//
final Menu menu = mgr.createContextMenu(treeViewer.getControl());
treeViewer.getControl().setMenu(menu);
// // Add this line to have others contextual menus
treeEditor.getSite().registerContextMenu(mgr, treeViewer);
}
getCreateTreeItemMenu().update(createActionsForTree);
}
}
/**
* Get the selected {@link DTreeItem item}.
*
* @return the selected tree items or an empty collection
*/
public Collection<DTreeItem> getSelectedItems() {
Collection<DTreeItem> result = Lists.newArrayList();
if (treeViewer.getTree().getSelectionCount() > 0) {
for (TreeItem item : treeViewer.getTree().getSelection()) {
Object data = item.getData();
if (data instanceof DTreeItem) {
result.add((DTreeItem) data);
}
}
}
return result;
}
/**
* Create the menus according to the {@link TreeMapping} and the associated
* {@link CreateTool} and {@link DeleteTool}.
*
*/
private void calculateAvailableMenus(final Map<TreeMapping, List<AbstractToolAction>> mappingToCreateActions, final List<AbstractToolAction> createActionsForTree) {
final TreeDescription treeDescription = ((DTree) dRepresentation).getDescription();
if (treeDescription != null) {
// Actions on lines
calculateAvailableMenusForLine(treeDescription.getSubItemMappings(), mappingToCreateActions, new ArrayList<TreeItemMapping>());
// Actions on table
final EList<? extends TreeItemCreationTool> createLineTools = treeDescription.getCreateTreeItem();
for (final TreeItemCreationTool createTool : createLineTools) {
final CreateToolItemAction createLineAction = new CreateToolItemAction(createTool, getEditingDomain(), getTreeCommandFactory());
createLineAction.setTable((DTree) dRepresentation);
createActionsForTree.add(createLineAction);
}
}
}
/**
* Create the menus according to the {@link LineMapping} and the associated
* {@link CreateTool} and {@link DeleteTool}.
*
* @param lineMappings
* List of {@link LineMapping}
* @param mappingToCreateActions
* A map which associates {@link TreeMapping} with the
* corresponding list of {@link AbstractToolAction} (
* {@link CreateLineAction} or {@link CreateTargetColumnAction})
*/
private void calculateAvailableMenusForLine(final EList<TreeItemMapping> lineMappings, final Map<TreeMapping, List<AbstractToolAction>> mappingToCreateActions,
final List<TreeItemMapping> processedLineMappings) {
for (final TreeItemMapping treeItemMapping : lineMappings) {
if (treeItemMapping != null && !mappingToCreateActions.keySet().contains(treeItemMapping)) {
final EList<? extends TreeItemCreationTool> createTools = treeItemMapping.getCreate();
List<AbstractToolAction> existingCreateTools = mappingToCreateActions.get(treeItemMapping);
if (existingCreateTools == null) {
existingCreateTools = new ArrayList<AbstractToolAction>();
}
for (final TreeItemCreationTool createTool : createTools) {
existingCreateTools.add(new CreateToolItemAction(createTool, getEditingDomain(), getTreeCommandFactory()));
}
mappingToCreateActions.put(treeItemMapping, existingCreateTools);
}
if (treeItemMapping != null && !processedLineMappings.contains(treeItemMapping)) {
processedLineMappings.add(treeItemMapping);
calculateAvailableMenusForLine(treeItemMapping.getAllSubMappings(), mappingToCreateActions, processedLineMappings);
}
}
}
/**
* Return the TableCommandFactory.
*
* @return the TableCommandFactory
*/
public ITreeCommandFactory getTreeCommandFactory() {
return treeCommandFactory;
}
/**
* Check the tree.
*
* @param tree
* the tree to test
* @return true if the tree is equals to the dTree of this manager, false
* otherwise
*/
public boolean isSameTree(final DTree tree) {
return ((DTree) dRepresentation).equals(tree);
}
/**
* Changed descriptionFileChanged state.
*
* @param modified
* Indicates whether the odesign file has changed since the last
* load menus
*/
@Override
public void setDescriptionFileChanged(final boolean modified) {
descriptionFileChanged = modified;
}
/**
* Return the menu which is display in the toolBar.
*
* @return the menu for create lines on the root
*/
public EditorCreateTreeItemMenuAction getCreateTreeItemMenu() {
return createTreeItemMenu;
}
/**
* Release resources.
*/
@Override
public void dispose() {
descriptionFileChangedNotifier.dispose();
descriptionFileChangedNotifier = null;
treeUIUpdater.dispose();
treeUIUpdater = null;
selectTableElementsListener.dispose();
selectTableElementsListener = null;
dTreeContentProvider.dispose();
dTreeContentProvider = null;
super.dispose();
createTreeItemMenu.dispose();
}
@Override
public void updateDRepresentation(DRepresentation newDRepresentation) {
this.dRepresentation = newDRepresentation;
treeUIUpdater.dispose();
treeUIUpdater = new TreeUIUpdater((DTreeViewer) treeViewer, dRepresentation);
getTreeViewer().getTree().removeAll();
getTreeViewer().getTree().clearAll(true);
treeLayout = new TreeColumnLayout();
composite.setLayout(treeLayout);
treeViewer.setInput(dRepresentation);
// Expands the line according to the model
treeViewer.setExpandedElements(TreeHelper.getExpandedItems((DTree) dRepresentation).toArray());
}
}