001package arcommon.ui.window;
002
003import java.awt.Container;
004import java.awt.Dimension;
005import java.awt.GridBagConstraints;
006import java.awt.GridBagLayout;
007import java.awt.event.ActionEvent;
008import java.awt.event.ActionListener;
009import java.awt.event.WindowEvent;
010
011import javax.swing.JButton;
012import javax.swing.JLabel;
013import javax.swing.JPanel;
014import javax.swing.SwingUtilities;
015import javax.swing.border.EmptyBorder;
016
017import arcommon.MyTimer;
018import arcommon.ui.other.GUIUtil;
019
020public class SimpleProgressJDialog extends AbstractJDialog {
021    
022    public static final int PREFERRED_WIDTH_NOT_SET = -1;
023
024    private static SimpleProgressJDialog progressJDialog;
025    
026    public static SimpleProgressJDialog createProgressJDialog(String title) {
027        return createProgressJDialog(title, "Started");
028    }
029    
030    public static SimpleProgressJDialog createProgressJDialog(String title, String progress) {
031        return createProgressJDialog(title, progress, true);
032    }
033    
034    public static SimpleProgressJDialog createProgressJDialog(String title, String progress, int preferredWidth) {
035        return createProgressJDialog(title, progress, preferredWidth, true);
036    }
037    
038    public static SimpleProgressJDialog createProgressJDialog(String title, String progress, boolean cancelable) {
039        return createProgressJDialog(title, progress, PREFERRED_WIDTH_NOT_SET, cancelable);
040    }
041    
042    /**
043     * Note: The dialog is setVisible(true) invoked later!!
044     */
045    public static SimpleProgressJDialog createProgressJDialog(final String title, final String progress, final int preferredWidth, final boolean cancelable) {
046        GUIUtil.runInEventThreadAndWait(new Runnable() {
047            public void run() {
048                if (progressJDialog != null) {
049                    progressJDialog.dispose();
050                    progressJDialog = null;
051                }
052                
053                progressJDialog = new SimpleProgressJDialog(title, progress, preferredWidth, cancelable);
054            }
055        });
056
057        return progressJDialog;
058    }
059
060    private boolean hasBeenCanceled;
061    
062    private JLabel progressJLabel;
063    private JButton cancelJButton;
064    
065    public SimpleProgressJDialog(String title) {
066        this(title, "Started");
067    }
068    
069    public SimpleProgressJDialog(String title, String progress) {
070        this(title, progress, true);
071    }
072    
073    public SimpleProgressJDialog(String title, String progress, int preferredWidth) {
074        this(title, progress, preferredWidth, true);
075    }
076    
077    public SimpleProgressJDialog(String title, String progress, boolean cancelable) {
078        this(title, progress, PREFERRED_WIDTH_NOT_SET, true);
079    }
080    
081    public SimpleProgressJDialog(String title, String progress, int preferredWidth, boolean cancelable) {
082        super(title, cancelable);
083        
084        JPanel panel = new JPanel();
085        getContentPane().add(panel);
086        panel.setLayout(new GridBagLayout());
087        panel.setBorder(new EmptyBorder(3, 1, 3, 3));
088        
089        GridBagConstraints gridBagConstraints = new GridBagConstraints();
090        gridBagConstraints.fill = GridBagConstraints.BOTH;
091        gridBagConstraints.weightx = 1.0;
092        gridBagConstraints.weighty = 1.0;
093        gridBagConstraints.gridy = 0;
094        gridBagConstraints.gridx = 0;
095
096        progressJLabel = new JLabel(progress);
097        panel.add(progressJLabel, gridBagConstraints);
098
099        if (cancelable) {
100            gridBagConstraints.fill = GridBagConstraints.VERTICAL;
101            gridBagConstraints.anchor = GridBagConstraints.CENTER;
102            gridBagConstraints.gridy = 1;
103            gridBagConstraints.weightx = 0.0;
104            gridBagConstraints.weighty = 0.0;
105            
106            cancelJButton = new JButton("Cancel");
107            panel.add(cancelJButton, gridBagConstraints);
108            cancelJButton.requestFocus();
109            cancelJButton.addActionListener(new ActionListener() {
110                public void actionPerformed(ActionEvent e) {
111                    requestCancel();
112                }
113            });
114        }
115        
116        if (preferredWidth != PREFERRED_WIDTH_NOT_SET) {
117            progressJLabel.setPreferredSize(new Dimension(preferredWidth, progressJLabel.getPreferredSize().height));
118        }
119        
120        setResizable(false);
121        
122        // This is run invoked later to avoid that calling setVisible(true) on this modal dialog causes the GUI thread to be blocked here.
123        Runnable runnable1 = new Runnable() {
124            public void run() {
125                setVisible(true);
126            }
127        };
128        SwingUtilities.invokeLater(runnable1);
129        
130        // This is run invoked later as a separate runnable to make it run after setVisible(true) to make it work, but setVisible(true) is blocking until the dialog is closed, so two runnables are required.
131        // The MyTimer.addActionListener(1... code is added to make GUIUtil.packAndCenterOnScreen() work the first time the dialog is shown. If it fails the dialog is very small. It is unclear why this solves the problem.
132        MyTimer.addActionListener(1, new ActionListener() {
133            @Override
134            public void actionPerformed(ActionEvent e) {
135                final ActionListener actionListenerFinal = this;
136                Runnable runnable2 = new Runnable() {
137                    public void run() {
138                        Container parent = SimpleProgressJDialog.this.getParent();
139                        if (parent == null) {
140                            GUIUtil.packAndCenterOnScreen(SimpleProgressJDialog.this);
141                        } else {
142                            SimpleProgressJDialog.this.pack();
143                            GUIUtil.centerOnParent(SimpleProgressJDialog.this);
144                        }
145                        Dimension size = SimpleProgressJDialog.this.getSize();
146                        boolean visible = SimpleProgressJDialog.this.isVisible();
147                        if ((size.height > 50 && size.width > 50) || !visible) {
148                            MyTimer.removeActionListener(actionListenerFinal);
149                        }
150                    }
151                };
152                SwingUtilities.invokeLater(runnable2);
153            }
154        });
155    }
156    
157    public void setProgress(final String progress) {
158        Runnable runnable = new Runnable() {
159            public void run() {
160                if (!isVisible()) {
161                    return ;
162                }
163                
164                progressJLabel.setText(progress);
165            }
166        };
167        GUIUtil.runInEventThread(runnable);
168    }
169    
170    @Override
171    protected boolean canClose() {
172        return false;
173    }
174    
175    @Override
176    public void windowClosing(WindowEvent e) {
177        super.windowClosing(e);
178        requestCancel();
179    }
180    
181    public void requestCancel() {
182        Runnable runnable = new Runnable() {
183            public void run() {
184                if (cancelJButton == null) {
185                    return ;
186                }
187                
188                cancelJButton.setEnabled(false);
189                cancelJButton.setText("Cancel requested");
190                hasBeenCanceled = true;
191            }
192        };
193        GUIUtil.runInEventThread(runnable);
194    }
195    
196    public boolean hasBeenCanceled() {
197        return hasBeenCanceled;
198    }
199    
200    private static final long serialVersionUID = -8259154457653693257L;
201}