001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Component;
007import java.awt.GridBagConstraints;
008import java.awt.GridBagLayout;
009import java.awt.event.ActionListener;
010import java.awt.event.ComponentEvent;
011import java.awt.event.ComponentListener;
012
013import javax.swing.BorderFactory;
014import javax.swing.BoundedRangeModel;
015import javax.swing.JButton;
016import javax.swing.JDialog;
017import javax.swing.JLabel;
018import javax.swing.JOptionPane;
019import javax.swing.JPanel;
020import javax.swing.JProgressBar;
021import javax.swing.JScrollPane;
022import javax.swing.UIManager;
023
024import org.openstreetmap.josm.Main;
025import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor.ProgressMonitorDialog;
026import org.openstreetmap.josm.gui.widgets.JosmTextArea;
027import org.openstreetmap.josm.tools.GBC;
028import org.openstreetmap.josm.tools.ImageProvider;
029
030public class PleaseWaitDialog extends JDialog implements ProgressMonitorDialog {
031
032    private final JProgressBar progressBar = new JProgressBar();
033
034    public final JLabel currentAction = new JLabel("");
035    private final JLabel customText = new JLabel("");
036    public final BoundedRangeModel progress = progressBar.getModel();
037    private JButton btnCancel;
038    private JButton btnInBackground;
039    /** the text area and the scroll pane for the log */
040    private JosmTextArea taLog = new JosmTextArea(5,50);
041    private  JScrollPane spLog;
042
043    private void initDialog() {
044        setLayout(new GridBagLayout());
045        JPanel pane = new JPanel(new GridBagLayout());
046        pane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
047        pane.add(currentAction, GBC.eol().fill(GBC.HORIZONTAL));
048        pane.add(customText, GBC.eol().fill(GBC.HORIZONTAL));
049        pane.add(progressBar, GBC.eop().fill(GBC.HORIZONTAL));
050        JPanel buttons = new JPanel();
051        buttons.setLayout(new GridBagLayout());
052        btnCancel = new JButton(tr("Cancel"));
053        btnCancel.setIcon(ImageProvider.get("cancel"));
054        btnCancel.setToolTipText(tr("Click to cancel the current operation"));
055        buttons.add(btnCancel);
056        btnInBackground = new JButton(tr("In background"));
057        btnInBackground.setToolTipText(tr("Click to run job in background"));
058        buttons.add(btnInBackground, GBC.std().fill(GBC.VERTICAL).insets(5, 0, 0, 0));
059        pane.add(buttons, GBC.eol().anchor(GBC.CENTER));
060        GridBagConstraints gc = GBC.eol().fill(GBC.BOTH);
061        gc.weighty = 1.0;
062        gc.weightx = 1.0;
063        pane.add(spLog = new JScrollPane(taLog), gc);
064        spLog.setVisible(false);
065        setContentPane(pane);
066        setCustomText("");
067        setLocationRelativeTo(getParent());
068        addComponentListener(new ComponentListener() {
069            @Override
070            public void componentHidden(ComponentEvent e) {}
071            @Override
072            public void componentMoved(ComponentEvent e) {}
073            @Override
074            public void componentShown(ComponentEvent e) {}
075            @Override
076            public void componentResized(ComponentEvent ev) {
077                int w = getWidth();
078                if(w > 200) {
079                    Main.pref.putInteger("progressdialog.size",w);
080                }
081            }
082        });
083    }
084
085    /**
086     * Constructs a new {@code PleaseWaitDialog}.
087     * @param parent the {@code Component} from which the dialog is displayed. Can be {@code null}.
088     */
089    public PleaseWaitDialog(Component parent) {
090        super(JOptionPane.getFrameForComponent(parent), ModalityType.DOCUMENT_MODAL);
091        initDialog();
092    }
093
094    @Override
095    public void setIndeterminate(boolean newValue) {
096        UIManager.put("ProgressBar.cycleTime", UIManager.getInt("ProgressBar.repaintInterval") * 100);
097        progressBar.setIndeterminate(newValue);
098    }
099
100    protected void adjustLayout() {
101        invalidate();
102        setDropTarget(null); // Workaround to JDK bug 7027598/7100524/7169912 (#8613)
103        pack();
104        setSize(Main.pref.getInteger("progressdialog.size", 600), getSize().height);
105    }
106
107    /**
108     * Sets a custom text line below currentAction. Can be used to display additional information
109     * @param text
110     */
111    @Override
112    public void setCustomText(String text) {
113        if(text == null || text.trim().length() == 0) {
114            customText.setVisible(false);
115            adjustLayout();
116            return;
117        }
118        customText.setText(text);
119        if (!customText.isVisible()) {
120            customText.setVisible(true);
121            adjustLayout();
122        }
123    }
124
125    @Override
126    public void setCurrentAction(String text) {
127        currentAction.setText(text);
128    }
129
130    /**
131     * Appends a log message to the progress dialog. If the log area isn't visible yet
132     * it becomes visible. The height of the progress dialog is slightly increased too.
133     *
134     * @param message the message to append to the log. Ignore if null or white space only.
135     */
136    @Override
137    public void appendLogMessage(String message) {
138        if (message == null || message.trim().length() ==0 )
139            return;
140        if (!spLog.isVisible()) {
141            spLog.setVisible(true);
142            taLog.setVisible(true);
143            adjustLayout();
144        }
145        taLog.append(message);
146        taLog.append("\n");
147        spLog.getVerticalScrollBar().setValue(spLog.getVerticalScrollBar().getMaximum());
148    }
149
150    /**
151     * Sets whether the cancel button is enabled or not
152     *
153     * @param enabled true, if the cancel button is enabled; false otherwise
154     */
155    public void setCancelEnabled(boolean enabled) {
156        btnCancel.setEnabled(enabled);
157    }
158
159    public void setInBackgroundPossible(boolean value) {
160        btnInBackground.setVisible(value);
161    }
162
163    /**
164     * Installs a callback for the cancel button. If callback is null, all action listeners
165     * are removed from the cancel button.
166     *
167     * @param callback the cancel callback
168     */
169    public void setCancelCallback(ActionListener callback) {
170        if (callback == null) {
171            ActionListener[] listeners = btnCancel.getActionListeners();
172            for (ActionListener l: listeners) {
173                btnCancel.removeActionListener(l);
174            }
175        } else {
176            btnCancel.addActionListener(callback);
177        }
178    }
179
180    /**
181     * Installs a callback for the "In background" button. If callback is null, all action listeners
182     * are removed from the cancel button.
183     *
184     * @param callback the cancel callback
185     */
186    public void setInBackgroundCallback(ActionListener callback) {
187        if (callback == null) {
188            ActionListener[] listeners = btnInBackground.getActionListeners();
189            for (ActionListener l: listeners) {
190                btnInBackground.removeActionListener(l);
191            }
192        } else {
193            btnInBackground.addActionListener(callback);
194        }
195    }
196
197    @Override
198    public void updateProgress(int progress) {
199        this.progress.setValue(progress);
200        this.progressBar.repaint();
201    }
202
203}