net.sourceforge.squirrel_sql.client.gui.ProgressAbortDialog.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.squirrel_sql.client.gui.ProgressAbortDialog.java

Source

/*
 * Copyright (C) 2011 Stefan Willinger
 * wis775@users.sourceforge.net
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package net.sourceforge.squirrel_sql.client.gui;

import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Window;
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.text.DateFormat;
import java.util.Date;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import org.apache.commons.lang.StringUtils;

import net.sourceforge.squirrel_sql.client.action.SquirrelAction;
import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
import net.sourceforge.squirrel_sql.fw.sql.ProgressAbortCallback;
import net.sourceforge.squirrel_sql.fw.sql.ProgressCallBack;
import net.sourceforge.squirrel_sql.fw.util.StringManager;
import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
import net.sourceforge.squirrel_sql.fw.util.StringUtilities;
import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;

/**
 * A monitor, which provide certain information to the user about a long running
 * operation. The user get information about the progress of a long running
 * operation. In addition to the progress bar, each step is reported in a
 * "history area". This monitor provides the opportunity to cancel the
 * operation. Sometimes it is not known, how many tasks must be completed, until
 * the whole operation is finished. For this case, the monitor can be run in
 * "indeterminate" mode. Depending on indeterminate or not, we can or cann't
 * claim that the overall progress is finished.
 * 
 * @see JProgressBar#isIndeterminate()
 * @author Stefan Willinger
 * 
 */
public class ProgressAbortDialog extends JDialog implements ProgressAbortCallback {

    private final ProgressAbortDialog instance = this;

    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = 1L;

    /** Logger for this class. */
    public final static ILogger s_log = LoggerController.createLogger(ProgressAbortDialog.class);

    /** Internationalized strings for this class */
    private static final StringManager s_stringMgr = StringManagerFactory
            .getStringManager(ProgressAbortDialog.class);

    static interface i18n {
        // i18n[ProgressAbortDialog.defaultLoadingPrefix=Loading:]
        String DEFAULT_LOADING_PREFIX = s_stringMgr.getString("ProgressAbortDialog.defaultLoadingPrefix");

        // i18n[ProgressAbortDialog.initialLoadingPrefix=Loading...]
        String INITIAL_LOADING_PREFIX = s_stringMgr.getString("ProgressAbortDialog.initialLoadingPrefix");

        // i18n[ProgressAbortDialog.confirmCancel=Should the export be
        // canceled?]
        String CONFIRM_CANCEL = s_stringMgr.getString("ProgressAbortDialog.confirmCancel");

        String TITEL_PROGRESS = s_stringMgr.getString("ProgressAbortDialog.titelProgress");

        String CANCEL = s_stringMgr.getString("ProgressAbortDialog.cancel");

        String CANCEL_FEEDBACK = s_stringMgr.getString("ProgressAbortDialog.cancelFeedback");
    }

    /**
     * Date format for the history area.
     */
    private DateFormat dateFormat = DateFormat.getTimeInstance();

    /**
     * The number of task, until the operation is completed.
     */
    private int itemCount = 0;

    /**
     * The progress-bar itself
     */
    private JProgressBar progressBar = null;

    /**
     * The place to show the current task
     */
    private JLabel statusLabel = null;

    /**
     * The place to show additional information about the current task
     */
    private JLabel additionalStatusLabel = null;

    private String _loadingPrefix = i18n.DEFAULT_LOADING_PREFIX;

    /**
     * True, if we dont know, how many tasks are neccesary to complete the
     * operation
     * 
     * @see JProgressBar#setIndeterminate(boolean)
     */
    private boolean indeterminate;

    /**
     * A callback handler, if the user decided to abort the operation.
     */
    private IAbortEventHandler abortHandler;

    private JButton cancelButton;

    /**
     * Area to display the already completed tasks of this operation.
     */
    private JTextArea historyArea;

    /**
     * Description of the long running operation.
     */
    private JComponent taskDescriptionComponent;

    /**
     * Flag, if the operation should be canceled
     */
    private boolean canceled;

    /**
     * Someone told us, that all tasks are done.
     */
    private boolean finished;

    /**
     * A simple description for this task
     */
    private String simpleTaskDescription = null;

    /**
     * Constructor which accepts a Dialog owner
     * 
     * @param owner
     *            the owner Dialog from which the dialog is displayed or null if
     *            this dialog has no owner
     * @param title
     *            the String to display in the dialog's title bar
     * @param totalItems
     *            the total number of items at which point progress will
     *            indicate complete
     * @param indeterminate
     *            true, if the {@link JProgressBar} should be used in the
     *            indeterminate mode.
     * @param abortHandler
     *            If the underlying tasks maybe aborted, then a abort Handler is
     *            needed. Otherwise null.
     * @see JProgressBar#setIndeterminate(boolean)
     */
    public ProgressAbortDialog(Dialog owner, String title, String description, int totalItems,
            boolean indeterminate, IAbortEventHandler abortHandler) {
        super(owner, title);
        init(description, totalItems, indeterminate, abortHandler);
    }

    /**
     * Constructor which accepts a Frame owner
     * 
     * @param owner
     *            the owner Frame from which the dialog is displayed or null if
     *            this dialog has no owner
     * @param title
     *            the String to display in the dialog's title bar
     * @param totalItems
     *            the total number of items at which point progress will
     *            indicate complete
     * @param indeterminate
     *            true, if the {@link JProgressBar} should be used in the
     *            indeterminate mode.
     * @param abortHandler
     *            If the underlying tasks maybe aborted, then a abort Handler is
     *            needed. Otherwise null.
     * @see JProgressBar#setIndeterminate(boolean)
     */
    public ProgressAbortDialog(Frame owner, String title, String description, int totalItems, boolean indeterminate,
            IAbortEventHandler abortHandler) {
        super(owner, title);
        setLocationRelativeTo(owner);
        init(description, totalItems, indeterminate, abortHandler);
    }

    /**
     * @see net.sourceforge.squirrel_sql.fw.sql.ProgressCallBack#setTotalItems(int)
     */
    @Override
    public void setTotalItems(int totalItems) {
        itemCount = totalItems;
        progressBar.setMaximum(totalItems);
    }

    /**
     * Sets the text that is displayed before each thing being loaded. By
     * default this is the string "Loading:".
     * 
     * @param loadingPrefix
     */
    @Override
    public void setLoadingPrefix(String loadingPrefix) {
        if (loadingPrefix != null) {
            _loadingPrefix = loadingPrefix;
        }
    }

    /**
     * @see net.sourceforge.squirrel_sql.fw.sql.ProgressCallBack#currentlyLoading(java.lang.String)
     */
    @Override
    public void currentlyLoading(final String simpleName) {
        final StringBuilder statusText = appendPrefixed(simpleName);

        try {
            GUIUtils.processOnSwingEventThread(new Runnable() {
                public void run() {
                    statusLabel.setText(statusText.toString());
                    setTaskStatus(null);
                    progressBar.setValue(progressBar.getValue() + 1);

                    if (finishedLoading()) {
                        ProgressAbortDialog.this.setVisible(false);
                        return;
                    }
                }
            });
        } catch (Exception e) {
            s_log.error("Unexpected exception: " + e.getMessage(), e);
        }
    }

    private StringBuilder appendPrefixed(String simpleName) {
        final StringBuilder statusText = new StringBuilder();
        statusText.append(_loadingPrefix);
        statusText.append(" ");
        statusText.append(simpleName);

        appendToHistory(statusText.toString());
        return statusText;
    }

    /**
     * @see net.sourceforge.squirrel_sql.fw.sql.ProgressAbortCallback#setTaskStatus(java.lang.String)
     */
    @Override
    public void setTaskStatus(final String status) {
        final StringBuilder statusText = new StringBuilder();

        if (StringUtils.isNotBlank(status)) {
            statusText.append(status);
        } else {
            statusText.append(" ");
        }

        try {
            GUIUtils.processOnSwingEventThread(new Runnable() {
                public void run() {
                    String statusTextToAppend = statusText.toString();
                    additionalStatusLabel.setText(statusTextToAppend);
                    if (StringUtils.isNotBlank(statusTextToAppend)) {
                        appendPrefixed(statusTextToAppend);
                    }
                }

            });
        } catch (Exception e) {
            s_log.error("Unexpected exception: " + e.getMessage(), e);
        }
    }

    /**
     * @param string
     */
    private void appendToHistory(String string) {
        if (this.historyArea != null) {
            final StringBuilder sb = new StringBuilder();
            sb.append(dateFormat.format(new Date()));
            sb.append(": ");
            sb.append(string);
            sb.append(StringUtilities.getEolStr());

            Runnable runnable = new Runnable() {
                public void run() {
                    historyArea.append(sb.toString());
                    historyArea.setCaretPosition(historyArea.getDocument().getLength());
                }
            };

            GUIUtils.processOnSwingEventThread(runnable);
        }
    }

    /**
     * Checks, if the overall progress is finished. If this monitor runs in
     * indeterminate mode, we didn't know, if the progress is finished.
     * Otherwise, the finish state is interpreted, if we have reached the
     * necessary task count.
     * 
     * @see net.sourceforge.squirrel_sql.fw.sql.ProgressCallBack#finishedLoading()
     */
    @Override
    public boolean finishedLoading() {
        if (finished) {
            progressBar.setIndeterminate(false);
            return true;
        }

        if (this.indeterminate) {
            return false;
        }
        return progressBar.getValue() == itemCount;
    }

    /**
     * Dispose this monitor.
     * 
     * @see java.awt.Window#dispose()
     */
    @Override
    public void dispose() {
        GUIUtils.processOnSwingEventThread(new Runnable() {
            @Override
            public void run() {
                callDisposeFromSuperClass();
            }
        });
    }

    /**
     * Since {@link #dispose()} uses an {@link Runnable}, we needs an delegate
     * to call the overridden dispose method.
     */
    private void callDisposeFromSuperClass() {
        super.dispose();
    }

    /**
     * @see java.awt.Dialog#setVisible(boolean)
     */
    @Override
    public void setVisible(final boolean b) {
        GUIUtils.processOnSwingEventThread(new Runnable() {
            @Override
            public void run() {
                callSetVisibleFromSuperClass(b);
            }
        });
    }

    /**
     * Since {@link #setVisible(boolean)} uses an {@link Runnable}, we needs an
     * delegate to call the overridden setVisible method.
     */
    private void callSetVisibleFromSuperClass(final boolean b) {
        super.setVisible(b);
    }

    private void init(String description, int totalItems, boolean intermediate, IAbortEventHandler abortHandler) {
        itemCount = totalItems;
        this.indeterminate = intermediate;
        this.abortHandler = abortHandler;
        this.simpleTaskDescription = description;
        final Window owner = super.getOwner();
        final ProgressAbortDialog dialog = this;
        createGUI();
        setLocationRelativeTo(owner);
        dialog.setVisible(true);
    }

    private void createGUI() {
        JPanel dialogPanel = new JPanel(new GridBagLayout());
        dialogPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
        GridBagConstraints c;

        c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 0;
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1.0;
        c.weighty = 0.5;
        c.anchor = GridBagConstraints.WEST;
        c.insets = new Insets(4, 0, 4, 0);
        taskDescriptionComponent = createTaskDescripion();
        dialogPanel.add(taskDescriptionComponent, c);

        c.gridy++;
        JPanel progressPanel = new JPanel(new GridBagLayout());
        progressPanel.setMinimumSize(new Dimension(400, 200));
        progressPanel.setPreferredSize(new Dimension(400, 200));
        progressPanel.setBorder(BorderFactory.createTitledBorder("Progress"));
        dialogPanel.add(progressPanel, c);

        c.gridy = 0;
        c.gridx = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 0.0;
        c.weighty = 0.0;
        c.insets = new Insets(4, 10, 4, 10);
        statusLabel = new JLabel(i18n.INITIAL_LOADING_PREFIX);
        progressPanel.add(statusLabel, c);

        c.gridy++;
        c.insets = new Insets(4, 10, 4, 10);
        additionalStatusLabel = new JLabel(" "); // Must be a space :-)
        progressPanel.add(additionalStatusLabel, c);

        c.gridy++;
        c.weightx = 1.0;
        progressBar = new JProgressBar(0, itemCount);
        progressBar.setIndeterminate(indeterminate);
        progressPanel.add(progressBar, c);

        c.gridy++;
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1.0;
        c.weighty = 1.0;
        historyArea = new JTextArea();
        historyArea.setEditable(false);
        JScrollPane jScrollPane = new JScrollPane(historyArea);
        progressPanel.add(jScrollPane, c);

        if (abortHandler != null) {
            cancelButton = new JButton(new CancelAction());

            c.gridy++;
            c.anchor = GridBagConstraints.WEST;
            c.fill = GridBagConstraints.HORIZONTAL;
            c.weightx = 0.0;
            c.weighty = 0.0;
            dialogPanel.add(cancelButton, c);
        }

        super.getContentPane().add(dialogPanel);
        super.pack();
        super.setSize(new Dimension(450, 450));
        super.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        super.addWindowListener(new WindowCloseListener());
    }

    /**
     * Creates the description of the task/operation. Per default, this will be
     * a simple {@link JLabel} with the {@link #simpleTaskDescription} of this
     * dialog.. A subclass may override this for a solution, that is more
     * sophisticated.
     * 
     * @see #simpleTaskDescription
     * @return a {@link JComponent} as task description
     */
    protected JComponent createTaskDescripion() {
        return new JLabel(this.simpleTaskDescription);
    }

    /**
     * Just for playing...
     */
    public static void main(String[] args) throws Exception {
        IAbortEventHandler handler = new IAbortEventHandler() {

            @Override
            public void cancel() {
                System.out.println("echo");
            }
        };

        ProgressCallBack dialog = new ProgressAbortDialog((Frame) null, "myTitle", "myDescription", 0, true,
                handler);
        Thread.sleep(3000);
        dialog.currentlyLoading("Running query");
        Thread.sleep(3000);
        dialog.currentlyLoading("1 Row(s) exported");
        Thread.sleep(3000);
        dialog.currentlyLoading("100 Row(s) exported");
        Thread.sleep(3000);
        dialog.currentlyLoading("1000 Row(s) exported");
        dialog.currentlyLoading("Finished");

    }

    /**
     * @see net.sourceforge.squirrel_sql.fw.sql.IAbortController#isStop()
     */
    @Override
    public boolean isStop() {
        return this.canceled;
    }

    /**
     * @see net.sourceforge.squirrel_sql.fw.sql.IAbortController#isVisble()
     */
    @Override
    public boolean isVisble() {
        return super.isVisible();
    }

    /**
     * @see net.sourceforge.squirrel_sql.fw.sql.ProgressAbortCallback#setFinished()
     */
    @Override
    public void setFinished() {
        this.finished = true;
    }

    /**
     * A WindowListener that responds to windowClosed events. To close the
     * window, means the same as pressing the cancel button.
     */
    private class WindowCloseListener extends WindowAdapter {
        /**
         * @see java.awt.event.WindowAdapter#windowClosing(java.awt.event.WindowEvent)
         */
        @Override
        public void windowClosing(WindowEvent e) {
            new CancelAction().clickCancel();
        }
    }

    /**
     * Action to handle the request for canceling.
     * The user will be asked, if he really want to cancel the progress.
     * @author Stefan Willinger
     *
     */
    private class CancelAction extends AbstractAction {

        public CancelAction() {
            super(i18n.CANCEL);
        }

        /**
         * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            clickCancel();
        }

        /**
         * Ask the user, if he really want to cancel the action. If yes, do the cancel.
         */
        public void clickCancel() {
            int ret = JOptionPane.showConfirmDialog(instance, i18n.CONFIRM_CANCEL);
            if (JOptionPane.YES_OPTION == ret) {
                appendToHistory(i18n.CANCEL_FEEDBACK);
                canceled = true;
                cancelButton.setEnabled(false);
                abortHandler.cancel();
            }
        }

    }

}