/******************************************************************************* * Copyright (c) 2010-2015 Henshin developers. 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: * TU Berlin, University of Luxembourg, SES S.A. *******************************************************************************/ package de.tub.tfs.muvitor.gef.editparts; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.ecore.ENamedElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.gef.DragTracker; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.TreeEditPart; import org.eclipse.gef.editparts.AbstractTreeEditPart; import org.eclipse.gef.tools.DragTreeItemsTracker; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.views.properties.IPropertySource; import de.tub.tfs.muvitor.gef.directedit.IDirectEditPart; import de.tub.tfs.muvitor.gef.directedit.MuvitorTreeDirectEditManager; import de.tub.tfs.muvitor.gef.directedit.MuvitorTreeDirectEditPolicy; import de.tub.tfs.muvitor.properties.EObjectPropertySource; import de.tub.tfs.muvitor.ui.MuvitorTreeEditor; import de.tub.tfs.muvitor.ui.utils.MuvitorNotifierService; /** * This edit part is a convenience implementation with the following frequently * used features for EObjects: * * <ul> * <li>On activation of this edit part, an EMF adapter is being installed on a * this edit parts' model, listening for model changes and passing the * notifications to {@link #notifyChanged(Notification)} and to the * {@link MuvitorNotifierService}. More adapters can freely be installed which * will be removed on deactivation. * <li>In {@link #createPropertySource()} an default * {@link EObjectPropertySource} showing all model features is created for this * edit part. {@link #getAdapter(Class)} provides the property source the the * properties view. Subclasses may override both. * <li>This edit part is prepared for GEF direct editing. Subclasses just need * to implement {@link IDirectEditPart} to enable this feature. * <li> {@link #performDirectEdit()} and {@link #performOpen()} may be overridden * to handle direct edit or open requests differently. By default, * {@link #performOpen()} will try to open a view with the model, * {@link #performDirectEdit()} will try to directly edit the property of the * specified feature ID specified by implementing {@link IDirectEditPart}. * </ul> * * @author Tony Modica */ abstract public class AdapterTreeEditPart<T extends EObject> extends AbstractTreeEditPart { /** * The {@link Adapter}s that have been associated to some {@link EObject}s. * * @see #registerAdapter(Adapter, EObject) */ final private Map<Adapter, EObject> adapters = new HashMap<Adapter, EObject>(); /** * By default, an Adapter will be installed on the model, passing the * notifications to {@link #notifyChanged(Notification)} and to the * {@link MuvitorNotifierService}. * * @param model * the model of this editpart */ public AdapterTreeEditPart(final T model) { super(model); installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new MuvitorTreeDirectEditPolicy()); registerAdapter(new AdapterImpl() { @SuppressWarnings("synthetic-access") @Override public final void notifyChanged(final Notification msg) { AdapterTreeEditPart.this.notifyChanged(msg); final int featureId = msg.getFeatureID(EcorePackage.class); switch (featureId) { case EcorePackage.ENAMED_ELEMENT__NAME: refreshVisuals(); break; } MuvitorNotifierService.notifyListeners(msg); } }); } /** * Removes this edit part's adapters from the model. Subclasses may override * but must call super implementation! * * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#deactivate() */ @Override public void deactivate() { if (isActive()) { for (final Entry<Adapter, EObject> entry : adapters.entrySet()) { entry.getValue().eAdapters().remove(entry.getKey()); } super.deactivate(); } } /** * Subclasses may override but must call super implementation! * * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ @SuppressWarnings("rawtypes") @Override public Object getAdapter(final Class key) { if (IPropertySource.class == key) { return createPropertySource(); } return super.getAdapter(key); } /** * Convenient method that casts the model to the appropriate type. * * @return The model of this EditPart appropriately casted. */ @SuppressWarnings("unchecked") public final T getCastedModel() { return (T) getModel(); } /* * (non-Javadoc) * * @see * org.eclipse.gef.editparts.AbstractTreeEditPart#getDragTracker(org.eclipse * .gef.Request) */ @Override public DragTracker getDragTracker(final Request req) { return new DragTreeItemsTracker(this); } /** * Handles the standard GEF requests "direct edit" and "open" on this edit * part. * * @see #performDirectEdit() * @see #performOpen() * @see org.eclipse.gef.editparts.AbstractEditPart#performRequest(org.eclipse.gef.Request) */ @Override public void performRequest(final Request request) { if (RequestConstants.REQ_DIRECT_EDIT == request.getType() && checkTreeItem()) { performDirectEdit(); } else if (RequestConstants.REQ_OPEN == request.getType()) { performOpen(); } } /** * Convenience method to register a custom {@link Adapter} on this * editpart's model. * * @param adapter * an {@link Adapter} to register on this editpart's model * @see #registerAdapter(Adapter, EObject) */ public final void registerAdapter(final Adapter adapter) { registerAdapter(adapter, getCastedModel()); } /** * Via this method subclasses can install custom {@link Adapter}s in * {@link #activate()} listening to changes on a specific {@link EObject}. * All registered adapters will be deregistered by default in * {@link #deactivate()}. * * @param adapter * an {@link Adapter} to register on the model * @param model * an {@link EObject} to observe with the passed adapter */ public final void registerAdapter(final Adapter adapter, final EObject model) { adapters.put(adapter, model); model.eAdapters().add(adapter); } /** * Convenience method to let {@link #notifyChanged(Notification)} receive * notifications from additional EObjects. This method registers an * {@link Adapter} on some EObject that just forwards its notifications. * * @param adapter * an {@link Adapter} to register on this editpart's model * @see #registerAdapter(Adapter, EObject) */ public final void registerAdapter(final EObject model) { registerAdapter(new AdapterImpl() { @Override public void notifyChanged(final Notification msg) { AdapterTreeEditPart.this.notifyChanged(msg); MuvitorNotifierService.notifyListeners(msg); } }, model); } /** * This forces the top element to be shown as a tree element. * * @see org.eclipse.gef.editparts.AbstractTreeEditPart#setWidget(org.eclipse.swt.widgets.Widget) */ @Override public final void setWidget(final Widget widget) { if (widget instanceof Tree) { final TreeItem item = new TreeItem((Tree) widget, SWT.MULTI); // // create the treeitem for the root super.setWidget(item); } else { super.setWidget(widget); } } /** * Gets the model object, for which a graphical view will be opened. This is * per default the contained model itself, but could also be something else * (e.g the model of the parent {@link EditPart}). * * @return the model object for the graphical viewer. * * @see #performOpen() */ protected EObject getGraphicalViewModel() { return getCastedModel(); } /** * Creates a default {@link EObjectPropertySource} showing all model * features for this edit part. Subclasses may override. * * @return the property source for this edit part */ protected IPropertySource createPropertySource() { return new EObjectPropertySource(getCastedModel()); } /** * By default, if the model is an {@link ENamedElement}, the text of this * tree edit part will be its name. */ @Override protected String getText() { if (getCastedModel() instanceof ENamedElement) { return ((ENamedElement) getCastedModel()).getName(); } return super.getText(); } /** * By default, an Adapter will be registered with this editpart's model that * passes notifications to this method, which subclasses are expected to * override. This can be extended to receive notifications from other * EObjects as well with {@link #registerAdapter(EObject)}. */ protected void notifyChanged(final Notification notification) { refresh(); } /** * Creates and opens a {@link MuvitorTreeDirectEditManager} by default, * handling direct editing, if this edit part implements * {@link IDirectEditPart}. Subclasses may override to perform some other * action here. */ protected void performDirectEdit() { if (this instanceof IDirectEditPart) { new MuvitorTreeDirectEditManager(this).show(); } } /** * Used to check before a view could be opened for the model object. * Subclasses may override. * * @return {@code true} per default */ protected boolean canShowView() { return true; } /** * Calls MuvitorTreeEditor.showView(getCastedModel) and expands the * {@link TreeItem} widget by default. Subclasses may override to perform * some other actions here. */ protected void performOpen() { Object myWidget = getWidget(); boolean hasView = canShowView(); if (myWidget != null) { if (myWidget instanceof TreeItem) { boolean isExpanded = ((TreeItem) myWidget).getExpanded(); ((TreeItem) myWidget).setExpanded((hasView && true) || !isExpanded); } } if (hasView) { MuvitorTreeEditor.showView(getGraphicalViewModel()); } } /** * Finds a child {@link TreeEditPart} for a given model object. * * @param model * the child model object * @return the child {@link TreeEditPart}, if found or {@code null}, * otherwise */ protected AbstractTreeEditPart findEditPartFor(Object model) { for (Object child : getChildren()) { AbstractTreeEditPart castedChild = (AbstractTreeEditPart) child; if (castedChild.getModel() == model) { return castedChild; } } return null; } }