org.jdesktop.application.DefaultInputBlocker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swixml Show documentation
Show all versions of swixml Show documentation
GUI generating engine for Java applications
/*
* Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.application;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.RootPaneContainer;
import javax.swing.Timer;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;
final class DefaultInputBlocker extends Task.InputBlocker {
private static final Logger logger = Logger.getLogger(DefaultInputBlocker.class.getName());
private JDialog modalDialog = null;
DefaultInputBlocker(Task task, Task.BlockingScope scope, Object target, ApplicationAction action) {
super(task, scope, target, action);
}
private void setActionTargetBlocked(boolean f) {
javax.swing.Action action = (javax.swing.Action)getTarget();
action.setEnabled(!f);
}
private void setComponentTargetBlocked(boolean f) {
Component c = (Component)getTarget();
c.setEnabled(!f);
// Note: can't set the cursor on a disabled component
}
/* Accumulates a list of all of the descendants of root whose name
* begins with "BlockingDialog"
*/
private void blockingDialogComponents(Component root, List rv) {
String rootName = root.getName();
if ((rootName != null) && rootName.startsWith("BlockingDialog")) {
rv.add(root);
}
if (root instanceof Container) {
for(Component child : ((Container)root).getComponents()) {
blockingDialogComponents(child, rv);
}
}
}
private List blockingDialogComponents(Component root) {
List rv = new ArrayList();
blockingDialogComponents(root, rv);
return rv;
}
/* Inject resources from both the Task's ResourceMap and the
* ApplicationAction's ResourceMap. We add the action's name
* prefix to all of the components before the second step.
*/
private void injectBlockingDialogComponents(Component root) {
ResourceMap taskResourceMap = getTask().getResourceMap();
if (taskResourceMap != null) {
taskResourceMap.injectComponents(root);
}
ApplicationAction action = getAction();
if (action != null) {
ResourceMap actionResourceMap = action.getResourceMap();
String actionName = action.getName();
for(Component c : blockingDialogComponents(root)) {
c.setName(actionName + "." + c.getName());
}
actionResourceMap.injectComponents(root);
}
}
/* Creates a dialog whose visuals are initialized from the
* following Task resources:
* BlockingDialog.title
* BlockingDialog.optionPane.icon
* BlockingDialog.optionPane.message
* BlockingDialog.cancelButton.text
* BlockingDialog.cancelButton.icon
* BlockingDialog.progressBar.stringPainted
*
* If the Task has an Action then use the actionName as a prefix
* and look up the resources again, in the action's ResourceMap
* (that's the @Action's ApplicationActionMap ResourceMap really):
* actionName.BlockingDialog.title
* actionName.BlockingDialog.optionPane.icon
* actionName.BlockingDialog.optionPane.message
* actionName.BlockingDialog.cancelButton.text
* actionName.BlockingDialog.cancelButton.icon
* actionName.BlockingDialog.progressBar.stringPainted
*/
private JDialog createBlockingDialog() {
JOptionPane optionPane = new JOptionPane();
/* If the task can be canceled, then add the cancel
* button. Otherwise clear the default OK button.
*/
if (getTask().getUserCanCancel()) {
JButton cancelButton = new JButton();
cancelButton.setName("BlockingDialog.cancelButton");
ActionListener doCancelTask = new ActionListener() {
public void actionPerformed(ActionEvent ignore) {
getTask().cancel(true);
}
};
cancelButton.addActionListener(doCancelTask);
optionPane.setOptions(new Object[]{cancelButton});
}
else {
optionPane.setOptions(new Object[]{}); // no OK button
}
/* Create the JDialog. If the task can be canceled, then
* map closing the dialog window to canceling the task.
*/
Component dialogOwner = (Component)getTarget();
String taskTitle = getTask().getTitle();
String dialogTitle = (taskTitle == null) ? "BlockingDialog" : taskTitle;
final JDialog dialog = optionPane.createDialog(dialogOwner, dialogTitle);
dialog.setModal(true);
dialog.setName("BlockingDialog");
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
WindowListener dialogCloseListener = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (getTask().getUserCanCancel()) {
getTask().cancel(true);
dialog.setVisible(false);
}
}
};
dialog.addWindowListener(dialogCloseListener);
optionPane.setName("BlockingDialog.optionPane");
injectBlockingDialogComponents(dialog);
/* Reset the JOptionPane's message property after injecting
* an initial value for the message string.
*/
recreateOptionPaneMessage(optionPane);
dialog.pack();
return dialog;
}
/* Replace the default message panel with one that where the
* message text can be selected and that includes a status bar for
* task progress. We inject resources here because the
* JOptionPane#setMessage() doesn't add the panel to the JOptionPane
* immediately.
*/
private void recreateOptionPaneMessage(JOptionPane optionPane) {
Object message = optionPane.getMessage();
if (message instanceof String) {
Font font = optionPane.getFont();
final JTextArea textArea = new JTextArea((String)message);
textArea.setFont(font);
int lh = textArea.getFontMetrics(font).getHeight();
Insets margin = new Insets(0, 0, lh, 24); // top left bottom right
textArea.setMargin(margin);
textArea.setEditable(false);
textArea.setWrapStyleWord(true);
textArea.setBackground(optionPane.getBackground());
JPanel panel = new JPanel(new BorderLayout());
panel.add(textArea, BorderLayout.CENTER);
final JProgressBar progressBar = new JProgressBar();
progressBar.setName("BlockingDialog.progressBar");
progressBar.setIndeterminate(true);
PropertyChangeListener taskPCL = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if ("progress".equals(e.getPropertyName())) {
progressBar.setIndeterminate(false);
progressBar.setValue((Integer)e.getNewValue());
updateStatusBarString(progressBar);
}
else if ("message".equals(e.getPropertyName())) {
textArea.setText((String)e.getNewValue());
}
}
};
getTask().addPropertyChangeListener(taskPCL);
panel.add(progressBar, BorderLayout.SOUTH);
injectBlockingDialogComponents(panel);
optionPane.setMessage(panel);
}
}
private void updateStatusBarString(JProgressBar progressBar) {
if (!progressBar.isStringPainted()) {
return;
}
/* The initial value of the progressBar string is the format.
* We save the format string in a client property. The format
* String will be applied four values (see below). The default
* format String is in resources/Application.properties, it's:
* "%02d:%02d, %02d:%02d remaining"
*/
String key = "progressBarStringFormat";
if (progressBar.getClientProperty(key) == null) {
progressBar.putClientProperty(key, progressBar.getString());
}
String fmt = (String)progressBar.getClientProperty(key);
if (progressBar.getValue() <= 0) {
progressBar.setString("");
}
else if (fmt == null) {
progressBar.setString(null);
}
else {
double pctComplete = progressBar.getValue() / 100.0;
long durSeconds = getTask().getExecutionDuration(TimeUnit.SECONDS);
long durMinutes = durSeconds / 60;
long remSeconds = (long)(0.5 + ((double)durSeconds / pctComplete)) - durSeconds;
long remMinutes = remSeconds / 60;
String s = String.format(fmt, durMinutes, durSeconds - (durMinutes * 60),
remMinutes, remSeconds - (remMinutes * 60));
progressBar.setString(s);
}
}
private void showBusyGlassPane(boolean f) {
RootPaneContainer rpc = null;
Component root = (Component)getTarget();
while(root != null) {
if (root instanceof RootPaneContainer) {
rpc = (RootPaneContainer)root;
break;
}
root = root.getParent();
}
if (rpc != null) {
if (f) {
JMenuBar menuBar = rpc.getRootPane().getJMenuBar();
if (menuBar != null) {
menuBar.putClientProperty(this, menuBar.isEnabled());
menuBar.setEnabled(false);
}
JComponent glassPane = new BusyGlassPane();
InputVerifier retainFocusWhileVisible = new InputVerifier() {
public boolean verify(JComponent c) {
return !c.isVisible();
}
};
glassPane.setInputVerifier(retainFocusWhileVisible);
Component oldGlassPane = rpc.getGlassPane();
rpc.getRootPane().putClientProperty(this, oldGlassPane);
rpc.setGlassPane(glassPane);
glassPane.setVisible(true);
glassPane.revalidate();
}
else {
JMenuBar menuBar = rpc.getRootPane().getJMenuBar();
if (menuBar != null) {
boolean enabled = (Boolean)menuBar.getClientProperty(this);
menuBar.putClientProperty(this, null);
menuBar.setEnabled(enabled);
}
Component oldGlassPane = (Component)rpc.getRootPane().getClientProperty(this);
rpc.getRootPane().putClientProperty(this, null);
if (!oldGlassPane.isVisible()) {
rpc.getGlassPane().setVisible(false);
}
rpc.setGlassPane(oldGlassPane); // sets oldGlassPane.visible
}
}
}
/* Note: unfortunately, the busy cursor is reset when the modal
* dialog is shown.
*/
private static class BusyGlassPane extends JPanel {
BusyGlassPane() {
super(null, false);
setVisible(false);
setOpaque(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
MouseInputListener blockMouseEvents = new MouseInputAdapter() {};
addMouseMotionListener(blockMouseEvents);
addMouseListener(blockMouseEvents);
}
}
/* If an action was specified then return the value of the
* actionName.BlockingDialogTimer.delay resource from the action's
* resourceMap. Otherwise return the value of the
* BlockingDialogTimer.delay resource from the Task's ResourceMap.
* The latter's default in defined in resources/Application.properties.
*/
private int blockingDialogDelay() {
Integer delay = null;
String key = "BlockingDialogTimer.delay";
ApplicationAction action = getAction();
if (action != null) {
ResourceMap actionResourceMap = action.getResourceMap();
String actionName = action.getName();
delay = actionResourceMap.getInteger(actionName + "." + key);
}
ResourceMap taskResourceMap = getTask().getResourceMap();
if ((delay == null) && (taskResourceMap != null)) {
delay = taskResourceMap.getInteger(key);
}
return (delay == null) ? 0 : delay.intValue();
}
private void showBlockingDialog(boolean f) {
if (f) {
if (modalDialog != null) {
String msg = String.format("unexpected InputBlocker state [%s] %s", f, this);
logger.warning(msg);
modalDialog.dispose();
}
modalDialog = createBlockingDialog();
ActionListener showModalDialog = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (modalDialog != null) { // already dismissed
modalDialog.setVisible(true);
}
}
};
Timer showModalDialogTimer = new Timer(blockingDialogDelay(), showModalDialog);
showModalDialogTimer.setRepeats(false);
showModalDialogTimer.start();
}
else {
if (modalDialog != null) {
modalDialog.dispose();
modalDialog = null;
}
else {
String msg = String.format("unexpected InputBlocker state [%s] %s", f, this);
logger.warning(msg);
}
}
}
@Override protected void block() {
switch (getScope()) {
case ACTION:
setActionTargetBlocked(true);
break;
case COMPONENT:
setComponentTargetBlocked(true);
break;
case WINDOW:
case APPLICATION:
showBusyGlassPane(true);
showBlockingDialog(true);
break;
}
}
@Override protected void unblock() {
switch (getScope()) {
case ACTION:
setActionTargetBlocked(false);
break;
case COMPONENT:
setComponentTargetBlocked(false);
break;
case WINDOW:
case APPLICATION:
showBusyGlassPane(false);
showBlockingDialog(false);
break;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy