Clover coverage report - PLT Utilities Test Coverage (plt-20120304-r5436)
Coverage timestamp: Sat Mar 3 2012 22:01:56 CST
file stats: LOC: 731   Methods: 88
NCLOC: 399   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
SwingUtil.java 0% 0% 0% 0%
coverage
 1    /*BEGIN_COPYRIGHT_BLOCK*
 2   
 3    PLT Utilities BSD License
 4   
 5    Copyright (c) 2007-2010 JavaPLT group at Rice University
 6    All rights reserved.
 7   
 8    Developed by: Java Programming Languages Team
 9    Rice University
 10    http://www.cs.rice.edu/~javaplt/
 11   
 12    Redistribution and use in source and binary forms, with or without modification, are permitted
 13    provided that the following conditions are met:
 14   
 15    - Redistributions of source code must retain the above copyright notice, this list of conditions
 16    and the following disclaimer.
 17    - Redistributions in binary form must reproduce the above copyright notice, this list of
 18    conditions and the following disclaimer in the documentation and/or other materials provided
 19    with the distribution.
 20    - Neither the name of the JavaPLT group, Rice University, nor the names of the library's
 21    contributors may be used to endorse or promote products derived from this software without
 22    specific prior written permission.
 23   
 24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 25    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 26    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND
 27    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 29    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 30    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 31    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32   
 33    *END_COPYRIGHT_BLOCK*/
 34   
 35    package edu.rice.cs.plt.swing;
 36   
 37    import java.io.File;
 38    import java.io.Reader;
 39    // import java.io.FileFilter; not imported to avoid ambiguity
 40    import java.util.Collections;
 41    import java.util.Map;
 42    import java.util.HashMap;
 43   
 44    import java.applet.Applet;
 45    import java.awt.*;
 46    import java.awt.event.ActionListener;
 47    import java.awt.event.ActionEvent;
 48    import java.awt.event.WindowAdapter;
 49    import java.awt.event.WindowEvent;
 50    import java.awt.datatransfer.Clipboard;
 51    import java.awt.datatransfer.Transferable;
 52    import java.awt.datatransfer.DataFlavor;
 53    import java.awt.datatransfer.UnsupportedFlavorException;
 54    // import javax.swing.filechooser.FileFilter; not imported to avoid ambiguity
 55    import java.lang.reflect.InvocationTargetException;
 56    import java.net.URL;
 57    import java.net.URLClassLoader;
 58   
 59    import javax.swing.*;
 60    import javax.swing.text.*;
 61    import javax.swing.border.Border;
 62   
 63    import edu.rice.cs.plt.lambda.LambdaUtil;
 64    import edu.rice.cs.plt.lambda.WrappedException;
 65    import edu.rice.cs.plt.lambda.Runnable1;
 66    import edu.rice.cs.plt.lambda.Predicate;
 67    import edu.rice.cs.plt.reflect.ReflectException;
 68    import edu.rice.cs.plt.reflect.ReflectUtil;
 69    import edu.rice.cs.plt.io.IOUtil;
 70    import edu.rice.cs.plt.io.FilePredicate;
 71   
 72    import static edu.rice.cs.plt.debug.DebugUtil.error;
 73   
 74    public class SwingUtil {
 75   
 76    /**
 77    * Create a shade of gray with the given degree of darkness. {@code 0.0f} corresponds to white;
 78    * {@code 1.0f} corresponds to black.
 79    */
 80  0 public static Color gray(float degree) {
 81  0 float x = 1.0f - degree;
 82  0 return new Color(x, x, x);
 83    }
 84   
 85    /**
 86    * Boilerplate for creating a JFrame to be used as the main application window. Sets the title and
 87    * preferred size to the given values. Sets the default close operation to "exit on close."
 88    */
 89  0 public static JFrame makeMainApplicationFrame(String title, int width, int height) {
 90  0 JFrame result = new JFrame(title);
 91  0 result.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 92  0 result.getContentPane().setPreferredSize(new Dimension(width, height));
 93  0 return result;
 94    }
 95   
 96    /**
 97    * Boilerplate for creating a JFrame. Sets the title and preferred size to the given values. Sets the
 98    * default close operation to "dispose on close."
 99    */
 100  0 public static JFrame makeDisposableFrame(String title, int width, int height) {
 101  0 JFrame result = new JFrame(title);
 102  0 result.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
 103    // cast is necessary for 1.4 compatibility
 104  0 result.getContentPane().setPreferredSize(new Dimension(width, height));
 105  0 return result;
 106    }
 107   
 108    /**
 109    * Boilerplate for creating a JFrame. Sets the title and preferred size to the given values. Sets the
 110    * default close operation to "hide on close."
 111    */
 112  0 public static JFrame makeReusableFrame(String title, int width, int height) {
 113  0 JFrame result = new JFrame(title);
 114  0 result.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
 115    // cast is necessary for 1.4 compatibility
 116  0 result.getContentPane().setPreferredSize(new Dimension(width, height));
 117  0 return result;
 118    }
 119   
 120  0 public static void onWindowClosed(Window w, final Runnable r) {
 121  0 w.addWindowListener(new WindowAdapter() {
 122  0 public void windowClosed(WindowEvent e) { r.run(); }
 123    });
 124    }
 125   
 126    /** Shortcut for the boilerplate code to pack and display a window (such as a {@code JFrame}). */
 127  0 public static void displayWindow(Window w) {
 128  0 w.pack();
 129  0 w.setVisible(true);
 130    }
 131   
 132    /** Make a JPanel with a horizontal {@link BoxLayout}. */
 133  0 public static JPanel makeHorizontalBoxPanel() {
 134  0 JPanel result = new JPanel();
 135  0 result.setLayout(new BoxLayout(result, BoxLayout.X_AXIS));
 136  0 return result;
 137    }
 138   
 139    /** Make a JPanel with a horizontal {@link BoxLayout}; set the border to the given margin on all sides. */
 140  0 public static JPanel makeHorizontalBoxPanel(int margin) {
 141  0 return makeHorizontalBoxPanel(margin, margin, margin, margin);
 142    }
 143   
 144    /** Make a JPanel with a horizontal {@link BoxLayout}; set the border to the given vertical/horizontal margins. */
 145  0 public static JPanel makeHorizontalBoxPanel(int vMargin, int hMargin) {
 146  0 return makeHorizontalBoxPanel(vMargin, hMargin, vMargin, hMargin);
 147    }
 148   
 149    /** Make a JPanel with a horizontal {@link BoxLayout}; set the border to the given margins. */
 150  0 public static JPanel makeHorizontalBoxPanel(int topMargin, int leftMargin,
 151    int bottomMargin, int rightMargin) {
 152  0 JPanel result = makeHorizontalBoxPanel();
 153  0 result.setBorder(BorderFactory.createEmptyBorder(topMargin, leftMargin, bottomMargin, rightMargin));
 154  0 return result;
 155    }
 156   
 157    /** Make a JPanel with a vertical {@link BoxLayout}. */
 158  0 public static JPanel makeVerticalBoxPanel() {
 159  0 JPanel result = new JPanel();
 160  0 result.setLayout(new BoxLayout(result, BoxLayout.Y_AXIS));
 161  0 return result;
 162    }
 163   
 164    /** Make a JPanel with a vertical {@link BoxLayout}; set the border to the given margin on all sides. */
 165  0 public static JPanel makeVerticalBoxPanel(int margin) {
 166  0 return makeVerticalBoxPanel(margin, margin, margin, margin);
 167    }
 168   
 169    /** Make a JPanel with a vertical {@link BoxLayout}; set the border to the given vertical/horizontal margins. */
 170  0 public static JPanel makeVerticalBoxPanel(int vMargin, int hMargin) {
 171  0 return makeVerticalBoxPanel(vMargin, hMargin, vMargin, hMargin);
 172    }
 173   
 174    /** Make a JPanel with a vertical {@link BoxLayout}; set the border to the given margins. */
 175  0 public static JPanel makeVerticalBoxPanel(int topMargin, int leftMargin,
 176    int bottomMargin, int rightMargin) {
 177  0 JPanel result = makeVerticalBoxPanel();
 178  0 result.setBorder(BorderFactory.createEmptyBorder(topMargin, leftMargin, bottomMargin, rightMargin));
 179  0 return result;
 180    }
 181   
 182    /** Make a JPanel with a {@link BorderLayout}. */
 183  0 public static JPanel makeBorderPanel() {
 184  0 return new JPanel(new BorderLayout());
 185    }
 186   
 187    /** Make a JPanel with a {@link BorderLayout}; set the border to the given margin on all sides. */
 188  0 public static JPanel makeBorderPanel(int margin) {
 189  0 return makeBorderPanel(margin, margin, margin, margin);
 190    }
 191   
 192    /** Make a JPanel with a {@link BorderLayout}; set the border to the given vertical/horizontal margins. */
 193  0 public static JPanel makeBorderPanel(int vMargin, int hMargin) {
 194  0 return makeBorderPanel(vMargin, hMargin, vMargin, hMargin);
 195    }
 196   
 197    /** Make a JPanel with a {@link BorderLayout}; set the border to the given margins. */
 198  0 public static JPanel makeBorderPanel(int topMargin, int leftMargin,
 199    int bottomMargin, int rightMargin) {
 200  0 JPanel result = makeBorderPanel();
 201  0 result.setBorder(BorderFactory.createEmptyBorder(topMargin, leftMargin, bottomMargin, rightMargin));
 202  0 return result;
 203    }
 204   
 205    /** Make a JPanel with a {@link FlowLayout}. */
 206  0 public static JPanel makeFlowPanel() {
 207    // default is FlowLayout
 208  0 return new JPanel();
 209    }
 210   
 211    /** Make a JPanel with a {@link FlowLayout}; set the border to the given margin on all sides. */
 212  0 public static JPanel makeFlowPanel(int margin) {
 213  0 return makeFlowPanel(margin, margin, margin, margin);
 214    }
 215   
 216    /** Make a JPanel with a {@link FlowLayout}; set the border to the given vertical/horizontal margins. */
 217  0 public static JPanel makeFlowPanel(int vMargin, int hMargin) {
 218  0 return makeFlowPanel(vMargin, hMargin, vMargin, hMargin);
 219    }
 220   
 221    /** Make a JPanel with a {@link FlowLayout}; set the border to the given margins. */
 222  0 public static JPanel makeFlowPanel(int topMargin, int leftMargin,
 223    int bottomMargin, int rightMargin) {
 224  0 JPanel result = makeFlowPanel();
 225  0 result.setBorder(BorderFactory.createEmptyBorder(topMargin, leftMargin, bottomMargin, rightMargin));
 226  0 return result;
 227    }
 228   
 229    /** Add the given components to {@code parent} in sequence. Intended to reduce clutter in GUI code. */
 230  0 public static void add(Container parent, Component... children) {
 231  0 for (Component child : children) { parent.add(child); }
 232    }
 233   
 234    /** Set the background color property of the given components. Intended to reduce clutter in GUI code. */
 235  0 public static void setBackground(Color c, Component... components) {
 236  0 for (Component cm : components) { cm.setBackground(c); }
 237    }
 238   
 239    /** Set the foreground color property of the given components. Intended to reduce clutter in GUI code. */
 240  0 public static void setForeground(Color c, Component... components) {
 241  0 for (Component cm : components) { cm.setForeground(c); }
 242    }
 243   
 244    /** Set the border of the given components. Intended to reduce clutter in GUI code. */
 245  0 public static void setBorder(Border b, JComponent... components) {
 246  0 for (JComponent c : components) { c.setBorder(b); }
 247    }
 248   
 249    /** Set the border of the given components to an empty border with the given margin. */
 250  0 public static void setEmptyBorder(int margin, JComponent... components) {
 251  0 setBorder(BorderFactory.createEmptyBorder(margin, margin, margin, margin), components);
 252    }
 253   
 254    /** Set the border of the given components to an empty border with the given vertical/horizontal margins. */
 255  0 public static void setEmptyBorder(int vMargin, int hMargin, JComponent... components) {
 256  0 setBorder(BorderFactory.createEmptyBorder(vMargin, hMargin, vMargin, hMargin), components);
 257    }
 258   
 259    /** Set the border of the given components to an empty border with the given margins. */
 260  0 public static void setEmptyBorder(int top, int left, int bottom, int right, JComponent... components) {
 261  0 setBorder(BorderFactory.createEmptyBorder(top, left, bottom, right), components);
 262    }
 263   
 264    /** Set the border of the given components to a black line border. */
 265  0 public static void setLineBorder(JComponent... components) {
 266  0 setBorder(BorderFactory.createLineBorder(Color.BLACK), components);
 267    }
 268   
 269    /** Set the font property of the given components. Intended to reduce clutter in GUI code. */
 270  0 public static void setFont(Font f, Component... components) {
 271  0 for (Component c : components) { c.setFont(f); }
 272    }
 273   
 274    /** Set the font property of the given components. Intended to reduce clutter in GUI code. */
 275  0 public static void setFont(String name, int size, Component... components) {
 276  0 setFont(new Font(name, Font.PLAIN, size), components);
 277    }
 278   
 279    /** Set the font of the given components to the logical {@code "Serif"} font of the given size. */
 280  0 public static void setSerifFont(int size, Component... components) {
 281  0 setFont(new Font("Serif", Font.PLAIN, size), components);
 282    }
 283   
 284    /** Set the font of the given components to the logical {@code "SansSerif"} font of the given size. */
 285  0 public static void setSansSerifFont(int size, Component... components) {
 286  0 setFont(new Font("SansSerif", Font.PLAIN, size), components);
 287    }
 288   
 289    /** Set the font of the given components to the logical {@code "Monospaced"} font of the given size. */
 290  0 public static void setMonospacedFont(int size, Component... components) {
 291  0 setFont(new Font("Monospaced", Font.PLAIN, size), components);
 292    }
 293   
 294    /** Set the font of the given components to the logical {@code "Dialog"} font of the given size. */
 295  0 public static void setDialogFont(int size, Component... components) {
 296  0 setFont(new Font("Dialog", Font.PLAIN, size), components);
 297    }
 298   
 299    /** Set the font of the given components to the logical {@code "DialogInput"} font of the given size. */
 300  0 public static void setDialogInputFont(int size, Component... components) {
 301  0 setFont(new Font("DialogInput", Font.PLAIN, size), components);
 302    }
 303   
 304    /** Set the enabled property of the given components. Intended to reduce clutter in GUI code. */
 305  0 public static void setEnabled(boolean b, Component... components) {
 306  0 for (Component c : components) { c.setEnabled(b); }
 307    }
 308   
 309    /** Set the focusable property of the given components. Intended to reduce clutter in GUI code. */
 310  0 public static void setFocusable(boolean b, Component... components) {
 311  0 for (Component c : components) { c.setFocusable(b); }
 312    }
 313   
 314    /** Set the visible property of the given components. Intended to reduce clutter in GUI code. */
 315  0 public static void setVisible(boolean b, Component... components) {
 316  0 for (Component c : components) { c.setVisible(b); }
 317    }
 318   
 319    /** Set the opaque property of the given components. Intended to reduce clutter in GUI code. */
 320  0 public static void setOpaque(boolean b, JComponent... components) {
 321  0 for (JComponent c : components) { c.setOpaque(b); }
 322    }
 323   
 324    /**
 325    * Set the preferred size of the given components. Intended to reduce clutter in GUI code.
 326    * For Java 1.4 compatibility, the arguments must be {@code JComponent}s, not arbitrary
 327    * {@code Component}s.
 328    */
 329  0 public static void setPreferredSize(Dimension d, JComponent... components) {
 330  0 for (JComponent c : components) { c.setPreferredSize(d); }
 331    }
 332   
 333    /**
 334    * Set the preferred size of the given components. Intended to reduce clutter in GUI code.
 335    * For Java 1.4 compatibility, the arguments must be {@code JComponent}s, not arbitrary
 336    * {@code Component}s.
 337    */
 338  0 public static void setPreferredSize(int width, int height, JComponent... components) {
 339  0 setPreferredSize(new Dimension(width, height), components);
 340    }
 341   
 342    /**
 343    * Set the maximum size of the given components. Intended to reduce clutter in GUI code.
 344    * For Java 1.4 compatibility, the arguments must be {@code JComponent}s, not arbitrary
 345    * {@code Component}s.
 346    */
 347  0 public static void setMaximumSize(Dimension d, JComponent... components) {
 348  0 for (JComponent c : components) { c.setMaximumSize(d); }
 349    }
 350   
 351    /**
 352    * Set the maximum size of the given components. Intended to reduce clutter in GUI code.
 353    * For Java 1.4 compatibility, the arguments must be {@code JComponent}s, not arbitrary
 354    * {@code Component}s.
 355    */
 356  0 public static void setMaximumSize(int width, int height, JComponent... components) {
 357  0 setMaximumSize(new Dimension(width, height), components);
 358    }
 359   
 360    /**
 361    * Set the minimum size of the given components. Intended to reduce clutter in GUI code.
 362    * For Java 1.4 compatibility, the arguments must be {@code JComponent}s, not arbitrary
 363    * {@code Component}s.
 364    */
 365  0 public static void setMinimumSize(Dimension d, JComponent... components) {
 366  0 for (JComponent c : components) { c.setMinimumSize(d); }
 367    }
 368   
 369    /**
 370    * Set the minimum size of the given components. Intended to reduce clutter in GUI code.
 371    * For Java 1.4 compatibility, the arguments must be {@code JComponent}s, not arbitrary
 372    * {@code Component}s.
 373    */
 374  0 public static void setMinimumSize(int width, int height, JComponent... components) {
 375  0 setMinimumSize(new Dimension(width, height), components);
 376    }
 377   
 378    /** Make the given components horizontally-left aligned. */
 379  0 public static void setLeftAlignment(JComponent... components) {
 380  0 setAlignmentX(Component.LEFT_ALIGNMENT, components);
 381    }
 382   
 383    /** Make the given components horizontally-right aligned. */
 384  0 public static void setRightAlignment(JComponent... components) {
 385  0 setAlignmentX(Component.RIGHT_ALIGNMENT, components);
 386    }
 387   
 388    /** Make the given components horizontally-center aligned. */
 389  0 public static void setHorizontalCenterAlignment(JComponent... components) {
 390  0 setAlignmentX(Component.CENTER_ALIGNMENT, components);
 391    }
 392   
 393    /** Make the given components vertically-top aligned. */
 394  0 public static void setTopAlignment(JComponent... components) {
 395  0 setAlignmentY(Component.TOP_ALIGNMENT, components);
 396    }
 397   
 398    /** Make the given components vertically-bottom aligned. */
 399  0 public static void setBottomAlignment(JComponent... components) {
 400  0 setAlignmentY(Component.BOTTOM_ALIGNMENT, components);
 401    }
 402   
 403    /** Make the given components vertically-center aligned. */
 404  0 public static void setVerticalCenterAlignment(JComponent... components) {
 405  0 setAlignmentY(Component.CENTER_ALIGNMENT, components);
 406    }
 407   
 408    /** Set the horizontal alignment of the given components. Intended to reduce clutter in GUI code. */
 409  0 public static void setAlignmentX(float a, JComponent... components) {
 410  0 for (JComponent c : components) { c.setAlignmentX(a); }
 411    }
 412   
 413    /** Set the vertical alignment of the given components. Intended to reduce clutter in GUI code. */
 414  0 public static void setAlignmentY(float a, JComponent... components) {
 415  0 for (JComponent c : components) { c.setAlignmentY(a); }
 416    }
 417   
 418   
 419    /**
 420    * Runs the task synchronously if the current thread is the event thread; otherwise passes it to the
 421    * event thread to be run asynchronously after all events already on the queue have been processed.
 422    */
 423  0 public static void invokeLater(Runnable task) {
 424  0 if (EventQueue.isDispatchThread()) { task.run(); }
 425  0 else { EventQueue.invokeLater(task); }
 426    }
 427   
 428    /**
 429    * Runs the the task in the event thread, blocking until it is complete.
 430    *
 431    * @throws WrappedException Wrapping an {@link InterruptedException} if the current thread is interrupted
 432    * @throws RuntimeException If an exception is thrown by {@code task}
 433    */
 434  0 public static void invokeAndWait(Runnable task) {
 435  0 if (EventQueue.isDispatchThread()) { task.run(); }
 436    else {
 437  0 try { EventQueue.invokeAndWait(task); }
 438  0 catch (InterruptedException e) { throw new WrappedException(e); }
 439    catch (InvocationTargetException e) {
 440  0 Throwable cause = e.getCause();
 441    // must be a RuntimeException or an Error, because Runnable's can't have checked exceptions
 442  0 if (cause instanceof RuntimeException) { throw (RuntimeException) cause; }
 443  0 else if (cause instanceof Error) { throw (Error) cause; }
 444  0 else { error.log("Unexpected InvocationTargetException caused by invokeAndWait", cause); }
 445    }
 446    }
 447    }
 448   
 449    /**
 450    * Wait for all items in the event queue to be handled. This thread will block until all items
 451    * <em>currently</em> on the event queue have been handled.
 452    * @throws IllegalStateException If this is the event dispatch thread (it is impossible to wait
 453    * for the event queue to clear if this code is running in the
 454    * event queue).
 455    * @throws InterruptedException If this thread is interrupted while waiting.
 456    */
 457  0 public static void clearEventQueue() throws InterruptedException {
 458  0 if (SwingUtilities.isEventDispatchThread()) {
 459  0 throw new IllegalStateException("Can't clear the event queue from within the event dispatch thread");
 460    }
 461  0 try { SwingUtilities.invokeAndWait(LambdaUtil.NO_OP); }
 462    catch (InvocationTargetException e) {
 463    // Should never happen: Runnable is a no-op.
 464  0 error.log(e);
 465    }
 466    }
 467   
 468    /**
 469    * Call @link{#clearEventQueue}, but ignore any resulting InterruptedException. This method does
 470    * not guarantee that the queue will actually be cleared.
 471    * @throws IllegalStateException If this is the event dispatch thread (it is impossible to wait
 472    * for the event queue to clear if this code is running in the
 473    * event queue).
 474    */
 475  0 public static void attemptClearEventQueue() {
 476  0 try { clearEventQueue(); }
 477    catch (InterruptedException e) { /* ignore */ }
 478    }
 479   
 480    /** Convert a {@code Runnable} to an {@code ActionListener} */
 481  0 public static ActionListener asActionListener(final Runnable r) {
 482  0 return new ActionListener() {
 483  0 public void actionPerformed(ActionEvent e) { r.run(); }
 484    };
 485    }
 486   
 487    /** Convert a {@code Runnable1} to an {@code ActionListener} */
 488  0 public static ActionListener asActionListener(final Runnable1<? super ActionEvent> r) {
 489  0 return new ActionListener() {
 490  0 public void actionPerformed(ActionEvent e) { r.run(e); }
 491    };
 492    }
 493   
 494    /** Convert an {@code ActionListener} to a {@code Runnable1} */
 495  0 public static Runnable1<ActionEvent> asRunnable(final ActionListener l) {
 496  0 return new Runnable1<ActionEvent>() {
 497  0 public void run(ActionEvent e) { l.actionPerformed(e); }
 498    };
 499    }
 500   
 501    /**
 502    * Define a Swing file filter (for use with {@link javax.swing.JFileChooser}s) in terms if a
 503    * {@code FileFilter}.
 504    */
 505  0 public static javax.swing.filechooser.FileFilter asSwingFileFilter(final java.io.FileFilter filter,
 506    final String description) {
 507  0 return new javax.swing.filechooser.FileFilter() {
 508  0 public boolean accept(File f) { return filter.accept(f); }
 509  0 public String getDescription() { return description; }
 510    };
 511    }
 512   
 513    /**
 514    * Define a Swing file filter (for use with {@link javax.swing.JFileChooser}s) in terms if a
 515    * {@code FileFilter}.
 516    */
 517  0 public static javax.swing.filechooser.FileFilter asSwingFileFilter(Predicate<? super File> p, String description) {
 518  0 return asSwingFileFilter((java.io.FileFilter) IOUtil.asFilePredicate(p), description);
 519    }
 520   
 521    /**
 522    * Define a Swing file filter (for use with {@link javax.swing.JFileChooser}s) in terms if a
 523    * {@code FileFilter}. (Defined to disambiguate, since both other declarations apply to FilePredicates.)
 524    */
 525  0 public static javax.swing.filechooser.FileFilter asSwingFileFilter(FilePredicate p, String description) {
 526  0 return asSwingFileFilter((java.io.FileFilter) p, description);
 527    }
 528   
 529    /** Create an action to invoke {@link Window#dispose} on the given window */
 530  0 public static ActionListener disposeAction(final Window w) {
 531  0 return new ActionListener() {
 532  0 public void actionPerformed(ActionEvent e) { w.dispose(); }
 533    };
 534    }
 535   
 536    /** Invoke {@link #showPopup} with title {@code "Debug Message"}. This may be called by any thread. */
 537  0 public static void showDebug(String msg) { showPopup("Debug Message", msg); }
 538   
 539    /**
 540    * Show a modal message box with an OK button. This may be called by any thread.
 541    *
 542    * @param msg String to display
 543    * @param title Title of the box
 544    */
 545  0 public static void showPopup(final String title, final String msg) {
 546  0 invokeAndWait(new Runnable() { public void run() {
 547  0 TextAreaMessageDialog.showDialog(null, title, msg);
 548    } } );
 549    }
 550   
 551    /**
 552    * Load the given applet class from the sources at URL {@code classPath} and display it in a window
 553    * with title {@code "Applet Viewer"}.
 554    */
 555  0 public static void showApplet(URL classPath, String className, int width, int height) throws ReflectException {
 556  0 showApplet("Applet Viewer", classPath, className, width, height, Collections.<String, String>emptyMap());
 557    }
 558   
 559    /**
 560    * Load the given applet class from the sources at URL {@code classPath} and display it in a window
 561    * with the given title.
 562    */
 563  0 public static void showApplet(String title, URL classPath, String className, int width, int height)
 564    throws ReflectException {
 565  0 showApplet(title, classPath, className, width, height, Collections.<String, String>emptyMap());
 566    }
 567   
 568    /**
 569    * Load the given applet class from the sources at URL {@code classPath} and display it in a window
 570    * with the given title.
 571    */
 572  0 public static void showApplet(String title, URL classPath, String className, int width, int height,
 573    Map<String, String> params) throws ReflectException {
 574  0 Applet a = (Applet) ReflectUtil.loadObject(new URLClassLoader(new URL[]{ classPath }), className);
 575  0 showApplet(title, a, width, height, classPath, params);
 576    }
 577   
 578    /** Display the given applet in a window with title {@code "Applet Viewer"}. */
 579  0 public static void showApplet(Applet applet, int width, int height) {
 580  0 showApplet("Applet Viewer", applet, width, height, null, Collections.<String, String>emptyMap());
 581    }
 582   
 583    /** Display the given applet in a window with the given title. */
 584  0 public static void showApplet(String title, Applet applet, int width, int height) {
 585  0 showApplet(title, applet, width, height, null, Collections.<String, String>emptyMap());
 586    }
 587   
 588    /**
 589    * Display the given applet in a window with the given title; if it is not {@code null}, sources
 590    * are assumed to be rooted at the given URL.
 591    */
 592  0 public static void showApplet(String title, Applet applet, int width, int height, URL root) {
 593  0 showApplet(title, applet, width, height, root);
 594    }
 595   
 596    /**
 597    * Display the given applet in a window with the given title; if it is not {@code null}, sources
 598    * are assumed to be rooted at the given URL.
 599    */
 600  0 public static void showApplet(String title, Applet applet, int width, int height, URL root,
 601    Map<String, String> params) {
 602  0 JFrame frame = makeDisposableFrame(title, width, height);
 603  0 frame.add(new AppletComponent(applet, width, height, root, params));
 604  0 displayWindow(frame);
 605    }
 606   
 607    /** @return a string with the current clipboard selection, or null if not available. */
 608  0 public static String getClipboardSelection(Component c) {
 609  0 Clipboard cb = c.getToolkit().getSystemClipboard();
 610  0 if (cb==null) return null;
 611  0 Transferable t = cb.getContents(null);
 612  0 if (t==null) return null;
 613  0 String s = null;
 614  0 try {
 615  0 Reader r = DataFlavor.stringFlavor.getReaderForText(t);
 616  0 s = IOUtil.toString(r);
 617    }
 618    catch(UnsupportedFlavorException ufe) { /* ignore, return null */ }
 619    catch(java.io.IOException ioe) { /* ignore, return null */ }
 620  0 return s;
 621    }
 622   
 623   
 624    /**
 625    * Search the given text component's list of actions for an action with the given name.
 626    * Returns {@code null} if no such action is found. See {@link DefaultEditorKit} for a list of
 627    * action name constants.
 628    */
 629  0 public static Action getAction(JTextComponent component, String actionName) {
 630  0 for (Action a : component.getActions()) {
 631  0 if (actionName.equals(a.getValue(Action.NAME))) { return a; }
 632    }
 633  0 return null;
 634    }
 635   
 636    /**
 637    * Create a map view of {@code component.getActions()}, where the keys are the action
 638    * names. See {@link DefaultEditorKit} for a list of action name constants.
 639    */
 640  0 public static Map<String, Action> getActions(JTextComponent component) {
 641  0 Map<String, Action> result = new HashMap<String, Action>();
 642  0 for (Action a : component.getActions()) {
 643    // Documentation for Action.NAME asserts that it will always be a String
 644  0 result.put((String) a.getValue(Action.NAME), a);
 645    }
 646  0 return result;
 647    }
 648   
 649   
 650   
 651    /** Convenience method for {@link #setPopupLoc(Window, Component)} that gets the owner from {@code popup.getOwner()} */
 652  0 public static void setPopupLoc(Window popup) { setPopupLoc(popup, popup.getOwner()); }
 653   
 654    /**
 655    * <p>Sets the location of the popup in a consistent way. If the popup has an owner, the popup is centered over the
 656    * owner. If the popup has no owner (owner == null), the popup is centered over the first monitor. In either case,
 657    * the popup is moved and scaled if any part of it is not on the screen. This method should be called for all popups
 658    * to maintain consistancy.</p>
 659    * <p>This method should only be called from the event thread.</p>
 660    *
 661    * @param popup the popup window
 662    * @param owner the parent component for the popup, or {@code null}
 663    */
 664  0 public static void setPopupLoc(Window popup, Component owner) {
 665  0 Rectangle frameRect = popup.getBounds();
 666   
 667  0 Point ownerLoc = null;
 668  0 Dimension ownerSize = null;
 669  0 if(owner!=null) {
 670  0 ownerLoc = owner.getLocation();
 671  0 ownerSize = owner.getSize();
 672    }
 673    else {
 674    //for multi-monitor support
 675    //Question: do we want it to popup on the first monitor always?
 676  0 GraphicsDevice[] dev = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
 677  0 Rectangle rec = dev[0].getDefaultConfiguration().getBounds();
 678  0 ownerLoc = rec.getLocation();
 679  0 ownerSize = rec.getSize();
 680    }
 681   
 682    // center it on owner
 683  0 Point loc = new Point(ownerLoc.x + (ownerSize.width - frameRect.width) / 2,
 684    ownerLoc.y + (ownerSize.height - frameRect.height) / 2);
 685  0 frameRect.setLocation(loc);
 686   
 687    // now find the GraphicsConfiguration the popup is on
 688  0 GraphicsConfiguration gcBest = null;
 689  0 int gcBestArea = -1;
 690  0 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
 691  0 GraphicsDevice[] gs = ge.getScreenDevices();
 692  0 for (GraphicsDevice gd: gs) {
 693  0 GraphicsConfiguration gc = gd.getDefaultConfiguration();
 694  0 Rectangle isect = frameRect.intersection(gc.getBounds());
 695  0 int gcArea = isect.width*isect.height;
 696  0 if (gcArea>gcBestArea) {
 697  0 gcBest = gc;
 698  0 gcBestArea = gcArea;
 699    }
 700    }
 701   
 702    // make it fit on the screen
 703  0 Rectangle screenRect = gcBest.getBounds();
 704  0 Dimension screenSize = screenRect.getSize();
 705  0 Dimension frameSize = popup.getSize();
 706   
 707  0 if (frameSize.height > screenSize.height) frameSize.height = screenSize.height;
 708  0 if (frameSize.width > screenSize.width) frameSize.width = screenSize.width;
 709   
 710  0 frameRect.setSize(frameSize);
 711   
 712    // center it on owner again
 713  0 loc = new Point(ownerLoc.x + (ownerSize.width - frameRect.width) / 2,
 714    ownerLoc.y + (ownerSize.height - frameRect.height) / 2);
 715  0 frameRect.setLocation(loc);
 716   
 717    // now fit it on the screen
 718  0 if (frameRect.x < screenRect.x) frameRect.x = screenRect.x;
 719  0 if (frameRect.x + frameRect.width > screenRect.x + screenRect.width)
 720  0 frameRect.x = screenRect.x + screenRect.width - frameRect.width;
 721   
 722  0 if (frameRect.y < screenRect.y) frameRect.y = screenRect.y;
 723  0 if (frameRect.y + frameRect.height > screenRect.y + screenRect.height)
 724  0 frameRect.y = screenRect.y + screenRect.height - frameRect.height;
 725   
 726  0 popup.setSize(frameRect.getSize());
 727  0 popup.setLocation(frameRect.getLocation());
 728    }
 729   
 730   
 731    }