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    private final JLabel currentAction = new JLabel("");
035    private final JLabel customText = new JLabel("");
036    public final transient 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 final 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
072            @Override
073            public void componentMoved(ComponentEvent e) {}
074
075            @Override
076            public void componentShown(ComponentEvent e) {}
077
078            @Override
079            public void componentResized(ComponentEvent ev) {
080                int w = getWidth();
081                if (w > 200) {
082                    Main.pref.putInteger("progressdialog.size", w);
083                }
084            }
085        });
086    }
087
088    /**
089     * Constructs a new {@code PleaseWaitDialog}.
090     * @param parent the {@code Component} from which the dialog is displayed. Can be {@code null}.
091     */
092    public PleaseWaitDialog(Component parent) {
093        super(JOptionPane.getFrameForComponent(parent), ModalityType.DOCUMENT_MODAL);
094        initDialog();
095    }
096
097    @Override
098    public void setIndeterminate(boolean newValue) {
099        UIManager.put("ProgressBar.cycleTime", UIManager.getInt("ProgressBar.repaintInterval") * 100);
100        progressBar.setIndeterminate(newValue);
101    }
102
103    protected void adjustLayout() {
104        invalidate();
105        setDropTarget(null); // Workaround to JDK bug 7027598/7100524/7169912 (#8613)
106        pack();
107        setSize(Main.pref.getInteger("progressdialog.size", 600), getSize().height);
108    }
109
110    /**
111     * Sets a custom text line below currentAction. Can be used to display additional information
112     * @param text custom text
113     */
114    @Override
115    public void setCustomText(String text) {
116        if (text == null || text.trim().isEmpty()) {
117            customText.setVisible(false);
118            adjustLayout();
119            return;
120        }
121        customText.setText(text);
122        if (!customText.isVisible()) {
123            customText.setVisible(true);
124            adjustLayout();
125        }
126    }
127
128    @Override
129    public void setCurrentAction(String text) {
130        currentAction.setText(text);
131    }
132
133    /**
134     * Appends a log message to the progress dialog. If the log area isn't visible yet
135     * it becomes visible. The height of the progress dialog is slightly increased too.
136     *
137     * @param message the message to append to the log. Ignore if null or white space only.
138     */
139    @Override
140    public void appendLogMessage(String message) {
141        if (message == null || message.trim().isEmpty())
142            return;
143        if (!spLog.isVisible()) {
144            spLog.setVisible(true);
145            taLog.setVisible(true);
146            adjustLayout();
147        }
148        taLog.append(message);
149        taLog.append("\n");
150        spLog.getVerticalScrollBar().setValue(spLog.getVerticalScrollBar().getMaximum());
151    }
152
153    /**
154     * Sets whether the cancel button is enabled or not
155     *
156     * @param enabled true, if the cancel button is enabled; false otherwise
157     */
158    public void setCancelEnabled(boolean enabled) {
159        btnCancel.setEnabled(enabled);
160    }
161
162    public void setInBackgroundPossible(boolean value) {
163        btnInBackground.setVisible(value);
164    }
165
166    /**
167     * Installs a callback for the cancel button. If callback is null, all action listeners
168     * are removed from the cancel button.
169     *
170     * @param callback the cancel callback
171     */
172    public void setCancelCallback(ActionListener callback) {
173        if (callback == null) {
174            ActionListener[] listeners = btnCancel.getActionListeners();
175            for (ActionListener l: listeners) {
176                btnCancel.removeActionListener(l);
177            }
178        } else {
179            btnCancel.addActionListener(callback);
180        }
181    }
182
183    /**
184     * Installs a callback for the "In background" button. If callback is null, all action listeners
185     * are removed from the cancel button.
186     *
187     * @param callback the cancel callback
188     */
189    public void setInBackgroundCallback(ActionListener callback) {
190        if (callback == null) {
191            ActionListener[] listeners = btnInBackground.getActionListeners();
192            for (ActionListener l: listeners) {
193                btnInBackground.removeActionListener(l);
194            }
195        } else {
196            btnInBackground.addActionListener(callback);
197        }
198    }
199
200    @Override
201    public void updateProgress(int progress) {
202        this.progress.setValue(progress);
203        this.progressBar.repaint();
204    }
205}