001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.ui.graphicGraph; 033 034import java.util.regex.Matcher; 035import java.util.regex.Pattern; 036 037import org.graphstream.graph.implementations.AbstractElement; 038import org.graphstream.ui.graphicGraph.stylesheet.Selector; 039import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants; 040 041/** 042 * Super class of all graphic node, edge, and sprite elements. 043 * 044 * <p> 045 * Each graphic element references a style, a graphic graph and has a label. 046 * </p> 047 * 048 * <p> 049 * The element also defines the basic behaviour to reload the style when needed, 050 * defines abstract methods to set and get the position and bounds in spaces of 051 * the element, and to do appropriate actions when specific predefined 052 * attributes change (most of them starting with the prefix "ui."). 053 * </p> 054 * 055 * <p> 056 * The graphic element has the ability to store attributes like any other graph 057 * element, however the attributes stored by the graphic element are restricted. 058 * There is a filter on the attribute adding methods that let pass only : 059 * <ul> 060 * <li>All attributes starting with "ui.".</li> 061 * <li>The "x", "y", "z", "xy" and "xyz" attributes.</li> 062 * <li>The "stylesheet" attribute.</li> 063 * <li>The "label" attribute.</li> 064 * </ul> 065 * All other attributes are filtered and not stored. The result is that if the 066 * graphic graph is used as an input (a source of graph events) some attributes 067 * will not pass through the filter. 068 * </p> 069 */ 070public abstract class GraphicElement extends AbstractElement { 071 /** 072 * Interface for renderers registered in each style group. 073 */ 074 public interface SwingElementRenderer { 075 } 076 077 /** 078 * Graph containing this element. 079 */ 080 protected GraphicGraph mygraph; 081 082 /** 083 * The label or null if not specified. 084 */ 085 public String label; 086 087 /** 088 * The node style. 089 */ 090 public StyleGroup style; 091 092 /** 093 * Associated GUI component. 094 */ 095 public Object component; 096 097 /** 098 * Do not show. 099 */ 100 public boolean hidden = false; 101 102 /** 103 * New element. 104 */ 105 public GraphicElement(String id, GraphicGraph graph) { 106 super(id); 107 this.mygraph = graph; 108 } 109 110 public GraphicGraph myGraph() { 111 return mygraph; 112 } 113 114 @Override 115 protected boolean nullAttributesAreErrors() { 116 return mygraph.nullAttributesAreErrors(); 117 } 118 119 /** 120 * Type of selector for the graphic element (Node, Edge, Sprite ?). 121 */ 122 public abstract Selector.Type getSelectorType(); 123 124 /** 125 * Style group. An style group may reference several elements. 126 * 127 * @return The style group corresponding to this element. 128 */ 129 public StyleGroup getStyle() { 130 return style; 131 } 132 133 /** 134 * Label or null if not set. 135 */ 136 public String getLabel() { 137 return label; 138 } 139 140 /** 141 * Abscissa of the element, always in GU (graph units). For edges this is 142 * the X of the "from" node. 143 */ 144 public abstract double getX(); 145 146 /** 147 * Ordinate of the element, always in GU (graph units). For edges this is 148 * the Y of the "from" node. 149 */ 150 public abstract double getY(); 151 152 /** 153 * Depth of the element, always in GU (graph units). For edges this is the Z 154 * of the "from" node. 155 */ 156 public abstract double getZ(); 157 158 /** 159 * The associated GUI component. 160 * 161 * @return An object. 162 */ 163 public Object getComponent() { 164 return component; 165 } 166 167 // Commands 168 169 /** 170 * The graphic element was removed from the graphic graph, clean up. 171 */ 172 protected abstract void removed(); 173 174 /** 175 * Try to force the element to move at the give location in graph units 176 * (GU). For edges, this may move the two attached nodes. 177 * 178 * @param x 179 * The new X. 180 * @param y 181 * The new Y. 182 * @param z 183 * the new Z. 184 */ 185 public abstract void move(double x, double y, double z); 186 187 /** 188 * Set the GUI component of this element. 189 * 190 * @param component 191 * The component. 192 */ 193 public void setComponent(Object component) { 194 this.component = component; 195 } 196 197 /** 198 * Handle the "ui.class", "label", "ui.style", etc. attributes. 199 */ 200 @Override 201 protected void attributeChanged(AttributeChangeEvent event, 202 String attribute, Object oldValue, Object newValue) { 203 if (event == AttributeChangeEvent.ADD 204 || event == AttributeChangeEvent.CHANGE) { 205 if (attribute.charAt(0) == 'u' && attribute.charAt(1) == 'i') { 206 if (attribute.equals("ui.class")) { 207 mygraph.styleGroups.checkElementStyleGroup(this); 208 // mygraph.styleGroups.removeElement( tis ); 209 // mygraph.styleGroups.addElement( this ); 210 mygraph.graphChanged = true; 211 } else if (attribute.equals("ui.label")) { 212 label = StyleConstants.convertLabel(newValue); 213 mygraph.graphChanged = true; 214 } else if (attribute.equals("ui.style")) { 215 // Cascade the new style in the style sheet. 216 217 if (newValue instanceof String) { 218 try { 219 mygraph.styleSheet.parseStyleFromString( 220 new Selector(getSelectorType(), getId(), 221 null), (String) newValue); 222 } catch (java.io.IOException e) { 223 System.err.printf( 224 "Error while parsing style for %S '%s' :", 225 getSelectorType(), getId()); 226 System.err.printf(" %s%n", e.getMessage()); 227 System.err.printf(" The style was ignored"); 228 } 229 230 mygraph.graphChanged = true; 231 } else { 232 System.err.printf("ERROR !!%n"); 233 } 234 } else if (attribute.equals("ui.hide")) { 235 hidden = true; 236 mygraph.graphChanged = true; 237 } else if (attribute.equals("ui.clicked")) { 238 style.pushEventFor(this, "clicked"); 239 mygraph.graphChanged = true; 240 } else if (attribute.equals("ui.selected")) { 241 style.pushEventFor(this, "selected"); 242 mygraph.graphChanged = true; 243 } else if (attribute.equals("ui.color")) { 244 style.pushElementAsDynamic(this); 245 mygraph.graphChanged = true; 246 } else if (attribute.equals("ui.size")) { 247 style.pushElementAsDynamic(this); 248 mygraph.graphChanged = true; 249 } else if (attribute.equals("ui.icon")) { 250 mygraph.graphChanged = true; 251 } 252 // else if( attribute.equals( "ui.state" ) ) 253 // { 254 // if( newValue == null ) 255 // state = null; 256 // else if( newValue instanceof String ) 257 // state = (String) newValue; 258 // } 259 } else if (attribute.equals("label")) { 260 label = StyleConstants.convertLabel(newValue); 261 mygraph.graphChanged = true; 262 } 263 } else // REMOVE 264 { 265 if (attribute.charAt(0) == 'u' && attribute.charAt(1) == 'i') { 266 if (attribute.equals("ui.class")) { 267 Object o = attributes.remove("ui.class"); // Not yet removed 268 // at 269 // this point ! 270 mygraph.styleGroups.checkElementStyleGroup(this); 271 attributes.put("ui.class", o); 272 mygraph.graphChanged = true; 273 } else if (attribute.equals("ui.label")) { 274 label = ""; 275 mygraph.graphChanged = true; 276 } else if (attribute.equals("ui.hide")) { 277 hidden = false; 278 mygraph.graphChanged = true; 279 } else if (attribute.equals("ui.clicked")) { 280 style.popEventFor(this, "clicked"); 281 mygraph.graphChanged = true; 282 } else if (attribute.equals("ui.selected")) { 283 style.popEventFor(this, "selected"); 284 mygraph.graphChanged = true; 285 } else if (attribute.equals("ui.color")) { 286 style.popElementAsDynamic(this); 287 mygraph.graphChanged = true; 288 } else if (attribute.equals("ui.size")) { 289 style.popElementAsDynamic(this); 290 mygraph.graphChanged = true; 291 } 292 } else if (attribute.equals("label")) { 293 label = ""; 294 mygraph.graphChanged = true; 295 } 296 } 297 } 298 299 // Overriding of standard attribute changing to filter them. 300 301 protected static Pattern acceptedAttribute; 302 303 static { 304 acceptedAttribute = Pattern 305 .compile("(ui[.].*)|(layout[.].*)|x|y|z|xy|xyz|label|stylesheet"); 306 } 307 308 @Override 309 public void addAttribute(String attribute, Object... values) { 310 Matcher matcher = acceptedAttribute.matcher(attribute); 311 312 if (matcher.matches()) 313 super.addAttribute(attribute, values); 314 } 315}