/*******************************************************************************
* Copyright (c) 2014, 2016 itemis AG 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:
* Alexander Nyßen (itemis AG) - initial API and implementation
* Matthias Wienand (itemis AG) - skip feedback and handles when determining viewer (bug #498298)
*
* Note: Parts of this interface have been transferred from org.eclipse.gef.editparts.AbstractEditPart and org.eclipse.gef.editparts.AbstractGraphicalEditPart.
*
*******************************************************************************/
package org.eclipse.gef.mvc.fx.parts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.gef.common.activate.ActivatableSupport;
import org.eclipse.gef.common.activate.IActivatable;
import org.eclipse.gef.common.adapt.AdaptableSupport;
import org.eclipse.gef.common.adapt.AdapterKey;
import org.eclipse.gef.common.adapt.inject.InjectAdapters;
import org.eclipse.gef.common.beans.property.ReadOnlyListWrapperEx;
import org.eclipse.gef.common.beans.property.ReadOnlyMultisetProperty;
import org.eclipse.gef.common.beans.property.ReadOnlyMultisetWrapper;
import org.eclipse.gef.common.beans.property.ReadOnlySetMultimapProperty;
import org.eclipse.gef.common.beans.property.ReadOnlySetMultimapWrapper;
import org.eclipse.gef.common.collections.CollectionUtils;
import org.eclipse.gef.common.collections.ObservableMultiset;
import org.eclipse.gef.common.collections.ObservableSetMultimap;
import org.eclipse.gef.mvc.fx.behaviors.IBehavior;
import org.eclipse.gef.mvc.fx.handlers.IHandler;
import org.eclipse.gef.mvc.fx.policies.IPolicy;
import org.eclipse.gef.mvc.fx.viewer.IViewer;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.google.common.reflect.TypeToken;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyListProperty;
import javafx.beans.property.ReadOnlyMapProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.scene.Node;
/**
* The {@link AbstractVisualPart} is an abstract implementation of the
* {@link IVisualPart} interface.
*
* @author anyssen
*
* @param <V>
* The visual node used by this {@link AbstractVisualPart}.
*/
public abstract class AbstractVisualPart<V extends Node>
implements IVisualPart<V> {
/**
* The 'default' used for attaching/detaching to anchorages, in case no
* explicit role is given.
*/
private static final String DEFAULT_ANCHORAGE_ROLE = "default";
private ActivatableSupport acs = new ActivatableSupport(this);
private AdaptableSupport<IVisualPart<V>> ads = new AdaptableSupport<>(this);
private ReadOnlyObjectWrapper<IVisualPart<? extends Node>> parentProperty = new ReadOnlyObjectWrapper<>();
private ObservableList<IVisualPart<? extends Node>> children = CollectionUtils
.observableArrayList();
private ObservableList<IVisualPart<? extends Node>> childrenUnmodifiable;
private ReadOnlyListWrapperEx<IVisualPart<? extends Node>> childrenUnmodifiableProperty;
private ObservableSetMultimap<IVisualPart<? extends Node>, String> anchorages = CollectionUtils
.observableHashMultimap();
private ObservableSetMultimap<IVisualPart<? extends Node>, String> anchoragesUnmodifiable;
private ReadOnlySetMultimapWrapper<IVisualPart<? extends Node>, String> anchoragesUnmodifiableProperty;
private ObservableMultiset<IVisualPart<? extends Node>> anchoreds = CollectionUtils
.observableHashMultiset();
private ObservableMultiset<IVisualPart<? extends Node>> anchoredsUnmodifiable;
private ReadOnlyMultisetWrapper<IVisualPart<? extends Node>> anchoredsUnmodifiableProperty;
private BooleanProperty refreshVisualProperty = new SimpleBooleanProperty(
this, REFRESH_VISUAL_PROPERTY, true);
private V visual;
private ReadOnlyObjectWrapper<IViewer> viewerProperty = new ReadOnlyObjectWrapper<>();
/**
* Activates this {@link IVisualPart} (if it is not already active) by
* setting (and propagating) the new active state first and delegating to
* {@link #doActivate()} afterwards. During the call to
* {@link #doActivate()}, {@link #isActive()} will thus already return
* <code>true</code>. If the {@link IVisualPart} is already active, this
* operation will be a no-op.
*
* @see #deactivate()
* @see #isActive()
*/
@Override
public final void activate() {
acs.activate(null, this::doActivate);
}
/**
* Activates the adapters registered at this {@link AbstractVisualPart}.
*/
protected void activateAdapters() {
// XXX: We keep a sorted map of adapters so activation
// is performed in a deterministic order
new TreeMap<>(ads.getAdapters()).values().forEach((adapter) -> {
if (adapter instanceof IActivatable) {
((IActivatable) adapter).activate();
}
});
}
/**
* Activates the children of this {@link AbstractVisualPart}.
*/
protected void activateChildren() {
for (IVisualPart<? extends Node> child : children) {
child.activate();
}
}
@Override
public ReadOnlyBooleanProperty activeProperty() {
return acs.activeProperty();
}
@Override
public ReadOnlyObjectProperty<IViewer> adaptableProperty() {
return viewerProperty.getReadOnlyProperty();
}
@Override
public ReadOnlyMapProperty<AdapterKey<?>, Object> adaptersProperty() {
return ads.adaptersProperty();
}
@Override
public void addChild(IVisualPart<? extends Node> child) {
addChild(child, children.size());
}
@Override
public void addChild(IVisualPart<? extends Node> child, int index) {
if (children.contains(child)) {
throw new IllegalArgumentException("Cannot add " + child
+ " as child of " + this + " because its already a child.");
}
// System.out.println(
// "Add child " + child + " to " + this + " with index " + index);
children.add(index, child);
child.setParent(this);
refreshVisual();
doAddChildVisual(child, index);
child.refreshVisual();
if (isActive()) {
child.activate();
}
}
@Override
public void addChildren(
List<? extends IVisualPart<? extends Node>> children) {
addChildren(children, this.children.size());
}
@Override
public void addChildren(
List<? extends IVisualPart<? extends Node>> children, int index) {
if (!Collections.disjoint(this.children, children)) {
List<? extends IVisualPart<? extends Node>> alreadyContainedChildren = new ArrayList<>(
children);
children.retainAll(this.children);
throw new IllegalArgumentException(
"Cannot add " + children + " as children of " + this
+ " because the following are already children: "
+ alreadyContainedChildren + ".");
}
for (int i = 0; i < children.size(); i++) {
addChild(children.get(i), index + i);
}
}
@Override
public ReadOnlySetMultimapProperty<IVisualPart<? extends Node>, String> anchoragesUnmodifiableProperty() {
if (anchoragesUnmodifiableProperty == null) {
anchoragesUnmodifiableProperty = new ReadOnlySetMultimapWrapper<>(
getAnchoragesUnmodifiable());
}
return anchoragesUnmodifiableProperty.getReadOnlyProperty();
}
@Override
public ReadOnlyMultisetProperty<IVisualPart<? extends Node>> anchoredsUnmodifiableProperty() {
if (anchoredsUnmodifiableProperty == null) {
anchoredsUnmodifiableProperty = new ReadOnlyMultisetWrapper<>(
getAnchoredsUnmodifiable());
}
return anchoredsUnmodifiableProperty.getReadOnlyProperty();
}
@Override
public void attachAnchored(IVisualPart<? extends Node> anchored) {
// determine the viewer before adding the anchored
IViewer oldViewer = getViewer();
// register if we obtain a link to the viewer
HashMultiset<IVisualPart<? extends Node>> newAnchoreds = HashMultiset
.create(anchoreds);
newAnchoreds.add(anchored);
IViewer newViewer = determineViewer(getParent(), newAnchoreds);
// unregister from old viewer in case we were registered (oldViewer !=
// null) and the viewer changes (newViewer != oldViewer)
if (oldViewer != null && newViewer != oldViewer) {
oldViewer.unsetAdapter(this);
}
// detach anchoreds (and fire change notifications)
anchoreds.add(anchored);
// if we obtain a link to the viewer then register at new viewer
if (newViewer != null && newViewer != oldViewer) {
newViewer.setAdapter(this,
String.valueOf(System.identityHashCode(this)));
}
}
@Override
public void attachToAnchorage(IVisualPart<? extends Node> anchorage) {
attachToAnchorage(anchorage, DEFAULT_ANCHORAGE_ROLE);
}
@Override
public void attachToAnchorage(IVisualPart<? extends Node> anchorage,
String role) {
if (anchorage == null) {
throw new IllegalArgumentException("Anchorage may not be null.");
}
if (role == null) {
throw new IllegalArgumentException("Role may not be null.");
}
if (anchorages.containsEntry(anchorage, role)) {
throw new IllegalArgumentException("Already attached to anchorage "
+ anchorage + " with role '" + role + "'.");
}
// System.out.println("Attach " + this + " to anchorage " + anchorage
// + " with role " + role);
// attach
anchorages.put(anchorage, role);
anchorage.attachAnchored(this);
// attach visuals
anchorage.refreshVisual();
doAttachToAnchorageVisual(anchorage, role);
refreshVisual();
}
@Override
public ReadOnlyListProperty<IVisualPart<? extends Node>> childrenUnmodifiableProperty() {
if (childrenUnmodifiableProperty == null) {
childrenUnmodifiableProperty = new ReadOnlyListWrapperEx<>(this,
CHILDREN_PROPERTY, getChildrenUnmodifiable());
}
return childrenUnmodifiableProperty.getReadOnlyProperty();
}
/**
* Deactivates this {@link IVisualPart} (if it is active) by delegating to
* {@link #doDeactivate()} first and setting (and propagating) the new
* active state afterwards. During the call to {@link #doDeactivate()},
* {@link #isActive()} will thus still return <code>true</code>. If the
* {@link IVisualPart} is not active, this operation will be a no-op.
*
* @see #activate()
* @see #isActive()
*/
@Override
public final void deactivate() {
acs.deactivate(this::doDeactivate, null);
}
/**
* Deactivates the adapters registered at this {@link AbstractVisualPart}.
*/
protected void deactivateAdapters() {
// XXX: We keep a sorted map of adapters so deactivation
// is performed in a deterministic order
new TreeMap<>(ads.getAdapters()).values().forEach((adapter) -> {
if (adapter instanceof IActivatable) {
((IActivatable) adapter).deactivate();
}
});
}
/**
* Deactivates the children of this {@link AbstractVisualPart}.
*/
protected void deactivateChildren() {
for (IVisualPart<? extends Node> child : children) {
child.deactivate();
}
}
@Override
public void detachAnchored(IVisualPart<? extends Node> anchored) {
// determine viewer before and after removing the anchored
IViewer oldViewer = getViewer();
HashMultiset<IVisualPart<? extends Node>> oldAnchoreds = HashMultiset
.create(anchoreds);
oldAnchoreds.remove(anchored);
IViewer newViewer = determineViewer(getParent(), oldAnchoreds);
// unregister from old viewer in case we were registered (oldViewer !=
// null) and the viewer changes (newViewer != oldViewer)
if (oldViewer != null && newViewer != oldViewer) {
oldViewer.unsetAdapter(this);
}
// detach anchoreds (and fire change notifications)
anchoreds.remove(anchored);
// if we obtain a link to the viewer then register at new viewer
if (newViewer != null && newViewer != oldViewer) {
newViewer.setAdapter(this,
String.valueOf(System.identityHashCode(this)));
}
}
@Override
public void detachFromAnchorage(IVisualPart<? extends Node> anchorage) {
detachFromAnchorage(anchorage, DEFAULT_ANCHORAGE_ROLE);
}
@Override
public void detachFromAnchorage(IVisualPart<? extends Node> anchorage,
String role) {
if (anchorage == null) {
throw new IllegalArgumentException("Anchorage may not be null.");
}
if (role == null) {
throw new IllegalArgumentException("Role may not be null.");
}
if (!anchorages.containsEntry(anchorage, role)) {
throw new IllegalArgumentException("Not attached to anchorage "
+ anchorage + " with role '" + role + "'.");
}
// System.out.println("Detach " + this + " from anchorage " + anchorage
// + " with role " + role);
// detach visuals
doDetachFromAnchorageVisual(anchorage, role);
// detach
anchorage.detachAnchored(this);
anchorages.remove(anchorage, role);
}
/**
* Determines the viewer reference via the given parent or any of the given
* anchoreds.
*
* @param parent
* The parent to obtain the viewer from.
* @param anchoreds
* The anchoreds to alternatively obtain the viewer from.
* @return The viewer, if it could be determined via the parent or any of
* the anchoreds.
*/
protected IViewer determineViewer(IVisualPart<? extends Node> parent,
Multiset<IVisualPart<? extends Node>> anchoreds) {
if (parent != null && parent.getRoot() != null) {
return parent.getRoot().getViewer();
}
return null;
}
@Override
public void dispose() {
// dispose adapters
ads.dispose();
}
/**
* Activates this {@link AbstractVisualPart}, which activates its children
* and adapters.
*/
protected void doActivate() {
activateAdapters();
activateChildren();
}
/**
* Performs the addition of the child's <i>visual</i> to this
* {@link IVisualPart}'s visual.
*
* @param child
* The {@link IVisualPart} being added
* @param index
* The child's position
* @see #addChild(IVisualPart, int)
*/
protected void doAddChildVisual(IVisualPart<? extends Node> child,
int index) {
throw new UnsupportedOperationException(
"Need to properly implement addChildVisual(IVisualPart, int) for "
+ this.getClass());
}
/**
* Attaches this part's visual to the visual of the given anchorage.
*
* @param anchorage
* The anchorage {@link IVisualPart}.
* @param role
* The anchorage role.
*/
protected void doAttachToAnchorageVisual(
IVisualPart<? extends Node> anchorage, String role) {
throw new UnsupportedOperationException(
"Need to implement attachToAnchorageVisual(IVisualPart, String) for "
+ this.getClass());
}
/**
* Creates this part's visual.
*
* @return This part's visual.
*/
protected abstract V doCreateVisual();
/**
* Deactivates this {@link AbstractVisualPart}, which deactivates its
* children and adapters.
*/
protected void doDeactivate() {
deactivateChildren();
deactivateAdapters();
}
/**
* Detaches this part's visual from the visual of the given anchorage.
*
* @param anchorage
* The anchorage {@link IVisualPart}.
* @param role
* The anchorage role.
*/
protected void doDetachFromAnchorageVisual(
IVisualPart<? extends Node> anchorage, String role) {
throw new UnsupportedOperationException(
"Need to implement detachFromAnchorageVisual(IVisualPart, String) for "
+ this.getClass());
}
/**
* Refreshes this part's visualization based on this part's content.
*
* @param visual
* This part's visual.
*/
protected abstract void doRefreshVisual(V visual);
/**
* Removes the child's visual from this {@link IVisualPart}'s visual.
*
* @param child
* The child {@link IVisualPart}.
* @param index
* The index of the child whose visual is to be removed.
*/
protected void doRemoveChildVisual(IVisualPart<? extends Node> child,
int index) {
throw new UnsupportedOperationException(
"Need to implement removeChildVisual(IVisualPart, int) for "
+ this.getClass());
}
@Override
public IViewer getAdaptable() {
return viewerProperty.get();
}
@Override
public <T> T getAdapter(AdapterKey<T> key) {
return ads.getAdapter(key);
}
@Override
public <T> T getAdapter(Class<T> classKey) {
return ads.getAdapter(classKey);
}
@Override
public <T> T getAdapter(TypeToken<T> key) {
return ads.getAdapter(key);
}
@Override
public <T> AdapterKey<T> getAdapterKey(T adapter) {
return ads.getAdapterKey(adapter);
}
@Override
public ObservableMap<AdapterKey<?>, Object> getAdapters() {
return ads.getAdapters();
}
@Override
public <T> Map<AdapterKey<? extends T>, T> getAdapters(
Class<? super T> classKey) {
return ads.getAdapters(classKey);
}
@Override
public <T> Map<AdapterKey<? extends T>, T> getAdapters(
TypeToken<? super T> key) {
return ads.getAdapters(key);
}
@Override
public ObservableSetMultimap<IVisualPart<? extends Node>, String> getAnchoragesUnmodifiable() {
if (anchoragesUnmodifiable == null) {
anchoragesUnmodifiable = CollectionUtils
.unmodifiableObservableSetMultimap(anchorages);
}
return anchoragesUnmodifiable;
}
@Override
public ObservableMultiset<IVisualPart<? extends Node>> getAnchoredsUnmodifiable() {
if (anchoredsUnmodifiable == null) {
anchoredsUnmodifiable = CollectionUtils
.unmodifiableObservableMultiset(anchoreds);
}
return anchoredsUnmodifiable;
}
@Override
public Map<AdapterKey<? extends IBehavior>, IBehavior> getBehaviors() {
return ads.getAdapters(IBehavior.class);
}
@Override
public ObservableList<IVisualPart<? extends Node>> getChildrenUnmodifiable() {
if (childrenUnmodifiable == null) {
childrenUnmodifiable = FXCollections
.unmodifiableObservableList(children);
}
return childrenUnmodifiable;
}
@Override
public Map<AdapterKey<? extends IHandler>, IHandler> getHandlers() {
return ads.getAdapters(IHandler.class);
}
@Override
public IVisualPart<? extends Node> getParent() {
return parentProperty.get();
}
@Override
public Map<AdapterKey<? extends IPolicy>, IPolicy> getPolicies() {
return ads.getAdapters(IPolicy.class);
}
@Override
public IRootPart<? extends Node> getRoot() {
// start at first parent as the root part will directly return itself
IVisualPart<? extends Node> parent = getParent();
// walk up the part hierarchy until the root part (which has no parent)
// is found
while (parent != null && parent.getParent() != null) {
parent = parent.getParent();
}
// check if we really reached the root part
if (parent instanceof IRootPart) {
return (IRootPart<? extends Node>) parent;
}
// return null if the root part could not be determined
return null;
}
@Override
public V getVisual() {
if (visual == null) {
visual = doCreateVisual();
}
return visual;
}
/**
* @return <code>true</code> if this {@link IVisualPart} is active.
*/
@Override
public boolean isActive() {
return acs.isActive();
}
@Override
public boolean isRefreshVisual() {
return refreshVisualProperty.get();
}
@Override
public ReadOnlyObjectProperty<IVisualPart<? extends Node>> parentProperty() {
return parentProperty.getReadOnlyProperty();
}
/**
* Refreshes this {@link IVisualPart}'s <i>visuals</i>. Delegates to
* {@link #doRefreshVisual(Node)} in case {@link #isRefreshVisual()} is not
* set to <code>false</code>.
*/
@Override
public final void refreshVisual() {
if (visual != null && isRefreshVisual()) {
// System.out.println("Refresh visual of " + this);
doRefreshVisual(visual);
}
}
@Override
public BooleanProperty refreshVisualProperty() {
return refreshVisualProperty;
}
/**
* Called when a link to the {@link IViewer} is obtained. Registers this
* {@link IVisualPart} for its "main" visual (i.e. the one returned by
* {@link #getVisual()}) at the {@link IViewer#getVisualPartMap()} of the
* given {@link IViewer}. To simplify matters, this {@link IVisualPart} only
* has to register itself for its "main" visual, i.e. if the "main" visual
* contains a number of children visuals, it does not need to register
* itself for those children visuals. Therefore, if the visualization
* changes dynamically, the registration at the visual-part-map does not
* need to be updated. Consequently, when looking up an {@link IVisualPart}
* for a given visual in the visual-part-map, it is required to walk up the
* visual hierarchy until a registered visual is found.
*
* @param viewer
* The {@link IViewer} to register at.
*/
protected void register(IViewer viewer) {
registerAtVisualPartMap(viewer, getVisual());
}
/**
* Registers this part for the given visual in the visual-part-map of the
* given {@link IViewer}.
*
* @param viewer
* The {@link IViewer} of which the visual-part-map is extended.
* @param visual
* The visual for which this part is registered in the viewer's
* visual-part-map.
*/
protected void registerAtVisualPartMap(IViewer viewer, V visual) {
viewer.getVisualPartMap().put(visual, this);
}
@Override
public void removeChild(IVisualPart<? extends Node> child) {
if (!children.contains(child)) {
throw new IllegalArgumentException("Cannot remove " + child
+ " as child of " + this + " because it is no child.");
}
// System.out.println("Remove child " + child + " from " + this + ".");
if (isActive()) {
child.deactivate();
}
doRemoveChildVisual(child, children.indexOf(child));
child.setParent(null);
children.remove(child);
}
@Override
public void removeChildren(
List<? extends IVisualPart<? extends Node>> children) {
if (!this.children.containsAll(children)) {
List<? extends IVisualPart<? extends Node>> notContainedChildren = new ArrayList<>(
children);
notContainedChildren.removeAll(this.children);
throw new IllegalArgumentException(
"Cannot remove " + children + " as children of " + this
+ " because the following are no children: "
+ notContainedChildren + ".");
}
// TODO: use children.removeAll and perform the de-registration here
for (IVisualPart<? extends Node> child : children) {
removeChild(child);
}
}
@Override
public void reorderChild(IVisualPart<? extends Node> child, int index) {
int oldIndex = getChildrenUnmodifiable().indexOf(child);
if (oldIndex < 0) {
throw new IllegalArgumentException("Cannot reorder child " + child
+ " because it is no child.");
}
// TODO: this could be made more performant (reordering the children and
// visuals)
removeChild(child);
addChild(child, index);
}
@Override
public void setAdaptable(IViewer viewer) {
IViewer oldViewer = viewerProperty.get();
if (oldViewer != null && viewer != oldViewer) {
unregister(oldViewer);
}
viewerProperty.set(viewer);
if (viewer != null && viewer != oldViewer) {
register(viewer);
}
}
@Override
public <T> void setAdapter(T adapter) {
ads.setAdapter(adapter);
}
@Override
public <T> void setAdapter(T adapter, String role) {
ads.setAdapter(adapter, role);
}
@Override
public <T> void setAdapter(TypeToken<T> adapterType, T adapter) {
ads.setAdapter(adapterType, adapter);
}
@InjectAdapters
@Override
public <T> void setAdapter(TypeToken<T> adapterType, T adapter,
String role) {
ads.setAdapter(adapterType, adapter, role);
}
/**
* Sets the parent {@link IVisualPart}.
*/
@Override
public void setParent(IVisualPart<? extends Node> newParent) {
IVisualPart<? extends Node> oldParent = parentProperty.get();
// ensure there is no action if the parent did not change
if (oldParent == newParent) {
return;
}
// determine how parent change will affect the viewer reference
final IViewer oldViewer = getViewer();
final IViewer newViewer = determineViewer(newParent,
getAnchoredsUnmodifiable());
// unregister from old viewer in case we were registered (oldViewer !=
// null) and the viewer changes (newViewer != oldViewer)
if (oldViewer != null && newViewer != oldViewer) {
oldViewer.unsetAdapter(this);
}
// change the parent property (which will notify listeners)
parentProperty.set(newParent);
// if we obtain a link to the viewer then register at new viewer
if (newViewer != null && newViewer != oldViewer) {
newViewer.setAdapter(this,
String.valueOf(System.identityHashCode(this)));
}
}
@Override
public void setRefreshVisual(boolean isRefreshVisual) {
refreshVisualProperty.set(isRefreshVisual);
}
/**
* Called when the link to the {@link IViewer} is lost. Unregisters this
* {@link IVisualPart} for its "main" visual (i.e. the one returned by
* {@link #getVisual()}) from the {@link IViewer#getVisualPartMap()} of the
* given {@link IViewer}. To simplify matters, this {@link IVisualPart} only
* has to unregister itself for its "main" visual, i.e. if the "main" visual
* contains a number of children visuals, it does not need to unregister
* itself for those children visuals. Therefore, if the visualization
* changes dynamically, the registration at the visual-part-map does not
* need to be updated. Consequently, when looking up an {@link IVisualPart}
* for a given visual in the visual-part-map, it is required to walk up the
* visual hierarchy until a registered visual is found.
*
* @param viewer
* The {@link IViewer} to unregister from.
*/
protected void unregister(IViewer viewer) {
unregisterFromVisualPartMap(viewer, getVisual());
}
/**
* Removes the given visual from the visual-part-map of the given viewer.
*
* @param viewer
* The {@link IViewer} of which the visual-part-map is changed.
* @param visual
* The visual which is removed from the visual-part-map.
*/
protected void unregisterFromVisualPartMap(IViewer viewer, V visual) {
Map<Node, IVisualPart<? extends Node>> registry = viewer
.getVisualPartMap();
if (registry.get(visual) != this) {
throw new IllegalArgumentException("Not registered under visual");
}
registry.remove(visual);
}
@Override
public <T> void unsetAdapter(T adapter) {
ads.unsetAdapter(adapter);
}
}