
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