/******************************************************************************* * Copyright (c) 2009, 2012, 2016 QNX Software Systems 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: * QNX Software Systems - initial API and implementation * Freescale Semiconductor * SSI Schaefer *******************************************************************************/ package org.eclipse.debug.internal.ui.groups; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.internal.core.groups.GroupLaunchConfigurationDelegate; import org.eclipse.debug.internal.core.groups.GroupLaunchElement; import org.eclipse.debug.internal.core.groups.GroupLaunchElement.GroupElementPostLaunchAction; import org.eclipse.debug.internal.ui.DebugPluginImages; import org.eclipse.debug.internal.ui.DebugUIMessages; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; import org.eclipse.debug.internal.ui.SWTFactory; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.ILaunchConfigurationDialog; import org.eclipse.debug.ui.ILaunchConfigurationTab; import org.eclipse.jface.viewers.BaseLabelProvider; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ICheckStateProvider; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; 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.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.activities.WorkbenchActivityHelper; /** * Tab group for Launch Group. * * @since 3.12 */ public class GroupLaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup { static class ContentProvider implements IStructuredContentProvider, ITreeContentProvider { protected List<GroupLaunchElement> input; @Override public Object[] getElements(Object inputElement) { return getChildren(inputElement); } @Override public void dispose() { input = null; } @Override @SuppressWarnings("unchecked") // nothing we can do about this public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (newInput instanceof List<?>) { input = (List<GroupLaunchElement>) newInput; } } @Override public Object[] getChildren(Object parentElement) { return (parentElement == input) ? input.toArray() : null; } @Override public Object getParent(Object element) { return (element == input) ? null : input; } @Override public boolean hasChildren(Object element) { return (element == input) ? (input.size() > 0) : false; } } static class LabelProvider extends BaseLabelProvider implements ITableLabelProvider { @Override public Image getColumnImage(Object element, int columnIndex) { if (!(element instanceof GroupLaunchElement)) { return null; } if (columnIndex == 0) { GroupLaunchElement el = (GroupLaunchElement) element; if (el.data == null || !isValidLaunchReference(el.data)) { Image errorImage = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK); return errorImage; } try { String key = el.data.getType().getIdentifier(); return DebugPluginImages.getImage(key); } catch (CoreException e) { Image errorImage = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK); return errorImage; } } return null; } @Override public String getColumnText(Object element, int columnIndex) { if (!(element instanceof GroupLaunchElement)) { return null; } GroupLaunchElement el = (GroupLaunchElement) element; // launch name if (columnIndex == 0) { try { return (el.data != null) ? el.data.getType().getName() + "::" + el.name : el.name; //$NON-NLS-1$ } catch (CoreException e) { return el.name; } } // launch mode if (columnIndex == 1) { return el.mode + (el.adoptIfRunning ? DebugUIMessages.GroupLaunchConfigurationTabGroup_lblAdopt : ""); //$NON-NLS-1$ } // launch post action if (columnIndex == 2) { GroupElementPostLaunchAction action = el.action; switch (action) { case NONE: return ""; //$NON-NLS-1$ case WAIT_FOR_TERMINATION: return action.getDescription(); case DELAY: final Object actionParam = el.actionParam; return NLS.bind(DebugUIMessages.GroupLaunchConfigurationTabGroup_13, actionParam instanceof Integer ? Integer.toString((Integer) actionParam) : "?"); //$NON-NLS-1$ case OUTPUT_REGEXP: return NLS.bind(DebugUIMessages.GroupLaunchConfigurationTabGroup_0, el.actionParam); default: assert false : "new post launch action missing logic here"; //$NON-NLS-1$ return ""; //$NON-NLS-1$ } } return null; } } static class CheckStateProvider implements ICheckStateProvider { /* (non-Javadoc) * @see org.eclipse.jface.viewers.ICheckStateProvider#isChecked(java.lang.Object) */ @Override public boolean isChecked(Object element) { if (element instanceof GroupLaunchElement) { return ((GroupLaunchElement)element).enabled; } return false; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ICheckStateProvider#isGrayed(java.lang.Object) */ @Override public boolean isGrayed(Object element) { return false; } } static abstract class ButtonComposite extends Composite implements SelectionListener { Button upButton; Button downButton; Button addButton; Button deleteButton; Button editButton; public ButtonComposite(Composite parent, int style) { super(parent, style); setLayout(new GridLayout()); upButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_1); downButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_2); editButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_3); addButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_4); deleteButton = createPushButton(this, DebugUIMessages.GroupLaunchConfigurationTabGroup_5); } protected abstract void updateWidgetEnablement(); /** * Helper method to create a push button. * * @param parent * the parent control * @param key * the resource name used to supply the button's label text * @return Button */ protected Button createPushButton(Composite parent, String key) { Button button = SWTFactory.createPushButton(parent, key, null); button.addSelectionListener(this); return button; } @Override public void widgetDefaultSelected(SelectionEvent e) { // nothing } @Override public void widgetSelected(SelectionEvent e) { Widget widget = e.widget; if (widget == upButton) { upPressed(); } else if (widget == downButton) { downPressed(); } else if (widget == addButton) { addPressed(); } else if (widget == deleteButton) { deletePressed(); } else if (widget == editButton) { editPressed(); } } protected abstract void addPressed(); protected abstract void editPressed(); protected abstract void deletePressed(); protected abstract void downPressed(); protected abstract void upPressed(); } static class GroupLaunchTab extends AbstractLaunchConfigurationTab { protected CheckboxTreeViewer treeViewer; protected List<GroupLaunchElement> input = new ArrayList<GroupLaunchElement>(); /** * copy of the initial state of the configuration used for cycle * checking. This is not updated when the user changes settings! */ private ILaunchConfiguration self; @Override public void createControl(Composite parent) { Composite comp = new Composite(parent, SWT.NONE); setControl(comp); //comp.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN)); comp.setLayout(new GridLayout(2, false)); treeViewer = new CheckboxTreeViewer(comp, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION); Tree table = treeViewer.getTree(); table.setFont(parent.getFont()); treeViewer.setContentProvider(new ContentProvider()); treeViewer.setLabelProvider(new LabelProvider()); treeViewer.setCheckStateProvider(new CheckStateProvider()); table.setHeaderVisible(true); table.setLayoutData(new GridData(GridData.FILL_BOTH)); TreeColumn col1 = new TreeColumn(table, SWT.NONE); col1.setText(DebugUIMessages.GroupLaunchConfigurationTabGroup_6); col1.setWidth(300); TreeColumn col2 = new TreeColumn(table, SWT.NONE); col2.setText(DebugUIMessages.GroupLaunchConfigurationTabGroup_7); col2.setWidth(100); TreeColumn col3 = new TreeColumn(table, SWT.NONE); col3.setText(DebugUIMessages.GroupLaunchConfigurationTabGroup_12); col3.setWidth(100); treeViewer.setInput(input); final ButtonComposite buts = new ButtonComposite(comp, SWT.NONE) { @Override protected void addPressed() { GroupLaunchConfigurationSelectionDialog dialog = GroupLaunchConfigurationSelectionDialog.createDialog( treeViewer.getControl().getShell(), GroupLaunchElement.MODE_INHERIT, false, self); if (dialog.open() == Window.OK) { ILaunchConfiguration[] configs = dialog.getSelectedLaunchConfigurations(); if (configs.length < 1) { return; } for (ILaunchConfiguration config : configs) { GroupLaunchElement el = new GroupLaunchElement(); input.add(el); el.index = input.size() - 1; el.enabled = true; applyFromDialog(el, dialog, config); treeViewer.refresh(true); treeViewer.setChecked(el, el.enabled); } updateWidgetEnablement(); updateLaunchConfigurationDialog(); } } @Override protected void updateWidgetEnablement(){ downButton.setEnabled(isDownEnabled()); upButton.setEnabled(isUpEnabled()); int selectionCount = getSelectionCount(); editButton.setEnabled(selectionCount == 1); deleteButton.setEnabled(selectionCount > 0); } @Override protected void editPressed() { int index = getSingleSelectionIndex(); if (index < 0) { return; } GroupLaunchElement el = input.get(index); GroupLaunchConfigurationSelectionDialog dialog = GroupLaunchConfigurationSelectionDialog.createDialog( treeViewer.getControl().getShell(), el.mode, true, self); if (isValidLaunchReference(el.data)) { dialog.setInitialSelection(el); } if (dialog.open() == Window.OK) { ILaunchConfiguration[] confs = dialog.getSelectedLaunchConfigurations(); if (confs.length < 0) { return; } assert confs.length == 1 : "invocation of the dialog for editing an entry sholdn't allow OK to be hit if the user chooses multiple launch configs in the dialog"; //$NON-NLS-1$ applyFromDialog(el, dialog, confs[0]); treeViewer.refresh(true); updateWidgetEnablement(); updateLaunchConfigurationDialog(); } } private void applyFromDialog(GroupLaunchElement el, GroupLaunchConfigurationSelectionDialog dialog, ILaunchConfiguration config) { el.name = config.getName(); el.data = config; el.mode = dialog.getMode(); el.action = dialog.getAction(); el.adoptIfRunning = dialog.getAdoptIfRunning(); el.actionParam = dialog.getActionParam(); } @Override protected void deletePressed() { int[] indices = getMultiSelectionIndices(); if (indices.length < 1) { return; } // need to delete from high to low for (int i = indices.length - 1; i >= 0; i--) { input.remove(indices[i]); } treeViewer.refresh(true); updateWidgetEnablement(); updateLaunchConfigurationDialog(); } /** * @return the index of the selection if a single item is * selected. If zero or multiple are selected, -1 is * returned */ private int getSingleSelectionIndex() { StructuredSelection sel = (StructuredSelection) treeViewer.getSelection(); if (sel.size() != 1) { return -1; } GroupLaunchElement el = ((GroupLaunchElement) sel .getFirstElement()); return input.indexOf(el); } /** * @return the indices of one or more selected items. Indices * are always returned in ascending order */ private int[] getMultiSelectionIndices() { StructuredSelection sel = (StructuredSelection) treeViewer.getSelection(); List<Integer> indices = new ArrayList<Integer>(); for (Iterator<?> iter = sel.iterator(); iter.hasNext(); ) { GroupLaunchElement el = (GroupLaunchElement) iter.next(); indices.add(input.indexOf(el)); } int[] result = new int[indices.size()]; for (int i = 0; i < result.length; i++) { result[i] = indices.get(i); } return result; } private int getSelectionCount() { return ((StructuredSelection)treeViewer.getSelection()).size(); } @Override protected void downPressed() { if (!isDownEnabled()) { return; } int index = getSingleSelectionIndex(); GroupLaunchElement x = input.get(index); input.set(index, input.get(index + 1)); input.set(index + 1, x); treeViewer.refresh(true); updateWidgetEnablement(); updateLaunchConfigurationDialog(); } protected boolean isDownEnabled() { final int index = getSingleSelectionIndex(); return (index >= 0) && (index != input.size() - 1); } protected boolean isUpEnabled(){ return getSingleSelectionIndex() > 0; } @Override protected void upPressed() { if (!isUpEnabled()) { return; } int index = getSingleSelectionIndex(); GroupLaunchElement x = input.get(index); input.set(index, input.get(index - 1)); input.set(index - 1, x); treeViewer.refresh(true); updateWidgetEnablement(); updateLaunchConfigurationDialog(); } }; treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { buts.updateWidgetEnablement(); } }); treeViewer.getTree().addSelectionListener(new SelectionAdapter(){ @Override public void widgetDefaultSelected(SelectionEvent e) { buts.editPressed(); } }); treeViewer.addCheckStateListener(new ICheckStateListener(){ @Override public void checkStateChanged(CheckStateChangedEvent event) { ((GroupLaunchElement)event.getElement()).enabled = event.getChecked(); updateLaunchConfigurationDialog(); } }); buts.updateWidgetEnablement(); GridData layoutData = new GridData(GridData.GRAB_VERTICAL); layoutData.verticalAlignment = SWT.BEGINNING; buts.setLayoutData(layoutData); } @Override public String getName() { return DebugUIMessages.GroupLaunchConfigurationTabGroup_10; } @Override public Image getImage() { return DebugUITools.getImage(IInternalDebugUIConstants.IMG_OBJS_LAUNCH_GROUP); } @Override public void initializeFrom(ILaunchConfiguration configuration) { try { self = configuration.copy(configuration.getName()); } catch (CoreException e) { DebugPlugin.log(e); } // replace the input from previously shown launch configurations input = GroupLaunchConfigurationDelegate.createLaunchElements(configuration); if (treeViewer != null) { treeViewer.setInput(input); } } @Override public void performApply(ILaunchConfigurationWorkingCopy configuration) { GroupLaunchConfigurationDelegate.storeLaunchElements(configuration, input); } @Override public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { // defaults is empty list } /* (non-Javadoc) * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration) */ @Override public boolean isValid(ILaunchConfiguration launchConfig) { setMessage(null); setErrorMessage(null); int validLaunches = 0; // test if each launch is valid for (GroupLaunchElement element : input) { if (element.enabled) { if ( element.data == null) { // error referencing invalid launch setErrorMessage(MessageFormat.format(DebugUIMessages.GroupLaunchConfigurationTabGroup_14, element.name)); return false; } else if (!isValidLaunchReference(element.data)) { // error referencing invalid launch setErrorMessage(MessageFormat.format(DebugUIMessages.GroupLaunchConfigurationTabGroup_15, element.name)); return false; } validLaunches++; } } if (validLaunches < 1) { // must have at least one valid and enabled launch setErrorMessage(DebugUIMessages.GroupLaunchConfigurationTabGroup_16); return false; } return true; } } public GroupLaunchConfigurationTabGroup() { // nothing } /** * Test if a launch configuration is a valid reference. * * @param config configuration reference * @return <code>true</code> if it is a valid reference, <code>false</code> * if launch configuration should be filtered */ public static boolean isValidLaunchReference(ILaunchConfiguration config) { if (config == null) { return false; } return DebugUIPlugin.doLaunchConfigurationFiltering(config) && !WorkbenchActivityHelper.filterItem(config); } @Override public void createTabs(ILaunchConfigurationDialog dialog, String mode) { ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {// new GroupLaunchTab(), // new CommonTabLite() // }; setTabs(tabs); } }