All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sourceforge.squirrel_sql.client.gui.ProgressAbortDialog Maven / Gradle / Ivy

/*
 * Copyright (C) 2011 Stefan Willinger
 * [email protected]
 *
 * 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) {
			StringBuilder sb = new StringBuilder();
			sb.append(dateFormat.format(new Date()));
			sb.append(": ");
			sb.append(string);
			sb.append(StringUtilities.getEolStr());
			this.historyArea.append(sb.toString());
			this.historyArea.setCaretPosition(historyArea.getDocument().getLength());
		}
	}

	/**
	 * 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();
			}
		}
		

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy