blob: 70819f64665df9b7fd8dccc685355db2af3bcecc [file] [log] [blame]
/*******************************************************************************
* 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
* Alex Blewitt <alex.blewitt@gmail.com> - Replace new Boolean with Boolean.valueOf - https://bugs.eclipse.org/470244
*******************************************************************************/
package org.eclipse.search.internal.ui.util;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Cursor;
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.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.ControlEnableState;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.jface.wizard.ProgressMonitorPart;
import org.eclipse.search.internal.ui.SearchMessages;
public abstract class ExtendedDialogWindow extends TrayDialog implements IRunnableContext {
private Control fContents;
private Button fCancelButton;
private List<Button> fActionButtons;
// The number of long running operation executed from the dialog.
private long fActiveRunningOperations;
// The progress monitor
private boolean fUseEmbeddedProgressMonitorPart;
private ProgressMonitorPart fProgressMonitorPart;
private MessageDialog fWindowClosingDialog;
private static final String FOCUS_CONTROL= "focusControl"; //$NON-NLS-1$
public ExtendedDialogWindow(Shell shell) {
super(shell);
fActionButtons= new ArrayList<>();
}
@Override
protected boolean isResizable() {
return true;
}
//---- Hooks to reimplement in subclasses -----------------------------------
/**
* @param enable Use the embedded progress monitor part
*/
public void setUseEmbeddedProgressMonitorPart(boolean enable) {
fUseEmbeddedProgressMonitorPart= enable;
}
/**
* Hook called when the user has pressed the button to perform
* the dialog's action. If the method returns <code>false</code>
* the dialog stays open. Otherwise the dialog is going to be closed.
* @param buttonId Id of the button activated
* @return If the method returns <code>false</code>
* the dialog stays open.
*/
protected boolean performAction(int buttonId) {
return true;
}
/**
* Hook called when the user has pressed the button to cancel
* the dialog. If the method returns <code>false</code> the dialog
* stays open. Otherwise the dialog is going to be closed.
* @return If the method returns <code>false</code>
* the dialog stays open.
*/
protected boolean performCancel() {
return true;
}
//---- UI creation ----------------------------------------------------------
/**
* Create the page area.
* @param parent The parent composite
* @return The created control
*/
protected abstract Control createPageArea(Composite parent);
/**
* Add buttons to the dialog's button bar.
*
* Subclasses may override.
*
* @param parent the button bar composite
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
fCancelButton= createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
}
protected Button createActionButton(Composite parent, int id, String label,
boolean defaultButton) {
Button actionButton= createButton(parent, id, label, defaultButton);
fActionButtons.add(actionButton);
return actionButton;
}
/**
* Creates the layout of the extended dialog window.
* @param parent The parent composite
* @return The created control
*/
@Override
protected Control createDialogArea(Composite parent) {
Composite result= (Composite) super.createDialogArea(parent);
fContents= createPageArea(result);
fContents.setLayoutData(new GridData(GridData.FILL_BOTH));
if (fUseEmbeddedProgressMonitorPart) {
// Insert a progress monitor
fProgressMonitorPart= new ProgressMonitorPart(result, new GridLayout(), SWT.DEFAULT);
fProgressMonitorPart.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
fProgressMonitorPart.setVisible(false);
applyDialogFont(fProgressMonitorPart);
}
Label separator= new Label(result, SWT.SEPARATOR | SWT.HORIZONTAL);
separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
return result;
}
@Override
protected void buttonPressed(int buttonId) {
switch (buttonId) {
case IDialogConstants.CANCEL_ID:
if (fActiveRunningOperations == 0)
close();
break;
default:
if (performAction(buttonId))
close();
}
}
//---- Setters and Getters --------------------------------------------------
/**
* Set the enable state of the perform action button.
* @param state The new state
*/
public void setPerformActionEnabled(boolean state) {
for (Iterator<Button> buttons = fActionButtons.iterator(); buttons.hasNext(); ) {
Button element = buttons.next();
element.setEnabled(state);
}
}
//---- Operation stuff ------------------------------------------------------
@Override
public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) throws InvocationTargetException, InterruptedException {
// The operation can only be canceled if it is executed in a separate thread.
// Otherwise the UI is blocked anyway.
Object state= null;
try {
fActiveRunningOperations++;
state= aboutToStart(fork && cancelable);
if (fUseEmbeddedProgressMonitorPart) {
ModalContext.run(runnable, fork, fProgressMonitorPart, getShell().getDisplay());
} else {
new ProgressMonitorDialog(getShell()).run(fork, cancelable, runnable);
}
} finally {
if (state != null)
stopped(state);
fActiveRunningOperations--;
}
}
/**
* About to start a long running operation triggered through
* the wizard. So show the progress monitor and disable
* the wizard.
* @param enableCancelButton The cancel button enable state
* @return The saved UI state.
*/
protected synchronized Object aboutToStart(boolean enableCancelButton) {
HashMap<Object, Object> savedState= null;
Shell shell= getShell();
if (shell != null) {
Display d= shell.getDisplay();
// Save focus control
Control focusControl= d.getFocusControl();
if (focusControl != null && focusControl.getShell() != shell)
focusControl= null;
// Set the busy cursor to all shells.
setDisplayCursor(d, d.getSystemCursor(SWT.CURSOR_WAIT));
// Set the arrow cursor to the cancel component.
fCancelButton.setCursor(d.getSystemCursor(SWT.CURSOR_ARROW));
// Deactivate shell
savedState= saveUIState(enableCancelButton);
if (focusControl != null)
savedState.put(FOCUS_CONTROL, focusControl);
if (fUseEmbeddedProgressMonitorPart) {
// Attach the progress monitor part to the cancel button
fProgressMonitorPart.attachToCancelComponent(fCancelButton);
fProgressMonitorPart.setVisible(true);
}
}
return savedState;
}
/**
* A long running operation triggered through the wizard
* was stopped either by user input or by normal end.
* @param savedState The saveState returned by <code>aboutToStart</code>.
* @see #aboutToStart(boolean)
*/
protected synchronized void stopped(Object savedState) {
Assert.isTrue( savedState instanceof HashMap);
Shell shell= getShell();
if (shell != null) {
if (fUseEmbeddedProgressMonitorPart) {
fProgressMonitorPart.setVisible(false);
fProgressMonitorPart.removeFromCancelComponent(fCancelButton);
}
@SuppressWarnings("unchecked")
HashMap<Object, Object> state= (HashMap<Object, Object>)savedState;
restoreUIState(state);
setDisplayCursor(shell.getDisplay(), null);
fCancelButton.setCursor(null);
Control focusControl= (Control)state.get(FOCUS_CONTROL);
if (focusControl != null && ! focusControl.isDisposed())
focusControl.setFocus();
}
}
private void setDisplayCursor(Display d, Cursor c) {
Shell[] shells= d.getShells();
for (int i= 0; i < shells.length; i++)
shells[i].setCursor(c);
}
//---- UI state save and restoring ---------------------------------------------
private void restoreUIState(HashMap<Object, Object> state) {
restoreEnableState(fCancelButton, state);
for (Iterator<Button> actionButtons = fActionButtons.iterator(); actionButtons.hasNext(); ) {
Button button = actionButtons.next();
restoreEnableState(button, state);
}
ControlEnableState pageState= (ControlEnableState)state.get("tabForm"); //$NON-NLS-1$
pageState.restore();
}
/*
* Restores the enable state of the given control.
*/
protected void restoreEnableState(Control w, HashMap<Object, Object> h) {
if (!w.isDisposed()) {
Boolean b= (Boolean)h.get(w);
if (b != null)
w.setEnabled(b.booleanValue());
}
}
private HashMap<Object, Object> saveUIState(boolean keepCancelEnabled) {
HashMap<Object, Object> savedState= new HashMap<>(10);
saveEnableStateAndSet(fCancelButton, savedState, keepCancelEnabled);
for (Iterator<Button> actionButtons = fActionButtons.iterator(); actionButtons.hasNext(); ) {
Button button = actionButtons.next();
saveEnableStateAndSet(button, savedState, false);
}
savedState.put("tabForm", ControlEnableState.disable(fContents)); //$NON-NLS-1$
return savedState;
}
private void saveEnableStateAndSet(Control w, HashMap<Object, Object> h, boolean enabled) {
if (!w.isDisposed()) {
h.put(w, Boolean.valueOf(w.isEnabled()));
w.setEnabled(enabled);
}
}
@Override
protected void handleShellCloseEvent() {
if (okToClose())
super.handleShellCloseEvent();
}
/**
* The dialog is going to be closed. Check if there is a running
* operation. If so, post an alert saying that the wizard can't
* be closed.
* @return If false is returned, the dialog should stay open
*/
public boolean okToClose() {
if (fActiveRunningOperations > 0) {
synchronized (this) {
fWindowClosingDialog= createClosingDialog();
}
fWindowClosingDialog.open();
synchronized (this) {
fWindowClosingDialog= null;
}
return false;
}
return true;
}
private MessageDialog createClosingDialog() {
MessageDialog result=
new MessageDialog(
getShell(),
SearchMessages.SearchDialogClosingDialog_title,
null,
SearchMessages.SearchDialogClosingDialog_message,
MessageDialog.QUESTION,
new String[] {IDialogConstants.OK_LABEL},
0);
return result;
}
/**
* @return Returns the cancel component that is to be used to cancel
* a long running operation.
*/
protected Control getCancelComponent() {
return fCancelButton;
}
}