4 * Copyright 2010 Codist Monk.
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 package net
.sourceforge
.aprog
.swing
;
27 import java
.awt
.Component
;
28 import java
.awt
.Container
;
29 import java
.awt
.Dimension
;
30 import java
.awt
.GridBagConstraints
;
31 import java
.awt
.GridBagLayout
;
32 import java
.awt
.Window
;
33 import java
.awt
.datatransfer
.DataFlavor
;
34 import java
.awt
.dnd
.DnDConstants
;
35 import java
.awt
.dnd
.DropTargetDropEvent
;
37 import java
.io
.IOException
;
38 import java
.lang
.reflect
.InvocationTargetException
;
39 import java
.lang
.reflect
.Method
;
40 import java
.util
.Arrays
;
41 import java
.util
.Collections
;
42 import java
.util
.HashMap
;
43 import java
.util
.List
;
45 import java
.util
.logging
.Level
;
47 import javax
.imageio
.ImageIO
;
48 import javax
.swing
.AbstractButton
;
49 import javax
.swing
.ImageIcon
;
50 import javax
.swing
.JMenu
;
51 import javax
.swing
.JMenuBar
;
52 import javax
.swing
.JMenuItem
;
53 import javax
.swing
.JScrollPane
;
54 import javax
.swing
.SwingUtilities
;
55 import javax
.swing
.UIManager
;
57 import net
.sourceforge
.aprog
.tools
.Tools
;
60 * This class provides utility static methods to help build Swing GUIs.
61 * <br>According to the JDK, accessing and modifying AWT components should only be done
62 * in the AWT Event Dispatching Thread.
63 * <br>The methods in this class enforce this rule by calling {@link #checkAWT()} to make sure
64 * that they are used in the proper thread.
66 * @author codistmonk (creation 2010-06-26)
68 public final class SwingTools
{
71 * Private default constructor to prevent instantiation.
73 private SwingTools() {
80 public static final String DEFAULT_IMAGES_BASE
= "images/";
85 public static final String ICON_FORMAT
= "png";
90 public static final String ROLLOVER_DISABLED_ICON_SUFFIX
= "_disabled." + ICON_FORMAT
;
95 public static final String ROLLOVER_NORMAL_ICON_SUFFIX
= "." + ICON_FORMAT
;
100 public static final String ROLLOVER_SELECTED_ICON_SUFFIX
= "_selected." + ICON_FORMAT
;
105 public static final String ROLLOVER_ROLLOVER_ICON_SUFFIX
= "_rollover." + ICON_FORMAT
;
110 public static final String ROLLOVER_ROLLOVER_SELECTED_ICON_SUFFIX
= "_rollover_selected." + ICON_FORMAT
;
112 private static final Map
<String
, ImageIcon
> iconCache
= new HashMap
<String
, ImageIcon
>();
114 private static String imagesBase
= DEFAULT_IMAGES_BASE
;
122 public static final String
getImagesBase() {
132 public static final void setImagesBase(final String imagesBase
) {
133 SwingTools
.imagesBase
= imagesBase
;
138 * @param resourceName
143 * @throws RuntimeException if the resource cannot be loaded
145 public static final ImageIcon
getIcon(final String resourceName
) {
147 final ImageIcon cachedIcon
= iconCache
.get(resourceName
);
149 if (cachedIcon
!= null) {
153 final ImageIcon icon
= new ImageIcon(ImageIO
.read(
154 SwingTools
.class.getClassLoader().getResourceAsStream(getImagesBase() + resourceName
)));
156 iconCache
.put(resourceName
, icon
);
159 } catch (final IOException exception
) {
160 return Tools
.throwUnchecked(exception
);
166 * @param resourceName
172 public static final ImageIcon
getIconOrNull(final String resourceName
) {
174 return getIcon(resourceName
);
175 } catch (final Exception exception
) {
192 public static final void add(final Container container
, final Component component
, final GridBagConstraints constraints
) {
195 if (!(container
.getLayout() instanceof GridBagLayout
)) {
196 container
.setLayout(new GridBagLayout());
199 final GridBagLayout layout
= (GridBagLayout
) container
.getLayout();
201 layout
.setConstraints(component
, constraints
);
203 container
.add(component
);
208 * @param <T> the actual type of {@code button}
214 * @param borderPainted if {@code false}, then the preferred size is set to the size of the image,
215 * and the background and border are not drawn; if {@code true}, then {@code button} is left in its current state
216 * @return {@code button}
219 public static final <T
extends AbstractButton
> T
rollover(final T button
, final String imageName
, final boolean borderPainted
) {
222 button
.setRolloverEnabled(true);
223 button
.setDisabledIcon(getIconOrNull(imageName
+ ROLLOVER_DISABLED_ICON_SUFFIX
));
224 button
.setIcon(getIconOrNull(imageName
+ ROLLOVER_NORMAL_ICON_SUFFIX
));
225 button
.setSelectedIcon(getIconOrNull(imageName
+ ROLLOVER_SELECTED_ICON_SUFFIX
));
226 button
.setRolloverIcon(getIconOrNull(imageName
+ ROLLOVER_ROLLOVER_ICON_SUFFIX
));
227 button
.setRolloverSelectedIcon(getIconOrNull(imageName
+ ROLLOVER_ROLLOVER_SELECTED_ICON_SUFFIX
));
229 if (!borderPainted
) {
230 if (button
.getIcon() != null) {
231 button
.setPreferredSize(new Dimension(button
.getIcon().getIconWidth(), button
.getIcon().getIconHeight()));
234 button
.setBorderPainted(false);
241 * Encloses {@code component} in a scroll pane.
250 public static final JScrollPane
scrollable(final Component component
) {
253 return new JScrollPane(component
);
257 * Centers {@code window} on the screen.
259 * @param <W> the actual type of {@code window}
264 * @return {@code window}
268 public static final <W
extends Window
> W
center(final W window
) {
271 window
.setLocationRelativeTo(null);
284 public static final JMenuBar
menuBar(final JMenu
... menus
) {
287 final JMenuBar result
= new JMenuBar();
289 for (final JMenu menu
: menus
) {
300 * @param subMenuItems
306 public static final JMenu
menu(final String title
, final JMenuItem
... subMenuItems
) {
309 final JMenu result
= new JMenu(title
);
311 for (final JMenuItem subMenu
: subMenuItems
) {
312 if (subMenu
== null) {
313 result
.addSeparator();
327 * @return a list of files, or an empty list if {@code event} cannot provide a list of files or a string
330 * @throws RuntimeException if an error occurs
332 @SuppressWarnings("unchecked")
333 public static final List
<File
> getFiles(final DropTargetDropEvent event
) {
334 event
.acceptDrop(DnDConstants
.ACTION_COPY_OR_MOVE
);
337 if (event
.getCurrentDataFlavorsAsList().contains(DataFlavor
.javaFileListFlavor
)) {
338 return (List
<File
>)event
.getTransferable().getTransferData(DataFlavor
.javaFileListFlavor
);
341 if (event
.getCurrentDataFlavorsAsList().contains(DataFlavor
.stringFlavor
)) {
342 return Arrays
.asList(new File((String
)event
.getTransferable().getTransferData(DataFlavor
.stringFlavor
)));
345 return Collections
.emptyList();
346 } catch (final Exception exception
) {
347 return Tools
.throwUnchecked(exception
);
351 public static final void useSystemLookAndFeel() {
353 UIManager
.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
354 } catch (final Exception exception
) {
355 Tools
.getLoggerForThisMethod().log(Level
.WARNING
, "", exception
);
360 * Executes in the AWT Event Dispatching Thread a runnable invoking
361 * the caller method with the specified arguments,
362 * or does nothing if the method is called in that thread.
363 * <br>This method can be used to simplify code that needs to be executed in AWT
364 * by taking care of generating an anonymous inner class implementing {@link Runnable}.
367 * public final void f() {
368 * // Warning: this section might get executed 2 times in different threads
370 * if (SwingTools.canInvokeThisMethodInAWT(this)) {
371 * // This section is executed only once in the AWT Event Dispatching Thread
372 * // For instance, the following instruction doesn't throw
373 * SwingTools.checkAWT();
376 * // Warning: this section might get executed 2 times in different threads
380 * @param object The caller object or {@code null} if the caller is static
384 * @return {@code true} if and only if the method is called in the AWT Event Dispatching Thread
385 * @throws RuntimeException if an error occurs
387 public static final boolean canInvokeThisMethodInAWT(final Object object
, final Object
... arguments
) {
388 if (SwingUtilities
.isEventDispatchThread()) {
392 final Class
<?
> callerClass
= Tools
.getCallerClass();
393 final String callerMethodName
= Tools
.getCallerMethodName();
396 SwingUtilities
.invokeAndWait(new Runnable() {
399 public final void run() {
400 for (final Method method
: Tools
.add(callerClass
.getMethods(), callerClass
.getDeclaredMethods())) {
401 if (method
.getName().equals(callerMethodName
)) {
403 method
.invoke(object
, arguments
);
405 } catch (final InvocationTargetException exception
) {
406 Tools
.throwUnchecked(exception
.getCause());
407 } catch (final Exception exception
) {
414 } catch (final InterruptedException exception
) {
415 Tools
.getLoggerForThisMethod().log(Level
.WARNING
, null, exception
);
416 } catch (final InvocationTargetException exception
) {
417 Tools
.throwUnchecked(exception
.getCause());
425 * @throws IllegalStateException if the current thread is not the AWT Event Dispatching Thread
427 public static final void checkAWT() {
428 if (!SwingUtilities
.isEventDispatchThread()) {
429 throw new IllegalStateException("This section must be executed in the AWT Event Dispatching Thread");
435 * @throws IllegalStateException if the current thread is the AWT Event Dispatching Thread
437 public static final void checkNotAWT() {
438 if (SwingUtilities
.isEventDispatchThread()) {
439 throw new IllegalStateException("This section must not be executed in the AWT Event Dispatching Thread");