// Copyright (c) 2003-present, Jodd Team (http://jodd.org) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. package jodd.swingspy; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JRootPane; import javax.swing.KeyStroke; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Toolkit; import java.awt.Dialog; import java.awt.Frame; import java.awt.event.AWTEventListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; /** * SwingSpy. */ public class SwingSpy { private static final int MOUSE_HOTKEY = MouseEvent.SHIFT_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK; private static SwingSpy spy; protected JDialog spyDialog; protected SwingSpyPanel spyPanel; protected SwingSpyGlassPane spyGlass; public SwingSpy() { Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { /** * Mouse hot-key combination clicked. */ public void eventDispatched(AWTEvent awtEvent) { MouseEvent event = (MouseEvent) awtEvent; if ((event.getModifiersEx() == MOUSE_HOTKEY) && (event.getClickCount() == 1)) { showSpyDialog((Component) event.getSource()); event.consume(); } } }, AWTEvent.MOUSE_EVENT_MASK); printUsageMessage(); } /** * Print SwingSpy usage message. */ protected void printUsageMessage() { System.out.println("SHIFT-CTRL-CLICK on any component to activate."); } /** * Installs the SwingSpy globally if not already installed. */ public static void install() { if (spy == null) { System.out.print("SwingSpy has been installed. "); spy = new SwingSpy(); } else { System.out.println("SwingSpy already installed."); } } /** * Shows spy dialog on selected component. */ public void showSpyDialog(final Component component) { showSpyDialog(SwingUtilities.getRoot(component), component); } /** * Shows spy dialog or reload existing one. * * @param rootComponent root component * @param component current component */ public void showSpyDialog(final Component rootComponent, final Component component) { if (spyDialog != null) { spyDialog.setVisible(true); spyGlass.setVisible(true); spyPanel.reload(rootComponent, component); return; } if (rootComponent instanceof RootPaneContainer) { RootPaneContainer rootPane = (RootPaneContainer) rootComponent; spyGlass = new SwingSpyGlassPane(rootPane); rootPane.setGlassPane(spyGlass); rootPane.getGlassPane().setVisible(true); Toolkit.getDefaultToolkit().addAWTEventListener(spyGlass, AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK); } SwingUtilities.invokeLater(new Runnable() { public void run() { initSpyDialog(rootComponent, component); } }); } /** * Initializes Spy dialog. */ protected void initSpyDialog(Component rootComponent, Component component) { if (rootComponent instanceof Dialog) { spyDialog = new CaddyDialog((Dialog) rootComponent) { @Override protected JRootPane createRootPane() { return createSpyRootPane(); } }; } else if (rootComponent instanceof Frame) { spyDialog = new CaddyDialog((Frame) rootComponent) { @Override protected JRootPane createRootPane() { return createSpyRootPane(); } }; } else { spyDialog = new JDialog() { @Override protected JRootPane createRootPane() { return createSpyRootPane(); } }; } spyDialog.setName("SwingSpy"); spyDialog.setTitle("SwingSpy"); spyDialog.setModal(false); spyDialog.setAlwaysOnTop(true); Container contentPane = spyDialog.getContentPane(); contentPane.setLayout(new BorderLayout()); spyPanel = new SwingSpyPanel(); spyPanel.reload(rootComponent, component); contentPane.add(spyPanel); spyDialog.pack(); spyDialog.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { super.windowClosed(e); spyGlass.setVisible(false); spyDialog = null; } }); spyDialog.setLocationRelativeTo(null); spyDialog.setVisible(true); } /** * Creates spy root pane for spy dialog. */ protected JRootPane createSpyRootPane() { JRootPane rootPane = new JRootPane(); KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); rootPane.registerKeyboardAction(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { spyGlass.setVisible(false); spyDialog.setVisible(false); } }, escapeKeyStroke, JComponent.WHEN_IN_FOCUSED_WINDOW); return rootPane; } }