/******************************************************************************* * Copyright (c) 2009, 2015 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.internal.debug.ui.launch; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.layout.PixelConverter; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnPixelData; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ICheckStateProvider; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableLayout; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; 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.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.tcf.internal.debug.launch.TCFLaunchDelegate; import org.eclipse.tcf.internal.debug.launch.TCFLaunchDelegate.PathMapRule; import org.eclipse.tcf.internal.debug.ui.Activator; import org.eclipse.tcf.internal.debug.ui.ImageCache; import org.eclipse.tcf.services.IPathMap; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; // TODO: add source lookup container that represents ATTR_PATH_MAP public class TCFPathMapTab extends AbstractLaunchConfigurationTab { private CheckboxTableViewer viewer; private Button button_add; private Button button_edit; private Button button_remove; private Button button_up; private Button button_down; private MenuItem item_add; private MenuItem item_edit; private MenuItem item_remove; private MenuItem item_up; private MenuItem item_down; protected static final int SIZING_TABLE_WIDTH = 500; protected static final int SIZING_TABLE_HEIGHT = 300; private static final String[] column_ids = { "", //$NON-NLS-1$ IPathMap.PROP_SOURCE, IPathMap.PROP_DESTINATION, IPathMap.PROP_CONTEXT_QUERY, }; private static final String[] column_names = { "", //$NON-NLS-1$ "Source", //$NON-NLS-1$ "Destination", //$NON-NLS-1$ "Context query", //$NON-NLS-1$ }; protected final static String PROP_ENABLED = "Enabled"; //$NON-NLS-1$ protected final static String PROP_GENERATED = "Generated"; //$NON-NLS-1$ private final static String ATTR_PATH_MAP_V1 = TCFLaunchDelegate.ATTR_PATH_MAP + "V1"; //$NON-NLS-1$ private static final String TAB_ID = "org.eclipse.tcf.launch.pathMapTab"; //$NON-NLS-1$ private List<IPathMap.PathMapRule> map; private class FileMapContentProvider implements IStructuredContentProvider { public Object[] getElements(Object input) { return map.toArray(new IPathMap.PathMapRule[map.size()]); } public void inputChanged(Viewer viewer, Object old_input, Object new_input) { } public void dispose() { } } private class FileCheckStateProvider implements ICheckStateProvider { @Override public boolean isChecked(Object element) { IPathMap.PathMapRule e = (IPathMap.PathMapRule)element; if (e.getProperties().containsKey(PROP_ENABLED)) { return Boolean.parseBoolean(e.getProperties().get(PROP_ENABLED).toString()); } return true; } @Override public boolean isGrayed(Object element) { return false; } } private class FileCheckStateListener implements ICheckStateListener { @Override public void checkStateChanged(CheckStateChangedEvent event) { IPathMap.PathMapRule rule = (IPathMap.PathMapRule)event.getElement(); if (rule.getProperties().containsKey(PROP_GENERATED) && Boolean.parseBoolean(rule.getProperties().get(PROP_GENERATED).toString())) { viewer.refresh(); return; } if (event.getChecked()) rule.getProperties().remove(PROP_ENABLED); else rule.getProperties().put(PROP_ENABLED, Boolean.FALSE); viewer.refresh(); updateLaunchConfigurationDialog(); } } private class FileMapLabelProvider extends LabelProvider implements ITableLabelProvider, IColorProvider { public Image getColumnImage(Object element, int column) { return null; } public String getColumnText(Object element, int column) { IPathMap.PathMapRule e = (IPathMap.PathMapRule)element; Object o = e.getProperties().get(column_ids[column]); if (o == null) return ""; //$NON-NLS-1$ return o.toString(); } @Override public Color getForeground(Object element) { if (element instanceof IPathMap.PathMapRule) { IPathMap.PathMapRule rule = (IPathMap.PathMapRule)element; if (rule.getProperties().containsKey(PROP_GENERATED) && Boolean.parseBoolean(rule.getProperties().get(PROP_GENERATED).toString())) { return PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_GRAY); } } return null; } @Override public Color getBackground(Object element) { return null; } } private Exception init_error; public String getName() { return "Path Map"; //$NON-NLS-1$ } @Override public Image getImage() { return ImageCache.getImage(ImageCache.IMG_PATH); } @Override public String getId() { return TAB_ID; } public void createControl(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(1, false); composite.setFont(parent.getFont()); composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true, 1, 1)); createTable(composite); createCustomControls(composite); setControl(composite); } /** * Hook to add custom controls below the path map rules table. * * @param parent The parent composite. Must not be <code>null</code>. */ protected void createCustomControls(Composite parent) { // Nothing to do } private void createTable(Composite parent) { Font font = parent.getFont(); Label map_label = new Label(parent, SWT.WRAP); map_label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); map_label.setFont(font); map_label.setText("File Path Map Rules:"); //$NON-NLS-1$ Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(2, false); layout.marginHeight = 0; layout.marginWidth = 0; composite.setFont(font); composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_BOTH)); Table table = new Table(composite, SWT.CHECK | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION); table.setFont(font); configureTable(table); viewer = new CheckboxTableViewer(table); viewer.setContentProvider(new FileMapContentProvider()); viewer.setLabelProvider(new FileMapLabelProvider()); viewer.setCheckStateProvider(new FileCheckStateProvider()); viewer.addCheckStateListener(new FileCheckStateListener()); viewer.setColumnProperties(column_ids); viewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { updateButtons(); } }); viewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { if (button_edit.isEnabled()) { onEdit((IStructuredSelection)viewer.getSelection()); } } }); configureTableViewer(viewer); table.pack(true); createTableButtons(composite); } /** * Hook to configure the checkbox table viewer. * * @param viewer The checkbox table viewer. Must not be <code>null</code>. */ protected void configureTableViewer(CheckboxTableViewer viewer) { // Nothing to do } protected void configureTable(final Table table) { GridData data = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING); data.widthHint = SIZING_TABLE_WIDTH; data.heightHint = SIZING_TABLE_HEIGHT; table.setLayoutData(data); final TableColumn colEnable = new TableColumn(table, 0); colEnable.setResizable(false); colEnable.setAlignment(SWT.CENTER); colEnable.setText(getColumnText(0)); final TableColumn colSource = new TableColumn(table, 1); colSource.setResizable(true); colSource.setAlignment(SWT.LEFT); colSource.setText(getColumnText(1)); final TableColumn colDest = new TableColumn(table, 2); colDest.setResizable(true); colDest.setAlignment(SWT.LEFT); colDest.setText(getColumnText(2)); TableColumn colQuery = null; if (showContextQuery()) { colQuery = new TableColumn(table, 3); colQuery.setResizable(true); colQuery.setAlignment(SWT.LEFT); colQuery.setText(getColumnText(3)); } TableLayout layout = new TableLayout(); layout.addColumnData(new ColumnPixelData(30)); layout.addColumnData(new ColumnPixelData(300)); layout.addColumnData(new ColumnPixelData(300)); if (showContextQuery()) layout.addColumnData(new ColumnPixelData(100)); table.setLayout(layout); table.setHeaderVisible(true); table.setLinesVisible(true); } protected boolean showContextQuery() { return true; } protected String getColumnText(int c) { if (c < column_names.length && c >= 0) return column_names[c]; return ""; //$NON-NLS-1$ } private void createTableButtons(Composite parent) { Font font = parent.getFont(); Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; composite.setFont(font); composite.setLayout(layout); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL); composite.setLayoutData(gd); Menu menu = new Menu(viewer.getTable()); SelectionAdapter sel_adapter = null; button_add = new Button(composite, SWT.PUSH); button_add.setText(" &Add... "); //$NON-NLS-1$ gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL); PixelConverter converter= new PixelConverter(button_add); gd.widthHint = converter.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); button_add.setLayoutData(gd); button_add.addSelectionListener(sel_adapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onAdd(); } }); item_add = new MenuItem(menu, SWT.PUSH); item_add.setText("&Add..."); //$NON-NLS-1$ item_add.addSelectionListener(sel_adapter); item_add.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ADD)); button_edit = new Button(composite, SWT.PUSH); button_edit.setText(" &Edit... "); //$NON-NLS-1$ button_edit.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); button_edit.addSelectionListener(sel_adapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onEdit((IStructuredSelection)viewer.getSelection()); } }); item_edit= new MenuItem(menu, SWT.PUSH); item_edit.setText("&Edit..."); //$NON-NLS-1$ item_edit.addSelectionListener(sel_adapter); button_remove = new Button(composite, SWT.PUSH); button_remove.setText(" &Remove "); //$NON-NLS-1$ button_remove.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); button_remove.addSelectionListener(sel_adapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onRemove((IStructuredSelection)viewer.getSelection()); } }); item_remove = new MenuItem(menu, SWT.PUSH); item_remove.setText("&Remove"); //$NON-NLS-1$ item_remove.addSelectionListener(sel_adapter); item_remove.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_ETOOL_DELETE)); new MenuItem(menu, SWT.SEPARATOR); button_up = new Button(composite, SWT.PUSH); button_up.setText(" &Up "); //$NON-NLS-1$ button_up.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); button_up.addSelectionListener(sel_adapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onUp(); } }); item_up = new MenuItem(menu, SWT.PUSH); item_up.setText("&Up"); //$NON-NLS-1$ item_up.addSelectionListener(sel_adapter); button_down = new Button(composite, SWT.PUSH); button_down.setText(" &Down "); //$NON-NLS-1$ button_down.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); button_down.addSelectionListener(sel_adapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onDown(); } }); item_down = new MenuItem(menu, SWT.PUSH); item_down.setText("&Down"); //$NON-NLS-1$ item_down.addSelectionListener(sel_adapter); viewer.getTable().setMenu(menu); } private void onAdd() { // To guarantee a predictable path map properties iteration order, // we have to use a LinkedHashMap. int index = viewer.getTable().getSelectionIndex(); final PathMapRule rule = new PathMapRule(new LinkedHashMap<String,Object>()); PathMapRuleDialog dialog = new PathMapRuleDialog(getShell(), null, rule, true, showContextQuery()); if (dialog.open() == Window.OK) { if (index >= 0) map.add(index, rule); else map.add(0, rule); viewer.refresh(true); updateLaunchConfigurationDialog(); viewer.setSelection(new StructuredSelection(rule), true); } } private void onEdit(IStructuredSelection selection) { IPathMap.PathMapRule rule = (IPathMap.PathMapRule)selection.getFirstElement(); PathMapRuleDialog dialog = new PathMapRuleDialog(getShell(), null, rule, true, showContextQuery()); dialog.open(); viewer.refresh(true); updateLaunchConfigurationDialog(); } private void onRemove(IStructuredSelection selection) { for (Iterator<?> i = ((IStructuredSelection)viewer.getSelection()).iterator(); i.hasNext();) { IPathMap.PathMapRule rule = (IPathMap.PathMapRule)i.next(); map.remove(rule); } viewer.refresh(true); updateLaunchConfigurationDialog(); } private void onUp() { int index = viewer.getTable().getSelectionIndex(); IPathMap.PathMapRule rule = map.remove(index); map.add(index-1, rule); viewer.refresh(true); updateLaunchConfigurationDialog(); viewer.setSelection(new StructuredSelection(rule), true); } private void onDown() { int index = viewer.getTable().getSelectionIndex(); IPathMap.PathMapRule rule = map.remove(index); map.add(index+1, rule); viewer.refresh(true); updateLaunchConfigurationDialog(); viewer.setSelection(new StructuredSelection(rule), true); } protected final TableViewer getViewer() { return viewer; } List<IPathMap.PathMapRule> getPathMap() { return Collections.unmodifiableList(map); } public void initializeFrom(ILaunchConfiguration config) { setErrorMessage(null); setMessage(null); map = new ArrayList<IPathMap.PathMapRule>(); initializePathMap(map, config); viewer.setInput(config); updateLaunchConfigurationDialog(); } /** * Initialize the given path map. * * @param map The path map to initialize. Must not be <code>null</code>. * @param config The launch configuration. Must not be <code>null</code>. */ protected void initializePathMap(List<IPathMap.PathMapRule> map, ILaunchConfiguration config) { Assert.isNotNull(map); Assert.isNotNull(config); try { String s = config.getAttribute(TCFLaunchDelegate.ATTR_PATH_MAP, ""); //$NON-NLS-1$ String s1 = config.getAttribute(ATTR_PATH_MAP_V1, ""); //$NON-NLS-1$ List<PathMapRule> m = TCFLaunchDelegate.parsePathMapAttribute(s); List<PathMapRule> m1 = TCFLaunchDelegate.parsePathMapAttribute(s1); for (IPathMap.PathMapRule rule : m1) { map.add(rule); } int i = -1; for (IPathMap.PathMapRule rule : m) { if (map.contains(rule)) i = map.indexOf(rule); else map.add(++i, rule); } } catch (Exception e) { init_error = e; setErrorMessage("Cannot read launch configuration: " + e); //$NON-NLS-1$ Activator.log(e); } } public void performApply(ILaunchConfigurationWorkingCopy config) { for (IPathMap.PathMapRule m : map) m.getProperties().remove(IPathMap.PROP_ID); StringBuffer bf = new StringBuffer(); StringBuffer bf1 = new StringBuffer(); for (IPathMap.PathMapRule m : map) { if (m.getProperties().containsKey(PROP_GENERATED)) { if (Boolean.parseBoolean(m.getProperties().get(PROP_GENERATED).toString())) { continue; } } boolean enabled = true; if (m.getProperties().containsKey(PROP_ENABLED)) { enabled = Boolean.parseBoolean(m.getProperties().get(PROP_ENABLED).toString()); } if (enabled) { m.getProperties().remove(PROP_ENABLED); bf.append(m.toString()); } bf1.append(m.toString()); } if (bf.length() == 0) config.removeAttribute(TCFLaunchDelegate.ATTR_PATH_MAP); else config.setAttribute(TCFLaunchDelegate.ATTR_PATH_MAP, bf.toString()); if (bf1.length() == 0) config.removeAttribute(ATTR_PATH_MAP_V1); else config.setAttribute(ATTR_PATH_MAP_V1, bf1.toString()); } public void setDefaults(ILaunchConfigurationWorkingCopy config) { config.removeAttribute(TCFLaunchDelegate.ATTR_PATH_MAP); config.removeAttribute(ATTR_PATH_MAP_V1); } @Override protected void updateLaunchConfigurationDialog() { super.updateLaunchConfigurationDialog(); updateButtons(); } protected void updateButtons() { boolean singleSelection = ((IStructuredSelection)viewer.getSelection()).size() == 1; int index = viewer.getTable().getSelectionIndex(); int count = viewer.getTable().getItemCount(); IPathMap.PathMapRule selected = (IPathMap.PathMapRule)((IStructuredSelection)viewer.getSelection()).getFirstElement(); boolean isGenerated = selected != null && selected.getProperties().containsKey(PROP_GENERATED) ? Boolean.parseBoolean(selected.getProperties().get(PROP_GENERATED).toString()) : false; button_remove.setEnabled(!viewer.getSelection().isEmpty() && !isGenerated); button_edit.setEnabled(singleSelection && !isGenerated); button_up.setEnabled(singleSelection && index > 0 && !isGenerated); button_down.setEnabled(singleSelection && index < count-1 && !isGenerated); item_remove.setEnabled(!viewer.getSelection().isEmpty() && !isGenerated); item_edit.setEnabled(singleSelection && !isGenerated); item_up.setEnabled(singleSelection && index > 0 && !isGenerated); item_down.setEnabled(singleSelection && index < count-1 && !isGenerated); } @Override public boolean isValid(ILaunchConfiguration config) { setMessage(null); if (init_error != null) { setErrorMessage("Cannot read launch configuration: " + init_error); //$NON-NLS-1$ return false; } setErrorMessage(null); return true; } }