View Javadoc

1   package com.eviware.soapui.support.components;
2   
3   import java.awt.AlphaComposite;
4   import java.awt.Color;
5   import java.awt.Component;
6   import java.awt.Composite;
7   import java.awt.Dimension;
8   import java.awt.Graphics;
9   import java.awt.Graphics2D;
10  import java.awt.Image;
11  import java.awt.Point;
12  import java.awt.Rectangle;
13  import java.awt.RenderingHints;
14  import java.awt.event.ActionEvent;
15  import java.awt.event.ActionListener;
16  import java.awt.event.MouseEvent;
17  import java.awt.event.MouseListener;
18  import java.awt.event.MouseMotionListener;
19  import java.awt.image.BufferedImage;
20  
21  import javax.swing.BorderFactory;
22  import javax.swing.ImageIcon;
23  import javax.swing.JButton;
24  import javax.swing.JComponent;
25  import javax.swing.JLabel;
26  import javax.swing.JLayeredPane;
27  import javax.swing.JPanel;
28  import javax.swing.JPopupMenu;
29  import javax.swing.JScrollPane;
30  import javax.swing.JViewport;
31  import javax.swing.MenuSelectionManager;
32  
33  import com.eviware.soapui.support.UISupport;
34  
35  /***
36   * This is a button which is designed to be the corner component of a
37   * <code>JScrollPane</code>. It triggers a popup menu which holds a scaled
38   * image of the component contained inside the <code>JScrollPane</code>.
39   */
40  
41  public class PreviewCorner extends JButton implements ActionListener
42  {
43  	private String _corner;
44  	private PreviewPopup _previewPopup;
45  
46  	/***
47  	 * @param scrollPane
48  	 *           the <code>JScrollPane</code> to preview
49  	 * @param zoomIcon
50  	 *           the icon to use for the button
51  	 * @param doCloseAfterClick
52  	 *           When <code>true</code> the preview popup menu is closed on
53  	 *           mouse click.
54  	 * @param corner
55  	 *           Supposed to be one of the four corners of a
56  	 *           <code>JScrollPane</code>, like
57  	 *           <code>JScrollPane.LOWER_RIGHT_CORNER</code> for example, which
58  	 *           should match the position of the corner component of the scroll
59  	 *           pane. Note: If this parameter is not set correctly,
60  	 *           <code>JScrollPane.UPPER_LEFT_CORNER</code> will be used
61  	 *           instead.
62  	 */
63  
64  	public PreviewCorner( JScrollPane scrollPane, ImageIcon zoomIcon, boolean doCloseAfterClick, String corner )
65  	{
66  
67  		super( zoomIcon );
68  		this._corner = corner;
69  
70  		// Creates the popup menu, containing the scaled image of the component.
71  		_previewPopup = new PreviewPopup( scrollPane, doCloseAfterClick );
72  
73  		setToolTipText( "Show a Panorama View of the contained editor" );
74  
75  		// The action listener is used to trigger the popup menu.
76  		addActionListener( this );
77  	}
78  
79  	public PreviewCorner( JScrollPane scrollPane, ImageIcon zoomIcon, String corner )
80  	{
81  		this( scrollPane, zoomIcon, false, corner );
82  	}
83  
84  	public void actionPerformed( ActionEvent e )
85  	{
86  		_previewPopup.showUpInCorner( this, _corner );
87  	}
88  
89  	public void release()
90  	{
91  		_previewPopup.release();
92  		removeActionListener( this );
93  	}
94  }
95  
96  class PreviewPopup extends JPopupMenu implements MouseListener, MouseMotionListener
97  {
98  
99  	private JScrollPane _scrollPane;
100 	private JViewport _viewPort;
101 
102 	private JLabel _zoomWindow; // the JLabel containing the scaled image
103 
104 	private JPanel _cursorLabel; // the JLabel mimicking the fake rectangle
105 											// cursor
106 
107 	// This component will hold both JLabels _zoomWindow and _cursorLabel,
108 	// the latter on top of the other.
109 	private JLayeredPane _layeredPane;
110 
111 	private int _iconWidth;
112 	private int _iconHeight;
113 
114 	private boolean _doCloseAfterClick;
115 
116 	float _ratio;
117 
118 	// DELTA is the space between the scroll pane and the preview popup menu.
119 	private static int DELTA = 5;
120 
121 	public PreviewPopup( JScrollPane scrollPane, boolean doCloseAfterClick )
122 	{
123 		this.setBorder( BorderFactory.createEtchedBorder() );
124 
125 		_doCloseAfterClick = doCloseAfterClick;
126 
127 		_scrollPane = scrollPane;
128 		_viewPort = _scrollPane.getViewport();
129 
130 		_zoomWindow = new JLabel();
131 		_cursorLabel = createCursor();
132 
133 		_layeredPane = new JLayeredPane();
134 
135 		_layeredPane.add( _zoomWindow, new Integer( 0 ) );
136 		_layeredPane.add( _cursorLabel, new Integer( 1 ) );
137 
138 		// Creates a blank transparent cursor to be used as the cursor of
139 		// the popup menu.
140 		BufferedImage bim = new BufferedImage( 1, 1, BufferedImage.TYPE_4BYTE_ABGR );
141 		setCursor( getToolkit().createCustomCursor( bim, ( new Point( 0, 0 ) ), "PreviewCursor" ) );
142 
143 		this.add( _layeredPane );
144 
145 		// Adds the mouse input listeners to the _layeredPane to scroll the
146 		// viewport and to move the fake cursor (_cursorLabel).
147 		_layeredPane.addMouseListener( this );
148 		_layeredPane.addMouseMotionListener( this );
149 	}
150 
151 	public void release()
152 	{
153 		if( getParent() != null )
154 			getParent().remove( this );
155 
156 		_layeredPane.removeMouseListener( this );
157 		_layeredPane.removeMouseMotionListener( this );
158 		
159 		_scrollPane = null;
160 		_layeredPane = null;
161 		_viewPort = null;
162 	}
163 
164 	/***
165 	 * By default, the right corner of a popup menu is positionned at the right
166 	 * of a mouse click. What we want is to have the preview popup menu
167 	 * positionned <i>inside</i> the scroll pane, near the corner component. The
168 	 * purpose of this method is to display the scaled image of the component of
169 	 * the scroll pane, and to calculate the correct position of the preview
170 	 * popup menu.
171 	 */
172 
173 	public void showUpInCorner( Component c, String corner )
174 	{
175 
176 		if( _viewPort.getComponentCount() == 0 )
177 			return;
178 
179 		float scaleFactor = 1.1f; // _viewPort.getComponent( 0 ).getHeight() /
180 											// _scrollPane.getHeight() * 2;
181 
182 		// if (_viewPort.getWidth() < (_viewPort.getHeight() * scaleFactor))
183 		// _ratio =( int ) ( _viewPort.getComponent(0).getWidth() /
184 		// (_viewPort.getWidth() / scaleFactor) );
185 		//
186 		// else
187 		// _ratio = ( int ) (( _viewPort.getComponent(0).getHeight() /
188 		// (_viewPort.getHeight()) / scaleFactor ));
189 		_ratio = ( ( ( float ) _viewPort.getComponent( 0 ).getHeight()
190 					/ ( ( float ) _viewPort.getHeight() ) / scaleFactor ) );
191 
192 		if( _ratio < 2 )
193 			_ratio = 2;
194 
195 		// System.out.println( "ratio = " + _ratio );
196 
197 		int zoomWindowImageWidth = ( int ) ( _viewPort.getComponent( 0 ).getWidth() / _ratio );
198 		if( zoomWindowImageWidth < 10 )
199 		{
200 			UISupport.showInfoMessage( "Viewport too large for readable image, use scrollbar instead" );
201 			return;
202 		}
203 
204 		int zoomWindowImageHeight = ( int ) ( _viewPort.getComponent( 0 ).getHeight() / _ratio );
205 
206 		// System.out.println( "ratio = " + _ratio + ", zoomWindowImageWidth = " +
207 		// zoomWindowImageWidth +
208 		// ", zoomWindowImageHeight = " + zoomWindowImageHeight);
209 
210 		/*
211 		 * Image componentImage =
212 		 * captureComponentViewAsBufferedImage(_viewPort.getComponent(0))
213 		 * .getScaledInstance( zoomWindowImageWidth, zoomWindowImageHeight,
214 		 * Image.SCALE_SMOOTH);
215 		 */
216 		/*
217 		 * Based on Shannon Hickey's comments. This is much faster way to scale
218 		 * instance Thanks! Shannon
219 		 */
220 		Image capture = captureComponentViewAsBufferedImage( _viewPort.getComponent( 0 ) );
221 		Image componentImage = new BufferedImage( zoomWindowImageWidth, zoomWindowImageHeight, BufferedImage.TYPE_INT_RGB );
222 		Graphics2D g2d = ( Graphics2D ) componentImage.getGraphics();
223 		/* if you want smoother scaling */
224 		if( zoomWindowImageWidth > 15 )
225 			g2d.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
226 		g2d.drawImage( capture, 0, 0, zoomWindowImageWidth, zoomWindowImageHeight, null );
227 		g2d.dispose();
228 
229 		// Converts the Image to an ImageIcon to be used with a JLabel.
230 		ImageIcon componentIcon = new ImageIcon( componentImage );
231 
232 		_iconWidth = componentIcon.getIconWidth();
233 		_iconHeight = componentIcon.getIconHeight();
234 
235 		_zoomWindow.setIcon( componentIcon );
236 
237 		_zoomWindow.setBounds( 0, 0, _iconWidth, _iconHeight );
238 
239 		int cursorWidth = ( int ) ( _viewPort.getWidth() / _ratio );
240 
241 		int cursorHeight = ( int ) ( _viewPort.getHeight() / _ratio );
242 
243 		_cursorLabel.setBounds( 0, 0, cursorWidth, cursorHeight );
244 
245 		_layeredPane.setPreferredSize( new Dimension( _iconWidth, _iconHeight ) );
246 
247 		int dx = componentIcon.getIconWidth() + DELTA;
248 		int dy = componentIcon.getIconHeight() + DELTA;
249 
250 		if( corner == JScrollPane.UPPER_LEFT_CORNER )
251 			;
252 		else if( corner == JScrollPane.UPPER_RIGHT_CORNER )
253 			dx = -dx;
254 		else if( corner == JScrollPane.LOWER_RIGHT_CORNER )
255 		{
256 			dx = -dx;
257 			dy = -dy;
258 		}
259 		else if( corner == JScrollPane.LOWER_LEFT_CORNER )
260 			dy = -dy;
261 
262 		if( dy < 0 && Math.abs( dy ) > _viewPort.getHeight() )
263 		{
264 			dy = -_viewPort.getHeight()-10;
265 		}
266 		
267 //		System.out.println( "Showing at " + dx + ", " + dy );
268 		
269 		// Shows the popup menu at the right place.
270 		this.show( c, dx, dy );
271 
272 	}
273 
274 	public JPanel createCursor()
275 	{
276 		JPanel label = new JPanel()
277 		{
278 
279 			@Override
280 			protected void paintComponent( Graphics g )
281 			{
282 				Composite composite = ( ( Graphics2D ) g ).getComposite();
283 				( ( Graphics2D ) g ).setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.2f ) );
284 				super.paintComponent( g );
285 				( ( Graphics2D ) g ).setComposite( composite );
286 			}
287 
288 		};
289 		label.setBorder( BorderFactory.createLineBorder( Color.gray ) );
290 		label.setVisible( false );
291 		label.setOpaque( true );
292 		label.setBackground( Color.orange.darker() );
293 		return label;
294 	}
295 
296 	public void mouseClicked( MouseEvent e )
297 	{
298 	}
299 
300 	public void mouseEntered( MouseEvent e )
301 	{
302 		// When the mouse enters the preview popup menu, set the visibility
303 		// of the fake cursor to true.
304 		_cursorLabel.setVisible( true );
305 	}
306 
307 	public void mouseExited( MouseEvent e )
308 	{
309 		// When the mouse exits the preview popup menu, set the visibility
310 		// of the fake cursor to false.
311 		_cursorLabel.setVisible( false );
312 	}
313 
314 	public void mousePressed( MouseEvent e )
315 	{
316 	}
317 
318 	public void mouseReleased( MouseEvent e )
319 	{
320 		// When the mouse is released, set the visibility of the preview
321 		// popup menu to false only if doCloseAfterClick is set to true.
322 		if( _doCloseAfterClick )
323 		{
324 			this.setVisible( false );
325 			_cursorLabel.setVisible( false );
326 			MenuSelectionManager.defaultManager().clearSelectedPath();
327 			setInvoker( null );
328 		}
329 	}
330 
331 	public void mouseDragged( MouseEvent e )
332 	{
333 		moveCursor( e.getX(), e.getY() );
334 		scrollViewPort();
335 	}
336 
337 	public void mouseMoved( MouseEvent e )
338 	{
339 		moveCursor( e.getX(), e.getY() );
340 		scrollViewPort();
341 	}
342 
343 	/***
344 	 * Centers the fake cursor (_cursorLabel) position on the coordinates
345 	 * specified in the parameters.
346 	 */
347 	private void moveCursor( int x, int y )
348 	{
349 		int dx = x - _cursorLabel.getWidth() / 2;
350 		int dy = y - _cursorLabel.getHeight() / 2;
351 		_cursorLabel.setLocation( dx, dy );
352 	}
353 
354 	/***
355 	 * Scrolls the viewport according to the fake cursor position in the preview
356 	 * popup menu.
357 	 */
358 	private void scrollViewPort()
359 	{
360 		Point cursorLocation = _cursorLabel.getLocation();
361 		int dx = ( int ) Math.max( cursorLocation.getX(), 0 );
362 		int dy = ( int ) Math.max( cursorLocation.getY(), 0 );
363 
364 		dx = ( int ) ( dx * _ratio );
365 		dy = ( int ) ( dy * _ratio );
366 
367 		( ( JComponent ) _viewPort.getComponent( 0 ) ).scrollRectToVisible( new Rectangle( dx, dy, _viewPort.getWidth(),
368 					_viewPort.getHeight() ) );
369 	}
370 
371 	/***
372 	 * takes a java component and generates an image out of it.
373 	 * 
374 	 * @param c
375 	 *           the component for which image needs to be generated
376 	 * @return the generated image
377 	 */
378 	public static BufferedImage captureComponentViewAsBufferedImage( Component c )
379 	{
380 		Dimension size = c.getSize();
381 		BufferedImage bufferedImage = new BufferedImage( size.width, size.height, BufferedImage.TYPE_INT_RGB );
382 		Graphics bufferedGraphics = bufferedImage.createGraphics();
383 		c.paint( bufferedGraphics );
384 		return bufferedImage;
385 	}
386 
387 }