Package org.eclipse.jface.dialogs

Source Code of org.eclipse.jface.dialogs.ProgressMonitorDialog$ProgressMonitor

/*******************************************************************************
* Copyright (c) 2000, 2010 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.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

/**
* A modal dialog that displays progress during a long running operation.
* <p>
* This concrete dialog class can be instantiated as is, or further subclassed
* as required.
* </p>
* <p>
* Typical usage is:
*
* <pre>
*  
*    try {
*       IRunnableWithProgress op = ...;
*       new ProgressMonitorDialog(activeShell).run(true, true, op);
*    } catch (InvocationTargetException e) {
*       // handle exception
*    } catch (InterruptedException e) {
*       // handle cancelation
*    }
*   
*  
* </pre>
*
* </p>
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
*/
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;

    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);
      }
    }

    public void done() {
      if (!progressIndicator.isDisposed()) {
        progressIndicator.sendRemainingWork();
        progressIndicator.done();
      }
    }

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

    public boolean isCanceled() {
      return fIsCanceled;
    }

    public void setCanceled(boolean b) {
      fIsCanceled = b;
      if (locked) {
        clearBlocked();
      }
    }

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

    public void worked(int work) {
      internalWorked(work);
    }

    public void internalWorked(double work) {
      if (!progressIndicator.isDisposed()) {
        progressIndicator.worked(work);
      }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
     */
    public void clearBlocked() {
      if (getShell() == null || getShell().isDisposed())
        return;
      locked = false;
      updateForClearBlocked();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(org.eclipse.core.runtime.IStatus)
     */
    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. <code>open</code> is non-blocking.
   *
   * @param parent
   *            the parent shell, or <code>null</code> 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(new Runnable() {
        public void run() {
          setOperationCancelButtonEnabled(b);
        }
      });
    }
  }

  /**
   * The cancel button has been pressed.
   *
   * @since 3.0
   */
  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();
  }

  /*
   * (non-Javadoc) Method declared on Window.
   */
  /**
   * The <code>ProgressMonitorDialog</code> implementation of this method
   * only closes the dialog if there are no currently running runnables.
   */
  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;
  }

  /*
   * (non-Javadoc) Method declared in Window.
   */
  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, new Listener() {
      public void handleEvent(Event event) {
        // We need to async the message update since the Show precedes
        // visibility
        shell.getDisplay().asyncExec(new Runnable() {
          public void run() {
            setMessage(message, true);
          }
        });
      }
    });
  }

  /*
   * (non-Javadoc) Method declared on Dialog.
   */
  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);
  }

  /*
   * (non-Javadoc) Method declared on Dialog.
   */
  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;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jface.window.Window#getInitialSize()
   */
  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 <code>IRunnableWithProgress</code>
   * using the progress monitor for this progress dialog and blocks until the
   * runnable has been run, regardless of the value of <code>fork</code>.
   * The dialog is opened before the runnable is run, and closed after it
   * completes. It is recommended that <code>fork</code> is set to true in
   * most cases. If <code>fork</code> is set to <code>false</code>, the
   * runnable will run in the UI thread and it is the runnable's
   * responsibility to call <code>Display.readAndDispatch()</code> to ensure
   * UI responsiveness.
   */
  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 <code>true</code>
   *
   * @return <code>true</code> to open the dialog before run,
   *         <code>false</code> 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
   *            <code>true</code> to open the dialog before run,
   *            <code>false</code> 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 <code>getOpenOnRun</code>,
   * 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
   *            <code>true</code> if the end user can cancel this progress
   *            dialog, and <code>false</code> 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
   *            <code>true</code> to enable the cancel button, and
   *            <code>false</code> to disable it
   * @since 3.0
   */
  protected void setOperationCancelButtonEnabled(boolean b) {
    operationCancelableState = b;
    if (cancel != null && !cancel.isDisposed()) {
      cancel.setEnabled(b)
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getImage()
   */
  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();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jface.window.Window#open()
   */
  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;
  }
}
TOP

Related Classes of org.eclipse.jface.dialogs.ProgressMonitorDialog$ProgressMonitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.