/*
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*/
package org.jdesktop.swingx;
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.UIManager.LookAndFeelInfo;
import org.jdesktop.swingx.search.SearchFactory;
/**
* Base class for supporting inclusion of interactive tests into a JUnit test case.
* Note that the interactive tests are NOT executed by the JUnit framework and
* are not automated. They are typically used for visual inspection of features
* during development. It is convenient to include the interactive tests along with
* the automated JUnit tests since they may share resources and it keeps tests
* focused in a single place.
* <p>
* All interactive test methods should be prefixed with "interactive",
* e.g. interactiveTestTableSorting().</p>
* <p>
* The test class's <code>main</code> method should be used to control which
* interactive tests should run. Use <code>runInteractiveTests()</code> method
* to run all interactive tests in the class.</p>
* <p>
* Ultimately we need to investigate moving to a mechanism which can help automate
* interactive tests. JFCUnit is being investigated. In the meantime, this
* is quick and dirty and cheap.
* </p>
* @author Amy Fowler
* @version 1.0
*/
public abstract class InteractiveTestCase extends junit.framework.TestCase {
private static final Logger LOG = Logger
.getLogger(InteractiveTestCase.class.getName());
protected Point frameLocation = new Point(0,0);
protected boolean systemLF;
public InteractiveTestCase() {
super();
String className = getClass().getName();
int lastDot = className.lastIndexOf(".");
String lastElement = className.substring(lastDot + 1);
setName(lastElement);
}
public InteractiveTestCase(String testTitle) {
super(testTitle);
}
//----------------- run
/**
* Runs all tests which are prefixed by "interactive" and contain the given snippet.
* @throws Exception
*/
public void runInteractive(String snippet) throws Exception {
runInteractiveTests("interactive.*" + snippet + ".*");
}
/**
* Runs all tests whose method names match the specified regex pattern.
* @param regexPattern regular expression pattern used to match test method names
* @throws java.lang.Exception
*/
public void runInteractiveTests(String regexPattern) throws java.lang.Exception {
setUp();
Class<?> testClass = getClass();
Method methods[] = testClass.getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().matches(regexPattern)) {
try {
methods[i].invoke(this);
}
catch (Exception e) {
System.out.println("could not run interactive test: " +
methods[i].getName());
e.printStackTrace();
}
}
}
if (methods.length == 0) {
System.out.println("no test methods found matching the pattern: "+
regexPattern);
}
tearDown();
}
/**
* Runs all test methods which are prefixed with "interactive".
* @throws java.lang.Exception
*/
public void runInteractiveTests() throws java.lang.Exception {
runInteractiveTests("interactive.*");
}
//------------------------ creating/showing frame
/**
* Creates and returns a JXFrame with the specified title, containing
* the component wrapped into a JScrollPane.
*
* @param component the JComponent to wrap
* @param title the title to show in the frame
* @return a configured, packed and located JXFrame.
*/
public JXFrame wrapWithScrollingInFrame(JComponent component, String title) {
JScrollPane scroller = new JScrollPane(component);
return wrapInFrame(scroller, title);
}
/**
* Creates and returns a JXFrame with the specified title, containing
* two components individually wrapped into a JScrollPane.
*
* @param leftComp the left JComponent to wrap
* @param rightComp the right JComponent to wrap
* @param title the title to show in the frame
* @return a configured, packed and located JXFrame
*/
public JXFrame wrapWithScrollingInFrame(JComponent leftComp, JComponent rightComp, String title) {
JComponent comp = Box.createHorizontalBox();
comp.add(new JScrollPane(leftComp));
comp.add(new JScrollPane(rightComp));
JXFrame frame = wrapInFrame(comp, title);
return frame;
}
/**
* Creates and returns a JXFrame with the specified title, containing the
* component. First frame get's the menubar as defined by
* createAndFillMenuBar. Closing the first frame will exit.
*
* @param component the JComponent to wrap
* @param title the title to show in the frame
* @return a configured, packed and located JXFrame.
*/
public JXFrame wrapInFrame(JComponent component, String title) {
JXFrame frame = new JXFrame(title, false);
JToolBar toolbar = new JToolBar();
frame.getRootPaneExt().setToolBar(toolbar);
frame.getContentPane().add(BorderLayout.CENTER, component);
frame.setLocation(frameLocation);
if (frameLocation.x == 0) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle(title + " [close me and all tests will close]");
frame.setJMenuBar(createAndFillMenuBar(component));
}
frameLocation.x += 30;
frameLocation.y += 30;
frame.pack();
return frame;
}
/**
* Creates, shows and returns a JXFrame with the specified title, containing
* the component wrapped into a JScrollPane.
*
* @param component the JComponent to wrap
* @param title the title to show in the frame
* @return a configured, packed and located JXFrame.
* @see #wrapWithScrollingInFrame(JComponent, String)
*/
public JXFrame showWithScrollingInFrame(JComponent component, String title) {
JXFrame frame = wrapWithScrollingInFrame(component, title);
frame.setVisible(true);
return frame;
}
/**
* Creates and returns a JXFrame with the specified title, containing
* two components individually wrapped into a JScrollPane.
*
* @param leftComp the left JComponent to wrap
* @param rightComp the right JComponent to wrap
* @param title the title to show in the frame
* @return a configured, packed and located JXFrame
*/
public JXFrame showWithScrollingInFrame(JComponent leftComp, JComponent rightComp, String title) {
JXFrame frame = wrapWithScrollingInFrame(leftComp, rightComp, title);
frame.setVisible(true);
return frame;
}
/**
* Creates, shows and returns a JXFrame with the specified title, containing
* the component.
*
* @param component the JComponent to wrap
* @param title the title to show in the frame
* @return a configured, packed and located JXFrame.
*/
public JXFrame showInFrame(JComponent component, String title) {
return showInFrame(component, title, false);
}
/**
* Creates, shows and returns a JXFrame with the specified title, containing
* the component. Creates and adds a Menubar if showMenu is true.
*
* @param component the JComponent to wrap
* @param title the title to show in the frame
* @param showMenu flag to indicate whether a JMenuBar should be added
* @return a configured, packed and located JXFrame.
*/
public JXFrame showInFrame(JComponent component, String title, boolean showMenu) {
JXFrame frame = wrapInFrame(component, title, showMenu);
frame.setVisible(true);
return frame;
}
public JXFrame wrapInFrame(JComponent component, String title, boolean showMenu) {
JXFrame frame = wrapInFrame(component, title);
if (showMenu) {
frame.setJMenuBar(createAndFillMenuBar(component));
}
frame.pack();
return frame;
}
/**
* Packs and shows the frame.
*
* @param frame
*/
public void show(final JXFrame frame) {
frame.pack();
frame.setVisible(true);
}
/**
* Packs, sizes and shows the frame.
*
* @param frame
*/
public void show(final JXFrame frame, int width, int height) {
frame.pack();
frame.setSize(width, height);
frame.setVisible(true);
}
//--- toolbar, statusbar
public AbstractButton addAction(JXFrame frame, Action action) {
JToolBar toolbar = frame.getRootPaneExt().getToolBar();
if (toolbar != null) {
AbstractButton button = toolbar.add(action);
button.setFocusable(false);
return button;
}
return null;
}
/**
* Creates and adds a button toggling the frame's componentOrientation.
* @param frame
*/
public void addComponentOrientationToggle(final JXFrame frame) {
Action toggleComponentOrientation = new AbstractAction("toggle orientation") {
@Override
public void actionPerformed(ActionEvent e) {
ComponentOrientation current = frame.getComponentOrientation();
if (current.isLeftToRight()) {
frame.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
} else {
frame.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
}
frame.getRootPane().revalidate();
frame.invalidate();
frame.validate();
frame.repaint();
}
};
addAction(frame, toggleComponentOrientation);
}
public void toggleComponentOrientation(JComponent frame) {
ComponentOrientation current = frame.getComponentOrientation();
if (current.isLeftToRight()) {
frame.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
} else {
frame.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
}
frame.revalidate();
frame.repaint();
}
/**
* Creates and adds a button toggling the frame's componentOrientation.
* @param frame
*/
public void addSearchModeToggle(final JXFrame frame) {
Action action = new AbstractAction("toggle batch/incremental"){
@Override
public void actionPerformed(ActionEvent e) {
boolean useFindBar = !SearchFactory.getInstance().isUseFindBar(null, null);
SearchFactory.getInstance().setUseFindBar(useFindBar);
}
};
addAction(frame, action);
}
/**
* Creates and adds a button toggling the target's enabled property.
*
* @param frame
* @param target
*/
public void addEnabledToggle(JXFrame frame, final JComponent target) {
Action action = new AbstractAction("toggle enabled") {
@Override
public void actionPerformed(ActionEvent e) {
target.setEnabled(!target.isEnabled());
}
};
addAction(frame, action);
}
/**
* Creates and adds a button toggling the target's and its direct children's
* enabled property.
*
* @param frame
* @param target
*/
public void addEnabledToggleWithChildren(JXFrame frame, final JComponent target) {
Action action = new AbstractAction("toggle enabled (with children)") {
@Override
public void actionPerformed(ActionEvent e) {
target.setEnabled(!target.isEnabled());
for (int i = 0; i < target.getComponentCount(); i++) {
target.getComponent(i).setEnabled(target.isEnabled());
}
}
};
addAction(frame, action);
}
public void addMessage(JXFrame frame, String message) {
JXStatusBar statusBar = getStatusBar(frame);
statusBar.add(new JLabel(message), JXStatusBar.Constraint.ResizeBehavior.FILL);
}
/**
* Returns the <code>JXFrame</code>'s status bar. Lazily creates and
* sets an instance if necessary.
* @param frame the target frame
* @return the frame's statusbar
*/
public JXStatusBar getStatusBar(JXFrame frame) {
JXStatusBar statusBar = frame.getRootPaneExt().getStatusBar();
if (statusBar == null) {
statusBar = new JXStatusBar();
frame.setStatusBar(statusBar);
}
return statusBar;
}
/**
* Adds the component to the statusbar of the frame.
*
* @param frame
* @param component
*/
public void addStatusComponent(JXFrame frame, JComponent component) {
getStatusBar(frame).add(component);
frame.pack();
}
/**
* @param frame
* @param string
*/
public void addStatusMessage(JXFrame frame, String message) {
JXStatusBar bar = getStatusBar(frame);
bar.add(new JLabel(message));
frame.pack();
}
//------------------------ menu
public JMenuBar getMenuBar(JXFrame frame) {
JMenuBar bar = frame.getJMenuBar();
if (bar == null) {
bar = new JMenuBar();
frame.setJMenuBar(bar);
}
return bar;
}
/**
* Creates, fills and returns a JMenuBar.
*
* @param component the component that was added to the frame.
* @return a menu bar filled with actions as defined in createAndAddMenus
*
* @see #createAndAddMenus
*/
protected JMenuBar createAndFillMenuBar(JComponent component) {
JMenuBar bar = new JMenuBar();
createAndAddMenus(bar, component);
return bar;
}
/**
* Creates menus and adds them to the given menu bar.<p>
*
* This implementation adds a menu to choose the LF.
*
* @param bar the menubar to fill
* @param component the component that was added to the frame.
*
*/
protected void createAndAddMenus(JMenuBar bar, JComponent component) {
bar.add(createPlafMenu());
}
/**
* @return
*/
protected JMenu createPlafMenu() {
return createPlafMenu(null);
}
/**
* Creates a menu filled with one SetPlafAction for each of the currently
* installed LFs.
*
* @param target the toplevel window to update, maybe null to indicate update
* of all application windows
* @return the menu to use for plaf switching.
*/
protected JMenu createPlafMenu(Window target) {
LookAndFeelInfo[] plafs = UIManager.getInstalledLookAndFeels();
JMenu menu = new JMenu("Set L&F");
for (LookAndFeelInfo info : plafs) {
menu.add(new SetPlafAction(info.getName(), info.getClassName(), target));
}
return menu;
}
// ---------------------- laf
/**
*
* Sets the lookAndFeel which has a name containing the given snippet.
* Does not update any component/-tree, just bare
* setting. May fail silently (Logging with level FINE) if there is no
* installed LAF with the name or the setting fails for other reasons.
*
* @param nameSnippet part of the name as published by the LAF.
*/
public static void setLAF(String nameSnippet) {
String laf = getLookAndFeelClassName(nameSnippet);
if (laf != null) {
try {
UIManager.setLookAndFeel(laf);
} catch (Exception e) {
LOG.log(Level.FINE, "problem in setting laf: " + laf, e);
}
}
}
/**
*
* Sets the lookAndFeel which has a name containing the given snippet. Throws
* if none installed. Does not update any component/-tree, just bare setting.
*
* @param nameSnippet part of the name as published by the LAF.
*
* @throws UnsupportedLookAndFeelException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws ClassNotFoundException
*
*/
public static void setLookAndFeel(String nameSnippet) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
String laf = getLookAndFeelClassName(nameSnippet);
if (laf != null) {
UIManager.setLookAndFeel(laf);
return;
}
throw new UnsupportedLookAndFeelException("no LAF installed with name snippet " + nameSnippet);
}
/**
* Returns a boolean indicating whether or not the UI has a LAF with a name
* containing the name snippet installed.
*
* @param nameSnippet part of the name as published by the LAF.
* @return a boolean indicating whether there's installed LAF with
* a name containing the name snippet.
*/
public static boolean hasLookAndFeel(String nameSnippet) {
return getLookAndFeelClassName(nameSnippet) != null;
}
/**
* Returns the class name of the installed LookAndFeel with a name
* containing the name snippet or null if none found.
*
* @param nameSnippet
* @return
*/
public static String getLookAndFeelClassName(String nameSnippet) {
LookAndFeelInfo[] plafs = UIManager.getInstalledLookAndFeels();
for (LookAndFeelInfo info : plafs) {
if (info.getName().contains(nameSnippet)) {
return info.getClassName();
}
}
return null;
}
/**
* Action to toggle plaf and update all toplevel windows of the
* current application. Used to setup the plaf-menu.
*/
private static class SetPlafAction extends AbstractAction {
private String plaf;
private Window toplevel;
@SuppressWarnings("unused")
public SetPlafAction(String name, String plaf) {
this(name, plaf, null);
}
/**
* Instantiates an action which updates the toplevel window to
* the given LAF.
*
* @param name the name of the action
* @param plaf the class name of the LAF to set
* @param toplevel the window to update, may be null to indicate
* update of all application windows
*/
public SetPlafAction(String name, String plaf, Window toplevel) {
super(name);
this.plaf = plaf;
this.toplevel = toplevel;
}
/**
* {@inheritDoc}
*/
@Override
public void actionPerformed(ActionEvent e) {
try {
UIManager.setLookAndFeel(plaf);
if (toplevel != null) {
SwingUtilities.updateComponentTreeUI(toplevel);
} else {
SwingXUtilities.updateAllComponentTreeUIs();
}
} catch (Exception e1) {
e1.printStackTrace();
LOG.log(Level.FINE, "problem in setting laf: " + plaf, e1);
}
}
}
/**
* PENDING: JW - this is about toggling the LF, does nothing to update the
* UI. Check all tests using this method to see if they make sense!
*
*
* @param system
*/
public static void setSystemLF(boolean system) {
String lfName = system ? UIManager.getSystemLookAndFeelClassName()
: UIManager.getCrossPlatformLookAndFeelClassName();
try {
UIManager.setLookAndFeel(lfName);
// systemLF = system;
} catch (Exception e1) {
LOG.info("exception when setting LF to " + lfName);
LOG.log(Level.FINE, "caused by ", e1);
}
}
/**
* Returns whether the current lf is the system lf. It assumes that the
* lf is either cross-platform or system. Not really safe
*/
public static boolean isSystemLF() {
LookAndFeel lf = UIManager.getLookAndFeel();
return UIManager.getSystemLookAndFeelClassName().equals(lf.getClass().getName());
}
/**
* Returns whether the current lf is the cross-platform lf. It assumes that the
* lf is either cross-platform or system. Not really safe
*/
public static boolean isCrossPlatformLF() {
LookAndFeel lf = UIManager.getLookAndFeel();
return UIManager.getCrossPlatformLookAndFeelClassName().equals(lf.getClass().getName());
}
}