/******************************************************************************* * Copyright (c) 2006, 2017 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 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 490755 *******************************************************************************/ package org.eclipse.debug.internal.ui.preferences; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchDelegate; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.core.LaunchManager; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.DefaultLabelProvider; import org.eclipse.debug.internal.ui.IDebugHelpContextIds; import org.eclipse.debug.internal.ui.SWTFactory; import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationPresentationManager; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.model.WorkbenchViewerComparator; /** * This class provides a preference page for selecting and changing preferred launch delegates for those of them * that have conflicting delegates. * * Delegates are considered to be conflicting if they are for the same launch configuration type, and apply to the same * mode sets. * * @since 3.3 */ public class LaunchersPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { /** * Class to collect and persist attributes to sufficiently describe a duplicate launch delegate */ class DuplicateDelegate { private ILaunchConfigurationType fType = null; private ILaunchDelegate[] fDelegates = null; private Set<String> fModes = null; public DuplicateDelegate(ILaunchConfigurationType type, ILaunchDelegate[] delegates, Set<String> modes) { fModes = modes; fType = type; fDelegates = delegates; } public ILaunchConfigurationType getType() { return fType; } public ILaunchDelegate[] getDelegates() { return fDelegates; } public Set<String> getModeSet() { return fModes; } } /** * label provider to extend the default one, provides labels to both the tree and table of this page */ class LabelProvider extends DefaultLabelProvider { @Override public String getText(Object element) { if(element instanceof ILaunchConfigurationType) { return super.getText(element); } else if(element instanceof DuplicateDelegate) { DuplicateDelegate dd = (DuplicateDelegate) element; return LaunchConfigurationPresentationManager.getDefault().getLaunchModeNames(dd.getModeSet()).toString(); } else if(element instanceof ILaunchDelegate){ return ((ILaunchDelegate) element).getName(); } return element.toString(); } } /** * This class is used to provide content to the tree */ class TreeProvider implements ITreeContentProvider { @Override public Object[] getChildren(Object parentElement) { if(parentElement instanceof ILaunchConfigurationType) { ILaunchConfigurationType type = (ILaunchConfigurationType) parentElement; Set<DuplicateDelegate> dupes = fDuplicates.get(type); if(dupes != null) { return dupes.toArray(); } return null; } return null; } @Override public boolean hasChildren(Object element) { return element instanceof ILaunchConfigurationType; } @Override public Object[] getElements(Object inputElement) { if(inputElement instanceof Map) { return ((Map<?, ?>) inputElement).keySet().toArray(); } return null; } @Override public Object getParent(Object element) {return null;} } private TreeViewer fTreeViewer = null; private CheckboxTableViewer fTableViewer = null; private Map<ILaunchConfigurationType, Set<DuplicateDelegate>> fDuplicates = null; private Map<DuplicateDelegate, ILaunchDelegate> fDupeSelections = null; private boolean fDirty = false; private Label fDescription = null; /** * Constructor */ public LaunchersPreferencePage() { setTitle(DebugPreferencesMessages.LaunchDelegatesPreferencePage_0); } @Override public void createControl(Composite parent) { super.createControl(parent); PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), IDebugHelpContextIds.LAUNCH_DELEGATES_PREFERENCE_PAGE); } @Override protected Control createContents(Composite parent) { Composite comp = SWTFactory.createComposite(parent, 2, 1, GridData.FILL_BOTH); SWTFactory.createWrapLabel(comp, DebugPreferencesMessages.LaunchDelegatesPreferencePage_1, 2, 300); boolean enabled = fDuplicates.size() > 0; if(!enabled) { SWTFactory.createVerticalSpacer(comp, 1); SWTFactory.createWrapLabel(comp, DebugPreferencesMessages.LaunchersPreferencePage_0, 2, 300); } SWTFactory.createVerticalSpacer(comp, 1); //tree Composite comp1 = SWTFactory.createComposite(comp, 1, 1, GridData.FILL_VERTICAL); SWTFactory.createLabel(comp1, DebugPreferencesMessages.LaunchDelegatesPreferencePage_2, 1); Tree tree = new Tree(comp1, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE); tree.setEnabled(enabled); tree.setFont(parent.getFont()); GridData gd = new GridData(GridData.FILL_BOTH); gd.grabExcessHorizontalSpace = false; tree.setLayoutData(gd); fTreeViewer = new TreeViewer(tree); fTreeViewer.setComparator(new WorkbenchViewerComparator()); fTreeViewer.setContentProvider(new TreeProvider()); fTreeViewer.setLabelProvider(new LabelProvider()); fTreeViewer.setInput(fDuplicates); fTreeViewer.expandToLevel(2); fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { Object obj = ((IStructuredSelection) event.getSelection()).getFirstElement(); if(obj instanceof DuplicateDelegate) { fTableViewer.setAllChecked(false); DuplicateDelegate dd = (DuplicateDelegate) obj; fTableViewer.setInput(dd.getDelegates()); fTableViewer.setSelection(null); obj = fDupeSelections.get(dd); if(obj != null) { fTableViewer.setChecked(obj, true); fTableViewer.setSelection(new StructuredSelection(obj)); } } else { fTableViewer.setInput(null); } } }); //table Composite comp2 = SWTFactory.createComposite(comp, comp.getFont(), 1, 1, GridData.FILL_BOTH); SWTFactory.createLabel(comp2, DebugPreferencesMessages.LaunchDelegatesPreferencePage_3, 1); Table table = new Table(comp2, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CHECK | SWT.SINGLE); table.setEnabled(enabled); table.setLayoutData(new GridData(GridData.FILL_BOTH)); table.setFont(parent.getFont()); fTableViewer = new CheckboxTableViewer(table); fTableViewer.setComparator(new WorkbenchViewerComparator()); fTableViewer.setLabelProvider(new LabelProvider()); fTableViewer.setContentProvider(new ArrayContentProvider()); fTableViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection ss = (IStructuredSelection) event.getSelection(); if(ss != null && !ss.isEmpty()) { ILaunchDelegate delegate = (ILaunchDelegate)ss.getFirstElement(); fDescription.setText(delegate.getDescription()); } else { fDescription.setText(IInternalDebugCoreConstants.EMPTY_STRING); } } }); fTableViewer.addCheckStateListener(new ICheckStateListener() { @Override public void checkStateChanged(CheckStateChangedEvent event) { fDirty = true; Object element = event.getElement(); boolean checked = event.getChecked(); //always set checked, this way users cannot 'undo' a change to selecting a preferred delegate //The story for this is that on startup if there are dupes, the user is prompted to pick a delegate, after that they cannot //return to a state of not being able to launch something, but can pick a different delegate fTableViewer.setCheckedElements(new Object[] {element}); //set the selection to be the checked element //https://bugs.eclipse.org/bugs/show_bug.cgi?id=233233 fTableViewer.setSelection(new StructuredSelection(element)); //persist the selection Object obj = ((IStructuredSelection) fTreeViewer.getSelection()).getFirstElement(); if(obj instanceof DuplicateDelegate) { fDupeSelections.remove(obj); if(checked) { fDupeSelections.put((DuplicateDelegate) obj, (ILaunchDelegate) element); } } } }); Group group = SWTFactory.createGroup(comp, DebugPreferencesMessages.LaunchDelegatesPreferencePage_4, 1, 2, GridData.FILL_BOTH); fDescription = SWTFactory.createWrapLabel(group, "", 1); //$NON-NLS-1$ return comp; } @Override public boolean performOk() { if(fDirty && fDupeSelections != null && fDupeSelections.size() > 0) { fDirty = false; DuplicateDelegate dd = null; ILaunchDelegate delegate = null; for (Iterator<DuplicateDelegate> iter = fDupeSelections.keySet().iterator(); iter.hasNext();) { dd = iter.next(); delegate = fDupeSelections.get(dd); try { dd.getType().setPreferredDelegate(dd.getModeSet(), delegate); } catch (CoreException e) {DebugUIPlugin.log(e);} } } return super.performOk(); } @Override public void init(IWorkbench workbench) { //init a listing of duplicate delegates arranged by type try { setPreferenceStore(DebugUIPlugin.getDefault().getPreferenceStore()); LaunchManager lm = (LaunchManager) DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType[] types = lm.getLaunchConfigurationTypes(); fDuplicates = new HashMap<ILaunchConfigurationType, Set<DuplicateDelegate>>(); fDupeSelections = new HashMap<DuplicateDelegate, ILaunchDelegate>(); ILaunchDelegate[] delegates = null; Set<Set<String>> modes = null; Set<String> modeset = null; Set<DuplicateDelegate> tmp = null; ILaunchDelegate prefdelegate = null; DuplicateDelegate dd = null; for(int i = 0; i < types.length; i++) { modes = types[i].getSupportedModeCombinations(); for (Iterator<Set<String>> iter = modes.iterator(); iter.hasNext();) { modeset = iter.next(); delegates = types[i].getDelegates(modeset); if(delegates.length > 1) { tmp = fDuplicates.get(types[i]); if(tmp == null) { tmp = new HashSet<DuplicateDelegate>(); } dd = new DuplicateDelegate(types[i], delegates, modeset); tmp.add(dd); fDuplicates.put(types[i], tmp); prefdelegate = types[i].getPreferredDelegate(modeset); if(prefdelegate != null) { fDupeSelections.put(dd, prefdelegate); } } } } } catch(CoreException e) {DebugUIPlugin.log(e);} } }