/*******************************************************************************
* Copyright (c) 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:
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
package org.eclipse.gef.mvc.fx.gestures;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.eclipse.gef.common.adapt.AdapterKey;
import org.eclipse.gef.common.adapt.IAdaptable;
import org.eclipse.gef.mvc.fx.domain.IDomain;
import org.eclipse.gef.mvc.fx.handlers.IHandler;
import org.eclipse.gef.mvc.fx.parts.IVisualPart;
import org.eclipse.gef.mvc.fx.parts.PartUtils;
import org.eclipse.gef.mvc.fx.viewer.IViewer;
import com.google.common.reflect.TypeToken;
import javafx.scene.Node;
/**
* The {@link DefaultHandlerResolver} is an {@link IHandlerResolver}
* that works in two stages:
* <ol>
* <li>Examining the active policies of other tools to find "multi-gesture"
* policies that implement or extend the given target policy type. If any
* "multi-gesture" policies are found, the target resolution finishes and these
* are returned as the target policies. Otherwise, the target resolution
* continues with the next stage.
* <li>Examining the policies of the visual parts that are contained within the
* root-to-target path in the visual part hierarchy. All policies that implement
* or extend the given target policy type are returned as target policies. The
* policies that are registered on the root part have highest precedence, i.e.
* they will be executed first, and the policies that are registered on the
* target part have lowest precedence, i.e. they will be executed last.
* </ol>
* For details, take a look at the
* {@link #resolve(IGesture, Node, IViewer, Class)} method.
*
* @author mwienand
*
*/
public class DefaultHandlerResolver extends IAdaptable.Bound.Impl<IDomain>
implements IHandlerResolver {
private final static class AdapterKeyComparator
implements Comparator<AdapterKey<?>> {
private boolean descending;
public AdapterKeyComparator(boolean descending) {
this.descending = descending;
}
@Override
public int compare(AdapterKey<?> lhs, AdapterKey<?> rhs) {
int cmp = lhs.getRole().compareTo(rhs.getRole());
return descending ? -cmp : cmp;
}
}
/**
* The comparator that is used to sort drag adapters.
*/
private final static AdapterKeyComparator ADAPTER_KEY_COMPARATOR = new AdapterKeyComparator(
true);
/**
* {@inheritDoc}
* <p>
* This strategy works in two stages:
* <ol>
* <li>Examining the active policies of other tools to find "multi-gesture"
* policies that implement or extend the given target policy type. If any
* "multi-gesture" policies are found, the target resolution finishes and
* these are returned as the target policies. Otherwise, the target
* resolution continues with the next stage.
* <li>Examining the policies of the visual parts that are contained within
* the root-to-target path in the visual part hierarchy. All policies that
* implement or extend the given target policy type are returned as target
* policies. The policies that are registered on the root part have highest
* precedence, i.e. they will be executed first, and the policies that are
* registered on the target part have lowest precedence, i.e. they will be
* executed last.
* </ol>
* The second stage is structured in two parts:
* <ol>
* <li>Determination of the target part.
* <li>Determination of the target policies based on the target part.
* </ol>
* The first {@link IVisualPart} that controls a {@link Node} within the
* parent hierarchy of the given target {@link Node} is used as the target
* part. If no target part can be found, the root part is used as the target
* part.
* <p>
* Beginning at the root part, and walking down the visual part hierarchy,
* all policies of the specified type are collected. The policies that are
* registered on one part are (lexicographically) sorted by their role, so
* that the target policy selection is deterministic.
* <p>
* For example, when you have 3 parts, the root part, an intermediate part,
* and a leaf part, the target policy selection for a policy of type X could
* yield the following results:
* <ol>
* <li>LayeredRootPart.X with role "0"
* <li>LayeredRootPart.X with role "1"
* <li>IntermediatePart.X with role "a"
* <li>IntermediatePart.X with role "b"
* <li>LeafPart.X with role "x"
* <li>LeafPart.X with role "y"
* </ol>
* These policies would then all be executed/notified about an input event
* by the calling tool.
*/
@Override
@SuppressWarnings({ "serial", "unchecked" })
public <T extends IHandler> List<? extends T> resolve(IGesture gesture,
Node target, IViewer viewer, Class<T> handlerClass) {
// System.out.println("\n=== determine target policies ===");
// System.out.println("viewer = " + viewer);
// System.out.println("raw target node = " + target);
// System.out.println("policy class = " + policyClass);
// determine outer targets, i.e. already running/active policies of
// other tools
// System.out.println("Outer target policies:");
List<T> outerTargetHandlers = new ArrayList<>();
Collection<IGesture> tools = viewer.getDomain()
.getAdapters(new TypeToken<IGesture>() {
}).values();
for (IGesture tool : tools) {
// System.out.println("[find active policies of " + tool + "]");
if (tool != gesture) {
for (IHandler policy : tool.getActiveHandlers(viewer)) {
if (policy.getClass().isAssignableFrom(handlerClass)) {
// System.out.println("add active policy " + policy);
try {
outerTargetHandlers.add((T) policy);
} catch (ClassCastException e) {
// ignore target policy if type parameter is not
// appropriate
}
}
}
}
}
// already active policies that can process the events take precedence
// over scene graph related target policies
if (!outerTargetHandlers.isEmpty()) {
// System.out.println("RETURN outer target policies:");
// for (T p : outerTargetPolicies) {
// System.out.println(p.getHost() + " -> " + p);
// }
return outerTargetHandlers;
}
// determine target part as the part that controls the first node in the
// scene graph hierarchy of the given target node
// System.out.println("Inner target policies:");
IVisualPart<? extends Node> targetPart = PartUtils
.retrieveVisualPart(viewer, target);
// System.out.println("target part = " + targetPart);
// collect all on-drag-policies on the way from the target part to the
// root part
IVisualPart<? extends Node> part = targetPart;
List<T> handlers = new ArrayList<>();
while (part != null) {
// System.out.println("[find policies for " + part + "]");
// determine on-drag-policies
Map<AdapterKey<? extends T>, T> partHandlers = part
.<T> getAdapters(handlerClass);
// sort descending by role (converted to integer)
List<AdapterKey<? extends T>> descendinglySortedKeys = new ArrayList<>(
partHandlers.keySet());
Collections.sort(descendinglySortedKeys, ADAPTER_KEY_COMPARATOR);
// add to the list of policies
for (AdapterKey<? extends T> key : descendinglySortedKeys) {
// System.out.println("add policy " + key);
handlers.add(partHandlers.get(key));
}
// go one level up in the hierarchy
part = part.getParent();
}
// reverse order in which policies are returned so that parent policies
// are called before child policies
Collections.reverse(handlers);
// System.out.println("RETURN in reverse order:");
// for (T p : policies) {
// System.out.println(p.getHost() + " -> " + p);
// }
return handlers;
}
}