/*
* soapUI, copyright (C) 2004-2011 eviware.com
*
* soapUI is free software; you can redistribute it and/or modify it under the
* terms of version 2.1 of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* soapUI 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 at gnu.org.
*/
package com.eviware.soapui.support.components;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.MenuSelectionManager;
import com.eviware.soapui.support.UISupport;
/**
* This is a button which is designed to be the corner component of a
* <code>JScrollPane</code>. It triggers a popup menu which holds a scaled image
* of the component contained inside the <code>JScrollPane</code>.
*/
public class PreviewCorner extends JButton implements ActionListener
{
private String _corner;
private PreviewPopup _previewPopup;
/**
* @param scrollPane
* the <code>JScrollPane</code> to preview
* @param zoomIcon
* the icon to use for the button
* @param doCloseAfterClick
* When <code>true</code> the preview popup menu is closed on mouse
* click.
* @param corner
* Supposed to be one of the four corners of a
* <code>JScrollPane</code>, like
* <code>JScrollPane.LOWER_RIGHT_CORNER</code> for example, which
* should match the position of the corner component of the scroll
* pane. Note: If this parameter is not set correctly,
* <code>JScrollPane.UPPER_LEFT_CORNER</code> will be used instead.
*/
public PreviewCorner( JScrollPane scrollPane, ImageIcon zoomIcon, boolean doCloseAfterClick, String corner )
{
super( zoomIcon );
this._corner = corner;
// Creates the popup menu, containing the scaled image of the component.
_previewPopup = new PreviewPopup( scrollPane, doCloseAfterClick );
setToolTipText( "Show a Panorama View of the contained editor" );
// The action listener is used to trigger the popup menu.
addActionListener( this );
}
public PreviewCorner( JScrollPane scrollPane, ImageIcon zoomIcon, String corner )
{
this( scrollPane, zoomIcon, false, corner );
}
public void actionPerformed( ActionEvent e )
{
_previewPopup.showUpInCorner( this, _corner );
}
public void release()
{
_previewPopup.release();
removeActionListener( this );
}
}
class PreviewPopup extends JPopupMenu implements MouseListener, MouseMotionListener
{
private JScrollPane _scrollPane;
private JViewport _viewPort;
private JLabel _zoomWindow; // the JLabel containing the scaled image
private JPanel _cursorLabel; // the JLabel mimicking the fake rectangle
// cursor
// This component will hold both JLabels _zoomWindow and _cursorLabel,
// the latter on top of the other.
private JLayeredPane _layeredPane;
private int _iconWidth;
private int _iconHeight;
private boolean _doCloseAfterClick;
float _ratio;
// DELTA is the space between the scroll pane and the preview popup menu.
private static int DELTA = 5;
public PreviewPopup( JScrollPane scrollPane, boolean doCloseAfterClick )
{
this.setBorder( BorderFactory.createEtchedBorder() );
_doCloseAfterClick = doCloseAfterClick;
_scrollPane = scrollPane;
_viewPort = _scrollPane.getViewport();
_zoomWindow = new JLabel();
_cursorLabel = createCursor();
_layeredPane = new JLayeredPane();
_layeredPane.add( _zoomWindow, new Integer( 0 ) );
_layeredPane.add( _cursorLabel, new Integer( 1 ) );
// Creates a blank transparent cursor to be used as the cursor of
// the popup menu.
BufferedImage bim = new BufferedImage( 1, 1, BufferedImage.TYPE_4BYTE_ABGR );
setCursor( getToolkit().createCustomCursor( bim, ( new Point( 0, 0 ) ), "PreviewCursor" ) );
this.add( _layeredPane );
// Adds the mouse input listeners to the _layeredPane to scroll the
// viewport and to move the fake cursor (_cursorLabel).
_layeredPane.addMouseListener( this );
_layeredPane.addMouseMotionListener( this );
}
public void release()
{
if( getParent() != null )
getParent().remove( this );
_layeredPane.removeMouseListener( this );
_layeredPane.removeMouseMotionListener( this );
_scrollPane = null;
_layeredPane = null;
_viewPort = null;
}
/**
* By default, the right corner of a popup menu is positionned at the right
* of a mouse click. What we want is to have the preview popup menu
* positionned <i>inside</i> the scroll pane, near the corner component. The
* purpose of this method is to display the scaled image of the component of
* the scroll pane, and to calculate the correct position of the preview
* popup menu.
*/
public void showUpInCorner( Component c, String corner )
{
if( _viewPort.getComponentCount() == 0 )
return;
float scaleFactor = 1.1f; // _viewPort.getComponent( 0 ).getHeight() /
// _scrollPane.getHeight() * 2;
// if (_viewPort.getWidth() < (_viewPort.getHeight() * scaleFactor))
// _ratio =( int ) ( _viewPort.getComponent(0).getWidth() /
// (_viewPort.getWidth() / scaleFactor) );
//
// else
// _ratio = ( int ) (( _viewPort.getComponent(0).getHeight() /
// (_viewPort.getHeight()) / scaleFactor ));
_ratio = ( ( ( float )_viewPort.getComponent( 0 ).getHeight() / ( ( float )_viewPort.getHeight() ) / scaleFactor ) );
if( _ratio < 2 )
_ratio = 2;
// System.out.println( "ratio = " + _ratio );
int zoomWindowImageWidth = ( int )( _viewPort.getComponent( 0 ).getWidth() / _ratio );
if( zoomWindowImageWidth < 10 )
{
UISupport.showInfoMessage( "Viewport too large for readable image, use scrollbar instead" );
return;
}
int zoomWindowImageHeight = ( int )( _viewPort.getComponent( 0 ).getHeight() / _ratio );
// System.out.println( "ratio = " + _ratio + ", zoomWindowImageWidth = " +
// zoomWindowImageWidth +
// ", zoomWindowImageHeight = " + zoomWindowImageHeight);
/*
* Image componentImage =
* captureComponentViewAsBufferedImage(_viewPort.getComponent(0))
* .getScaledInstance( zoomWindowImageWidth, zoomWindowImageHeight,
* Image.SCALE_SMOOTH);
*/
/*
* Based on Shannon Hickey's comments. This is much faster way to scale
* instance Thanks! Shannon
*/
Image capture = captureComponentViewAsBufferedImage( _viewPort.getComponent( 0 ) );
Image componentImage = new BufferedImage( zoomWindowImageWidth, zoomWindowImageHeight, BufferedImage.TYPE_INT_RGB );
Graphics2D g2d = ( Graphics2D )componentImage.getGraphics();
/* if you want smoother scaling */
if( zoomWindowImageWidth > 15 )
g2d.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
g2d.drawImage( capture, 0, 0, zoomWindowImageWidth, zoomWindowImageHeight, null );
g2d.dispose();
// Converts the Image to an ImageIcon to be used with a JLabel.
ImageIcon componentIcon = new ImageIcon( componentImage );
_iconWidth = componentIcon.getIconWidth();
_iconHeight = componentIcon.getIconHeight();
_zoomWindow.setIcon( componentIcon );
_zoomWindow.setBounds( 0, 0, _iconWidth, _iconHeight );
int cursorWidth = ( int )( _viewPort.getWidth() / _ratio );
int cursorHeight = ( int )( _viewPort.getHeight() / _ratio );
_cursorLabel.setBounds( 0, 0, cursorWidth, cursorHeight );
_layeredPane.setPreferredSize( new Dimension( _iconWidth, _iconHeight ) );
int dx = componentIcon.getIconWidth() + DELTA;
int dy = componentIcon.getIconHeight() + DELTA;
if( corner.equals( JScrollPane.UPPER_LEFT_CORNER ) )
;
else if( corner.equals( JScrollPane.UPPER_RIGHT_CORNER ) )
dx = -dx;
else if( corner.equals( JScrollPane.LOWER_RIGHT_CORNER ) )
{
dx = -dx;
dy = -dy;
}
else if( corner.equals( JScrollPane.LOWER_LEFT_CORNER ) )
dy = -dy;
if( dy < 0 && Math.abs( dy ) > _viewPort.getHeight() )
{
dy = -_viewPort.getHeight() - 10;
}
// System.out.println( "Showing at " + dx + ", " + dy );
// Shows the popup menu at the right place.
this.show( c, dx, dy );
}
public JPanel createCursor()
{
JPanel label = new JPanel()
{
@Override
protected void paintComponent( Graphics g )
{
Composite composite = ( ( Graphics2D )g ).getComposite();
( ( Graphics2D )g ).setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.2f ) );
super.paintComponent( g );
( ( Graphics2D )g ).setComposite( composite );
}
};
label.setBorder( BorderFactory.createLineBorder( Color.gray ) );
label.setVisible( false );
label.setOpaque( true );
label.setBackground( Color.orange.darker() );
return label;
}
public void mouseClicked( MouseEvent e )
{
}
public void mouseEntered( MouseEvent e )
{
// When the mouse enters the preview popup menu, set the visibility
// of the fake cursor to true.
_cursorLabel.setVisible( true );
}
public void mouseExited( MouseEvent e )
{
// When the mouse exits the preview popup menu, set the visibility
// of the fake cursor to false.
_cursorLabel.setVisible( false );
}
public void mousePressed( MouseEvent e )
{
}
public void mouseReleased( MouseEvent e )
{
// When the mouse is released, set the visibility of the preview
// popup menu to false only if doCloseAfterClick is set to true.
if( _doCloseAfterClick )
{
this.setVisible( false );
_cursorLabel.setVisible( false );
MenuSelectionManager.defaultManager().clearSelectedPath();
setInvoker( null );
}
}
public void mouseDragged( MouseEvent e )
{
moveCursor( e.getX(), e.getY() );
scrollViewPort();
}
public void mouseMoved( MouseEvent e )
{
moveCursor( e.getX(), e.getY() );
scrollViewPort();
}
/**
* Centers the fake cursor (_cursorLabel) position on the coordinates
* specified in the parameters.
*/
private void moveCursor( int x, int y )
{
int dx = x - _cursorLabel.getWidth() / 2;
int dy = y - _cursorLabel.getHeight() / 2;
_cursorLabel.setLocation( dx, dy );
}
/**
* Scrolls the viewport according to the fake cursor position in the preview
* popup menu.
*/
private void scrollViewPort()
{
Point cursorLocation = _cursorLabel.getLocation();
int dx = ( int )Math.max( cursorLocation.getX(), 0 );
int dy = ( int )Math.max( cursorLocation.getY(), 0 );
dx = ( int )( dx * _ratio );
dy = ( int )( dy * _ratio );
( ( JComponent )_viewPort.getComponent( 0 ) ).scrollRectToVisible( new Rectangle( dx, dy, _viewPort.getWidth(),
_viewPort.getHeight() ) );
}
/**
* takes a java component and generates an image out of it.
*
* @param c
* the component for which image needs to be generated
* @return the generated image
*/
public static BufferedImage captureComponentViewAsBufferedImage( Component c )
{
Dimension size = c.getSize();
BufferedImage bufferedImage = new BufferedImage( size.width, size.height, BufferedImage.TYPE_INT_RGB );
Graphics bufferedGraphics = bufferedImage.createGraphics();
c.paint( bufferedGraphics );
return bufferedImage;
}
}