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

org.eclipse.jface.dialogs.ProgressMonitorDialog Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jface.dialogs;

import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

/**
 * A modal dialog that displays progress during a long running operation.
 * 

* This concrete dialog class can be instantiated as is, or further subclassed * as required. *

*

* Typical usage is: * *

 *
 *
 *    try {
 *       IRunnableWithProgress op = ...;
 *       new ProgressMonitorDialog(activeShell).run(true, true, op);
 *    } catch (InvocationTargetException e) {
 *       // handle exception
 *    } catch (InterruptedException e) {
 *       // handle cancelation
 *    }
 *
 *
 * 
* *

*

* Note that the ProgressMonitorDialog is not intended to be used with multiple * runnables - this dialog should be discarded after completion of one * IRunnableWithProgress and a new one instantiated for use by a second or * sebsequent IRunnableWithProgress to ensure proper initialization. *

*

* Note that not forking the process will result in it running in the UI which * may starve the UI. The most obvious symptom of this problem is non * responsiveness of the cancel button. If you are running within the UI Thread * you should do the bulk of your work in another Thread to prevent starvation. * It is recommended that fork is set to true in most cases. *

*/ public class ProgressMonitorDialog extends IconAndMessageDialog implements IRunnableContext { /** * Name to use for task when normal task name is empty string. */ private static String DEFAULT_TASKNAME = JFaceResources .getString("ProgressMonitorDialog.message"); //$NON-NLS-1$ /** * Constants for label and monitor size */ private static int LABEL_DLUS = 21; private static int BAR_DLUS = 9; /** * The progress indicator control. */ protected ProgressIndicator progressIndicator; /** * The label control for the task. Kept for backwards compatibility. */ protected Label taskLabel; /** * The label control for the subtask. */ protected Label subTaskLabel; /** * The Cancel button control. */ protected Button cancel; /** * Indicates whether the Cancel button is to be shown. */ protected boolean operationCancelableState = false; /** * Indicates whether the Cancel button is to be enabled. */ protected boolean enableCancelButton; /** * The progress monitor. */ private ProgressMonitor progressMonitor = new ProgressMonitor(); /** * The name of the current task (used by ProgressMonitor). */ private String task; /** * The nesting depth of currently running runnables. */ private int nestingDepth; /** * The cursor used in the cancel button; */ protected Cursor arrowCursor; /** * The cursor used in the shell; */ private Cursor waitCursor; /** * Flag indicating whether to open or merely create the dialog before run. */ private boolean openOnRun = true; /** * Internal progress monitor implementation. */ private class ProgressMonitor implements IProgressMonitorWithBlocking { private String fSubTask = "";//$NON-NLS-1$ private volatile boolean fIsCanceled; /** * is the process forked */ protected boolean forked = false; /** * is locked */ protected boolean locked = false; @Override public void beginTask(String name, int totalWork) { if (progressIndicator.isDisposed()) { return; } if (name == null) { task = "";//$NON-NLS-1$ } else { task = name; } String s = task; if (s.length() <= 0) { s = DEFAULT_TASKNAME; } setMessage(s, false); if (!forked) { update(); } if (totalWork == UNKNOWN) { progressIndicator.beginAnimatedTask(); } else { progressIndicator.beginTask(totalWork); } } @Override public void done() { if (!progressIndicator.isDisposed()) { progressIndicator.sendRemainingWork(); progressIndicator.done(); } } @Override public void setTaskName(String name) { if (name == null) { task = "";//$NON-NLS-1$ } else { task = name; } String s = task; if (s.length() <= 0) { s = DEFAULT_TASKNAME; } setMessage(s, false); if (!forked) { update(); } } @Override public boolean isCanceled() { return fIsCanceled; } @Override public void setCanceled(boolean b) { fIsCanceled = b; if (locked) { clearBlocked(); } } @Override public void subTask(String name) { if (subTaskLabel.isDisposed()) { return; } if (name == null) { fSubTask = "";//$NON-NLS-1$ } else { fSubTask = name; } subTaskLabel.setText(shortenText(fSubTask, subTaskLabel)); if (!forked) { subTaskLabel.update(); } } @Override public void worked(int work) { internalWorked(work); } @Override public void internalWorked(double work) { if (!progressIndicator.isDisposed()) { progressIndicator.worked(work); } } @Override public void clearBlocked() { if (getShell() == null || getShell().isDisposed()) return; locked = false; updateForClearBlocked(); } @Override public void setBlocked(IStatus reason) { if (getShell() == null || getShell().isDisposed()) return; locked = true; updateForSetBlocked(reason); } } /** * Clear blocked state from the receiver. */ protected void updateForClearBlocked() { progressIndicator.showNormal(); setMessage(task, true); imageLabel.setImage(getImage()); } /** * Set blocked state from the receiver. * * @param reason * IStatus that gives the details */ protected void updateForSetBlocked(IStatus reason) { progressIndicator.showPaused(); setMessage(reason.getMessage(), true); imageLabel.setImage(getImage()); } /** * Creates a progress monitor dialog under the given shell. The dialog has a * standard title and no image. open is non-blocking. * * @param parent * the parent shell, or null to create a top-level * shell */ public ProgressMonitorDialog(Shell parent) { super(parent); // no close button on the shell style if (isResizable()) { setShellStyle(getDefaultOrientation() | SWT.BORDER | SWT.TITLE | SWT.APPLICATION_MODAL | SWT.RESIZE | SWT.MAX); } else { setShellStyle(getDefaultOrientation() | SWT.BORDER | SWT.TITLE | SWT.APPLICATION_MODAL); } setBlockOnOpen(false); } /** * Enables the cancel button (asynchronously). * * @param b * The state to set the button to. */ private void asyncSetOperationCancelButtonEnabled(final boolean b) { if (getShell() != null) { getShell().getDisplay().asyncExec(() -> setOperationCancelButtonEnabled(b)); } } /** * The cancel button has been pressed. * * @since 3.0 */ @Override protected void cancelPressed() { // NOTE: this was previously done from a listener installed on the // cancel button. On GTK, the listener installed by // Dialog.createButton is called first and this was throwing an // exception because the cancel button was already disposed cancel.setEnabled(false); progressMonitor.setCanceled(true); super.cancelPressed(); } /** * The ProgressMonitorDialog implementation of this method * only closes the dialog if there are no currently running runnables. */ @Override public boolean close() { if (getNestingDepth() <= 0) { clearCursors(); return super.close(); } return false; } /** * Clear the cursors in the dialog. * * @since 3.0 */ protected void clearCursors() { if (cancel != null && !cancel.isDisposed()) { cancel.setCursor(null); } Shell shell = getShell(); if (shell != null && !shell.isDisposed()) { shell.setCursor(null); } if (arrowCursor != null) { arrowCursor.dispose(); } if (waitCursor != null) { waitCursor.dispose(); } arrowCursor = null; waitCursor = null; } @Override protected void configureShell(final Shell shell) { super.configureShell(shell); shell.setText(JFaceResources.getString("ProgressMonitorDialog.title")); //$NON-NLS-1$ if (waitCursor == null) { waitCursor = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT); } shell.setCursor(waitCursor); // Add a listener to set the message properly when the dialog becomes // visible shell.addListener(SWT.Show, event -> shell.getDisplay().asyncExec(() -> setMessage(message, true))); } @Override protected void createButtonsForButtonBar(Composite parent) { // cancel button createCancelButton(parent); } /** * Creates the cancel button. * * @param parent * the parent composite * @since 3.0 */ protected void createCancelButton(Composite parent) { cancel = createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, true); if (arrowCursor == null) { arrowCursor = new Cursor(cancel.getDisplay(), SWT.CURSOR_ARROW); } cancel.setCursor(arrowCursor); setOperationCancelButtonEnabled(enableCancelButton); } @Override protected Control createDialogArea(Composite parent) { setMessage(DEFAULT_TASKNAME, false); createMessageArea(parent); // Only set for backwards compatibility taskLabel = messageLabel; // progress indicator progressIndicator = new ProgressIndicator(parent); GridData gd = new GridData(); gd.heightHint = convertVerticalDLUsToPixels(BAR_DLUS); gd.horizontalAlignment = GridData.FILL; gd.grabExcessHorizontalSpace = true; gd.horizontalSpan = 2; progressIndicator.setLayoutData(gd); // label showing current task subTaskLabel = new Label(parent, SWT.LEFT | SWT.WRAP); gd = new GridData(GridData.FILL_HORIZONTAL); gd.heightHint = convertVerticalDLUsToPixels(LABEL_DLUS); gd.horizontalSpan = 2; subTaskLabel.setLayoutData(gd); subTaskLabel.setFont(parent.getFont()); return parent; } @Override protected Point getInitialSize() { Point calculatedSize = super.getInitialSize(); if (calculatedSize.x < 450) { calculatedSize.x = 450; } return calculatedSize; } /** * Returns the progress monitor to use for operations run in this progress * dialog. * * @return the progress monitor */ public IProgressMonitor getProgressMonitor() { return progressMonitor; } /** * This implementation of IRunnableContext#run(boolean, boolean, * IRunnableWithProgress) runs the given IRunnableWithProgress * using the progress monitor for this progress dialog and blocks until the * runnable has been run, regardless of the value of fork. * The dialog is opened before the runnable is run, and closed after it * completes. It is recommended that fork is set to true in * most cases. If fork is set to false, the * runnable will run in the UI thread and it is the runnable's * responsibility to call Display.readAndDispatch() to ensure * UI responsiveness. */ @Override public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) throws InvocationTargetException, InterruptedException { setCancelable(cancelable); try { aboutToRun(); // Let the progress monitor know if they need to update in UI Thread progressMonitor.forked = fork; ModalContext.run(runnable, fork, getProgressMonitor(), getShell() .getDisplay()); } finally { finishedRun(); } } /** * Returns whether the dialog should be opened before the operation is run. * Defaults to true * * @return true to open the dialog before run, * false to only create the dialog, but not open it * @since 3.0 */ public boolean getOpenOnRun() { return openOnRun; } /** * Sets whether the dialog should be opened before the operation is run. * NOTE: Setting this to false and not forking a process may starve any * asyncExec that tries to open the dialog later. * * @param openOnRun * true to open the dialog before run, * false to only create the dialog, but not open * it * @since 3.0 */ public void setOpenOnRun(boolean openOnRun) { this.openOnRun = openOnRun; } /** * Returns the nesting depth of running operations. * * @return the nesting depth of running operations * @since 3.0 */ protected int getNestingDepth() { return nestingDepth; } /** * Increments the nesting depth of running operations. * * @since 3.0 */ protected void incrementNestingDepth() { nestingDepth++; } /** * Decrements the nesting depth of running operations. * * @since 3.0 * */ protected void decrementNestingDepth() { nestingDepth--; } /** * Called just before the operation is run. Default behaviour is to open or * create the dialog, based on the setting of getOpenOnRun, * and increment the nesting depth. * * @since 3.0 */ protected void aboutToRun() { if (getOpenOnRun()) { open(); } else { create(); } incrementNestingDepth(); } /** * Called just after the operation is run. Default behaviour is to decrement * the nesting depth, and close the dialog. * * @since 3.0 */ protected void finishedRun() { decrementNestingDepth(); close(); } /** * Sets whether the progress dialog is cancelable or not. * * @param cancelable * true if the end user can cancel this progress * dialog, and false if it cannot be canceled */ public void setCancelable(boolean cancelable) { if (cancel == null) { enableCancelButton = cancelable; } else { asyncSetOperationCancelButtonEnabled(cancelable); } } /** * Helper to enable/disable Cancel button for this dialog. * * @param b * true to enable the cancel button, and * false to disable it * @since 3.0 */ protected void setOperationCancelButtonEnabled(boolean b) { operationCancelableState = b; if (cancel != null && !cancel.isDisposed()) { cancel.setEnabled(b); } } @Override protected Image getImage() { return getInfoImage(); } /** * Set the message in the message label. * * @param messageString * The string for the new message. * @param force * If force is true then always set the message text. */ private void setMessage(String messageString, boolean force) { // must not set null text in a label message = messageString == null ? "" : messageString; //$NON-NLS-1$ if (messageLabel == null || messageLabel.isDisposed()) { return; } if (force || messageLabel.isVisible()) { messageLabel.setToolTipText(message); messageLabel.setText(shortenText(message, messageLabel)); } } /** * Update the message label. Required if the monitor is forked. */ private void update() { if (messageLabel == null || messageLabel.isDisposed()) { return; } messageLabel.update(); } @Override public int open() { // Check to be sure it is not already done. If it is just return OK. if (!getOpenOnRun()) { if (getNestingDepth() == 0) { return OK; } } int result = super.open(); // update message label just in case beginTask() has been invoked // already if (task == null || task.length() == 0) setMessage(DEFAULT_TASKNAME, true); else setMessage(task, true); return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy