//----------------------------------------------------------------------------//
// //
// M o d e l e s s O p t i o n P a n e //
// //
//----------------------------------------------------------------------------//
// <editor-fold defaultstate="collapsed" desc="hdr"> //
// Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. //
// This software is released under the GNU General Public License. //
// Goto http://kenai.com/projects/audiveris to report bugs or suggestions. //
//----------------------------------------------------------------------------//
// </editor-fold>
package omr.ui.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.HeadlessException;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.Exchanger;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
/**
* Class {@code ModelessOptionPane} is a basis for providing dialogs similar to
* the ones provided by JOptionPane, but in a modeless way.
*
* @author Hervé Bitteur
*/
public class ModelessOptionPane
extends JOptionPane
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
ModelessOptionPane.class);
//~ Methods ----------------------------------------------------------------
//-------------------//
// showConfirmDialog //
//-------------------//
/**
* Spawn a modeless dialog where the number of choices is determined by the
* {@code optionType} parameter.
*
* @param parentComponent determines the {@code Frame} in which the dialog
* is displayed; if {@code null}, or if the {@code parentComponent} has no
* {@code Frame}, a default {@code Frame} is used
* @param message the {@code Object} to display
* @param title the title string for the dialog
* @param optionType an int designating the options available on the
* dialog:
* {@code YES_NO_OPTION}, {@code YES_NO_CANCEL_OPTION}, or {@code
* OK_CANCEL_OPTION}
* @return an int indicating the option selected by the user
* @exception HeadlessException if {@code GraphicsEnvironment}/code> returns
* {@code true}
* @see java.awt.GraphicsEnvironment#isHeadless
*/
public static int showModelessConfirmDialog (Component parentComponent,
Object message,
String title,
int optionType)
throws HeadlessException
{
final Exchanger<Integer> exchanger = new Exchanger<>();
final JOptionPane pane = new JOptionPane(
message,
QUESTION_MESSAGE,
optionType);
Window window = getWindowForComponent(
parentComponent);
final JDialog dialog = (window instanceof Frame)
? new JDialog((Frame) window, title)
: new JDialog((Dialog) window, title);
WindowAdapter adapter = new WindowAdapter()
{
private boolean gotFocus = false;
@Override
public void windowGainedFocus (WindowEvent we)
{
// Once window gets focus, set initial focus
if (!gotFocus) {
pane.selectInitialValue();
gotFocus = true;
}
}
@Override
public void windowClosing (WindowEvent e)
{
e.getWindow()
.dispose();
try {
exchanger.exchange(optionOf(pane));
} catch (InterruptedException ex) {
logger.warn("Exchange got interrupted", ex);
}
}
};
dialog.addWindowListener(adapter);
dialog.addWindowFocusListener(adapter);
dialog.addComponentListener(
new ComponentAdapter()
{
@Override
public void componentShown (ComponentEvent ce)
{
// reset value to ensure closing works properly
pane.setValue(JOptionPane.UNINITIALIZED_VALUE);
}
});
pane.addPropertyChangeListener(
new PropertyChangeListener()
{
@Override
public void propertyChange (PropertyChangeEvent event)
{
// Let the defaultCloseOperation handle the closing
// if the user closed the window without selecting a button
// (newValue = null in that case). Otherwise, close the dialog.
if (dialog.isVisible()
&& (event.getSource() == pane)
&& (event.getPropertyName().equals(VALUE_PROPERTY))
&& (event.getNewValue() != null)
&& (event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE)) {
JOptionPane pane = (JOptionPane) event.getSource();
dialog.setVisible(false);
try {
exchanger.exchange(optionOf(pane));
} catch (InterruptedException ex) {
logger.warn("Exchange got interrupted", ex);
}
}
}
});
dialog.add(pane, BorderLayout.CENTER);
dialog.setResizable(false);
dialog.pack();
dialog.setLocationRelativeTo(parentComponent);
dialog.setAlwaysOnTop(true);
dialog.setVisible(true);
// Put the calling thread on wait, until the user has made a choice
try {
return exchanger.exchange(null);
} catch (InterruptedException ex) {
logger.warn("Exchange got interrupted", ex);
return JOptionPane.CANCEL_OPTION;
}
}
//-----------------------//
// getWindowForComponent //
//-----------------------//
private static Window getWindowForComponent (Component parentComponent)
throws HeadlessException
{
if (parentComponent == null) {
return getRootFrame();
}
if (parentComponent instanceof Frame
|| parentComponent instanceof Dialog) {
return (Window) parentComponent;
}
return getWindowForComponent(parentComponent.getParent());
}
//----------//
// optionOf //
//----------//
private static int optionOf (JOptionPane pane)
{
Object selectedValue = pane.getValue();
if (selectedValue == null) {
return JOptionPane.CLOSED_OPTION;
} else if (selectedValue instanceof Integer) {
return ((Integer) selectedValue).intValue();
} else {
return JOptionPane.CLOSED_OPTION;
}
}
}