All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graphstream.ui.swing.renderer.JComponentRenderer Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of GraphStream .
 * 
 * GraphStream is a library whose purpose is to handle static or dynamic
 * graph, create them from scratch, file or any source and display them.
 * 
 * This program is free software distributed under the terms of two licenses, the
 * CeCILL-C license that fits European law, and the GNU Lesser General Public
 * License. You can  use, modify and/ or redistribute the software under the terms
 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
 * URL  or under the terms of the GNU LGPL as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 * 
 * This program 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 * 
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
 */

 /**
  * @author Antoine Dutot 
  * @author Guilhelm Savin 
  * @author Hicham Brahimi 
  */
  
package org.graphstream.ui.swing.renderer;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.HashMap;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.Border;

import org.graphstream.ui.geom.Point3;
import org.graphstream.ui.graphicGraph.GraphicElement;
import org.graphstream.ui.graphicGraph.GraphicNode;
import org.graphstream.ui.graphicGraph.GraphicSprite;
import org.graphstream.ui.graphicGraph.StyleGroup;
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants;
import org.graphstream.ui.graphicGraph.stylesheet.Values;
import org.graphstream.ui.swing.Backend;
import org.graphstream.ui.view.camera.DefaultCamera2D;
import org.graphstream.ui.swing.SwingGraphRenderer;
import org.graphstream.ui.swing.renderer.shape.swing.basicShapes.SquareShape;
import org.graphstream.ui.swing.util.ColorManager;
import org.graphstream.ui.swing.util.FontCache;
import org.graphstream.ui.swing.util.ImageCache;
import org.graphstream.ui.view.util.GraphMetrics;

public class JComponentRenderer extends StyleRenderer {
	
	private SwingGraphRenderer mainRenderer;
	private StyleGroup styleGroup;
	
	/** The size of components. */
	protected Values size = null;
	
	/** The size in PX of components. */
	protected int width = 0;
	
	/** The size in PX of components. */
 	protected int height = 0;
	
	/** Association between Swing components and graph elements. */
	protected HashMap compToElement = new HashMap<>();

	/** The potential shadow. */
	protected SquareShape shadow = null;
 
	protected Object antialiasSetting = null;
	
	public JComponentRenderer(StyleGroup styleGroup, SwingGraphRenderer mainRenderer) {
		super(styleGroup);
		this.styleGroup = styleGroup ;
		this.mainRenderer = mainRenderer ;
	}

	@Override
	public void setupRenderingPass(Backend bck, DefaultCamera2D camera, boolean forShadow) {
		GraphMetrics metrics = camera.getMetrics();
		Graphics2D g = bck.graphics2D();

		size   = group.getSize();
		width  = (int)metrics.lengthToPx(size, 0);
		height = width ;
		if(size.size() > 1)
			height = (int)metrics.lengthToPx(size, 1) ;
  
		if(group.getShadowMode() != StyleConstants.ShadowMode.NONE)
			shadow = new SquareShape();
		else 
			shadow = null;
		
		antialiasSetting = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
		g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
	}

	@Override
	public void pushStyle(Backend bck, DefaultCamera2D camera, boolean forShadow) {
		if(shadow != null) {
			shadow.configureForGroup(bck, group, camera);
//		  	shadow.configure(bck, group, camera, null)
//		  	shadow.size(group, camera)
		}		
	}

	@Override
	public void pushDynStyle(Backend bck, DefaultCamera2D camera, GraphicElement element) {}

	@Override
	public void renderElement(Backend bck, DefaultCamera2D camera, GraphicElement element) {
		ComponentElement ce = getOrEquipWithJComponent(element);

		ce.setVisible(true);
		ce.updatePosition(camera);
		ce.updateLabel();
	
		if(ce.init == false)
		     checkStyle(camera, ce, true);
		else if(group.hasEventElements())
		     checkStyle(camera, ce, ! hadEvents);	// hadEvents allows to know if we just
		else checkStyle(camera, ce, hadEvents);		// changed the style due to an event	
	}												// and therefore must change the style.

	@Override
	public void renderShadow(Backend bck, DefaultCamera2D camera, GraphicElement element) {
		if(shadow != null) {
//			val pos = new Point2D.Double( element.getX, element.getY )
//
//			if( element.isInstanceOf[GraphicSprite] ) {
//				camera.getSpritePosition( element.asInstanceOf[GraphicSprite], pos, StyleConstants.Units.GU )
//			}
//			
////			shadow.setupContents( g, camera, element, null )
//			shadow.positionAndFit( g, camera, null, element, pos.x, pos.y )
			shadow.configureForElement(bck, element, null, camera);
			shadow.renderShadow(bck, camera, element, null);
		}
	}

	@Override
	public void elementInvisible(Backend bck, DefaultCamera2D camera, GraphicElement element) {
		getOrEquipWithJComponent(element).setVisible(false);		
	}

	@Override
	public void endRenderingPass(Backend bck, DefaultCamera2D camera, boolean forShadow) {
		bck.graphics2D().setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasSetting);
	}
	
	public void unequipElement(GraphicElement element) {
		if ( compToElement.get((JComponent)element.getComponent()) instanceof ComponentElement) {
			ComponentElement e = (ComponentElement)compToElement.get((JComponent)element.getComponent()) ;
			e.detach();
		}
	}
	
	/**
	 * Get the pair (swing component, graph element) corresponding to the given element. If the
	 * element is not yet associated with a Swing component, the binding is done.
	 */
	protected ComponentElement getOrEquipWithJComponent(GraphicElement element) {

		JComponent component = (JComponent)element.getComponent();
		ComponentElement ce = null;

		if(component == null) {
			switch (group.getJComponent()) {
				case BUTTON:
					ce = new ButtonComponentElement(element, new JButton(""));
					break;
				case TEXT_FIELD:
					ce = new TextFieldComponentElement(element, new JTextField(""));
					break;
				case PANEL:
					throw new RuntimeException("panel not yet available");
				default:
					 throw new RuntimeException("WTF ?!?");
			}
			
			if( ce != null ) {
				compToElement.put(ce.jComponent, ce);
			}
		}
		else {
			ce = compToElement.get(component);
		}
		
		return ce;
	}
	
	public void checkStyle(DefaultCamera2D camera, ComponentElement ce, boolean force) {
		if(force) {
			ce.checkIcon(camera);
			ce.checkBorder(camera, force);
			ce.setFill();
			ce.setTextAlignment();
			ce.setTextFont();
		}
	}
	
	
// Nested classes
 
	/**
	 * Represents the link between a JComponent and a GraphicElement.
	 * 
	 * Each of these component elements receive the action events of their button/text-field (for panel
	 * the user is free to do whatever he wants). They are in charge of adding and removing the
	 * component in the rendering surface, etc.
	 * 
	 * These elements also allow to push and remove the style to Swing components. We try to do this
	 * only when the style potentially changed, not at each redraw.
	 */
 	abstract class ComponentElement extends JPanel 
 	{
 		protected GraphicElement element ;
 		
 		/** Set to true if the element is not yet initialised with its style. */
		protected boolean init = false;

	// Access

		/** The Swing Component. */
 		protected JComponent jComponent ;

 	// Construction
		public ComponentElement(GraphicElement element) {
 			this.element = element ;
 	
 			setLayout(null);	// No layout in this panel, we set the component bounds ourselves.
 	 		mainRenderer.renderingSurface().add(this);
		}
 		
		/** Set of reset the fill mode and colour for the Swing component. */
		public void setFill() {
//			setBackground( group.getFillColor( 0 ) )
//			setOpaque( true )
//			if( group.getFillMode == StyleConstants.FillMode.PLAIN )
//				jComponent.setBackground( group.getFillColor( 0 ) )
		}
		
		/** Set or reset the text alignment for the Swing component. */
		public abstract void setTextAlignment();
		
		/** Set or reset the text font size, style and colour for the Swing component. */
		public abstract void setTextFont();
		
		/** Set or reset the label of the component. */
		public abstract void updateLabel();
		
		public void setBounds(int x, int y, int width, int height, DefaultCamera2D camera) {
			setBounds(x, y, width, height);
			
			int borderWidth = 0;
			
			if(group.getStrokeMode() != StyleConstants.StrokeMode.NONE && group.getStrokeWidth().value > 0)
				borderWidth = (int)camera.getMetrics().lengthToPx(group.getStrokeWidth());

			jComponent.setBounds(borderWidth, borderWidth, width-(borderWidth*2), height-(borderWidth*2));
		}
		
		/**
		 * Detach the Swing component from the graph element, remove the Swing component from its
		 * Swing container and remove any listeners on the Swing component. The ComponentElement
		 * is not usable after this.
		 */
		public void detach() { mainRenderer.renderingSurface().remove(this); }
		
	// Custom painting
		@Override
		public void paint(Graphics g) {
			paintComponent(g);	// XXX Remove this ??? XXX
			paintBorder(g);
			paintChildren(g);
		}
				
		/**
		 * Check the swing component follows the graph element position.
		 * @param camera The transformation from GU to PX.
		 */
		public void updatePosition(DefaultCamera2D camera) {
			if ( element instanceof GraphicNode) {
				positionNodeComponent( (GraphicNode)element, camera);
			}
			else if ( element instanceof GraphicSprite){
				positionSpriteComponent((GraphicSprite)element, camera);
			}
			else {
				throw new RuntimeException("WTF ?");
			}
		}

	// Command -- Utility, positioning
		private void positionNodeComponent(GraphicNode node, DefaultCamera2D camera) {
			Point3 pos = camera.transformGuToPx(node.getX(), node.getY(), 0);
					
			setBounds((int)(pos.x-(width/2)), (int)(pos.y-(height/2)), width, height, camera);
		}
		
		private void positionSpriteComponent(GraphicSprite sprite, DefaultCamera2D camera) {
			Point3 pos = camera.getSpritePosition( sprite, new Point3(), StyleConstants.Units.PX);
			
			setBounds((int)(pos.x-(width/2)), (int)(pos.y-(height/2)), width, height, camera);
		}
	
	// Command -- Utility, applying CSS style to Swing components
		public void checkBorder( DefaultCamera2D camera, boolean force ) {
			if(force) {
				if(group.getStrokeMode() != StyleConstants.StrokeMode.NONE && group.getStrokeWidth().value > 0)
					setBorder(createBorder(camera));
				else 
					setBorder(null);
			} 
			else {
				updateBorder(camera);
			}
		}

		private void updateBorder(DefaultCamera2D camera) {}

		private Border createBorder(DefaultCamera2D camera) {
			int width = (int)camera.getMetrics().lengthToPx( group.getStrokeWidth() );
			
			switch (group.getStrokeMode()) {
				case PLAIN: return BorderFactory.createLineBorder( ColorManager.getStrokeColor( group, 0 ), width );
				case DOTS: throw new RuntimeException( "TODO create dots and dashes borders for component to respect stroke-mode." );
				case DASHES: throw new RuntimeException( "TODO create dots and dashes borders for component to respect stroke-mode." );
				default:	return null ;
			}
		}
		
		public abstract void checkIcon(DefaultCamera2D camera) ;
 	}
 	
 	class TextFieldComponentElement extends ComponentElement implements ActionListener {
 		private JTextField comp ;
 		
		public TextFieldComponentElement(GraphicElement element, JTextField comp) {
			super(element);
			this.comp = comp ;
			this.element = element ;
			
			element.setComponent( comp );
			comp.addActionListener( this );
			add( comp );
			this.jComponent = comp ;
		}
 		
		@Override
		public void detach() {
			super.detach();
			comp.removeActionListener( this );
			remove( comp );
			element.setComponent( null );
	
			//component = null
			//element   = null
		}
		
		@Override
		public void actionPerformed(ActionEvent e) {
			element.label = ((JTextField)comp).getText();
			element.setAttribute( "ui.label", element.label );
			element.setAttribute( "ui.clicked" );
		}
		
		@Override
		public void setTextAlignment() {
			switch (group.getTextAlignment()) {
				case ABOVE: comp.setHorizontalAlignment(SwingConstants.CENTER); break;
				case UNDER: comp.setHorizontalAlignment(SwingConstants.CENTER); break;
				case ALONG: comp.setHorizontalAlignment(SwingConstants.CENTER); break;
				case JUSTIFY: comp.setHorizontalAlignment(SwingConstants.CENTER); break;
				case CENTER: comp.setHorizontalAlignment(SwingConstants.CENTER); break;
				case AT_RIGHT: comp.setHorizontalAlignment(SwingConstants.RIGHT); break;
				case RIGHT: comp.setHorizontalAlignment(SwingConstants.RIGHT); break;
				case AT_LEFT: comp.setHorizontalAlignment(SwingConstants.LEFT); break;
				case LEFT: comp.setHorizontalAlignment(SwingConstants.LEFT); break;
				default: break;
			}
		}
		
		@Override
		public void setTextFont() {
			Font font = FontCache.getDefaultFont( group.getTextStyle(), (int)group.getTextSize().value );
			if( ! group.getTextFont().equals( "default" ) )
				font = FontCache.getFont( group.getTextFont(), group.getTextStyle(), (int)group.getTextSize().value );
           
			comp.setFont( font );
			comp.setForeground( ColorManager.getTextColor( group, 0 ) );
		}
		
		@Override
		public void updateLabel() {
			if( ! comp.hasFocus() )
				comp.setText( element.getLabel() );
		}
		
		@Override
		public void checkIcon(DefaultCamera2D camera) {}
 	}
 	
 	class ButtonComponentElement extends ComponentElement implements ActionListener {
 		
 		private JButton comp ;
 		
 		public ButtonComponentElement(GraphicElement element, JButton comp) {
			super(element);
			this.comp = comp ;
			this.element = element ;
			
			element.setComponent( comp );
			comp.addActionListener( this );
			add( comp );
			this.jComponent = comp ;
		}
 		
 		@Override
 		public void detach() {
 			super.detach();
 			
 			comp.removeActionListener( this );
			remove(comp);
			element.setComponent( null );
	
//			component = null;
//			element   = null;
 		}
 		
 		@Override
 		public void actionPerformed(ActionEvent e) {
 			element.label = comp.getText();
 			element.setAttribute( "ui.label", element.label );
 			element.setAttribute( "ui.clicked" );
 			element.myGraph().setAttribute( "ui.clicked", element.getId() );
 		}
 		
 		@Override
		public void setTextAlignment() {
			switch (group.getTextAlignment()) {
				case ALONG: comp.setHorizontalAlignment(SwingConstants.CENTER); break;
				case JUSTIFY: comp.setHorizontalAlignment(SwingConstants.CENTER); break;
				case CENTER: comp.setHorizontalAlignment(SwingConstants.CENTER); break;
				case AT_RIGHT: comp.setHorizontalAlignment(SwingConstants.RIGHT); break;
				case RIGHT: comp.setHorizontalAlignment(SwingConstants.RIGHT); break;
				case AT_LEFT: comp.setHorizontalAlignment(SwingConstants.LEFT); break;
				case LEFT: comp.setHorizontalAlignment(SwingConstants.LEFT); break;
				case ABOVE: comp.setHorizontalAlignment(SwingConstants.TOP); break;
				case UNDER: comp.setHorizontalAlignment(SwingConstants.BOTTOM); break;
				default: break;
			}
		}
 		
 		@Override
		public void setTextFont() {
			Font font = FontCache.getDefaultFont( group.getTextStyle(), (int)group.getTextSize().value );
			if( ! group.getTextFont().equals( "default" ) )
				font = FontCache.getFont( group.getTextFont(), group.getTextStyle(), (int)group.getTextSize().value );
           
			comp.setFont( font );
			comp.setForeground( ColorManager.getTextColor( group, 0 ) );
		}
 		
 		@Override
 		public void updateLabel() {
 			String label = element.getLabel();
 					
 			if( label != null )
 				comp.setText( label ); 			
 		}
 		
 		@Override
 		public void checkIcon(DefaultCamera2D camera) {
 			if( group.getIconMode() != StyleConstants.IconMode.NONE ) {
 				String url   = group.getIcon();
 				BufferedImage image = ImageCache.loadImage( url );
				
				if( image != null ) {
					comp.setIcon( new ImageIcon( image ) );
					
					switch (group.getIconMode()) {
						case AT_LEFT:
							comp.setHorizontalTextPosition( SwingConstants.RIGHT );  
							comp.setVerticalTextPosition( SwingConstants.CENTER );
							break;
						case AT_RIGHT:
							comp.setHorizontalTextPosition( SwingConstants.LEFT  );
							comp.setVerticalTextPosition( SwingConstants.CENTER );
							break;
						case ABOVE:
							comp.setHorizontalTextPosition( SwingConstants.CENTER );
							comp.setVerticalTextPosition( SwingConstants.BOTTOM );
							break;
						case UNDER:
							comp.setHorizontalTextPosition( SwingConstants.CENTER );
							comp.setVerticalTextPosition( SwingConstants.TOP );
							break;
						default:
							throw new RuntimeException( "unknown image mode "+group.getIconMode() );
					}
				}
			} 			
 		}
 	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy