[UI-Dialogs] Revise ExtStatusDialog

  - Add nullable annotations
  - Add .clearStatus avoiding NPE
  - Make use of cancel button by ProgressMonitorPart

Signed-off-by: Stephan Wahlbrink <sw@wahlbrink.eu>
diff --git a/ecommons/org.eclipse.statet.ecommons.runtime.core/src/org/eclipse/statet/ecommons/runtime/core/StatusChangeListener.java b/ecommons/org.eclipse.statet.ecommons.runtime.core/src/org/eclipse/statet/ecommons/runtime/core/StatusChangeListener.java
index 44c0068..eac5bd5 100644
--- a/ecommons/org.eclipse.statet.ecommons.runtime.core/src/org/eclipse/statet/ecommons/runtime/core/StatusChangeListener.java
+++ b/ecommons/org.eclipse.statet.ecommons.runtime.core/src/org/eclipse/statet/ecommons/runtime/core/StatusChangeListener.java
@@ -16,7 +16,10 @@
 
 import org.eclipse.core.runtime.IStatus;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 
+
+@NonNullByDefault
 public interface StatusChangeListener {
 	
 	
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/dialogs/DialogUtils.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/dialogs/DialogUtils.java
index 09759a3..1681d4b 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/dialogs/DialogUtils.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/dialogs/DialogUtils.java
@@ -35,6 +35,8 @@
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 import org.eclipse.statet.jcommons.lang.Nullable;
 
+import org.eclipse.statet.ecommons.ui.swt.ControlEnableStates;
+
 
 /**
  * Util methods for dialogs
@@ -45,7 +47,7 @@
 	
 	public static final int HISTORY_MAX= 25;
 	
-	private static final String[] EMPTY_ARRAY_SETTING= new String[0];
+	private static final @NonNull String[] EMPTY_ARRAY_SETTING= new @NonNull String[0];
 	
 	
 	public static IDialogSettings getDialogSettings(final Bundle bundle, final String dialogId) {
@@ -85,7 +87,7 @@
 	 */
 	public static void saveHistorySettings(final IDialogSettings settings, final String key,
 			final String newItem) {
-		final String[] items= combineHistoryItems(settings.getArray(key), newItem);
+		final @NonNull String[] items= combineHistoryItems(settings.getArray(key), newItem);
 		settings.put(key, items);
 	}
 	
@@ -95,7 +97,8 @@
 	 * @param existingItems optional array of existing items
 	 * @param newItem optional new item
 	 */
-	public static String[] combineHistoryItems(final String[] existingItems, final String newItem) {
+	public static @NonNull String[] combineHistoryItems(
+			final @NonNull String @Nullable [] existingItems, final @Nullable String newItem) {
 		final LinkedHashSet<String> history= new LinkedHashSet<>(HISTORY_MAX);
 		if (newItem != null && newItem.length() > 0) {
 			history.add(newItem);
@@ -108,7 +111,7 @@
 		return history.toArray(new @NonNull String[history.size()]);
 	}
 	
-	public static String[] noNull(final String @Nullable [] array) {
+	public static @NonNull String[] noNull(final @NonNull String @Nullable [] array) {
 		return (array != null) ? array : EMPTY_ARRAY_SETTING;
 	}
 	
@@ -117,8 +120,7 @@
 	 * Recursively enables/disables all controls and their children.
 	 * {@link Control#setEnabled(boolean)}
 	 * 
-	 * See {@link org.eclipse.jface.dialogs.ControlEnableState ControlEnableState}
-	 * if saving state is required.
+	 * See {@link ControlEnableStates} if saving state is required.
 	 * 
 	 * @param control list of controls
 	 * @param exceptions
@@ -126,17 +128,14 @@
 	 */
 	public static void setEnabled(final List<? extends Control> controls,
 			final @Nullable List<? extends Control> exceptions, final boolean enable) {
-		for (final Control control : controls) {
+		for (final var control : controls) {
 			if ((exceptions != null && exceptions.contains(control))) {
 				continue;
 			}
 			control.setEnabled(enable);
 			if (control instanceof Composite) {
-				final Composite c= (Composite) control;
-				final Control[] children= c.getChildren();
-				if (children.length > 0) {
-					setEnabled(children, exceptions, enable);
-				}
+				final var composite= (Composite) control;
+				setEnabled(ImCollections.newList(composite.getChildren()), exceptions, enable);
 			}
 		}
 	}
@@ -145,14 +144,13 @@
 	 * Recursively enables/disables all controls and their children.
 	 * {@link Control#setEnabled(boolean)}
 	 * 
-	 * See {@link org.eclipse.jface.dialogs.ControlEnableState ControlEnableState}
-	 * if saving state is required.
+	 * See {@link ControlEnableStates} if saving state is required.
 	 * 
 	 * @param control array of controls
 	 * @param exceptions
 	 * @param enable
 	 */
-	public static void setEnabled(final Control[] controls,
+	public static void setEnabled(final @NonNull Control[] controls,
 			final @Nullable List<? extends Control> exceptions, final boolean enable) {
 		setEnabled(ImCollections.newList(controls), exceptions, enable);
 	}
@@ -161,8 +159,7 @@
 	 * Recursively enables/disables all controls and their children.
 	 * {@link Control#setEnabled(boolean)}
 	 * 
-	 * See {@link org.eclipse.jface.dialogs.ControlEnableState ControlEnableState}
-	 * if saving state is required.
+	 * See {@link ControlEnableStates} if saving state is required.
 	 * 
 	 * @param control a control
 	 * @param exceptions
@@ -184,35 +181,19 @@
 	 */
 	public static void setVisible(final List<? extends Control> controls,
 			final @Nullable List<? extends Control> exceptions, final boolean enable) {
-		for (final Control control : controls) {
+		for (final var control : controls) {
 			if ((exceptions != null && exceptions.contains(control))) {
 				continue;
 			}
 			control.setVisible(enable);
 			if (control instanceof Composite) {
-				final Composite c= (Composite) control;
-				final Control[] children= c.getChildren();
-				if (children.length > 0) {
-					setVisible(children, exceptions, enable);
-				}
+				final var composite= (Composite)control;
+				setVisible(ImCollections.newList(composite.getChildren()), exceptions, enable);
 			}
 		}
 	}
 	
 	/**
-	 * Recursively sets visible/invisible to all controls and their children.
-	 * {@link Control#setVisible(boolean)}
-	 * 
-	 * @param control array of controls
-	 * @param exceptions
-	 * @param enable
-	 */
-	public static void setVisible(final Control[] controls,
-			final @Nullable List<? extends Control> exceptions, final boolean enable) {
-		setVisible(ImCollections.newList(controls), exceptions, enable);
-	}
-	
-	/**
 	 * Recursively sets visible/invisible to the control and its children.
 	 * {@link Control#setVisible(boolean)}
 	 * 
@@ -230,7 +211,7 @@
 		int closest= Integer.MAX_VALUE;
 		
 		final Point toFind= Geometry.centerPoint(rectangle);
-		final Monitor[] monitors= toSearch.getMonitors();
+		final var monitors= toSearch.getMonitors();
 		Monitor result= monitors[0];
 		
 		for (int idx= 0; idx < monitors.length; idx++) {
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/dialogs/ExtStatusDialog.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/dialogs/ExtStatusDialog.java
index 2a1dcee..8e6c0a7 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/dialogs/ExtStatusDialog.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/dialogs/ExtStatusDialog.java
@@ -14,19 +14,23 @@
 
 package org.eclipse.statet.ecommons.ui.dialogs;
 
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
+
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
-import org.eclipse.jface.dialogs.ControlEnableState;
 import org.eclipse.jface.dialogs.Dialog;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.StatusDialog;
 import org.eclipse.jface.operation.IRunnableContext;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.operation.ModalContext;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.util.Util;
 import org.eclipse.jface.wizard.ProgressMonitorPart;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Point;
@@ -38,17 +42,25 @@
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.PlatformUI;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.databinding.jface.DataBindingSupport;
 import org.eclipse.statet.ecommons.runtime.core.StatusChangeListener;
+import org.eclipse.statet.ecommons.ui.swt.ControlEnableStates;
 import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
 
 
+@NonNullByDefault
 public class ExtStatusDialog extends StatusDialog implements IRunnableContext {
 	
 	
-	protected final static int WITH_RUNNABLE_CONTEXT = 1 << 0;
-	protected final static int WITH_DATABINDING_CONTEXT = 1 << 1;
-	protected final static int SHOW_INITIAL_STATUS = 1 << 2;
+	protected static final int WITH_RUNNABLE_CONTEXT= 1 << 0;
+	protected static final int WITH_DATABINDING_CONTEXT= 1 << 1;
+	protected static final int SHOW_INITIAL_STATUS= 1 << 2;
+	
+	private static final IStatus NO_STATUS= new Status(IStatus.OK, Policy.JFACE, IStatus.OK,
+			Util.ZERO_LENGTH_STRING, null );
 	
 	
 	public class StatusUpdater implements StatusChangeListener {
@@ -61,37 +73,43 @@
 	}
 	
 	
-	private final int fOptions;
+	private final int options;
 	
-	private Composite fProgressComposite;
-	private ProgressMonitorPart fProgressMonitorPart;
-	private Button fProgressMonitorCancelButton;
 	
-	private int fActiveRunningOperations;
+	/*- WITH_RUNNABLE_CONTEXT -*/
 	
-	private Control fProgressLastFocusControl;
-	private ControlEnableState fProgressLastContentEnableState;
-	private Control[] fProgressLastButtonControls;
-	private boolean[] fProgressLastButtonEnableStates;
+	private Composite progressComposite= nonNullLateInit();
+	private ProgressMonitorPart progressMonitorPart= nonNullLateInit();
 	
-	protected DataBindingSupport fDataBinding;
+	private int activeRunningOperations;
+	
+	private @Nullable Control progressLastFocusControl;
+	private @Nullable ControlEnableStates progressLastContentEnableState;
+	
+	
+	/*- WITH_DATABINDING_CONTEXT -*/
+	
+	private DataBindingSupport dataBinding= nonNullLateInit();
 	
 	
 	/**
 	 * @see StatusDialog#StatusDialog(Shell)
 	 */
-	public ExtStatusDialog(final Shell parent) {
+	public ExtStatusDialog(final @Nullable Shell parent) {
 		this(parent, 0);
 	}
 	
 	/**
 	 * @see StatusDialog#StatusDialog(Shell)
 	 * 
-	 * @param withRunnableContext create elements to provide {@link IRunnableContext}
+	 * @param options
+	 * @see #WITH_RUNNABLE_CONTEXT
+	 * @see #WITH_DATABINDING_CONTEXT
+	 * @see #SHOW_INITIAL_STATUS
 	 */
-	public ExtStatusDialog(final Shell parent, final int options) {
+	public ExtStatusDialog(final @Nullable Shell parent, final int options) {
 		super(parent);
-		fOptions = options;
+		this.options= options;
 	}
 	
 	
@@ -102,8 +120,8 @@
 	
 	@Override
 	protected Point getInitialSize() {
-		final Point savedSize = super.getInitialSize();
-		final Point minSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
+		final Point savedSize= super.getInitialSize();
+		final Point minSize= getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
 		return new Point(Math.max(savedSize.x, minSize.x), Math.max(savedSize.y, minSize.y));
 	}
 	
@@ -111,47 +129,47 @@
 	public void create() {
 		// E-3.6 Eclipse bug fixed?
 		super.create();
-		final Button button = getButton(IDialogConstants.OK_ID);
-		final Shell shell = getShell();
+		final Button button= getButton(IDialogConstants.OK_ID);
+		final Shell shell= getShell();
 		if (button != null && shell != null && !shell.isDisposed()) {
 			shell.setDefaultButton(button);
 		}
 		
-		if ((fOptions & WITH_DATABINDING_CONTEXT) != 0) {
+		if ((this.options & WITH_DATABINDING_CONTEXT) != 0) {
 			initBindings();
 		}
 	}
 	
 	protected void initBindings() {
-		final DataBindingSupport databinding = new DataBindingSupport(getDialogArea());
+		final DataBindingSupport databinding= new DataBindingSupport(getDialogArea());
 		addBindings(databinding);
 		databinding.installStatusListener(new StatusUpdater());
-		if ((fOptions & SHOW_INITIAL_STATUS) == 0) {
-			final IStatus status = getStatus();
+		if ((this.options & SHOW_INITIAL_STATUS) == 0) {
+			final IStatus status= getStatus();
 			updateStatus(Status.OK_STATUS);
 			updateButtonsEnableState(status);
 		}
-		fDataBinding = databinding;
+		this.dataBinding= databinding;
 	}
 	
 	protected void addBindings(final DataBindingSupport db) {
 	}
 	
 	protected DataBindingSupport getDataBinding() {
-		return fDataBinding;
+		return this.dataBinding;
 	}
 	
 	@Override
 	protected Control createButtonBar(final Composite parent) {
-		final Composite composite = (Composite) super.createButtonBar(parent);
-		final GridLayout layout = (GridLayout) composite.getLayout();
-		layout.verticalSpacing = 0;
+		final Composite composite= (Composite)super.createButtonBar(parent);
+		final GridLayout layout= (GridLayout)composite.getLayout();
+		layout.verticalSpacing= 0;
 		
-		if ((fOptions & WITH_RUNNABLE_CONTEXT) != 0) {
-			final Composite monitorComposite = createMonitorComposite(composite);
-			final Control[] children = composite.getChildren();
-			layout.numColumns = 3;
-			((GridData) children[0].getLayoutData()).horizontalSpan++;
+		if ((this.options & WITH_RUNNABLE_CONTEXT) != 0) {
+			final Composite monitorComposite= createMonitorComposite(composite);
+			final Control[] children= composite.getChildren();
+			layout.numColumns= 3;
+			((GridData)children[0].getLayoutData()).horizontalSpan++;
 			monitorComposite.moveBelow(children[1]);
 			monitorComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
 		}
@@ -160,91 +178,100 @@
 	}
 	
 	private Composite createMonitorComposite(final Composite parent) {
-		fProgressComposite = new Composite(parent, SWT.NULL);
-		final GridLayout layout = LayoutUtils.newCompositeGrid(2);
-		layout.marginLeft = LayoutUtils.defaultHMargin();
-		fProgressComposite.setLayout(layout);
+		this.progressComposite= new Composite(parent, SWT.NULL);
+		final GridLayout layout= LayoutUtils.newCompositeGrid(2);
+		layout.marginLeft= LayoutUtils.defaultHMargin();
+		this.progressComposite.setLayout(layout);
 		
-		fProgressMonitorPart = new ProgressMonitorPart(fProgressComposite, null);
-		fProgressMonitorPart.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
-		fProgressMonitorCancelButton = createButton(fProgressComposite, 1000, IDialogConstants.CANCEL_LABEL, true);
+		this.progressMonitorPart= new ProgressMonitorPart(this.progressComposite, null, true);
+		this.progressMonitorPart.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
 		
-		Dialog.applyDialogFont(fProgressComposite);
-		fProgressComposite.setVisible(false);
-		return fProgressComposite;
+		Dialog.applyDialogFont(this.progressComposite);
+		this.progressComposite.setVisible(false);
+		return this.progressComposite;
 	}
+	
 	@Override
-	public void run(final boolean fork, final boolean cancelable, final IRunnableWithProgress runnable) throws InvocationTargetException, InterruptedException {
-		if ((fOptions & WITH_RUNNABLE_CONTEXT) == 0) {
+	public void run(final boolean fork, final boolean cancelable,
+			final IRunnableWithProgress runnable) throws InvocationTargetException, InterruptedException {
+		if ((this.options & WITH_RUNNABLE_CONTEXT) == 0) {
 			throw new UnsupportedOperationException();
 		}
 		if (getShell() != null && getShell().isVisible()) {
-			if (fActiveRunningOperations == 0) {
+			if (this.activeRunningOperations == 0) {
 				// Save control state
-				fProgressLastFocusControl = getShell().getDisplay().getFocusControl();
-				if (fProgressLastFocusControl != null && fProgressLastFocusControl.getShell() != getShell()) {
-					fProgressLastFocusControl = null;
+				Control focusControl= getShell().getDisplay().getFocusControl();
+				if (focusControl != null && focusControl.getShell() != getShell()) {
+					focusControl= null;
 				}
+				this.progressLastFocusControl= focusControl;
 				
-				fProgressLastContentEnableState = ControlEnableState.disable(getDialogArea());
-				final List<Control> buttons= new ArrayList<>();
+				final List<Control> disable= new ArrayList<>();
+				disable.add(nonNullAssert(getDialogArea()));
 				for (final Control child : getButton(IDialogConstants.OK_ID).getParent().getChildren()) {
 					if (child instanceof Button) {
-						buttons.add(child);
+						disable.add(child);
 					}
 				}
-				fProgressLastButtonControls = buttons.toArray(new Control[buttons.size()]);
-				fProgressLastButtonEnableStates = new boolean[fProgressLastButtonControls.length];
-				for (int i = 0; i < fProgressLastButtonControls.length; i++) {
-					fProgressLastButtonEnableStates[i] = fProgressLastButtonControls[i].getEnabled();
-					fProgressLastButtonControls[i].setEnabled(false);
-				}
+				this.progressLastContentEnableState= ControlEnableStates.disable(disable);
 				
 				// Enable monitor
-				fProgressMonitorCancelButton.setEnabled(cancelable);
-				fProgressMonitorPart.attachToCancelComponent(fProgressMonitorCancelButton);
-				fProgressComposite.setVisible(true);
-				fProgressMonitorCancelButton.setFocus();
-				
+				this.progressMonitorPart.attachToCancelComponent(null);
+				this.progressComposite.setVisible(true);
 			}
 			
-			fActiveRunningOperations++;
+			this.activeRunningOperations++;
 			try {
-				ModalContext.run(runnable, fork, fProgressMonitorPart, getShell().getDisplay());
+				ModalContext.run(runnable, fork, this.progressMonitorPart, getShell().getDisplay());
 			} 
 			finally {
-				fActiveRunningOperations--;
+				this.activeRunningOperations--;
 				
-				if (fActiveRunningOperations == 0 && getShell() != null) {
-					fProgressComposite.setVisible(false);
-					fProgressLastContentEnableState.restore();
-					for (int i = 0; i < fProgressLastButtonControls.length; i++) {
-						fProgressLastButtonControls[i].setEnabled(fProgressLastButtonEnableStates[i]);
+				if (this.activeRunningOperations == 0 && getShell() != null) {
+					this.progressComposite.setVisible(false);
+					this.progressMonitorPart.removeFromCancelComponent(null);
+					
+					final ControlEnableStates contentEnableState= this.progressLastContentEnableState;
+					if (contentEnableState != null) {
+						this.progressLastContentEnableState= null;
+						contentEnableState.restore();
 					}
 					
-					fProgressMonitorPart.removeFromCancelComponent(fProgressMonitorCancelButton);
-					if (fProgressLastFocusControl != null) {
-						fProgressLastFocusControl.setFocus();
+					final Control focusControl= this.progressLastFocusControl;
+					if (focusControl != null) {
+						this.progressLastFocusControl= null;
+						focusControl.setFocus();
 					}
 				}
 			}
-		} 
+		}
 		else {
 			PlatformUI.getWorkbench().getProgressService().run(fork, cancelable, runnable);
 		}
 	}
 	
+	protected boolean isOperationRunning() {
+		return (this.activeRunningOperations > 0);
+	}
+	
+	
+	protected void clearStatus() {
+		updateStatus(NO_STATUS);
+	}
+	
 	@Override
 	protected void updateButtonsEnableState(final IStatus status) {
-		super.updateButtonsEnableState(status);
-		if (fActiveRunningOperations > 0) {
-			final Button okButton = getButton(IDialogConstants.OK_ID);
-			for (int i = 0; i < fProgressLastButtonControls.length; i++) {
-				if (fProgressLastButtonControls[i] == okButton) {
-					fProgressLastButtonEnableStates[i] = okButton.isEnabled();
-				}
+		if (isOperationRunning()) {
+			final ControlEnableStates contentEnableState= this.progressLastContentEnableState;
+			final Button okButton= getButton(IDialogConstants.OK_ID);
+			if (contentEnableState != null
+					&& okButton != null && !okButton.isDisposed() ) {
+				contentEnableState.updateState(okButton, false, !status.matches(IStatus.ERROR));
 			}
 		}
+		else {
+			super.updateButtonsEnableState(status);
+		}
 	}
 	
 }
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/ControlEnableStates.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/ControlEnableStates.java
new file mode 100644
index 0000000..b8a4284
--- /dev/null
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/ControlEnableStates.java
@@ -0,0 +1,180 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ecommons.ui.swt;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.statet.jcommons.collections.ImCollection;
+import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.collections.ImIdentityList;
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+
+/**
+ * Helper class to save the enable/disable state of controls including their children.
+ */
+@NonNullByDefault
+public class ControlEnableStates {
+	
+	
+	public static ControlEnableStates disable(final Collection<Control> controls,
+			final Collection<Control> exceptions) {
+		return new ControlEnableStates(
+				ImCollections.toList(controls), ImCollections.toIdentityList(exceptions),
+				false );
+	}
+	
+	public static ControlEnableStates disable(final Collection<Control> controls) {
+		return new ControlEnableStates(
+				ImCollections.toList(controls), ImCollections.emptyIdentityList(),
+				false );
+	}
+	
+	public static ControlEnableStates disable(final Control control,
+			final Collection<Control> exceptions) {
+		return new ControlEnableStates(
+				ImCollections.newList(control), ImCollections.toIdentityList(exceptions),
+				false );
+	}
+	
+	public static ControlEnableStates disable(final Control control) {
+		return new ControlEnableStates(
+				ImCollections.newList(control), ImCollections.emptyIdentityList(),
+				false );
+	}
+	
+	public static ControlEnableStates enable(final Collection<Control> controls,
+			final Collection<Control> exceptions) {
+		return new ControlEnableStates(
+				ImCollections.toList(controls), ImCollections.toIdentityList(exceptions),
+				true );
+	}
+	
+	public static ControlEnableStates enable(final Collection<Control> controls) {
+		return new ControlEnableStates(
+				ImCollections.toList(controls), ImCollections.emptyIdentityList(),
+				true );
+	}
+	
+	public static ControlEnableStates enable(final Control control,
+			final Collection<Control> exceptions) {
+		return new ControlEnableStates(
+				ImCollections.newList(control), ImCollections.toIdentityList(exceptions),
+				true );
+	}
+	
+	public static ControlEnableStates enable(final Control control) {
+		return new ControlEnableStates(
+				ImCollections.newList(control), ImCollections.emptyIdentityList(),
+				true );
+	}
+	
+	
+	private static class State {
+		
+		protected final Control control;
+		
+		protected boolean enabled;
+		
+		public State(final Control control, final boolean enabled) {
+			this.control= control;
+			this.enabled= enabled;
+		}
+		
+	}
+	
+	
+	private final ImIdentityList<Control> exceptions;
+	
+	private final ImList<State> states;
+	
+	
+	private ControlEnableStates(
+			final ImList<Control> controls, final ImIdentityList<Control> exceptions,
+			final boolean enabled) {
+		this.exceptions= exceptions;
+		final List<State> states= new ArrayList<>();
+		init(controls, enabled, states);
+		this.states= ImCollections.toList(states);
+	}
+	
+	
+	private void init(
+			final ImList<? extends Control> controls, final boolean enabled,
+			final List<State> states) {
+		for (final var control : controls) {
+			if (this.exceptions.contains(control)) {
+				continue;
+			}
+			if (control instanceof Composite) {
+				final var composite= (Composite)control;
+				init(ImCollections.newList(composite.getChildren()), enabled, states);
+			}
+			states.add(new State(control, control.getEnabled()));
+			control.setEnabled(enabled);
+		}
+	}
+	
+	private @Nullable State getState(final Control control) {
+		for (final var state : this.states) {
+			if (state.control == control) {
+				return state;
+			}
+		}
+		return null;
+	}
+	
+	
+	public void updateState(final ImCollection<? extends Control> controls, final boolean recursively,
+			final boolean enabled) {
+		for (final var control : controls) {
+			if (this.exceptions.contains(control)) {
+				continue;
+			}
+			final var state= getState(control);
+			if (state == null) {
+				continue;
+			}
+			if (recursively && control instanceof Composite) {
+				final var composite= (Composite)control;
+				updateState(ImCollections.newList(composite.getChildren()), recursively, enabled);
+			}
+			state.enabled= enabled;
+		}
+	}
+	
+	public void updateState(final Control control, final boolean recursively,
+			final boolean enabled) {
+		updateState(ImCollections.newList(control), recursively, enabled);
+	}
+	
+	
+	public void restore() {
+		for (final var state : this.states) {
+			if (state.control.isDisposed()) {
+				continue;
+			}
+			state.control.setEnabled(state.enabled);
+		}
+	}
+	
+}