/*
* Copyright 2008-2017 by Emeric Vernat
*
* This file is part of Java Melody.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.bull.javamelody.swing.util;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.PrintStream;
import javax.swing.FocusManager;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
/**
* Classe utilitaire pour Swing.
* @author Emeric Vernat
*/
public final class MSwingUtilities {
private MSwingUtilities() {
super();
}
/**
* Affiche la trace de l'exception dans la console d'erreur et affiche une boîte de dialogue pour afficher l'exception.
* @param throwable Throwable
*/
public static void showException(Throwable throwable) {
throwable.printStackTrace(getSystemErrorStream());
JOptionPane.showMessageDialog(null, throwable.toString(),
UIManager.getString("OptionPane.messageDialogTitle"), JOptionPane.ERROR_MESSAGE);
// on pourrait affichage une boîte de dialogue plus évoluée pour permettre d'afficher la stack trace en détail
}
/**
* Affiche une boîte de dialogue de confirmation.
* @param component Component
* @param message String
* @return boolean
*/
public static boolean showConfirmation(Component component, String message) {
return JOptionPane.showConfirmDialog(SwingUtilities.getWindowAncestor(component), message,
UIManager.getString("OptionPane.titleText"),
JOptionPane.OK_OPTION | JOptionPane.CANCEL_OPTION) == JOptionPane.OK_OPTION;
}
/**
* Affiche une boîte de dialogue d'information.
* @param component Component
* @param message String
*/
public static void showMessage(Component component, String message) {
JOptionPane.showMessageDialog(SwingUtilities.getWindowAncestor(component), message,
UIManager.getString("OptionPane.messageDialogTitle"),
JOptionPane.INFORMATION_MESSAGE);
}
/**
* Retourne l'instance courante de la classe componentClass contenant l'élément component. <br/>
* Cette méthode peut-être très utile pour récupérer une référence à un parent éloigné (ancêtre), en l'absence de référence directe du type attribut.
*
* <br/>
* Ex : un composant panel désire une référence sur sa JFrame parente, alors l'instruction suivante suffit : getAncestorOfClass(JFrame.class, panel)
*
* @return Component
* @param <T>
* le type du composant recherché
* @param componentClass
* Class
* @param component
* Component
*/
@SuppressWarnings("unchecked")
public static <T> T getAncestorOfClass(final Class<T> componentClass,
final Component component) {
return (T) SwingUtilities.getAncestorOfClass(componentClass, component);
}
/**
* Retourne le focusOwner permanent.<br/>
* Le focusOwner permanent est défini comme le dernier Component à avoir reçu un événement FOCUS_GAINED permanent.<br/>
* Le focusOwner et le focusOwner permanent sont équivalent sauf si un changement temporaire de focus<br/>
* est en cours. Si c'est le cas, le focusOwner permanent redeviendra &galement<br/>
* le focusOwner à la fin de ce changement de focus temporaire.
*
* @return Component
*/
public static Component getPermanentFocusOwner() {
// return new DefaultKeyboardFocusManager().getPermanentFocusOwner();
return FocusManager.getCurrentManager().getPermanentFocusOwner();
}
/**
* Retourne la fenêtre possédant le focus.
*
* @return Component
*/
public static Window getFocusedWindow() {
// return new DefaultKeyboardFocusManager().getFocusedWindow();
return FocusManager.getCurrentManager().getFocusedWindow();
}
/**
* Démarre un composant dans une Frame (utile pour écrire des méthodes main sur des panels en développement).
*
* @param component
* JComponent
* @return la Frame créée
*/
public static JFrame run(final JComponent component) {
final JFrame frame = new JFrame();
try {
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
component.setOpaque(true); // si opaque false, il y a des pbs de paint
frame.setContentPane(component);
frame.setTitle(component.getClass().getName());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (final Exception exception) {
exception.printStackTrace(getSystemErrorStream()); // NOPMD
}
return frame;
}
/**
* @return System.err
*/
private static PrintStream getSystemErrorStream() {
return System.err;
}
/**
* Démarre un composant dans une Frame sans pack() (utile pour écrire des méthodes main sur des panels en développement).
*
* @param component
* JComponent
* @return la Frame créée
*/
public static JFrame runUnpacked(final JComponent component) {
component.setPreferredSize(component.getSize());
return run(component);
}
/**
* Initialisation d'événements sur la touche Escape pour fermer les dialogues.
*/
public static void initEscapeClosesDialogs() {
final AWTEventListener awtEventListener = new AWTEventListener() {
/** {@inheritDoc} */
@Override
public void eventDispatched(final AWTEvent event) {
if (event instanceof KeyEvent
&& ((KeyEvent) event).getKeyCode() == KeyEvent.VK_ESCAPE
&& event.getID() == KeyEvent.KEY_PRESSED) {
escapePressed();
}
}
};
Toolkit.getDefaultToolkit().addAWTEventListener(awtEventListener, AWTEvent.KEY_EVENT_MASK);
}
/**
* La touche Esc a été pressée : fermer la dialogue modale ouverte.
*/
static void escapePressed() {
final Component focusOwner = getPermanentFocusOwner();
final Window focusedWindow = SwingUtilities.getWindowAncestor(focusOwner);
if (focusedWindow instanceof Dialog && ((Dialog) focusedWindow).isModal()) {
// try {
// final Robot robot = new Robot();
// robot.keyPress(KeyEvent.VK_ALT);
// robot.keyPress(KeyEvent.VK_F4);
// robot.keyRelease(KeyEvent.VK_F4);
// robot.keyRelease(KeyEvent.VK_ALT);
// } catch (final AWTException e) {
// throw new IllegalStateException(e);
// }
focusedWindow.dispose();
}
}
/**
* Redimensionne une ImageIcon.
* @param icon ImageIcon
* @param targetWidth int
* @param targetHeight int
* @return ImageIcon
*/
public static ImageIcon getScaledInstance(ImageIcon icon, int targetWidth, int targetHeight) {
return new ImageIcon(getScaledInstance(icon.getImage(), targetWidth, targetHeight));
}
/**
* Redimensionne une image.
* @param img Image
* @param targetWidth int
* @param targetHeight int
* @return Image
*/
public static Image getScaledInstance(Image img, int targetWidth, int targetHeight) {
final int type = BufferedImage.TYPE_INT_ARGB;
Image ret = img;
final int width = ret.getWidth(null);
final int height = ret.getHeight(null);
if (width != targetWidth && height != targetHeight) {
// a priori plus performant que Image.getScaledInstance
final BufferedImage scratchImage = new BufferedImage(targetWidth, targetHeight, type);
final Graphics2D g2 = scratchImage.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(ret, 0, 0, targetWidth, targetHeight, 0, 0, width, height, null);
g2.dispose();
ret = scratchImage;
}
return ret;
}
}