/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.search.internal.ui.util;
import com.google.dart.tools.search.internal.ui.SearchMessages;
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.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 java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
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<Button>();
}
/**
* 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;
}
//---- Hooks to reimplement in subclasses -----------------------------------
@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--;
}
}
/**
* 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);
}
}
/**
* @param enable Use the embedded progress monitor part
*/
public void setUseEmbeddedProgressMonitorPart(boolean enable) {
fUseEmbeddedProgressMonitorPart = enable;
}
//---- UI creation ----------------------------------------------------------
/**
* 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;
}
@Override
protected void buttonPressed(int buttonId) {
switch (buttonId) {
case IDialogConstants.CANCEL_ID:
if (fActiveRunningOperations == 0) {
close();
}
break;
default:
if (performAction(buttonId)) {
close();
}
}
}
protected Button createActionButton(Composite parent, int id, String label, boolean defaultButton) {
Button actionButton = createButton(parent, id, label, defaultButton);
fActionButtons.add(actionButton);
return actionButton;
}
/**
* 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);
}
/**
* 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;
}
//---- Setters and Getters --------------------------------------------------
/**
* Create the page area.
*
* @param parent The parent composite
* @return The created control
*/
protected abstract Control createPageArea(Composite parent);
//---- Operation stuff ------------------------------------------------------
/**
* @return Returns the cancel component that is to be used to cancel a long running operation.
*/
protected Control getCancelComponent() {
return fCancelButton;
}
@Override
protected void handleShellCloseEvent() {
if (okToClose()) {
super.handleShellCloseEvent();
}
}
@Override
protected boolean isResizable() {
return true;
}
/**
* 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;
}
//---- UI state save and restoring ---------------------------------------------
/**
* 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;
}
/*
* Restores the enable state of the given control.
*/
protected void restoreEnableState(Control w, @SuppressWarnings("rawtypes") HashMap h) {
if (!w.isDisposed()) {
Boolean b = (Boolean) h.get(w);
if (b != null) {
w.setEnabled(b.booleanValue());
}
}
}
/**
* 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("rawtypes")
HashMap state = (HashMap) 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 MessageDialog createClosingDialog() {
MessageDialog result = new MessageDialog(
getShell(),
SearchMessages.SearchDialogClosingDialog_title,
null,
SearchMessages.SearchDialogClosingDialog_message,
MessageDialog.QUESTION,
new String[] {IDialogConstants.OK_LABEL},
0);
return result;
}
private void restoreUIState(@SuppressWarnings("rawtypes") HashMap 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();
}
private void saveEnableStateAndSet(Control w, HashMap<Object, Object> h, boolean enabled) {
if (!w.isDisposed()) {
h.put(w, new Boolean(w.isEnabled()));
w.setEnabled(enabled);
}
}
private HashMap<Object, Object> saveUIState(boolean keepCancelEnabled) {
HashMap<Object, Object> savedState = new HashMap<Object, Object>(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 setDisplayCursor(Display d, Cursor c) {
Shell[] shells = d.getShells();
for (int i = 0; i < shells.length; i++) {
shells[i].setCursor(c);
}
}
}