From ff65a9edaad226d991b83cf3970880011232b1e7 Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Fri, 7 Dec 2012 22:27:42 +0100 Subject: [PATCH] Include preview mouse listeners support and bug fixes implemented in legend project. --- .../gephi/preview/PreviewControllerImpl.java | 96 +- .../org/gephi/preview/PreviewModelImpl.java | 1027 +++++++++-------- .../org/gephi/preview/ProcessingApplet.java | 90 +- .../gephi/preview/api/PreviewController.java | 15 + .../org/gephi/preview/api/PreviewModel.java | 148 +-- .../gephi/preview/api/PreviewMouseEvent.java | 94 ++ .../preview/spi/MouseResponsiveRenderer.java | 51 + .../preview/spi/PreviewMouseListener.java | 89 ++ .../org/gephi/preview/spi/package.html | 129 ++- src/main/javadoc/overview.html | 2 + 10 files changed, 1076 insertions(+), 665 deletions(-) create mode 100644 modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewMouseEvent.java create mode 100644 modules/PreviewAPI/src/main/java/org/gephi/preview/spi/MouseResponsiveRenderer.java create mode 100644 modules/PreviewAPI/src/main/java/org/gephi/preview/spi/PreviewMouseListener.java diff --git a/modules/PreviewAPI/src/main/java/org/gephi/preview/PreviewControllerImpl.java b/modules/PreviewAPI/src/main/java/org/gephi/preview/PreviewControllerImpl.java index 8bf5d9431..7ed1aae11 100644 --- a/modules/PreviewAPI/src/main/java/org/gephi/preview/PreviewControllerImpl.java +++ b/modules/PreviewAPI/src/main/java/org/gephi/preview/PreviewControllerImpl.java @@ -43,14 +43,13 @@ package org.gephi.preview; import java.awt.Dimension; import java.awt.Point; +import java.util.ArrayList; import java.util.LinkedHashMap; import org.gephi.data.attributes.api.AttributeController; import org.gephi.data.attributes.api.AttributeModel; import org.gephi.graph.api.*; import org.gephi.preview.api.*; -import org.gephi.preview.spi.ItemBuilder; -import org.gephi.preview.spi.RenderTargetBuilder; -import org.gephi.preview.spi.Renderer; +import org.gephi.preview.spi.*; import org.gephi.project.api.ProjectController; import org.gephi.project.api.Workspace; import org.gephi.project.api.WorkspaceListener; @@ -150,7 +149,21 @@ public class PreviewControllerImpl implements PreviewController { } } - Renderer[] renderers = model.getManagedEnabledRenderers(); + Renderer[] renderers; + if (!mousePressed) { + renderers = model.getManagedEnabledRenderers(); + } else { + ArrayList renderersList = new ArrayList(); + for(Renderer renderer: model.getManagedEnabledRenderers()){ + //Only mouse responsive renderers will be called while mouse is pressed + if(renderer instanceof MouseResponsiveRenderer){ + renderersList.add(renderer); + } + } + + renderers = renderersList.toArray(new Renderer[0]); + } + if (renderers == null) { renderers = getRegisteredRenderers(); } @@ -250,14 +263,11 @@ public class PreviewControllerImpl implements PreviewController { @Override public void render(RenderTarget target, Renderer[] renderers, Workspace workspace) { - render(target, renderers, getModel(workspace)); + render(target, renderers != null ? renderers : getModel(workspace).getManagedEnabledRenderers(), getModel(workspace)); } private synchronized void render(RenderTarget target, Renderer[] renderers, PreviewModelImpl previewModel) { if (previewModel != null) { - if (renderers == null) { - renderers = getRegisteredRenderers(); - } PreviewProperties properties = previewModel.getProperties(); //Progress @@ -265,10 +275,12 @@ public class PreviewControllerImpl implements PreviewController { if (target instanceof AbstractRenderTarget) { int tasks = 0; for (Renderer r : renderers) { - for (String type : previewModel.getItemTypes()) { - for (Item item : previewModel.getItems(type)) { - if (r.isRendererForitem(item, properties)) { - tasks++; + if (!mousePressed || r instanceof MouseResponsiveRenderer) { + for (String type : previewModel.getItemTypes()) { + for (Item item : previewModel.getItems(type)) { + if (r.isRendererForitem(item, properties)) { + tasks++; + } } } } @@ -280,14 +292,16 @@ public class PreviewControllerImpl implements PreviewController { //Render items for (Renderer r : renderers) { - for (String type : previewModel.getItemTypes()) { - for (Item item : previewModel.getItems(type)) { - if (r.isRendererForitem(item, properties)) { - r.render(item, target, properties); - Progress.progress(progressTicket); - if (target instanceof AbstractRenderTarget) { - if (((AbstractRenderTarget) target).isCancelled()) { - return; + if (!mousePressed || r instanceof MouseResponsiveRenderer) { + for (String type : previewModel.getItemTypes()) { + for (Item item : previewModel.getItems(type)) { + if (r.isRendererForitem(item, properties)) { + r.render(item, target, properties); + Progress.progress(progressTicket); + if (target instanceof AbstractRenderTarget) { + if (((AbstractRenderTarget) target).isCancelled()) { + return; + } } } } @@ -373,4 +387,46 @@ public class PreviewControllerImpl implements PreviewController { } return anyPluginRendererRegistered; } + private boolean mousePressed = false; + + @Override + public boolean sendMouseEvent(PreviewMouseEvent event){ + return sendMouseEvent(event, Lookup.getDefault().lookup(ProjectController.class).getCurrentWorkspace()); + } + + @Override + public boolean sendMouseEvent(PreviewMouseEvent event, Workspace workspace) { + if(workspace == null){ + return false; + } + + PreviewModel previewModel = getModel(workspace); + + //Avoid drag events arriving to listeners if they did not consume previous press event. + if ((event.type != PreviewMouseEvent.Type.DRAGGED && event.type != PreviewMouseEvent.Type.RELEASED) || mousePressed) { + for (PreviewMouseListener listener : previewModel.getEnabledMouseListeners()) { + switch (event.type) { + case CLICKED: + listener.mouseClicked(event, previewModel.getProperties(), workspace); + break; + case PRESSED: + mousePressed = true; + listener.mousePressed(event, previewModel.getProperties(), workspace); + break; + case DRAGGED: + listener.mouseDragged(event, previewModel.getProperties(), workspace); + break; + case RELEASED: + mousePressed = false; + listener.mouseReleased(event, previewModel.getProperties(), workspace); + } + if (event.isConsumed()) { + return true; + } + } + } + + mousePressed = false;//Avoid drag events arriving to listeners if they did not consume previous press event. + return false; + } } diff --git a/modules/PreviewAPI/src/main/java/org/gephi/preview/PreviewModelImpl.java b/modules/PreviewAPI/src/main/java/org/gephi/preview/PreviewModelImpl.java index 105358f54..0a0ea207b 100644 --- a/modules/PreviewAPI/src/main/java/org/gephi/preview/PreviewModelImpl.java +++ b/modules/PreviewAPI/src/main/java/org/gephi/preview/PreviewModelImpl.java @@ -1,499 +1,528 @@ -/* - Copyright 2008-2011 Gephi - Authors : Mathieu Bastian - Website : http://www.gephi.org - - This file is part of Gephi. - - DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - - Copyright 2011 Gephi Consortium. All rights reserved. - - The contents of this file are subject to the terms of either the GNU - General Public License Version 3 only ("GPL") or the Common - Development and Distribution License("CDDL") (collectively, the - "License"). You may not use this file except in compliance with the - License. You can obtain a copy of the License at - http://gephi.org/about/legal/license-notice/ - or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the - specific language governing permissions and limitations under the - License. When distributing the software, include this License Header - Notice in each file and include the License files at - /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the - License Header, with the fields enclosed by brackets [] replaced by - your own identifying information: - "Portions Copyrighted [year] [name of copyright owner]" - - If you wish your version of this file to be governed by only the CDDL - or only the GPL Version 3, indicate your decision by adding - "[Contributor] elects to include this software in this distribution - under the [CDDL or GPL Version 3] license." If you do not indicate a - single choice of license, a recipient has the option to distribute - your version of this file under either the CDDL, the GPL Version 3 or - to extend the choice of license to its licensees as provided above. - However, if you add GPL Version 3 code and therefore, elected the GPL - Version 3 license, then the option applies only if the new code is - made subject to such option by the copyright holder. - - Contributor(s): - - Portions Copyrighted 2011 Gephi Consortium. - */ -package org.gephi.preview; - -import java.awt.Dimension; -import java.awt.Point; -import java.beans.PropertyEditorManager; -import java.util.*; -import java.util.Map.Entry; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; -import javax.xml.stream.XMLStreamWriter; -import org.gephi.preview.api.*; -import org.gephi.preview.presets.DefaultPreset; -import org.gephi.preview.spi.Renderer; -import org.gephi.preview.types.DependantColor; -import org.gephi.preview.types.DependantOriginalColor; -import org.gephi.preview.types.EdgeColor; -import org.gephi.preview.types.propertyeditors.BasicDependantColorPropertyEditor; -import org.gephi.preview.types.propertyeditors.BasicDependantOriginalColorPropertyEditor; -import org.gephi.preview.types.propertyeditors.BasicEdgeColorPropertyEditor; -import org.gephi.project.api.Workspace; -import org.gephi.utils.Serialization; -import org.openide.util.Lookup; - -/** - * - * @author Mathieu Bastian - */ -public class PreviewModelImpl implements PreviewModel { - - private final PreviewController previewController; - private final Workspace workspace; - //Items - private final Map> typeMap; - private final Map sourceMap; - //Renderers - private ManagedRenderer[] managedRenderers; - //Properties - private PreviewProperties properties; - //Dimensions - private Dimension dimensions; - private Point topLeftPosition; - - public PreviewModelImpl(Workspace workspace) { - this(workspace, null); - } - - public PreviewModelImpl(Workspace workspace, PreviewController previewController) { - if (previewController != null) { - this.previewController = previewController; - } else { - this.previewController = Lookup.getDefault().lookup(PreviewController.class); - } - typeMap = new HashMap>(); - sourceMap = new HashMap(); - this.workspace = workspace; - - initBasicPropertyEditors(); - initManagedRenderers(); - } - - /** - * Makes sure that, at least, basic property editors are available for serializing and deserializing - */ - private void initBasicPropertyEditors() { - if (PropertyEditorManager.findEditor(DependantColor.class) == null) { - PropertyEditorManager.registerEditor(DependantColor.class, BasicDependantColorPropertyEditor.class); - } - if (PropertyEditorManager.findEditor(DependantOriginalColor.class) == null) { - PropertyEditorManager.registerEditor(DependantOriginalColor.class, BasicDependantOriginalColorPropertyEditor.class); - } - if (PropertyEditorManager.findEditor(EdgeColor.class) == null) { - PropertyEditorManager.registerEditor(EdgeColor.class, BasicEdgeColorPropertyEditor.class); - } - } - - /** - * Makes sure that, if more than one plugin extends a default renderer, only the one with the lowest position is enabled initially. - */ - private void initManagedRenderers() { - Renderer[] registeredRenderers = previewController.getRegisteredRenderers(); - - Set replacedRenderers = new HashSet(); - - managedRenderers = new ManagedRenderer[registeredRenderers.length]; - for (int i = 0; i < registeredRenderers.length; i++) { - Renderer r = registeredRenderers[i]; - Class superClass = r.getClass().getSuperclass(); - if (superClass != null && superClass.getName().startsWith("org.gephi.preview.plugin.renderers.")) { - managedRenderers[i] = new ManagedRenderer(r, !replacedRenderers.contains(superClass.getName())); - replacedRenderers.add(superClass.getName()); - } else { - managedRenderers[i] = new ManagedRenderer(r, true); - } - } - } - - private synchronized void initProperties() { - if (properties == null) { - properties = new PreviewProperties(); - - //Properties from renderers - for (Renderer renderer : getManagedEnabledRenderers()) { - PreviewProperty[] props = renderer.getProperties(); - for (PreviewProperty p : props) { - properties.addProperty(p); - } - } - - //Default preset - properties.applyPreset(new DefaultPreset()); - - //Defaut values - properties.putValue(PreviewProperty.VISIBILITY_RATIO, 1f); - } - } - - @Override - public PreviewProperties getProperties() { - initProperties(); - return properties; - } - - @Override - public Item[] getItems(String type) { - List list = typeMap.get(type); - if (list != null) { - return list.toArray(new Item[0]); - } - return new Item[0]; - } - - @Override - public Item getItem(String type, Object source) { - Item[] items = getItems(source); - for (Item item : items) { - if (item.getType().equals(type)) { - return item; - } - } - return null; - } - - @Override - public Item[] getItems(Object source) { - Object value = sourceMap.get(source); - if (value instanceof List) { - return ((List) value).toArray(new Item[0]); - } else if (value instanceof Item) { - return new Item[]{(Item) value}; - } - return new Item[0]; - } - - public String[] getItemTypes() { - return typeMap.keySet().toArray(new String[0]); - } - - public void loadItems(String type, Item[] items) { - //Add to type map - List typeList = typeMap.get(type); - if (typeList == null) { - typeList = new ArrayList(items.length); - typeList.addAll(Arrays.asList(items)); - typeMap.put(type, typeList); - - //Add to source map - for (Item item : items) { - Object value = sourceMap.get(item.getSource()); - if (value == null) { - sourceMap.put(item.getSource(), item); - } else if (value instanceof List) { - ((List) value).add(item); - } else { - List list = new ArrayList(); - list.add((Item) value); - list.add(item); - } - } - } else { - //Possible items to merge - for (Item item : items) { - Object value = sourceMap.get(item.getSource()); - if (value == null) { - //No other object attached to this item - typeList.add(item); - sourceMap.put(item.getSource(), item); - } else if (value instanceof Item && ((Item) value).getType().equals(item.getType())) { - //An object already exists with the same type and source, merge them - mergeItems(item, ((Item) value)); - } else if (value instanceof List) { - List list = (List) value; - for (Item itemSameSource : list) { - if (itemSameSource.getType().equals(item.getType())) { - //An object already exists with the same type and source, merge them - mergeItems(item, itemSameSource); - break; - } - } - } - } - } - } - - private Item mergeItems(Item item, Item toBeMerged) { - for (String key : toBeMerged.getKeys()) { - item.setData(key, toBeMerged.getData(key)); - } - return item; - } - - public void clear() { - typeMap.clear(); - sourceMap.clear(); - } - - public Workspace getWorkspace() { - return workspace; - } - - @Override - public Dimension getDimensions() { - return dimensions; - } - - @Override - public Point getTopLeftPosition() { - return topLeftPosition; - } - - public void setDimensions(Dimension dimensions) { - this.dimensions = dimensions; - } - - public void setTopLeftPosition(Point topLeftPosition) { - this.topLeftPosition = topLeftPosition; - } - - @Override - public ManagedRenderer[] getManagedRenderers() { - return managedRenderers; - } - - /** - * Makes sure that managedRenderers contains every renderer existing implementations. If some renderers are not in the list, they are added in default implementation order at the end of the list - * and not enabled. - */ - private void completeManagedRenderersListIfNecessary() { - if (managedRenderers != null) { - Set existing = new HashSet(); - for (ManagedRenderer mr : managedRenderers) { - existing.add(mr.getRenderer().getClass().getName()); - } - - List completeManagedRenderersList = new ArrayList(); - completeManagedRenderersList.addAll(Arrays.asList(managedRenderers)); - - for (Renderer renderer : previewController.getRegisteredRenderers()) { - if (!existing.contains(renderer.getClass().getName())) { - completeManagedRenderersList.add(new ManagedRenderer(renderer, false)); - } - } - - managedRenderers = completeManagedRenderersList.toArray(new ManagedRenderer[0]); - } - } - - /** - * Removes unnecessary properties from not enabled renderers - */ - private void reloadProperties() { - PreviewProperties oldProperties = getProperties(); - - properties = new PreviewProperties(); - - //Properties from renderers - for (Renderer renderer : getManagedEnabledRenderers()) { - PreviewProperty[] props = renderer.getProperties(); - for (PreviewProperty p : props) { - properties.addProperty(p); - } - } - - for (PreviewProperty property : oldProperties.getProperties()) { - if (properties.hasProperty(property.getName())) { - properties.putValue(property.getName(), property.getValue()); - } - } - - for (Entry property : oldProperties.getSimpleValues()) { - properties.putValue(property.getKey(), property.getValue()); - } - } - - @Override - public void setManagedRenderers(ManagedRenderer[] managedRenderers) { - //Validate no null ManagedRenderers - for (int i = 0; i < managedRenderers.length; i++) { - if (managedRenderers[i] == null) { - throw new IllegalArgumentException("managedRenderers should not contain null values"); - } - } - - this.managedRenderers = managedRenderers; - completeManagedRenderersListIfNecessary(); - reloadProperties(); - } - - @Override - public Renderer[] getManagedEnabledRenderers() { - if (managedRenderers != null) { - ArrayList renderers = new ArrayList(); - for (ManagedRenderer mr : managedRenderers) { - if (mr.isEnabled()) { - renderers.add(mr.getRenderer()); - } - } - return renderers.toArray(new Renderer[0]); - } else { - return null; - } - } - - //PERSISTENCE - public void writeXML(XMLStreamWriter writer) throws XMLStreamException { - writer.writeStartElement("previewmodel"); - - initProperties(); - //Write PreviewProperties: - for (PreviewProperty property : properties.getProperties()) { - String propertyName = property.getName(); - Object propertyValue = property.getValue(); - if (propertyValue != null) { - String text = Serialization.getValueAsText(propertyValue); - if (text != null) { - writer.writeStartElement("previewproperty"); - writer.writeAttribute("name", propertyName); - writer.writeCharacters(text); - writer.writeEndElement(); - } - } - } - - //Write preview simple values: - Iterator> simpleValuesIterator = properties.getSimpleValues().iterator(); - while (simpleValuesIterator.hasNext()) { - Entry simpleValueEntry; - simpleValueEntry = simpleValuesIterator.next(); - - if (simpleValueEntry.getKey().equals("width") - || simpleValueEntry.getKey().equals("height")) { - continue; - } - - Object value = simpleValueEntry.getValue(); - if (value != null) { - Class clazz = value.getClass(); - String text = Serialization.getValueAsText(value); - if (text != null) { - writer.writeStartElement("previewsimplevalue"); - writer.writeAttribute("name", simpleValueEntry.getKey()); - writer.writeAttribute("class", clazz.getName()); - writer.writeCharacters(text); - writer.writeEndElement(); - } - } - } - - //Write model managed renderers: - if (managedRenderers != null) { - for (ManagedRenderer managedRenderer : managedRenderers) { - writer.writeStartElement("managedrenderer"); - writer.writeAttribute("class", managedRenderer.getRenderer().getClass().getName()); - writer.writeAttribute("enabled", String.valueOf(managedRenderer.isEnabled())); - writer.writeEndElement(); - } - } - - - writer.writeEndElement(); - } - - public void readXML(XMLStreamReader reader) throws XMLStreamException { - PreviewProperties props = getProperties(); - - String propName = null; - boolean isSimpleValue = false; - String simpleValueClass = null; - - List managedRenderersList = new ArrayList(); - Map availableRenderers = new HashMap(); - for (Renderer renderer : Lookup.getDefault().lookupAll(Renderer.class)) { - availableRenderers.put(renderer.getClass().getName(), renderer); - Class superClass = renderer.getClass().getSuperclass(); - if (superClass != null && superClass.getName().startsWith("org.gephi.preview.plugin.renderers.")) { - availableRenderers.put(superClass.getName(), renderer);//For plugins replacing a default renderer - } - } - - boolean end = false; - while (reader.hasNext() && !end) { - int type = reader.next(); - - switch (type) { - case XMLStreamReader.START_ELEMENT: - String name = reader.getLocalName(); - if ("previewproperty".equalsIgnoreCase(name)) { - propName = reader.getAttributeValue(null, "name"); - isSimpleValue = false; - } else if ("previewsimplevalue".equalsIgnoreCase(name)) { - propName = reader.getAttributeValue(null, "name"); - simpleValueClass = reader.getAttributeValue(null, "class"); - isSimpleValue = true; - } else if ("managedrenderer".equalsIgnoreCase(name)) { - String rendererClass = reader.getAttributeValue(null, "class"); - if (availableRenderers.containsKey(rendererClass)) { - managedRenderersList.add(new ManagedRenderer(availableRenderers.get(rendererClass), Boolean.parseBoolean(reader.getAttributeValue(null, "enabled")))); - } - } - break; - case XMLStreamReader.CHARACTERS: - if (!reader.isWhiteSpace()) { - if (propName != null) { - if (!isSimpleValue) {//Read PreviewProperty: - PreviewProperty p = props.getProperty(propName); - if (p != null) { - Object value = Serialization.readValueFromText(reader.getText(), p.getType()); - if (value != null) { - p.setValue(value); - } - } - } else {//Read preview simple value: - if (simpleValueClass != null) { - if (!propName.equals("width") - && !propName.equals("height")) { - Object value = Serialization.readValueFromText(reader.getText(), simpleValueClass); - if (value != null) { - props.putValue(propName, value); - } - } - } - } - } - } - break; - case XMLStreamReader.END_ELEMENT: - if ("previewmodel".equalsIgnoreCase(reader.getLocalName())) { - end = true; - } - propName = null; - break; - } - } - - if (!managedRenderersList.isEmpty()) { - setManagedRenderers(managedRenderersList.toArray(new ManagedRenderer[0])); - } - } -} +/* + Copyright 2008-2011 Gephi + Authors : Mathieu Bastian + Website : http://www.gephi.org + + This file is part of Gephi. + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2011 Gephi Consortium. All rights reserved. + + The contents of this file are subject to the terms of either the GNU + General Public License Version 3 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with the + License. You can obtain a copy of the License at + http://gephi.org/about/legal/license-notice/ + or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the + specific language governing permissions and limitations under the + License. When distributing the software, include this License Header + Notice in each file and include the License files at + /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the + License Header, with the fields enclosed by brackets [] replaced by + your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + If you wish your version of this file to be governed by only the CDDL + or only the GPL Version 3, indicate your decision by adding + "[Contributor] elects to include this software in this distribution + under the [CDDL or GPL Version 3] license." If you do not indicate a + single choice of license, a recipient has the option to distribute + your version of this file under either the CDDL, the GPL Version 3 or + to extend the choice of license to its licensees as provided above. + However, if you add GPL Version 3 code and therefore, elected the GPL + Version 3 license, then the option applies only if the new code is + made subject to such option by the copyright holder. + + Contributor(s): + + Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.preview; + +import java.awt.Dimension; +import java.awt.Point; +import java.beans.PropertyEditorManager; +import java.util.*; +import java.util.Map.Entry; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; +import org.gephi.preview.api.*; +import org.gephi.preview.presets.DefaultPreset; +import org.gephi.preview.spi.MouseResponsiveRenderer; +import org.gephi.preview.spi.PreviewMouseListener; +import org.gephi.preview.spi.Renderer; +import org.gephi.preview.types.DependantColor; +import org.gephi.preview.types.DependantOriginalColor; +import org.gephi.preview.types.EdgeColor; +import org.gephi.preview.types.propertyeditors.BasicDependantColorPropertyEditor; +import org.gephi.preview.types.propertyeditors.BasicDependantOriginalColorPropertyEditor; +import org.gephi.preview.types.propertyeditors.BasicEdgeColorPropertyEditor; +import org.gephi.project.api.Workspace; +import org.gephi.utils.Serialization; +import org.openide.util.Lookup; + +/** + * + * @author Mathieu Bastian + */ +public class PreviewModelImpl implements PreviewModel { + + private final PreviewController previewController; + private final Workspace workspace; + //Items + private final Map> typeMap; + private final Map sourceMap; + //Renderers + private ManagedRenderer[] managedRenderers; + //Mouse listeners (of enabled renderers) + private PreviewMouseListener[] enabledMouseListeners; + //Properties + private PreviewProperties properties; + //Dimensions + private Dimension dimensions; + private Point topLeftPosition; + + public PreviewModelImpl(Workspace workspace) { + this(workspace, null); + } + + public PreviewModelImpl(Workspace workspace, PreviewController previewController) { + if (previewController != null) { + this.previewController = previewController; + } else { + this.previewController = Lookup.getDefault().lookup(PreviewController.class); + } + typeMap = new HashMap>(); + sourceMap = new HashMap(); + this.workspace = workspace; + + initBasicPropertyEditors(); + initManagedRenderers(); + prepareManagedListeners(); + } + + /** + * Makes sure that, at least, basic property editors are available for serializing and deserializing + */ + private void initBasicPropertyEditors() { + if (PropertyEditorManager.findEditor(DependantColor.class) == null) { + PropertyEditorManager.registerEditor(DependantColor.class, BasicDependantColorPropertyEditor.class); + } + if (PropertyEditorManager.findEditor(DependantOriginalColor.class) == null) { + PropertyEditorManager.registerEditor(DependantOriginalColor.class, BasicDependantOriginalColorPropertyEditor.class); + } + if (PropertyEditorManager.findEditor(EdgeColor.class) == null) { + PropertyEditorManager.registerEditor(EdgeColor.class, BasicEdgeColorPropertyEditor.class); + } + } + + /** + * Makes sure that, if more than one plugin extends a default renderer, only the one with the lowest position is enabled initially. + */ + private void initManagedRenderers() { + Renderer[] registeredRenderers = previewController.getRegisteredRenderers(); + + Set replacedRenderers = new HashSet(); + + managedRenderers = new ManagedRenderer[registeredRenderers.length]; + for (int i = 0; i < registeredRenderers.length; i++) { + Renderer r = registeredRenderers[i]; + Class superClass = r.getClass().getSuperclass(); + if (superClass != null && superClass.getName().startsWith("org.gephi.preview.plugin.renderers.")) { + managedRenderers[i] = new ManagedRenderer(r, !replacedRenderers.contains(superClass.getName())); + replacedRenderers.add(superClass.getName()); + } else { + managedRenderers[i] = new ManagedRenderer(r, true); + } + } + } + + private void prepareManagedListeners() { + ArrayList listeners = new ArrayList(); + + for (PreviewMouseListener listener : Lookup.getDefault().lookupAll(PreviewMouseListener.class)) { + for (Renderer renderer : getManagedEnabledRenderers()) { + if (renderer instanceof MouseResponsiveRenderer) { + if (((MouseResponsiveRenderer) renderer).needsPreviewMouseListener(listener) && !listeners.contains(listener)) { + listeners.add(listener); + } + } + } + } + + Collections.reverse(listeners);//First listeners to receive events will be the ones coming from last called renderers. + enabledMouseListeners = listeners.toArray(new PreviewMouseListener[0]); + } + + private synchronized void initProperties() { + if (properties == null) { + properties = new PreviewProperties(); + + //Properties from renderers + for (Renderer renderer : getManagedEnabledRenderers()) { + PreviewProperty[] props = renderer.getProperties(); + for (PreviewProperty p : props) { + properties.addProperty(p); + } + } + + //Default preset + properties.applyPreset(new DefaultPreset()); + + //Defaut values + properties.putValue(PreviewProperty.VISIBILITY_RATIO, 1f); + } + } + + @Override + public PreviewProperties getProperties() { + initProperties(); + return properties; + } + + @Override + public Item[] getItems(String type) { + List list = typeMap.get(type); + if (list != null) { + return list.toArray(new Item[0]); + } + return new Item[0]; + } + + @Override + public Item getItem(String type, Object source) { + Item[] items = getItems(source); + for (Item item : items) { + if (item.getType().equals(type)) { + return item; + } + } + return null; + } + + @Override + public Item[] getItems(Object source) { + Object value = sourceMap.get(source); + if (value instanceof List) { + return ((List) value).toArray(new Item[0]); + } else if (value instanceof Item) { + return new Item[]{(Item) value}; + } + return new Item[0]; + } + + public String[] getItemTypes() { + return typeMap.keySet().toArray(new String[0]); + } + + public void loadItems(String type, Item[] items) { + //Add to type map + List typeList = typeMap.get(type); + if (typeList == null) { + typeList = new ArrayList(items.length); + typeList.addAll(Arrays.asList(items)); + typeMap.put(type, typeList); + + //Add to source map + for (Item item : items) { + Object value = sourceMap.get(item.getSource()); + if (value == null) { + sourceMap.put(item.getSource(), item); + } else if (value instanceof List) { + ((List) value).add(item); + } else { + List list = new ArrayList(); + list.add((Item) value); + list.add(item); + } + } + } else { + //Possible items to merge + for (Item item : items) { + Object value = sourceMap.get(item.getSource()); + if (value == null) { + //No other object attached to this item + typeList.add(item); + sourceMap.put(item.getSource(), item); + } else if (value instanceof Item && ((Item) value).getType().equals(item.getType())) { + //An object already exists with the same type and source, merge them + mergeItems(item, ((Item) value)); + } else if (value instanceof List) { + List list = (List) value; + for (Item itemSameSource : list) { + if (itemSameSource.getType().equals(item.getType())) { + //An object already exists with the same type and source, merge them + mergeItems(item, itemSameSource); + break; + } + } + } + } + } + } + + private Item mergeItems(Item item, Item toBeMerged) { + for (String key : toBeMerged.getKeys()) { + item.setData(key, toBeMerged.getData(key)); + } + return item; + } + + public void clear() { + typeMap.clear(); + sourceMap.clear(); + } + + public Workspace getWorkspace() { + return workspace; + } + + @Override + public Dimension getDimensions() { + return dimensions; + } + + @Override + public Point getTopLeftPosition() { + return topLeftPosition; + } + + public void setDimensions(Dimension dimensions) { + this.dimensions = dimensions; + } + + public void setTopLeftPosition(Point topLeftPosition) { + this.topLeftPosition = topLeftPosition; + } + + @Override + public ManagedRenderer[] getManagedRenderers() { + return managedRenderers; + } + + /** + * Makes sure that managedRenderers contains every renderer existing implementations. If some renderers are not in the list, they are added in default implementation order at the end of the list + * and not enabled. + */ + private void completeManagedRenderersListIfNecessary() { + if (managedRenderers != null) { + Set existing = new HashSet(); + for (ManagedRenderer mr : managedRenderers) { + existing.add(mr.getRenderer().getClass().getName()); + } + + List completeManagedRenderersList = new ArrayList(); + completeManagedRenderersList.addAll(Arrays.asList(managedRenderers)); + + for (Renderer renderer : previewController.getRegisteredRenderers()) { + if (!existing.contains(renderer.getClass().getName())) { + completeManagedRenderersList.add(new ManagedRenderer(renderer, false)); + } + } + + managedRenderers = completeManagedRenderersList.toArray(new ManagedRenderer[0]); + } + } + + /** + * Removes unnecessary properties from not enabled renderers + */ + private void reloadProperties() { + PreviewProperties newProperties = new PreviewProperties();//Ensure that the properties object doesn't change + + //Properties from renderers + for (Renderer renderer : getManagedEnabledRenderers()) { + PreviewProperty[] props = renderer.getProperties(); + for (PreviewProperty p : props) { + newProperties.addProperty(p); + if (properties.hasProperty(p.getName())) { + newProperties.putValue(p.getName(), properties.getValue(p.getName()));//Keep old values + } + } + } + + //Remove old properties (this keeps simple values) + for (PreviewProperty p : properties.getProperties()) { + properties.removeProperty(p); + } + + //Set new properties + for (PreviewProperty property : newProperties.getProperties()) { + properties.addProperty(property); + } + } + + @Override + public void setManagedRenderers(ManagedRenderer[] managedRenderers) { + //Validate no null ManagedRenderers + for (int i = 0; i < managedRenderers.length; i++) { + if (managedRenderers[i] == null) { + throw new IllegalArgumentException("managedRenderers should not contain null values"); + } + } + + this.managedRenderers = managedRenderers; + completeManagedRenderersListIfNecessary(); + prepareManagedListeners(); + reloadProperties(); + } + + @Override + public Renderer[] getManagedEnabledRenderers() { + if (managedRenderers != null) { + ArrayList renderers = new ArrayList(); + for (ManagedRenderer mr : managedRenderers) { + if (mr.isEnabled()) { + renderers.add(mr.getRenderer()); + } + } + return renderers.toArray(new Renderer[0]); + } else { + return null; + } + } + + //PERSISTENCE + public void writeXML(XMLStreamWriter writer) throws XMLStreamException { + writer.writeStartElement("previewmodel"); + + initProperties(); + //Write PreviewProperties: + for (PreviewProperty property : properties.getProperties()) { + String propertyName = property.getName(); + Object propertyValue = property.getValue(); + if (propertyValue != null) { + String text = Serialization.getValueAsText(propertyValue); + if (text != null) { + writer.writeStartElement("previewproperty"); + writer.writeAttribute("name", propertyName); + writer.writeCharacters(text); + writer.writeEndElement(); + } + } + } + + //Write preview simple values: + Iterator> simpleValuesIterator = properties.getSimpleValues().iterator(); + while (simpleValuesIterator.hasNext()) { + Entry simpleValueEntry; + simpleValueEntry = simpleValuesIterator.next(); + + if (simpleValueEntry.getKey().equals("width") + || simpleValueEntry.getKey().equals("height")) { + continue; + } + + Object value = simpleValueEntry.getValue(); + if (value != null) { + Class clazz = value.getClass(); + String text = Serialization.getValueAsText(value); + if (text != null) { + writer.writeStartElement("previewsimplevalue"); + writer.writeAttribute("name", simpleValueEntry.getKey()); + writer.writeAttribute("class", clazz.getName()); + writer.writeCharacters(text); + writer.writeEndElement(); + } + } + } + + //Write model managed renderers: + if (managedRenderers != null) { + for (ManagedRenderer managedRenderer : managedRenderers) { + writer.writeStartElement("managedrenderer"); + writer.writeAttribute("class", managedRenderer.getRenderer().getClass().getName()); + writer.writeAttribute("enabled", String.valueOf(managedRenderer.isEnabled())); + writer.writeEndElement(); + } + } + + + writer.writeEndElement(); + } + + public void readXML(XMLStreamReader reader) throws XMLStreamException { + PreviewProperties props = getProperties(); + + String propName = null; + boolean isSimpleValue = false; + String simpleValueClass = null; + + List managedRenderersList = new ArrayList(); + Map availableRenderers = new HashMap(); + for (Renderer renderer : Lookup.getDefault().lookupAll(Renderer.class)) { + availableRenderers.put(renderer.getClass().getName(), renderer); + Class superClass = renderer.getClass().getSuperclass(); + if (superClass != null && superClass.getName().startsWith("org.gephi.preview.plugin.renderers.")) { + availableRenderers.put(superClass.getName(), renderer);//For plugins replacing a default renderer + } + } + + boolean end = false; + while (reader.hasNext() && !end) { + int type = reader.next(); + + switch (type) { + case XMLStreamReader.START_ELEMENT: + String name = reader.getLocalName(); + if ("previewproperty".equalsIgnoreCase(name)) { + propName = reader.getAttributeValue(null, "name"); + isSimpleValue = false; + } else if ("previewsimplevalue".equalsIgnoreCase(name)) { + propName = reader.getAttributeValue(null, "name"); + simpleValueClass = reader.getAttributeValue(null, "class"); + isSimpleValue = true; + } else if ("managedrenderer".equalsIgnoreCase(name)) { + String rendererClass = reader.getAttributeValue(null, "class"); + if (availableRenderers.containsKey(rendererClass)) { + managedRenderersList.add(new ManagedRenderer(availableRenderers.get(rendererClass), Boolean.parseBoolean(reader.getAttributeValue(null, "enabled")))); + } + } + break; + case XMLStreamReader.CHARACTERS: + if (!reader.isWhiteSpace()) { + if (propName != null) { + if (!isSimpleValue) {//Read PreviewProperty: + PreviewProperty p = props.getProperty(propName); + if (p != null) { + Object value = Serialization.readValueFromText(reader.getText(), p.getType()); + if (value != null) { + p.setValue(value); + } + } + } else {//Read preview simple value: + if (simpleValueClass != null) { + if (!propName.equals("width") + && !propName.equals("height")) { + Object value = Serialization.readValueFromText(reader.getText(), simpleValueClass); + if (value != null) { + props.putValue(propName, value); + } + } + } + } + } + } + break; + case XMLStreamReader.END_ELEMENT: + if ("previewmodel".equalsIgnoreCase(reader.getLocalName())) { + end = true; + } + propName = null; + break; + } + } + + if (!managedRenderersList.isEmpty()) { + setManagedRenderers(managedRenderersList.toArray(new ManagedRenderer[0])); + } + } + + @Override + public PreviewMouseListener[] getEnabledMouseListeners() { + return enabledMouseListeners; + } +} diff --git a/modules/PreviewAPI/src/main/java/org/gephi/preview/ProcessingApplet.java b/modules/PreviewAPI/src/main/java/org/gephi/preview/ProcessingApplet.java index 4c887515c..38f18398d 100644 --- a/modules/PreviewAPI/src/main/java/org/gephi/preview/ProcessingApplet.java +++ b/modules/PreviewAPI/src/main/java/org/gephi/preview/ProcessingApplet.java @@ -45,15 +45,11 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Point; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; +import java.awt.event.*; import java.util.HashMap; import java.util.Timer; import java.util.TimerTask; -import org.gephi.preview.api.PreviewController; -import org.gephi.preview.api.PreviewModel; -import org.gephi.preview.api.PreviewProperty; -import org.gephi.preview.api.RenderTarget; +import org.gephi.preview.api.*; import org.openide.util.Lookup; import processing.core.PApplet; import processing.core.PFont; @@ -143,24 +139,90 @@ public class ProcessingApplet extends PApplet implements MouseWheelListener { super.resizeRenderer(i, i1); } } + + private PVector screenPositionToModelPosition(PVector screenPos) { + PVector center = new PVector(width / 2f, height / 2f); + PVector scaledCenter = PVector.mult(center, scaling); + PVector scaledTrans = PVector.sub(center, scaledCenter); - @Override - public void mousePressed() { - ref.set(mouseX, mouseY, 0); + PVector modelPos = new PVector(screenPos.x, screenPos.y); + modelPos.sub(scaledTrans); + modelPos.div(scaling); + modelPos.sub(trans); + return modelPos; + } + + private PVector getMouseModelPosition(){ + return screenPositionToModelPosition(new PVector(mouseX, mouseY)); + } + + private PreviewMouseEvent buildPreviewMouseEvent(PreviewMouseEvent.Type type){ + PVector pos = getMouseModelPosition(); + PreviewMouseEvent.Button button; + + switch(mouseButton){ + case CENTER: + button = PreviewMouseEvent.Button.MIDDLE; + break; + case RIGHT: + button = PreviewMouseEvent.Button.RIGHT; + break; + case LEFT: + default: + button = PreviewMouseEvent.Button.LEFT; + } + + return new PreviewMouseEvent((int)pos.x, (int)pos.y, type, button, keyEvent); } + @Override + public void mouseClicked() { + if (previewController.sendMouseEvent(buildPreviewMouseEvent(PreviewMouseEvent.Type.CLICKED))) { + previewController.refreshPreview(); + redraw(); + } + } + + @Override + public void mousePressed() { + previewController.sendMouseEvent(buildPreviewMouseEvent(PreviewMouseEvent.Type.PRESSED)); + + previewController.refreshPreview(); + handleMousePress(); + redraw(); + } + @Override public void mouseDragged() { - setMoving(true); - trans.set(mouseX, mouseY, 0); - trans.sub(ref); - trans.div(scaling); // ensure const. moving speed whatever the zoom is - trans.add(lastMove); + if (!previewController.sendMouseEvent(buildPreviewMouseEvent(PreviewMouseEvent.Type.DRAGGED))) { + handleMouseDrag(); + } redraw(); } @Override public void mouseReleased() { + if (!previewController.sendMouseEvent(buildPreviewMouseEvent(PreviewMouseEvent.Type.RELEASED))) { + handleMouseRelease(); + } + + previewController.refreshPreview(); + redraw(); + } + + private void handleMousePress(){ + ref.set(mouseX, mouseY, 0); + } + + private void handleMouseDrag(){ + setMoving(true); + trans.set(mouseX, mouseY, 0); + trans.sub(ref); + trans.div(scaling); // ensure const. moving speed whatever the zoom is + trans.add(lastMove); + } + + private void handleMouseRelease() { lastMove.set(trans); setMoving(false); redraw(); diff --git a/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewController.java b/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewController.java index 18c18c8dd..f21ec60d9 100644 --- a/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewController.java +++ b/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewController.java @@ -169,4 +169,19 @@ public interface PreviewController { * @return True if any plugin renderer is found in the system */ public boolean isAnyPluginRendererRegistered(); + + /** + * Sends a PreviewMouseEvent to the current workspace, if any. + * @param event PreviewMouseEvent + * @return True if the event was consumed, false otherwise + */ + public boolean sendMouseEvent(PreviewMouseEvent event); + + /** + * Sends a PreviewMouseEvent to the given workspace. + * @param event PreviewMouseEvent + * @param workspace + * @return True if the event was consumed, false otherwise + */ + public boolean sendMouseEvent(PreviewMouseEvent event, Workspace workspace); } diff --git a/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewModel.java b/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewModel.java index 5bc97bbff..db9fa7afc 100644 --- a/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewModel.java +++ b/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewModel.java @@ -1,43 +1,43 @@ /* -Copyright 2008-2011 Gephi -Authors : Yudi Xue , Mathieu Bastian -Website : http://www.gephi.org + Copyright 2008-2011 Gephi + Authors : Yudi Xue , Mathieu Bastian + Website : http://www.gephi.org -This file is part of Gephi. + This file is part of Gephi. -DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. -Copyright 2011 Gephi Consortium. All rights reserved. + Copyright 2011 Gephi Consortium. All rights reserved. -The contents of this file are subject to the terms of either the GNU -General Public License Version 3 only ("GPL") or the Common -Development and Distribution License("CDDL") (collectively, the -"License"). You may not use this file except in compliance with the -License. You can obtain a copy of the License at -http://gephi.org/about/legal/license-notice/ -or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the -specific language governing permissions and limitations under the -License. When distributing the software, include this License Header -Notice in each file and include the License files at -/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the -License Header, with the fields enclosed by brackets [] replaced by -your own identifying information: -"Portions Copyrighted [year] [name of copyright owner]" + The contents of this file are subject to the terms of either the GNU + General Public License Version 3 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with the + License. You can obtain a copy of the License at + http://gephi.org/about/legal/license-notice/ + or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the + specific language governing permissions and limitations under the + License. When distributing the software, include this License Header + Notice in each file and include the License files at + /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the + License Header, with the fields enclosed by brackets [] replaced by + your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" -If you wish your version of this file to be governed by only the CDDL -or only the GPL Version 3, indicate your decision by adding -"[Contributor] elects to include this software in this distribution -under the [CDDL or GPL Version 3] license." If you do not indicate a -single choice of license, a recipient has the option to distribute -your version of this file under either the CDDL, the GPL Version 3 or -to extend the choice of license to its licensees as provided above. -However, if you add GPL Version 3 code and therefore, elected the GPL -Version 3 license, then the option applies only if the new code is -made subject to such option by the copyright holder. + If you wish your version of this file to be governed by only the CDDL + or only the GPL Version 3, indicate your decision by adding + "[Contributor] elects to include this software in this distribution + under the [CDDL or GPL Version 3] license." If you do not indicate a + single choice of license, a recipient has the option to distribute + your version of this file under either the CDDL, the GPL Version 3 or + to extend the choice of license to its licensees as provided above. + However, if you add GPL Version 3 code and therefore, elected the GPL + Version 3 license, then the option applies only if the new code is + made subject to such option by the copyright holder. -Contributor(s): + Contributor(s): -Portions Copyrighted 2011 Gephi Consortium. + Portions Copyrighted 2011 Gephi Consortium. */ package org.gephi.preview.api; @@ -47,19 +47,15 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; import org.gephi.preview.spi.ItemBuilder; +import org.gephi.preview.spi.PreviewMouseListener; import org.gephi.preview.spi.Renderer; /** - * The Preview Model contains all items and all preview properties. - *

- * Items are the visual elements built from the {@link Graph} by {@link ItemBuilder} - * implementations and can be retrieved from this class. Each item has a type and - * default types are {@link Item#NODE}, {@link Item#EDGE}, {@link Item#NODE_LABEL} - * and {@link Item#EDGE_LABEL}. - *

- * A preview model is attached to it's workspace and can be retrieved from the + * The Preview Model contains all items and all preview properties.

Items are the visual elements built from the {@link Graph} by {@link ItemBuilder} implementations and can be retrieved from this + * class. Each item has a type and default types are {@link Item#NODE}, {@link Item#EDGE}, {@link Item#NODE_LABEL} and {@link Item#EDGE_LABEL}.

A preview model is attached to it's workspace and + * can be retrieved from the * {@link PreviewController}. - * + * * @author Yudi Xue, Mathieu Bastian * @see Item * @see Renderer @@ -68,78 +64,88 @@ public interface PreviewModel { /** * Returns the preview properties attached to this model. + * * @return the preview properties */ public PreviewProperties getProperties(); /** - * Returns all items with type as type. - *

- * Default types are {@link Item#NODE}, {@link Item#EDGE}, {@link Item#NODE_LABEL} - * and {@link Item#EDGE_LABEL}. + * Returns all items with + * type as type.

Default types are {@link Item#NODE}, {@link Item#EDGE}, {@link Item#NODE_LABEL} and {@link Item#EDGE_LABEL}. + * * @param type the item's type * @return all items from this type */ public Item[] getItems(String type); /** - * Returns all items attached to source. - *

- * The source is the graph object behind the item (e.g. - * {@link Node} or {@link Edge}). Multiple items can be created from the same - * source object. For instance both Item.NODE and + * Returns all items attached to + * source.

The source is the graph object behind the item (e.g. + * {@link Node} or {@link Edge}). Multiple items can be created from the same source object. For instance both + * Item.NODE and * Item.NODE_LABEL have the node object as source. + * * @param source the item's source - * @return all items with source as source + * @return all items with + * source as source */ public Item[] getItems(Object source); /** - * Returns the item attached to source and with the type - * type. - *

- * The source is the graph object behind the item (e.g. - * {@link Node} or {@link Edge}) and the type a default or a custom type. - *

- * Default types are {@link Item#NODE}, {@link Item#EDGE}, {@link Item#NODE_LABEL} - * and {@link Item#EDGE_LABEL}. + * Returns the item attached to + * source and with the type + * type.

The source is the graph object behind the item (e.g. + * {@link Node} or {@link Edge}) and the type a default or a custom type.

Default types are {@link Item#NODE}, {@link Item#EDGE}, {@link Item#NODE_LABEL} and {@link Item#EDGE_LABEL}. + * * @param type the item's type * @param source the item's source object - * @return the item or null if not found + * @return the item or + * null if not found */ public Item getItem(String type, Object source); - + /** - *

Returns currently managed renderers, or null.

- *

If managedRenderers is set to null, all renderers will be executed when rendering, in default implementation order.

+ *

Returns currently managed renderers, or null.

If + * managedRenderers is set to null, all renderers will be executed when rendering, in default implementation order.

+ * * @return Enabled renderers or null */ public ManagedRenderer[] getManagedRenderers(); /** - *

Sets an user-defined array of managed renderers to use when rendering.

- *

Only the renderers marked as enabled will be executed when rendering, and respecting the array order

- *

If the input array does not contain a managed renderer for some renderer existing implementation, a new not enabled managed renderer will be added to the end of the input array

- *

If managedRenderers is set to null, all renderers will be executed when rendering, in default implementation order.

+ *

Sets an user-defined array of managed renderers to use when rendering.

Only the renderers marked as enabled will be executed when rendering, and respecting the array + * order

If the input array does not contain a managed renderer for some renderer existing implementation, a new not enabled managed renderer will be added to the end of the input + * array

If + * managedRenderers is set to null, all renderers will be executed when rendering, in default implementation order.

+ * * @param managedRenderers Managed renderers for future renderings */ public void setManagedRenderers(ManagedRenderer[] managedRenderers); - + /** - * Returns managedRenderers Renderers that are enabled, or null if managedRenderers is null. + * Returns + * managedRenderers Renderers that are enabled, or null if + * managedRenderers is null. + * * @return Enabled renderers or null */ public Renderer[] getManagedEnabledRenderers(); + /* + * Returns managedPreviewMouseListeners containing the PreviewMouseListeners that are declared by the current enabled managed renderers. + */ + public PreviewMouseListener[] getEnabledMouseListeners(); + /** * Returns the width and height of the graph in the graph coordinates. + * * @return the graph dimensions */ public Dimension getDimensions(); /** - * Returns the top left position in the graph coordinate (i.e. not the preview - * coordinates). + * Returns the top left position in the graph coordinate (i.e. not the preview coordinates). + * * @return the top left position point */ public Point getTopLeftPosition(); diff --git a/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewMouseEvent.java b/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewMouseEvent.java new file mode 100644 index 000000000..dcceb793a --- /dev/null +++ b/modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewMouseEvent.java @@ -0,0 +1,94 @@ +/* + Copyright 2008-2012 Gephi + Authors : Eduardo Ramos + Website : http://www.gephi.org + + This file is part of Gephi. + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2011 Gephi Consortium. All rights reserved. + + The contents of this file are subject to the terms of either the GNU + General Public License Version 3 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with the + License. You can obtain a copy of the License at + http://gephi.org/about/legal/license-notice/ + or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the + specific language governing permissions and limitations under the + License. When distributing the software, include this License Header + Notice in each file and include the License files at + /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the + License Header, with the fields enclosed by brackets [] replaced by + your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + If you wish your version of this file to be governed by only the CDDL + or only the GPL Version 3, indicate your decision by adding + "[Contributor] elects to include this software in this distribution + under the [CDDL or GPL Version 3] license." If you do not indicate a + single choice of license, a recipient has the option to distribute + your version of this file under either the CDDL, the GPL Version 3 or + to extend the choice of license to its licensees as provided above. + However, if you add GPL Version 3 code and therefore, elected the GPL + Version 3 license, then the option applies only if the new code is + made subject to such option by the copyright holder. + + Contributor(s): + + Portions Copyrighted 2012 Gephi Consortium. + */ +package org.gephi.preview.api; + +import java.awt.event.KeyEvent; + +/** + *

Mouse event for preview. Contains the event type and graph coordinates for the event. + * If you attend a PreviewMouseEvent, it should be marked as consumed.

+ *

The public keyEvent field contains the keyboard state for the given mouse event. Can be null.

+ * @author Eduardo Ramos + */ +public class PreviewMouseEvent { + + public enum Type { + CLICKED, + PRESSED, + RELEASED, + DRAGGED + } + + public enum Button{ + LEFT, + RIGHT, + MIDDLE + } + + public final Type type; + public final Button button; + public final int x; + public final int y; + private boolean consumed; + + /** + * Contains the keyboard state for the given mouse event. Can be null. + */ + public final KeyEvent keyEvent; + + public PreviewMouseEvent(int x, int y, Type type, Button button, KeyEvent keyEvent) { + this.x = x; + this.y = y; + this.type = type; + this.button = button; + this.keyEvent = keyEvent; + consumed = false; + } + + public boolean isConsumed() { + return consumed; + } + + public void setConsumed(boolean consumed) { + this.consumed = consumed; + } +} diff --git a/modules/PreviewAPI/src/main/java/org/gephi/preview/spi/MouseResponsiveRenderer.java b/modules/PreviewAPI/src/main/java/org/gephi/preview/spi/MouseResponsiveRenderer.java new file mode 100644 index 000000000..29c7c69da --- /dev/null +++ b/modules/PreviewAPI/src/main/java/org/gephi/preview/spi/MouseResponsiveRenderer.java @@ -0,0 +1,51 @@ +/* + Copyright 2008-2012 Gephi + Authors : Eduardo Ramos + Website : http://www.gephi.org + + This file is part of Gephi. + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2011 Gephi Consortium. All rights reserved. + + The contents of this file are subject to the terms of either the GNU + General Public License Version 3 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with the + License. You can obtain a copy of the License at + http://gephi.org/about/legal/license-notice/ + or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the + specific language governing permissions and limitations under the + License. When distributing the software, include this License Header + Notice in each file and include the License files at + /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the + License Header, with the fields enclosed by brackets [] replaced by + your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + If you wish your version of this file to be governed by only the CDDL + or only the GPL Version 3, indicate your decision by adding + "[Contributor] elects to include this software in this distribution + under the [CDDL or GPL Version 3] license." If you do not indicate a + single choice of license, a recipient has the option to distribute + your version of this file under either the CDDL, the GPL Version 3 or + to extend the choice of license to its licensees as provided above. + However, if you add GPL Version 3 code and therefore, elected the GPL + Version 3 license, then the option applies only if the new code is + made subject to such option by the copyright holder. + + Contributor(s): + + Portions Copyrighted 2012 Gephi Consortium. + */ +package org.gephi.preview.spi; + +/** + * Optionally implement this interface in a Renderer that needs to be responsive to mouse events. + * Only renderers that implement this interface will be drawn while mouse events are being attended (such as dragging). + * @author Eduardo Ramos + */ +public interface MouseResponsiveRenderer { + public boolean needsPreviewMouseListener(PreviewMouseListener previewMouseListener); +} diff --git a/modules/PreviewAPI/src/main/java/org/gephi/preview/spi/PreviewMouseListener.java b/modules/PreviewAPI/src/main/java/org/gephi/preview/spi/PreviewMouseListener.java new file mode 100644 index 000000000..70db92223 --- /dev/null +++ b/modules/PreviewAPI/src/main/java/org/gephi/preview/spi/PreviewMouseListener.java @@ -0,0 +1,89 @@ +/* + Copyright 2008-2012 Gephi + Authors : Eduardo Ramos + Website : http://www.gephi.org + + This file is part of Gephi. + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2011 Gephi Consortium. All rights reserved. + + The contents of this file are subject to the terms of either the GNU + General Public License Version 3 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with the + License. You can obtain a copy of the License at + http://gephi.org/about/legal/license-notice/ + or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the + specific language governing permissions and limitations under the + License. When distributing the software, include this License Header + Notice in each file and include the License files at + /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the + License Header, with the fields enclosed by brackets [] replaced by + your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + If you wish your version of this file to be governed by only the CDDL + or only the GPL Version 3, indicate your decision by adding + "[Contributor] elects to include this software in this distribution + under the [CDDL or GPL Version 3] license." If you do not indicate a + single choice of license, a recipient has the option to distribute + your version of this file under either the CDDL, the GPL Version 3 or + to extend the choice of license to its licensees as provided above. + However, if you add GPL Version 3 code and therefore, elected the GPL + Version 3 license, then the option applies only if the new code is + made subject to such option by the copyright holder. + + Contributor(s): + + Portions Copyrighted 2012 Gephi Consortium. + */ +package org.gephi.preview.spi; + +import org.gephi.preview.api.PreviewMouseEvent; +import org.gephi.preview.api.PreviewProperties; +import org.gephi.project.api.Workspace; + +/** + *

Listener for mouse events in Preview.

+ *

Listeners will always receive left mouse button events. Right button is reserved for zooming and moving the canvas

+ * + *

In order to enable a PreviewMouseListener, annotate it with ServiceProvider annotation and implement MouseResponsiveRenderer + * in a Renderer and return true for the listener in the needsPreviewMouseListener method.

+ * @author Eduardo Ramos + */ +public interface PreviewMouseListener { + + /** + * A single click event. + * @param event Mouse event + * @param properties Preview properties for the workspace + * @param workspace Current workspace + */ + public void mouseClicked(PreviewMouseEvent event, PreviewProperties properties, Workspace workspace); + + /** + * A mouse press event. If your listener needs to receive drag or release events, you must mark the previous press event as consumed. + * @param event Mouse event + * @param properties Preview properties for the workspace + * @param workspace Current workspace + */ + public void mousePressed(PreviewMouseEvent event, PreviewProperties properties, Workspace workspace); + + /** + * If your listener needs to receive drag events, you must mark the previous press event as consumed. + * @param event Mouse event + * @param properties Preview properties for the workspace + * @param workspace Current workspace + */ + public void mouseDragged(PreviewMouseEvent event, PreviewProperties properties, Workspace workspace); + + /** + * If your listener needs to receive release events, you must mark the previous press event as consumed. + * @param event Mouse event + * @param properties Preview properties for the workspace + * @param workspace Current workspace + */ + public void mouseReleased(PreviewMouseEvent event, PreviewProperties properties, Workspace workspace); +} diff --git a/modules/PreviewAPI/src/main/resources/org/gephi/preview/spi/package.html b/modules/PreviewAPI/src/main/resources/org/gephi/preview/spi/package.html index 9a4f0a001..bf8799f95 100644 --- a/modules/PreviewAPI/src/main/resources/org/gephi/preview/spi/package.html +++ b/modules/PreviewAPI/src/main/resources/org/gephi/preview/spi/package.html @@ -1,62 +1,69 @@ - - - - Interfaces for creating new renderers, item builders and render targets. -

Create a new Item Builder

-
  1. Create a new module and set Preview API, - Graph API, AttributesAPI and - Lookup as dependencies.
  2. -
  3. Create a new item class which implements Item or - extends AbstractItem. The AbstractItem - class is located in the PreviewPlugin module so add - it as dependency first. An item should be very simple but has a - unique identifier returned by its getType() method.
  4. -
  5. Create a new builder class that implements ItemBuilder
  6. -
  7. Implement the getType() method and returns the same - identifier than the Item you created earlier.
  8. -
  9. Implement the getItems() method by retrieving objects - from the given graph.
  10. -
  11. Add @ServiceProvider annotation to your builder, that it can - be found by the system. Set ItemBuilder as the - annotation parameter.
  12. -
-

Create a new Renderer

-
  1. Create a new module and set Preview API, - GraphAPI, Processing Wrapper, - iText Wrapper and Lookup as dependencies.
  2. -
  3. Create a new class that implements Renderer.
  4. -
  5. Implement the renderer methods.
  6. -
  7. Add @ServiceProvider annotation to your builder, that it can - be found by the system. Set Renderer as the - annotation parameter.
  8. -
-

Add data to an existing item

- To add an additional data attribute to a Node or Edge item, you need to create - a new item builder for the specific type. For instance if one want to add - a new attribute to nodes create a new ItemBuilder for the - type Item.Node. Simply return item objects with the data you - want to add. The system will automatically merge your new data to node items. -

Override a Renderer

- Default renderers can be completely replaced by custom implementations. The - implementation needs to customize its @ServiceProvider annotation - with the renderer path it is overriding: -
    -
  • @ServiceProvider(service=Renderer.class, supersedes="org.gephi.preview.plugin.renderers.NodeRenderer")
  • -
  • @ServiceProvider(service=Renderer.class, supersedes="org.gephi.preview.plugin.renderers.EdgeRenderer")
  • -
  • @ServiceProvider(service=Renderer.class, supersedes="org.gephi.preview.plugin.renderers.NodeLabelRenderer")
  • -
  • @ServiceProvider(service=Renderer.class, supersedes="org.gephi.preview.plugin.renderers.EdgeLabelRenderer")
  • -
  • @ServiceProvider(service=Renderer.class, supersedes="org.gephi.preview.plugin.renderers.ArrowRenderer")
  • -
-

Add a new PreviewUI settings panel

- Plug-ins can add UI components to the Preview Settings module. Additional components are placed in new tabs and have access to the - current PreviewModel and therefore PreviewProperties. -
  1. Create a new module and set Preview API and - Lookup as dependencies.
  2. -
  3. Create a new class that implements PreviewUI and implements - methods.
  4. -
  5. Add @ServiceProvider annotation to your builder, that it can - be found by the system. Set PreviewUI as the - 'service' annotation parameter.
  6. -
- + + + + Interfaces for creating new renderers, item builders and render targets. +

Create a new Item Builder

+
  1. Create a new module and set Preview API, + Graph API, AttributesAPI and + Lookup as dependencies.
  2. +
  3. Create a new item class which implements Item or + extends AbstractItem. The AbstractItem + class is located in the PreviewPlugin module so add + it as dependency first. An item should be very simple but has a + unique identifier returned by its getType() method.
  4. +
  5. Create a new builder class that implements ItemBuilder
  6. +
  7. Implement the getType() method and returns the same + identifier than the Item you created earlier.
  8. +
  9. Implement the getItems() method by retrieving objects + from the given graph.
  10. +
  11. Add @ServiceProvider annotation to your builder, that it can + be found by the system. Set ItemBuilder as the + annotation parameter.
  12. +
+

Create a new Renderer

+
  1. Create a new module and set Preview API, + GraphAPI, Processing Wrapper, + iText Wrapper and Lookup as dependencies.
  2. +
  3. Create a new class that implements Renderer.
  4. +
  5. Implement the renderer methods.
  6. +
  7. Add @ServiceProvider annotation to your builder, that it can + be found by the system. Set Renderer as the + annotation parameter.
  8. +
+

Add data to an existing item

+ To add an additional data attribute to a Node or Edge item, you need to create + a new item builder for the specific type. For instance if one want to add + a new attribute to nodes create a new ItemBuilder for the + type Item.Node. Simply return item objects with the data you + want to add. The system will automatically merge your new data to node items. +

Extend or replace an existing renderer

+

To extend or completely replace a default Renderer by your own implementation, + create a new Renderer and set the annotation like below. In addition add Preview Plugin module as a dependency. +

+ @ServiceProvider(service=Renderer.class, position=XXX) + public class MyRenderer extends NodeRenderer + +

Being XXX the new position of the renderer + Then you can reuse parts of the base class or just override them. +

Default renderers are: +

+
    +
  • org.gephi.preview.plugin.renderers.NodeRenderer
  • +
  • org.gephi.preview.plugin.renderers.EdgeRenderer
  • +
  • org.gephi.preview.plugin.renderers.NodeLabelRenderer
  • +
  • org.gephi.preview.plugin.renderers.EdgeLabelRenderer
  • +
  • org.gephi.preview.plugin.renderers.ArrowRenderer
  • +
+

Add a new PreviewUI settings panel

+ Plug-ins can add UI components to the Preview Settings module. Additional components are placed in new tabs and have access to the + current PreviewModel and therefore PreviewProperties. +
  1. Create a new module and set Preview API and + Lookup as dependencies.
  2. +
  3. Create a new class that implements PreviewUI and implements + methods.
  4. +
  5. Add @ServiceProvider annotation to your builder, that it can + be found by the system. Set PreviewUI as the + 'service' annotation parameter.
  6. +
+ \ No newline at end of file diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html index f6d463514..b4400f384 100644 --- a/src/main/javadoc/overview.html +++ b/src/main/javadoc/overview.html @@ -19,6 +19,8 @@

API Changes

    +
  • (December 07 2012) Add support for mouse listeners in Preview plugins. Create a PreviewMouseListener and implement MouseResponsiveRenderer interface in the renderers that use the listener. +
  • (April 10 2012) Add a getShortDescription() method to the StatisticsUI API. It enables to get a short description of statistics (used to display tooltips).
  • (March 26 2012) Add a needsItemBuilder method to Renderer in Preview API.