/* * SoapUI, Copyright (C) 2004-2016 SmartBear Software * * Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * * http://ec.europa.eu/idabc/eupl * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the Licence for the specific language governing permissions and limitations * under the Licence. */ package com.eviware.soapui.support.components; import com.eviware.soapui.support.UISupport; 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 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; /** * 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; } }