blob: d843e8b7d2d3ef45f022e18d7407a6d173b83e47 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM - Initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.progress;
import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
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;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.misc.Policy;
/**
* The ProgressMonitorJobsDialog is the progress monitor dialog used by the
* progress service to allow locks to show the current jobs.
*/
public class ProgressMonitorJobsDialog extends ProgressMonitorDialog {
private DetailedProgressViewer viewer;
/**
* The height of the viewer. Set when the details button is selected.
*/
private int viewerHeight = -1;
Composite viewerComposite;
private Button detailsButton;
private long watchTime = -1;
/** Flag to prevent nested closes. */
protected boolean alreadyClosed = false;
private IProgressMonitor wrapperedMonitor;
/**
* Cache initial enablement in case the enablement state is set before the
* button is created
*/
protected boolean enableDetailsButton = false;
/**
* Create a new instance of the receiver.
*
* @param parent dialogs parent shell
*/
public ProgressMonitorJobsDialog(Shell parent) {
super(parent);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite top = (Composite) super.createDialogArea(parent);
createExtendedDialogArea(parent);
return top;
}
/**
* Create the extensions to the dialog area.
*
* @param parent the parent composite
*/
protected void createExtendedDialogArea(Composite parent) {
viewerComposite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
viewerComposite.setLayout(layout);
GridData viewerData = new GridData(GridData.FILL_BOTH);
viewerData.horizontalSpan = 2;
viewerData.heightHint = 0;
viewerComposite.setLayoutData(viewerData);
}
/**
* The details button has been selected. Open or close the progress viewer as
* appropriate.
*/
void handleDetailsButtonSelect() {
Shell shell = getShell();
Point shellSize = shell.getSize();
Composite composite = (Composite) getDialogArea();
if (viewer != null) {
viewer.getControl().dispose();
viewer = null;
composite.layout();
shell.setSize(shellSize.x, shellSize.y - viewerHeight);
detailsButton.setText(ProgressMessages.ProgressMonitorJobsDialog_DetailsTitle);
} else {
// Abort if there are no jobs visible
if (ProgressManager.getInstance().getRootElements(Policy.DEBUG_SHOW_ALL_JOBS).length == 0) {
detailsButton.setEnabled(false);
return;
}
viewer = new DetailedProgressViewer(viewerComposite, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
viewer.setComparator(ProgressManagerUtil.getProgressViewerComparator());
viewer.setContentProvider(new ProgressViewerContentProvider(viewer, true, false) {
@Override
public Object[] getElements(Object inputElement) {
return super.getElements(inputElement);
}
});
viewer.setLabelProvider(new ProgressLabelProvider());
viewer.setInput(this);
GridData viewerData = new GridData(GridData.FILL_BOTH);
viewer.getControl().setLayoutData(viewerData);
GridData viewerCompositeData = (GridData) viewerComposite.getLayoutData();
viewerCompositeData.heightHint = convertHeightInCharsToPixels(10);
viewerComposite.layout(true);
viewer.getControl().setVisible(true);
viewerHeight = viewerComposite.computeTrim(0, 0, 0, viewerCompositeData.heightHint).height;
detailsButton.setText(ProgressMessages.ProgressMonitorJobsDialog_HideTitle);
shell.setSize(shellSize.x, shellSize.y + viewerHeight);
}
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
super.createButtonsForButtonBar(parent);
createDetailsButton(parent);
}
/**
* Create a spacer label to get the layout to not bunch the widgets.
*
* @param parent The parent of the new button.
*/
protected void createSpacer(Composite parent) {
// Make a label to force the spacing
Label spacer = new Label(parent, SWT.NONE);
spacer.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
}
/**
* Create the details button for the receiver.
*
* @param parent The parent of the new button.
*/
protected void createDetailsButton(Composite parent) {
detailsButton = createButton(parent, IDialogConstants.DETAILS_ID,
ProgressMessages.ProgressMonitorJobsDialog_DetailsTitle, false);
detailsButton.addSelectionListener(widgetSelectedAdapter(e -> handleDetailsButtonSelect()));
detailsButton.setCursor(arrowCursor);
detailsButton.setEnabled(enableDetailsButton);
}
@Override
protected Control createButtonBar(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
// create a layout with spacing and margins appropriate for the font
// size.
GridLayout layout = new GridLayout();
layout.numColumns = 1; // this is incremented by createButton
layout.makeColumnsEqualWidth = false;
layout.marginWidth = 0;
layout.marginHeight = 0;
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
composite.setLayout(layout);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = 2;
data.horizontalAlignment = GridData.END;
data.grabExcessHorizontalSpace = true;
composite.setLayoutData(data);
composite.setFont(parent.getFont());
// Add the buttons to the button bar.
if (arrowCursor == null) {
arrowCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_ARROW);
}
createButtonsForButtonBar(composite);
return composite;
}
@Override
protected void clearCursors() {
if (detailsButton != null && !detailsButton.isDisposed()) {
detailsButton.setCursor(null);
}
super.clearCursors();
}
@Override
protected void updateForSetBlocked(IStatus reason) {
if (alreadyClosed)
return;
super.updateForSetBlocked(reason);
enableDetails(true);
if (viewer == null) {
handleDetailsButtonSelect();
}
}
@Override
public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable)
throws InvocationTargetException, InterruptedException {
// if it is run in the UI Thread don't do anything.
if (!fork) {
enableDetails(false);
}
super.run(fork, cancelable, runnable);
}
/**
* Set the enable state of the details button now or when it will be created.
*
* @param enableState a boolean to indicate the preferred' state
*/
protected void enableDetails(boolean enableState) {
if (detailsButton == null) {
enableDetailsButton = enableState;
} else {
detailsButton.setEnabled(enableState);
}
}
/**
* Start watching the ticks. When the long operation time has passed open the
* dialog.
*/
public void watchTicks() {
watchTime = System.currentTimeMillis();
}
/**
* Create a monitor for the receiver that wrappers the superclasses monitor.
*
*/
public void createWrapperedMonitor() {
wrapperedMonitor = new IProgressMonitor() {
IProgressMonitor superMonitor = ProgressMonitorJobsDialog.super.getProgressMonitor();
@Override
public void beginTask(String name, int totalWork) {
superMonitor.beginTask(name, totalWork);
checkTicking();
}
/**
* Check if we have ticked in the last 800ms.
*/
private void checkTicking() {
if (watchTime < 0) {
return;
}
if ((System.currentTimeMillis() - watchTime) > ProgressManager.getInstance().getLongOperationTime()) {
watchTime = -1;
openDialog();
}
}
/**
* Open the dialog in the ui Thread
*/
private void openDialog() {
if (!PlatformUI.isWorkbenchRunning()) {
return;
}
PlatformUI.getWorkbench().getDisplay().syncExec(() -> {
// Reset the watch if it is not safe to open
if (!ProgressManagerUtil.safeToOpen(ProgressMonitorJobsDialog.this, null)) {
watchTicks();
return;
}
if (!alreadyClosed) {
open();
}
});
}
@Override
public void done() {
superMonitor.done();
checkTicking();
}
@Override
public void internalWorked(double work) {
superMonitor.internalWorked(work);
checkTicking();
}
@Override
public boolean isCanceled() {
return superMonitor.isCanceled();
}
@Override
public void setCanceled(boolean value) {
superMonitor.setCanceled(value);
}
@Override
public void setTaskName(String name) {
superMonitor.setTaskName(name);
checkTicking();
}
@Override
public void subTask(String name) {
superMonitor.subTask(name);
checkTicking();
}
@Override
public void worked(int work) {
superMonitor.worked(work);
checkTicking();
}
@Override
public void clearBlocked() {
// We want to open on blocking too
superMonitor.clearBlocked();
}
@Override
public void setBlocked(IStatus reason) {
openDialog();
superMonitor.setBlocked(reason);
}
};
}
@Override
public IProgressMonitor getProgressMonitor() {
if (wrapperedMonitor == null) {
createWrapperedMonitor();
}
return wrapperedMonitor;
}
@Override
public boolean close() {
alreadyClosed = true;// As this sometimes delayed cache if it was already closed
boolean result = super.close();
if (!result) {// If it fails reset the flag
alreadyClosed = false;
}
return result;
}
@Override
protected boolean isResizable() {
return true;
}
}