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
71 _previewPopup = new PreviewPopup( scrollPane, doCloseAfterClick );
72
73 setToolTipText( "Show a Panorama View of the contained editor" );
74
75
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;
103
104 private JPanel _cursorLabel;
105
106
107
108
109 private JLayeredPane _layeredPane;
110
111 private int _iconWidth;
112 private int _iconHeight;
113
114 private boolean _doCloseAfterClick;
115
116 float _ratio;
117
118
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
139
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
146
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;
180
181
182
183
184
185
186
187
188
189 _ratio = ( ( ( float ) _viewPort.getComponent( 0 ).getHeight()
190 / ( ( float ) _viewPort.getHeight() ) / scaleFactor ) );
191
192 if( _ratio < 2 )
193 _ratio = 2;
194
195
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
207
208
209
210
211
212
213
214
215
216
217
218
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
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
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
268
269
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
303
304 _cursorLabel.setVisible( true );
305 }
306
307 public void mouseExited( MouseEvent e )
308 {
309
310
311 _cursorLabel.setVisible( false );
312 }
313
314 public void mousePressed( MouseEvent e )
315 {
316 }
317
318 public void mouseReleased( MouseEvent e )
319 {
320
321
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 }