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 image
38 * 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 mouse
53 * 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 instead.
61 */
62
63 public PreviewCorner( JScrollPane scrollPane, ImageIcon zoomIcon, boolean doCloseAfterClick, String corner )
64 {
65
66 super( zoomIcon );
67 this._corner = corner;
68
69
70 _previewPopup = new PreviewPopup( scrollPane, doCloseAfterClick );
71
72 setToolTipText( "Show a Panorama View of the contained editor" );
73
74
75 addActionListener( this );
76 }
77
78 public PreviewCorner( JScrollPane scrollPane, ImageIcon zoomIcon, String corner )
79 {
80 this( scrollPane, zoomIcon, false, corner );
81 }
82
83 public void actionPerformed( ActionEvent e )
84 {
85 _previewPopup.showUpInCorner( this, _corner );
86 }
87
88 public void release()
89 {
90 _previewPopup.release();
91 removeActionListener( this );
92 }
93 }
94
95 class PreviewPopup extends JPopupMenu implements MouseListener, MouseMotionListener
96 {
97
98 private JScrollPane _scrollPane;
99 private JViewport _viewPort;
100
101 private JLabel _zoomWindow;
102
103 private JPanel _cursorLabel;
104
105
106
107
108 private JLayeredPane _layeredPane;
109
110 private int _iconWidth;
111 private int _iconHeight;
112
113 private boolean _doCloseAfterClick;
114
115 float _ratio;
116
117
118 private static int DELTA = 5;
119
120 public PreviewPopup( JScrollPane scrollPane, boolean doCloseAfterClick )
121 {
122 this.setBorder( BorderFactory.createEtchedBorder() );
123
124 _doCloseAfterClick = doCloseAfterClick;
125
126 _scrollPane = scrollPane;
127 _viewPort = _scrollPane.getViewport();
128
129 _zoomWindow = new JLabel();
130 _cursorLabel = createCursor();
131
132 _layeredPane = new JLayeredPane();
133
134 _layeredPane.add( _zoomWindow, new Integer( 0 ) );
135 _layeredPane.add( _cursorLabel, new Integer( 1 ) );
136
137
138
139 BufferedImage bim = new BufferedImage( 1, 1, BufferedImage.TYPE_4BYTE_ABGR );
140 setCursor( getToolkit().createCustomCursor( bim, ( new Point( 0, 0 ) ), "PreviewCursor" ) );
141
142 this.add( _layeredPane );
143
144
145
146 _layeredPane.addMouseListener( this );
147 _layeredPane.addMouseMotionListener( this );
148 }
149
150 public void release()
151 {
152 if( getParent() != null )
153 getParent().remove( this );
154
155 _layeredPane.removeMouseListener( this );
156 _layeredPane.removeMouseMotionListener( this );
157
158 _scrollPane = null;
159 _layeredPane = null;
160 _viewPort = null;
161 }
162
163 /***
164 * By default, the right corner of a popup menu is positionned at the right
165 * of a mouse click. What we want is to have the preview popup menu
166 * positionned <i>inside</i> the scroll pane, near the corner component. The
167 * purpose of this method is to display the scaled image of the component of
168 * the scroll pane, and to calculate the correct position of the preview
169 * popup menu.
170 */
171
172 public void showUpInCorner( Component c, String corner )
173 {
174
175 if( _viewPort.getComponentCount() == 0 )
176 return;
177
178 float scaleFactor = 1.1f;
179
180
181
182
183
184
185
186
187
188 _ratio = ( ( ( float )_viewPort.getComponent( 0 ).getHeight() / ( ( float )_viewPort.getHeight() ) / scaleFactor ) );
189
190 if( _ratio < 2 )
191 _ratio = 2;
192
193
194
195 int zoomWindowImageWidth = ( int )( _viewPort.getComponent( 0 ).getWidth() / _ratio );
196 if( zoomWindowImageWidth < 10 )
197 {
198 UISupport.showInfoMessage( "Viewport too large for readable image, use scrollbar instead" );
199 return;
200 }
201
202 int zoomWindowImageHeight = ( int )( _viewPort.getComponent( 0 ).getHeight() / _ratio );
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 Image capture = captureComponentViewAsBufferedImage( _viewPort.getComponent( 0 ) );
219 Image componentImage = new BufferedImage( zoomWindowImageWidth, zoomWindowImageHeight, BufferedImage.TYPE_INT_RGB );
220 Graphics2D g2d = ( Graphics2D )componentImage.getGraphics();
221
222 if( zoomWindowImageWidth > 15 )
223 g2d.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
224 g2d.drawImage( capture, 0, 0, zoomWindowImageWidth, zoomWindowImageHeight, null );
225 g2d.dispose();
226
227
228 ImageIcon componentIcon = new ImageIcon( componentImage );
229
230 _iconWidth = componentIcon.getIconWidth();
231 _iconHeight = componentIcon.getIconHeight();
232
233 _zoomWindow.setIcon( componentIcon );
234
235 _zoomWindow.setBounds( 0, 0, _iconWidth, _iconHeight );
236
237 int cursorWidth = ( int )( _viewPort.getWidth() / _ratio );
238
239 int cursorHeight = ( int )( _viewPort.getHeight() / _ratio );
240
241 _cursorLabel.setBounds( 0, 0, cursorWidth, cursorHeight );
242
243 _layeredPane.setPreferredSize( new Dimension( _iconWidth, _iconHeight ) );
244
245 int dx = componentIcon.getIconWidth() + DELTA;
246 int dy = componentIcon.getIconHeight() + DELTA;
247
248 if( corner == JScrollPane.UPPER_LEFT_CORNER )
249 ;
250 else if( corner == JScrollPane.UPPER_RIGHT_CORNER )
251 dx = -dx;
252 else if( corner == JScrollPane.LOWER_RIGHT_CORNER )
253 {
254 dx = -dx;
255 dy = -dy;
256 }
257 else if( corner == JScrollPane.LOWER_LEFT_CORNER )
258 dy = -dy;
259
260 if( dy < 0 && Math.abs( dy ) > _viewPort.getHeight() )
261 {
262 dy = -_viewPort.getHeight() - 10;
263 }
264
265
266
267
268 this.show( c, dx, dy );
269
270 }
271
272 public JPanel createCursor()
273 {
274 JPanel label = new JPanel()
275 {
276
277 @Override
278 protected void paintComponent( Graphics g )
279 {
280 Composite composite = ( ( Graphics2D )g ).getComposite();
281 ( ( Graphics2D )g ).setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.2f ) );
282 super.paintComponent( g );
283 ( ( Graphics2D )g ).setComposite( composite );
284 }
285
286 };
287 label.setBorder( BorderFactory.createLineBorder( Color.gray ) );
288 label.setVisible( false );
289 label.setOpaque( true );
290 label.setBackground( Color.orange.darker() );
291 return label;
292 }
293
294 public void mouseClicked( MouseEvent e )
295 {
296 }
297
298 public void mouseEntered( MouseEvent e )
299 {
300
301
302 _cursorLabel.setVisible( true );
303 }
304
305 public void mouseExited( MouseEvent e )
306 {
307
308
309 _cursorLabel.setVisible( false );
310 }
311
312 public void mousePressed( MouseEvent e )
313 {
314 }
315
316 public void mouseReleased( MouseEvent e )
317 {
318
319
320 if( _doCloseAfterClick )
321 {
322 this.setVisible( false );
323 _cursorLabel.setVisible( false );
324 MenuSelectionManager.defaultManager().clearSelectedPath();
325 setInvoker( null );
326 }
327 }
328
329 public void mouseDragged( MouseEvent e )
330 {
331 moveCursor( e.getX(), e.getY() );
332 scrollViewPort();
333 }
334
335 public void mouseMoved( MouseEvent e )
336 {
337 moveCursor( e.getX(), e.getY() );
338 scrollViewPort();
339 }
340
341 /***
342 * Centers the fake cursor (_cursorLabel) position on the coordinates
343 * specified in the parameters.
344 */
345 private void moveCursor( int x, int y )
346 {
347 int dx = x - _cursorLabel.getWidth() / 2;
348 int dy = y - _cursorLabel.getHeight() / 2;
349 _cursorLabel.setLocation( dx, dy );
350 }
351
352 /***
353 * Scrolls the viewport according to the fake cursor position in the preview
354 * popup menu.
355 */
356 private void scrollViewPort()
357 {
358 Point cursorLocation = _cursorLabel.getLocation();
359 int dx = ( int )Math.max( cursorLocation.getX(), 0 );
360 int dy = ( int )Math.max( cursorLocation.getY(), 0 );
361
362 dx = ( int )( dx * _ratio );
363 dy = ( int )( dy * _ratio );
364
365 ( ( JComponent )_viewPort.getComponent( 0 ) ).scrollRectToVisible( new Rectangle( dx, dy, _viewPort.getWidth(),
366 _viewPort.getHeight() ) );
367 }
368
369 /***
370 * takes a java component and generates an image out of it.
371 *
372 * @param c
373 * the component for which image needs to be generated
374 * @return the generated image
375 */
376 public static BufferedImage captureComponentViewAsBufferedImage( Component c )
377 {
378 Dimension size = c.getSize();
379 BufferedImage bufferedImage = new BufferedImage( size.width, size.height, BufferedImage.TYPE_INT_RGB );
380 Graphics bufferedGraphics = bufferedImage.createGraphics();
381 c.paint( bufferedGraphics );
382 return bufferedImage;
383 }
384
385 }