/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. Gephi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gephi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.visualization.events; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeData; import org.gephi.visualization.VizController; import org.gephi.visualization.apiimpl.GraphIO; import org.gephi.visualization.apiimpl.ModelImpl; import org.gephi.visualization.apiimpl.VizEvent; import org.gephi.visualization.apiimpl.VizEventListener; import org.gephi.visualization.apiimpl.VizEventManager; import org.gephi.visualization.opengl.AbstractEngine; /** * * @author Mathieu Bastian */ public class StandardVizEventManager implements VizEventManager { //Architecture private AbstractEngine engine; private GraphIO graphIO; // private ThreadPoolExecutor pool; private VizEventTypeHandler[] handlers; public StandardVizEventManager() { pool = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(10)); } public void initArchitecture() { engine = VizController.getInstance().getEngine(); graphIO = VizController.getInstance().getGraphIO(); //Set handlers ArrayList<VizEventTypeHandler> handlersList = new ArrayList<VizEventTypeHandler>(); handlersList.add(new VizEventTypeHandler(VizEvent.Type.MOUSE_LEFT_CLICK, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.MOUSE_LEFT_PRESS, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.MOUSE_MIDDLE_CLICK, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.MOUSE_MIDDLE_PRESS, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.MOUSE_RIGHT_CLICK, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.MOUSE_RIGHT_PRESS, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.MOUSE_MOVE, true)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.START_DRAG, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.DRAG, true)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.STOP_DRAG, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.NODE_LEFT_CLICK, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.MOUSE_LEFT_PRESSING, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.MOUSE_RELEASED, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.NODE_LEFT_PRESS, false)); handlersList.add(new VizEventTypeHandler(VizEvent.Type.NODE_LEFT_PRESSING, false)); Collections.sort(handlersList, new Comparator() { public int compare(Object o1, Object o2) { VizEvent.Type t1 = ((VizEventTypeHandler) o1).type; VizEvent.Type t2 = ((VizEventTypeHandler) o2).type; return t1.compareTo(t2); } }); handlers = handlersList.toArray(new VizEventTypeHandler[0]); } public void mouseLeftClick() { //Node Left click VizEventTypeHandler nodeLeftHandler = handlers[VizEvent.Type.NODE_LEFT_CLICK.ordinal()]; if (nodeLeftHandler.hasListeners() && VizController.getInstance().getVizConfig().isSelectionEnable()) { //Check if some node are selected ModelImpl[] modelArray = engine.getSelectedObjects(AbstractEngine.CLASS_NODE); if (modelArray.length > 0) { Node[] nodeArray = new Node[modelArray.length]; for (int i = 0; i < modelArray.length; i++) { nodeArray[i] = ((NodeData) modelArray[i].getObj()).getRootNode(); } nodeLeftHandler.dispatch(nodeArray); } } //Mouse left click VizEventTypeHandler mouseLeftHandler = handlers[VizEvent.Type.MOUSE_LEFT_CLICK.ordinal()]; if (mouseLeftHandler.hasListeners()) { ModelImpl[] modelArray = engine.getSelectedObjects(AbstractEngine.CLASS_NODE); if (modelArray.length == 0 || !VizController.getInstance().getVizConfig().isSelectionEnable()) { float[] mousePositionViewport = graphIO.getMousePosition(); float[] mousePosition3d = graphIO.getMousePosition3d(); float[] mousePos = new float[]{mousePositionViewport[0], mousePositionViewport[1], mousePosition3d[0], mousePosition3d[1]}; handlers[VizEvent.Type.MOUSE_LEFT_CLICK.ordinal()].dispatch(mousePos); } } } public void mouseLeftPress() { handlers[VizEvent.Type.MOUSE_LEFT_PRESS.ordinal()].dispatch(); pressingTick = PRESSING_FREQUENCY; VizEventTypeHandler pressHandler = handlers[VizEvent.Type.NODE_LEFT_PRESS.ordinal()]; if (pressHandler.hasListeners()) { //Check if some node are selected ModelImpl[] modelArray = engine.getSelectedObjects(AbstractEngine.CLASS_NODE); if (modelArray.length > 0) { Node[] nodeArray = new Node[modelArray.length]; for (int i = 0; i < modelArray.length; i++) { nodeArray[i] = ((NodeData) modelArray[i].getObj()).getRootNode(); } pressHandler.dispatch(nodeArray); } } } public void mouseMiddleClick() { handlers[VizEvent.Type.MOUSE_MIDDLE_CLICK.ordinal()].dispatch(); } public void mouseMiddlePress() { handlers[VizEvent.Type.MOUSE_LEFT_PRESS.ordinal()].dispatch(); } public void mouseMove() { handlers[VizEvent.Type.MOUSE_MOVE.ordinal()].dispatch(); } public void mouseRightClick() { handlers[VizEvent.Type.MOUSE_RIGHT_CLICK.ordinal()].dispatch(); } public void mouseRightPress() { handlers[VizEvent.Type.MOUSE_RIGHT_PRESS.ordinal()].dispatch(); } private static final int PRESSING_FREQUENCY = 5; private int pressingTick = 0; public void mouseLeftPressing() { if (pressingTick++ >= PRESSING_FREQUENCY) { pressingTick = 0; VizEventTypeHandler nodeHandler = handlers[VizEvent.Type.NODE_LEFT_PRESSING.ordinal()]; if (nodeHandler.hasListeners()) { //Check if some node are selected ModelImpl[] modelArray = engine.getSelectedObjects(AbstractEngine.CLASS_NODE); if (modelArray.length > 0) { Node[] nodeArray = new Node[modelArray.length]; for (int i = 0; i < modelArray.length; i++) { nodeArray[i] = ((NodeData) modelArray[i].getObj()).getRootNode(); } nodeHandler.dispatch(nodeArray); } } } } public void startDrag() { handlers[VizEvent.Type.START_DRAG.ordinal()].dispatch(); } public void stopDrag() { handlers[VizEvent.Type.STOP_DRAG.ordinal()].dispatch(); } private static final int DRAGGING_FREQUENCY = 5; private int draggingTick = 0; public void drag() { if (draggingTick++ >= DRAGGING_FREQUENCY) { draggingTick = 0; VizEventTypeHandler handler = handlers[VizEvent.Type.DRAG.ordinal()]; if (handler.hasListeners()) { float[] mouseDrag = Arrays.copyOf(graphIO.getMouseDrag(), 4); mouseDrag[2] = graphIO.getMouseDrag3d()[0]; mouseDrag[3] = graphIO.getMouseDrag3d()[1]; handler.dispatch(mouseDrag); } } } public void mouseReleased() { handlers[VizEvent.Type.MOUSE_RELEASED.ordinal()].dispatch(); } //Listeners public boolean hasListeners(VizEvent.Type type) { return handlers[type.ordinal()].hasListeners(); } public void addListener(VizEventListener listener) { handlers[listener.getType().ordinal()].addListener(listener); } public void removeListener(VizEventListener listener) { handlers[listener.getType().ordinal()].removeListener(listener); } public void addListener(VizEventListener[] listeners) { for (int i = 0; i < listeners.length; i++) { handlers[listeners[i].getType().ordinal()].addListener(listeners[i]); } } public void removeListener(VizEventListener[] listeners) { for (int i = 0; i < listeners.length; i++) { handlers[listeners[i].getType().ordinal()].removeListener(listeners[i]); } } private class VizEventTypeHandler { //Settings private final boolean limitRunning; //Data protected List<WeakReference<VizEventListener>> listeners; protected final VizEvent.Type type; protected Runnable runnable; //States protected boolean running; public VizEventTypeHandler(VizEvent.Type type, boolean limitRunning) { this.limitRunning = limitRunning; this.type = type; this.listeners = new ArrayList<WeakReference<VizEventListener>>(); runnable = new Runnable() { public void run() { fireVizEvent(null); running = false; } }; } protected synchronized void addListener(VizEventListener listener) { WeakReference<VizEventListener> weakListener = new WeakReference<VizEventListener>(listener); listeners.add(weakListener); } protected synchronized void removeListener(VizEventListener listener) { for (Iterator<WeakReference<VizEventListener>> itr = listeners.iterator(); itr.hasNext();) { WeakReference<VizEventListener> li = itr.next(); if (li.get() == listener) { itr.remove(); } } } protected void dispatch() { if (limitRunning && running) { return; } if (listeners.size() > 0) { running = true; pool.submit(runnable); } } protected void dispatch(final Object data) { if (limitRunning && running) { return; } if (listeners.size() > 0) { running = true; pool.submit(new Runnable() { public void run() { fireVizEvent(data); running = false; } }); } } protected boolean isRunning() { return running; } private synchronized void fireVizEvent(Object data) { VizEvent event = new VizEvent(this, type, data); for (int i = 0; i < listeners.size(); i++) { WeakReference<VizEventListener> weakListener = listeners.get(i); VizEventListener v = weakListener.get(); v.handleEvent(event); } } public boolean hasListeners() { return listeners.size() > 0; } protected int getIndex() { return type.ordinal(); } } }