/*******************************************************************************
 * Copyright (c) 2000, 2016 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 Corporation - initial API and implementation
 *     Benjamin Muskalla -Bug 29633
 *     Helena Halperin - Bug 298747
 *     Andrey Loskutov <loskutov@gmx.de> - Bug 378485, 460555, 463262
 *     Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654
 *     Patrik Suzzi <psuzzi@gmail.com> - Bug 486859
 *******************************************************************************/
package org.eclipse.ui.dialogs;

import static org.eclipse.jface.viewers.LabelProvider.createTextImageProvider;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.util.Util;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.osgi.util.TextProcessor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.program.Program;
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.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IFileEditorMapping;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.registry.EditorDescriptor;
import org.eclipse.ui.internal.registry.EditorRegistry;
import org.eclipse.ui.internal.registry.FileEditorMapping;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.statushandlers.StatusManager;
import org.osgi.framework.FrameworkUtil;

/**
 * This class is used to allow the user to select a dialog from the set of
 * internal and external editors.
 *
 * @since 3.3
 * @noextend This class is not intended to be subclassed by clients.
 */

public class EditorSelectionDialog extends Dialog {

	private static class TreeArrayContentProvider implements ITreeContentProvider {

		private static final Object[] EMPTY = new Object[0];

		@Override
		public Object[] getElements(Object inputElement) {
			if (inputElement == null || !inputElement.getClass().isArray()) {
				return EMPTY;
			}
			// see bug 9262 why we can't return the same array
			Object[] orig = (Object[]) inputElement;
			Object[] arr = new Object[orig.length];
			System.arraycopy(orig, 0, arr, 0, arr.length);
			return arr;
		}

		@Override
		public Object[] getChildren(Object parentElement) {
			return EMPTY;
		}

		@Override
		public Object getParent(Object element) {
			return null;
		}

		@Override
		public boolean hasChildren(Object element) {
			return false;
		}

	}

	private IEditorDescriptor selectedEditor;

	private IEditorDescriptor hiddenSelectedEditor;

	private Button externalButton;

	private FilteredTree editorTable;

	private Button browseExternalEditorsButton;

	private Button internalButton;

	private Button okButton;

	/**
	 * For internal use only.
	 *
	 * @noreference This field is not intended to be referenced by clients.
	 * @since 3.7
	 */
	protected static final String STORE_ID_INTERNAL_EXTERNAL = "EditorSelectionDialog.STORE_ID_INTERNAL_EXTERNAL";//$NON-NLS-1$

	private static final String STORE_ID_DESCR = "EditorSelectionDialog.STORE_ID_DESCR";//$NON-NLS-1$

	private static final String STORE_ID_FILE_EXTENSION = "EditorSelectionDialog.STORE_ID_FILE_EXTENSION";//$NON-NLS-1$

	private String message = WorkbenchMessages.EditorSelection_chooseAnEditor;

	// collection of IEditorDescriptor
	private IEditorDescriptor[] externalEditors;

	private IEditorDescriptor[] internalEditors;

	private IEditorDescriptor[] editorsToFilter;

	private DialogListener listener = new DialogListener();

	private ResourceManager resourceManager;

	private TreeViewer editorTableViewer;

	private String fileName;

	private Button rememberTypeButton;

	private Button rememberEditorButton;

	private static final String[] Executable_Filters;

	private static final int TABLE_WIDTH = 200;
	static {
		if (Util.isWindows()) {
			Executable_Filters = new String[] { "*.exe", "*.bat", "*.*" };//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		} else if (Util.isMac()) {
			Executable_Filters = new String[] { "*.app", "*" }; //$NON-NLS-1$ //$NON-NLS-2$
		} else {
			Executable_Filters = new String[] { "*" }; //$NON-NLS-1$
		}
	}

	/**
	 * Create an instance of this class.
	 *
	 * @param parentShell the parent shell
	 */
	public EditorSelectionDialog(Shell parentShell) {
		super(parentShell);
		resourceManager = new LocalResourceManager(JFaceResources.getResources(parentShell.getDisplay()));
	}

	/**
	 * This method is called if a button has been pressed.
	 */
	@Override
	protected void buttonPressed(int buttonId) {
		if (buttonId == IDialogConstants.OK_ID) {
			saveWidgetValues();
		}
		super.buttonPressed(buttonId);
	}

	/**
	 * Close the window.
	 */
	@Override
	public boolean close() {
		boolean result = super.close();
		resourceManager.dispose();
		resourceManager = null;
		return result;
	}

	@Override
	protected void configureShell(Shell shell) {
		super.configureShell(shell);
		shell.setText(WorkbenchMessages.EditorSelection_title);
		PlatformUI.getWorkbench().getHelpSystem().setHelp(shell, IWorkbenchHelpContextIds.EDITOR_SELECTION_DIALOG);
	}

	/**
	 * Creates and returns the contents of the upper part of the dialog (above the
	 * button bar).
	 *
	 * Subclasses should overide.
	 *
	 * @param parent the parent composite to contain the dialog area
	 * @return the dialog area control
	 */
	@Override
	protected Control createDialogArea(Composite parent) {
		Font font = parent.getFont();
		// create main group
		Composite contents = (Composite) super.createDialogArea(parent);
		((GridLayout) contents.getLayout()).numColumns = 2;

		// begin the layout
		Label textLabel = new Label(contents, SWT.WRAP);

		textLabel.setText(message);
		GridData data = new GridData();
		data.horizontalSpan = 2;
		data.horizontalAlignment = SWT.FILL;
		data.widthHint = TABLE_WIDTH;
		textLabel.setLayoutData(data);
		textLabel.setFont(font);

		Composite group = new Composite(contents, SWT.SHADOW_NONE);
		data = new GridData();
		data.grabExcessHorizontalSpace = true;
		data.horizontalAlignment = SWT.FILL;
		data.horizontalSpan = 2;
		group.setLayout(new RowLayout(SWT.HORIZONTAL));
		group.setLayoutData(data);

		internalButton = new Button(group, SWT.RADIO | SWT.LEFT);
		internalButton.setText(WorkbenchMessages.EditorSelection_internal);
		internalButton.addListener(SWT.Selection, listener);
		internalButton.setFont(font);

		externalButton = new Button(group, SWT.RADIO | SWT.LEFT);
		externalButton.setText(WorkbenchMessages.EditorSelection_external);
		externalButton.addListener(SWT.Selection, listener);
		externalButton.setFont(font);

		editorTable = new FilteredTree(contents, SWT.SINGLE | SWT.BORDER, new PatternFilter(), true);
		editorTableViewer = editorTable.getViewer();
		Tree tree = editorTableViewer.getTree();
		tree.addListener(SWT.Selection, listener);
		tree.addListener(SWT.DefaultSelection, listener);
		tree.addListener(SWT.MouseDoubleClick, listener);
		data = new GridData();
		data.widthHint = convertHorizontalDLUsToPixels(TABLE_WIDTH);
		data.horizontalAlignment = GridData.FILL;
		data.grabExcessHorizontalSpace = true;
		data.verticalAlignment = GridData.FILL;
		data.grabExcessVerticalSpace = true;
		data.horizontalSpan = 2;
		editorTable.setLayoutData(data);
		editorTable.setFont(font);
		data.heightHint = tree.getItemHeight() * 12;
		editorTableViewer.setContentProvider(new TreeArrayContentProvider());
		editorTableViewer.setLabelProvider(createTextImageProvider(element-> {
			IEditorDescriptor d = (IEditorDescriptor) element;
			return TextProcessor.process(d.getLabel(), "."); //$NON-NLS-1$
		}, element -> {
			IEditorDescriptor d = (IEditorDescriptor) element;
			return resourceManager.get(d.getImageDescriptor());
		}));

		browseExternalEditorsButton = new Button(contents, SWT.PUSH);
		browseExternalEditorsButton.setText(WorkbenchMessages.EditorSelection_browse);
		browseExternalEditorsButton.addListener(SWT.Selection, listener);
		data = new GridData();
		int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
		data.widthHint = Math.max(widthHint, browseExternalEditorsButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
		data.horizontalSpan = 2;
		browseExternalEditorsButton.setLayoutData(data);
		browseExternalEditorsButton.setFont(font);

		if (fileName != null) {

			rememberEditorButton = new Button(contents, SWT.CHECK | SWT.LEFT);
			rememberEditorButton.setText(NLS.bind(WorkbenchMessages.EditorSelection_rememberEditor, fileName));
			rememberEditorButton.addListener(SWT.Selection, listener);
			data = new GridData();
			data.horizontalSpan = 2;
			rememberEditorButton.setLayoutData(data);
			rememberEditorButton.setFont(font);

			String fileType = getFileType();
			if (!fileType.isEmpty()) {
				rememberTypeButton = new Button(contents, SWT.CHECK | SWT.LEFT);
				rememberTypeButton.setText(NLS.bind(WorkbenchMessages.EditorSelection_rememberType, fileType));
				rememberTypeButton.addListener(SWT.Selection, listener);
				data = new GridData();
				data.horizontalSpan = 2;
				rememberTypeButton.setLayoutData(data);
				rememberTypeButton.setFont(font);
			}
		}

		initializeSuggestion();
		restoreWidgetValues(); // Place buttons to the appropriate state

		// Run async to restore selection on *visible* dialog - otherwise three won't
		// scroll
		PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
			if (editorTable.isDisposed()) {
				return;
			}
			fillEditorTable();
			updateEnableState();
		});
		return contents;
	}

	private String getFileType() {
		if (fileName == null) {
			return ""; //$NON-NLS-1$
		}
		int lastDot = fileName.lastIndexOf('.');
		if (lastDot == -1 || lastDot >= fileName.length() - 1) {
			return ""; //$NON-NLS-1$
		}
		return fileName.substring(lastDot + 1, fileName.length());
	}

	/**
	 * Fills the editor table.
	 */
	protected void fillEditorTable() {
		IEditorDescriptor newSelection = selectedEditor;

		boolean showInternal = internalButton.getSelection();
		Object[] input = (Object[]) editorTableViewer.getInput();
		if (input != null) {
			// we are switching between external/internal editors
			boolean isShowingInternal = Arrays.equals(input, getInternalEditors());
			if (showInternal != isShowingInternal) {
				newSelection = hiddenSelectedEditor;
				if (!editorTableViewer.getSelection().isEmpty()) {
					hiddenSelectedEditor = (EditorDescriptor) editorTableViewer.getStructuredSelection()
							.getFirstElement();
				}
			}
		}

		editorTableViewer.setInput(showInternal ? getInternalEditors() : getExternalEditors());

		if (fileName != null && newSelection == null) {
			if (!showInternal) {
				newSelection = findBestExternalEditor();
			} else {
				newSelection = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(fileName);
			}
		}
		if (newSelection != null) {
			editorTableViewer.setSelection(new StructuredSelection(newSelection), true);
		}

		if (editorTableViewer.getSelection().isEmpty()) {
			// set focus to first element, but don't select it:
			Tree tree = editorTableViewer.getTree();
			if (tree.getItemCount() > 0) {
				tree.showItem(tree.getItem(0));
			}
		}
		editorTable.setFocus();
	}

	private static String getFileExtension(String fileName) {
		if (fileName == null) {
			return null;
		}
		int index = fileName.lastIndexOf('.');
		if (index != -1) {
			return fileName.substring(index);
		}
		return fileName;
	}

	/**
	 * Return the dialog store to cache values into
	 */
	protected IDialogSettings getDialogSettings() {
		IDialogSettings workbenchSettings = PlatformUI
				.getDialogSettingsProvider(FrameworkUtil.getBundle(EditorSelectionDialog.class)).getDialogSettings();
		IDialogSettings section = workbenchSettings.getSection("EditorSelectionDialog");//$NON-NLS-1$
		if (section == null) {
			section = workbenchSettings.addNewSection("EditorSelectionDialog");//$NON-NLS-1$
		}
		return section;
	}

	/**
	 * Get a list of registered programs from the OS
	 */
	protected IEditorDescriptor[] getExternalEditors() {
		if (externalEditors == null) {
			IProgressService ps = PlatformUI.getWorkbench().getService(IProgressService.class);
			// Since this can take a while, show the busy cursor.
			IRunnableWithProgress runnable = monitor -> {
				// Get the external editors available
				EditorRegistry reg = (EditorRegistry) WorkbenchPlugin.getDefault().getEditorRegistry();
				externalEditors = reg.getSortedEditorsFromOS();
				externalEditors = filterEditors(externalEditors);
			};
			try {
				// See bug 47556 - Program.getPrograms() requires a Display.getCurrent() != null
				ps.runInUI(PlatformUI.getWorkbench().getActiveWorkbenchWindow(), runnable, null);
			} catch (InvocationTargetException e) {
				Throwable cause = e.getCause();
				IStatus status;
				if (cause instanceof CoreException) {
					status = ((CoreException) cause).getStatus();
				} else {
					status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, "Error while retrieving native editors", //$NON-NLS-1$
							cause);
				}
				StatusManager.getManager().handle(status);
			} catch (InterruptedException e) {
				// Canceled by the user
			}

		}
		return externalEditors;
	}

	/**
	 * Returns an array of editors which have been filtered according to the array
	 * of editors in the editorsToFilter instance variable.
	 *
	 * @param editors an array of editors to filter
	 * @return a filtered array of editors
	 */
	protected IEditorDescriptor[] filterEditors(IEditorDescriptor[] editors) {
		if ((editors == null) || (editors.length < 1)) {
			return editors;
		}

		if ((editorsToFilter == null) || (editorsToFilter.length < 1)) {
			return editors;
		}

		List<IEditorDescriptor> filteredList = new ArrayList<>();
		for (IEditorDescriptor editor : editors) {
			boolean add = true;
			for (IEditorDescriptor element : editorsToFilter) {
				if (editor.getId().equals(element.getId())) {
					add = false;
				}
			}
			if (add) {
				filteredList.add(editor);
			}
		}

		return filteredList.toArray(new IEditorDescriptor[filteredList.size()]);
	}

	/**
	 * Returns the internal editors
	 */
	protected IEditorDescriptor[] getInternalEditors() {
		if (internalEditors == null) {
			EditorRegistry reg = (EditorRegistry) WorkbenchPlugin.getDefault().getEditorRegistry();
			internalEditors = reg.getSortedEditorsFromPlugins();
			internalEditors = filterEditors(internalEditors);
		}
		return internalEditors;
	}

	/**
	 * Return the editor the user selected
	 *
	 * @return the selected editor
	 */
	public IEditorDescriptor getSelectedEditor() {
		return selectedEditor;
	}

	/**
	 * Opens the prompt to select an external editor.
	 */
	protected void promptForExternalEditor() {
		FileDialog dialog = new FileDialog(getShell(), SWT.OPEN | SWT.PRIMARY_MODAL | SWT.SHEET);
		dialog.setFilterExtensions(Executable_Filters);
		String result = dialog.open();
		if (result != null) {
			EditorDescriptor editor = EditorDescriptor.createForProgram(result);

			/*
			 * add to our collection of cached external editors in case the user flips back
			 * and forth between internal/external
			 */
			IEditorDescriptor[] newEditors = new IEditorDescriptor[externalEditors.length + 1];
			System.arraycopy(externalEditors, 0, newEditors, 0, externalEditors.length);
			newEditors[newEditors.length - 1] = editor;
			externalEditors = newEditors;
			editorTableViewer.setInput(externalEditors);
			editorTableViewer.setSelection(new StructuredSelection(editor), true);
			editorTable.setFocus();
			selectedEditor = editor;
		}
	}

	/**
	 * Handle a double click event on the list
	 */
	protected void handleDoubleClickEvent() {
		buttonPressed(IDialogConstants.OK_ID);
	}

	private void initializeSuggestion() {
		if (fileName == null) {
			return;
		}
		IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry();
		IEditorDescriptor suggestion = editorRegistry.getDefaultEditor(fileName);
		if (suggestion != null && suggestion.isInternal()) {
			selectedEditor = suggestion;
		} else {
			selectedEditor = findBestExternalEditor();
		}
		boolean enableInternalList = selectedEditor == null || selectedEditor.isInternal();
		internalButton.setSelection(enableInternalList);
		externalButton.setSelection(!enableInternalList);
	}

	private IEditorDescriptor findBestExternalEditor() {
		if (fileName == null) {
			return null;
		}
		String extension = getFileExtension(fileName);
		Program program = Program.findProgram(extension);
		if (program != null) {
			for (IEditorDescriptor descriptor : getExternalEditors()) {
				if (descriptor instanceof EditorDescriptor
						&& program.equals(((EditorDescriptor) descriptor).getProgram())) {
					return descriptor;
				}
			}
		}
		return null;
	}

	/**
	 * Use the dialog store to restore widget values to the values that they held
	 * last time this wizard was used to completion, if the previous file has same
	 * extension.
	 */
	protected void restoreWidgetValues() {
		IDialogSettings settings = getDialogSettings();
		if (fileName == null || selectedEditor == null
				|| getFileExtension(fileName).equals(settings.get(STORE_ID_FILE_EXTENSION))) {
			boolean wasExternal = settings.getBoolean(STORE_ID_INTERNAL_EXTERNAL);
			internalButton.setSelection(!wasExternal);
			externalButton.setSelection(wasExternal);
			String id = settings.get(STORE_ID_DESCR);
			if (id != null) {
				IEditorDescriptor[] editors;
				if (wasExternal) {
					editors = getExternalEditors();
				} else {
					editors = getInternalEditors();
				}
				for (IEditorDescriptor desc : editors) {
					if (id.equals(desc.getId())) {
						selectedEditor = desc;
					}
				}
			}
		}
	}

	/**
	 * Since Finish was pressed, write widget values to the dialog store so that
	 * they will persist into the next invocation of this wizard page
	 */
	protected void saveWidgetValues() {
		IDialogSettings settings = getDialogSettings();
		// record whether use was viewing internal or external editors
		settings.put(STORE_ID_FILE_EXTENSION, getFileExtension(fileName));
		settings.put(STORE_ID_INTERNAL_EXTERNAL, !internalButton.getSelection());
		settings.put(STORE_ID_DESCR, selectedEditor.getId());
		String editorId = selectedEditor.getId();
		settings.put(STORE_ID_DESCR, editorId);
		boolean associateEditor = false;
		EditorRegistry reg = (EditorRegistry) WorkbenchPlugin.getDefault().getEditorRegistry();
		// remember editor for specific file
		if (rememberEditorButton != null && rememberEditorButton.getSelection()) {
			updateFileMappings(reg, true);
			reg.setDefaultEditor(fileName, selectedEditor);
			associateEditor = true;
		}
		// remember editor for given extension type
		if (rememberTypeButton != null && rememberTypeButton.getSelection()) {
			updateFileMappings(reg, false);
			reg.setDefaultEditor("*." + getFileType(), selectedEditor); //$NON-NLS-1$
			associateEditor = true;
		}
		if (associateEditor) {
			// bug 468906: always re-set editor mappings: this is needed to
			// rebuild internal editors map after setting the default editor
			List<IFileEditorMapping> newMappings = new ArrayList<>();
			newMappings.addAll(Arrays.asList(reg.getFileEditorMappings()));
			reg.setFileEditorMappings(newMappings.toArray(new FileEditorMapping[newMappings.size()]));
			reg.saveAssociations();
		}
	}

	/**
	 * Make sure EditorRegistry has editor mapping for the file name/type
	 */
	private void updateFileMappings(EditorRegistry reg, boolean useFileName) {
		IFileEditorMapping[] mappings = reg.getFileEditorMappings();
		boolean hasMapping = false;
		String fileType = getFileType();
		for (IFileEditorMapping mapping : mappings) {
			if (useFileName) {
				if (fileName.equals(mapping.getLabel())) {
					hasMapping = true;
					break;
				}
			} else if (fileType.equals(mapping.getExtension())) {
				hasMapping = true;
				break;
			}
		}
		if (hasMapping) {
			return;
		}
		FileEditorMapping mapping;
		if (useFileName) {
			mapping = new FileEditorMapping(fileName, null);
		} else {
			mapping = new FileEditorMapping(null, fileType);
		}
		List<IFileEditorMapping> newMappings = new ArrayList<>();
		newMappings.addAll(Arrays.asList(mappings));
		newMappings.add(mapping);
		FileEditorMapping[] array = newMappings.toArray(new FileEditorMapping[newMappings.size()]);
		reg.setFileEditorMappings(array);
	}

	/**
	 * Set the message displayed by this message dialog
	 *
	 * @param aMessage the message
	 */
	public void setMessage(String aMessage) {
		message = aMessage;
	}

	/**
	 * Set the file name which can be used to store the selected editor preference
	 *
	 * @param fileName the file name
	 * @since 3.107
	 */
	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	/**
	 * Set the editors which will not appear in the dialog.
	 *
	 * @param editors an array of editors
	 */
	public void setEditorsToFilter(IEditorDescriptor[] editors) {
		editorsToFilter = editors;
	}

	/**
	 * Update enabled state.
	 */
	protected void updateEnableState() {
		boolean enableExternal = externalButton.getSelection();
		browseExternalEditorsButton.setEnabled(enableExternal);
		updateOkButton();
	}

	@Override
	protected void createButtonsForButtonBar(Composite parent) {
		okButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
		createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
		// initially there is no selection so OK button should not be enabled
		okButton.setEnabled(false);

	}

	/**
	 * Update the button enablement state.
	 */
	protected void updateOkButton() {
		// Buttons are null during dialog creation
		if (okButton == null) {
			return;
		}
		// If there is no selection, do not enable OK button
		if (editorTableViewer.getSelection().isEmpty()) {
			okButton.setEnabled(false);
			return;
		}
		// At this point, there is a selection
		okButton.setEnabled(selectedEditor != null);
	}

	private class DialogListener implements Listener {

		@Override
		public void handleEvent(Event event) {
			if (event.type == SWT.MouseDoubleClick) {
				handleDoubleClickEvent();
				return;
			}
			if (event.widget == externalButton) {
				fillEditorTable();
			} else if (event.widget == browseExternalEditorsButton) {
				promptForExternalEditor();
			} else if (event.widget == editorTableViewer.getTree()) {
				if (!editorTableViewer.getSelection().isEmpty()) {
					selectedEditor = (EditorDescriptor) editorTableViewer.getStructuredSelection().getFirstElement();
				} else {
					selectedEditor = null;
					okButton.setEnabled(false);
				}
			}
			// 486859 both checked: checking one box unchecks the other
			if (rememberEditorButton != null && rememberTypeButton != null && rememberEditorButton.getSelection()
					&& rememberTypeButton.getSelection()) {
				if (event.widget == rememberEditorButton) {
					rememberTypeButton.setSelection(false);
				}
				if (event.widget == rememberTypeButton) {
					rememberEditorButton.setSelection(false);
				}
			}
			updateEnableState();
		}

	}

	@Override
	protected boolean isResizable() {
		return true;
	}
}
