/*******************************************************************************
* Copyright (c) 2000, 2011 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
* IBM Corporation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.launching;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Set;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.IBinaryParser;
import org.eclipse.cdt.core.IBinaryParser.IBinaryFile;
import org.eclipse.cdt.core.IBinaryParser.IBinaryShared;
import org.eclipse.cdt.core.model.CoreModelUtil;
import org.eclipse.cdt.core.settings.model.ICConfigExtensionReference;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.internal.ui.dialogfields.DialogField;
import org.eclipse.cdt.debug.internal.ui.dialogfields.IDialogFieldListener;
import org.eclipse.cdt.debug.internal.ui.dialogfields.IListAdapter;
import org.eclipse.cdt.debug.internal.ui.dialogfields.LayoutUtil;
import org.eclipse.cdt.debug.internal.ui.dialogfields.ListDialogField;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.utils.ui.controls.ControlFactory;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.CheckedTreeSelectionDialog;
/**
* The UI component to access the shared libraries search path.
*/
public class SolibSearchPathBlock extends Observable implements IMILaunchConfigurationComponent, IDialogFieldListener {
class AddDirectoryDialog extends Dialog {
protected Text fText;
private Button fBrowseButton;
private IPath fValue;
/**
* Constructor for AddDirectoryDialog.
*/
public AddDirectoryDialog(Shell parentShell) {
super(parentShell);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite)super.createDialogArea(parent);
Composite subComp = ControlFactory.createCompositeEx(composite, 2, GridData.FILL_HORIZONTAL);
((GridLayout)subComp.getLayout()).makeColumnsEqualWidth = false;
GridData data = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER);
data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
subComp.setLayoutData(data);
subComp.setFont(parent.getFont());
fText = new Text(subComp, SWT.SINGLE | SWT.BORDER);
fText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
fText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
updateOKButton();
}
});
fBrowseButton = ControlFactory.createPushButton(subComp, LaunchUIMessages.getString("GDBServerDebuggerPage.7")); //$NON-NLS-1$
data = new GridData();
data.horizontalAlignment = GridData.FILL;
fBrowseButton.setLayoutData(data);
fBrowseButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent evt) {
DirectoryDialog dialog = new DirectoryDialog(AddDirectoryDialog.this.getShell());
dialog.setMessage(LaunchUIMessages.getString("SolibSearchPathBlock.5")); //$NON-NLS-1$
String res = dialog.open();
if (res != null) {
fText.setText(res);
}
}
});
applyDialogFont(composite);
return composite;
}
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText(LaunchUIMessages.getString("SolibSearchPathBlock.Add_Directory")); //$NON-NLS-1$
}
public IPath getValue() {
return fValue;
}
private void setValue(String value) {
fValue = (value != null) ? new Path(value) : null;
}
@Override
protected void buttonPressed(int buttonId) {
if (buttonId == IDialogConstants.OK_ID) {
setValue(fText.getText());
}
else {
setValue(null);
}
super.buttonPressed(buttonId);
}
protected void updateOKButton() {
Button okButton = getButton(IDialogConstants.OK_ID);
String text = fText.getText();
okButton.setEnabled(isValid(text));
}
protected boolean isValid(String text) {
return (text.trim().length() > 0);
}
@Override
protected Control createButtonBar(Composite parent) {
Control control = super.createButtonBar(parent);
updateOKButton();
return control;
}
}
private Composite fControl;
public class SolibSearchPathListDialogField extends ListDialogField {
public SolibSearchPathListDialogField(IListAdapter adapter, String[] buttonLabels, ILabelProvider lprovider) {
super(adapter, buttonLabels, lprovider);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.dialogfields.ListDialogField#managedButtonPressed(int)
*/
@Override
protected boolean managedButtonPressed(int index) {
boolean result = super.managedButtonPressed(index);
if (result)
buttonPressed(index);
return result;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.mi.internal.ui.dialogfields.ListDialogField#getManagedButtonState(org.eclipse.jface.viewers.ISelection, int)
*/
@Override
protected boolean getManagedButtonState(ISelection sel, int index) {
if (index > 3)
return getButtonState(sel, index);
return super.getManagedButtonState(sel, index);
}
}
private static String[] fgStaticButtonLabels = new String[] {
LaunchUIMessages.getString("SolibSearchPathBlock.0"), //$NON-NLS-1$
LaunchUIMessages.getString("SolibSearchPathBlock.1"), //$NON-NLS-1$
LaunchUIMessages.getString("SolibSearchPathBlock.2"), //$NON-NLS-1$
LaunchUIMessages.getString("SolibSearchPathBlock.3"), //$NON-NLS-1$
LaunchUIMessages.getString("SolibSearchPathBlock.6"), //$NON-NLS-1$
null, // separator
};
private IProject fProject;
private Shell fShell;
private SolibSearchPathListDialogField fDirList;
private IListAdapter fCustomListAdapter;
private File[] fAutoSolibs = new File[0];
public SolibSearchPathBlock() {
this(new String[0], null);
}
public SolibSearchPathBlock(String[] customButtonLabels, IListAdapter customListAdapter) {
super();
fCustomListAdapter = customListAdapter;
int length = fgStaticButtonLabels.length;
if (customButtonLabels.length > 0)
length += customButtonLabels.length;
String[] buttonLabels = new String[length];
System.arraycopy(fgStaticButtonLabels, 0, buttonLabels, 0, fgStaticButtonLabels.length);
if (length > fgStaticButtonLabels.length) {
for (int i = fgStaticButtonLabels.length; i < length; ++i)
buttonLabels[i] = customButtonLabels[i - fgStaticButtonLabels.length];
}
IListAdapter listAdapter = new IListAdapter() {
public void customButtonPressed(DialogField field, int index) {
buttonPressed(index);
}
public void selectionChanged(DialogField field) {
}
};
ILabelProvider lp = new LabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof IPath)
return ((IPath)element).toOSString();
return super.getText(element);
}
};
fDirList = new SolibSearchPathListDialogField(listAdapter, buttonLabels, lp);
fDirList.setLabelText(LaunchUIMessages.getString("SolibSearchPathBlock.4")); //$NON-NLS-1$
fDirList.setUpButtonIndex(1);
fDirList.setDownButtonIndex(2);
fDirList.setRemoveButtonIndex(3);
fDirList.setDialogFieldListener(this);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#createControl(org.eclipse.swt.widgets.Composite)
*/
public void createControl(Composite parent) {
fShell = parent.getShell();
Composite comp = ControlFactory.createCompositeEx(parent, 2, GridData.FILL_BOTH);
((GridLayout)comp.getLayout()).makeColumnsEqualWidth = false;
((GridLayout)comp.getLayout()).marginHeight = 0;
((GridLayout)comp.getLayout()).marginWidth = 0;
comp.setFont(parent.getFont());
PixelConverter converter = new PixelConverter(comp);
fDirList.doFillIntoGrid(comp, 3);
LayoutUtil.setHorizontalSpan(fDirList.getLabelControl(null), 2);
LayoutUtil.setWidthHint(fDirList.getLabelControl(null), converter.convertWidthInCharsToPixels(30));
LayoutUtil.setHorizontalGrabbing(fDirList.getListControl(null));
fControl = comp;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration)
*/
public void initializeFrom(ILaunchConfiguration configuration) {
IProject project = null;
try {
String projectName = configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null);
if (projectName != null) {
projectName = projectName.trim();
if (projectName.length() > 0) {
project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
}
}
}
catch(CoreException e) {
}
setProject(project);
if (fDirList != null) {
try {
@SuppressWarnings("unchecked")
List<String> values = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH,
Collections.EMPTY_LIST);
ArrayList<Path> paths = new ArrayList<Path>(values.size());
Iterator<String> it = values.iterator();
while(it.hasNext()) {
paths.add(new Path(it.next()));
}
fDirList.addElements(paths);
}
catch(CoreException e) {
}
}
try {
fAutoSolibs = getAutoSolibs(configuration);
}
catch(CoreException e) {
}
}
public static File[] getAutoSolibs(ILaunchConfiguration configuration) throws CoreException {
@SuppressWarnings("unchecked")
List<String> autoSolibs = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB_LIST, Collections.EMPTY_LIST );
List<File> list = new ArrayList<File>(autoSolibs.size());
Iterator<String> it = autoSolibs.iterator();
while(it.hasNext()) {
list.add(new File(it.next()));
}
return list.toArray(new File[list.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
*/
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, Collections.EMPTY_LIST);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
*/
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
if (fDirList != null) {
@SuppressWarnings("unchecked")
List<IPath> elements = fDirList.getElements();
ArrayList<String> values = new ArrayList<String>(elements.size());
Iterator<IPath> it = elements.iterator();
while(it.hasNext()) {
values.add((it.next()).toOSString());
}
configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, values);
}
ArrayList<String> autoLibs = new ArrayList<String>(fAutoSolibs.length);
for (int i = 0; i < fAutoSolibs.length; ++i)
autoLibs.add(fAutoSolibs[i].getPath());
configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB_LIST, autoLibs);
}
protected void buttonPressed(int index) {
boolean changed = false;
if (index == 0) { // Add button
changed = addDirectory();
}
else if (index == 4) { //Select from list
changed = selectFromList();
}
else if (index >= fgStaticButtonLabels.length && fCustomListAdapter != null) {
fCustomListAdapter.customButtonPressed(fDirList, index);
changed = true;
}
if (changed) {
setChanged();
notifyObservers();
}
}
protected boolean getButtonState(ISelection sel, int index) {
if (index == 4) { // select from list
return (!sel.isEmpty());
}
return true;
}
protected Shell getShell() {
return fShell;
}
private boolean addDirectory() {
boolean changed = false;
AddDirectoryDialog dialog = new AddDirectoryDialog(getShell());
dialog.open();
IPath result = dialog.getValue();
if (result != null && !contains(result)) {
fDirList.addElement(result);
changed = true;
}
return changed;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#dispose()
*/
public void dispose() {
deleteObservers();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#getControl()
*/
public Control getControl() {
return fControl;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#isValid(org.eclipse.debug.core.ILaunchConfiguration)
*/
public boolean isValid(ILaunchConfiguration launchConfig) {
// TODO Auto-generated method stub
return false;
}
private boolean contains(IPath path) {
@SuppressWarnings("unchecked")
List<IPath> list = fDirList.getElements();
Iterator<IPath> it = list.iterator();
while(it.hasNext()) {
IPath p = it.next();
if (p.toFile().equals(path.toFile()))
return true;
}
return false;
}
protected IProject getProject() {
return fProject;
}
private void setProject(IProject project) {
fProject = project;
}
protected boolean selectFromList() {
boolean changed = false;
@SuppressWarnings("unchecked")
List<IPath> dirList = fDirList.getSelectedElements();
final HashSet<IPath> libs = new HashSet<IPath>(10);
if (generateLibraryList(dirList.toArray(new IPath[dirList.size()]), libs)) {
ITreeContentProvider cp = new ITreeContentProvider() {
public Object[] getChildren(Object parentElement) {
return getElements(parentElement);
}
public Object getParent(Object element) {
if (libs.contains(element))
return libs;
return null;
}
public boolean hasChildren(Object element) {
return false;
}
public Object[] getElements(Object inputElement) {
if (inputElement instanceof Set) {
return ((Set)inputElement).toArray();
}
return new Object[0];
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
};
LabelProvider lp = new LabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof File)
return ((File)element).getName();
return super.getText(element);
}
};
CheckedTreeSelectionDialog dialog = new CheckedTreeSelectionDialog(getShell(), lp, cp);
dialog.setTitle(LaunchUIMessages.getString("SolibSearchPathBlock.7")); //$NON-NLS-1$
dialog.setMessage(LaunchUIMessages.getString("SolibSearchPathBlock.8")); //$NON-NLS-1$
dialog.setEmptyListMessage(LaunchUIMessages.getString("SolibSearchPathBlock.9")); //$NON-NLS-1$
dialog.setComparator(new ViewerSorter());
dialog.setInput(libs);
dialog.setInitialElementSelections(Arrays.asList(fAutoSolibs));
if (dialog.open() == Window.OK) {
Object[] result = dialog.getResult();
fAutoSolibs = Arrays.asList(result).toArray(new File[result.length]);
changed = true;
}
}
return changed;
}
private boolean generateLibraryList(final IPath[] paths, final Set libs) {
boolean result = true;
IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
for (int i = 0; i < paths.length; ++i) {
File dir = paths[i].toFile();
if (dir.exists() && dir.isDirectory()) {
File[] all = dir.listFiles();
for (int j = 0; j < all.length; ++j) {
if (monitor.isCanceled()) {
throw new InterruptedException();
}
monitor.subTask(all[j].getPath());
String libName = getSharedLibraryName(all[j]);
if (libName != null) {
libs.add(new File(libName));
}
}
}
}
}
};
try {
IRunnableContext context = new ProgressMonitorDialog(getShell());
context.run(true, true, runnable);
}
catch(InvocationTargetException e) {
}
catch(InterruptedException e) {
result = false;
}
return result;
}
protected String getSharedLibraryName(File file) {
if (!file.isFile())
return null;
IProject project = getProject();
if (project != null) {
IPath fullPath = new Path(file.getPath());
try {
ICConfigExtensionReference[] binaryParsersExt = CCorePlugin.getDefault().getDefaultBinaryParserExtensions(project);
for(int i = 0; i < binaryParsersExt.length; i++) {
IBinaryParser parser = CoreModelUtil.getBinaryParser(binaryParsersExt[i]);
try {
IBinaryFile bin = parser.getBinary(fullPath);
if (bin instanceof IBinaryShared) {
String soname = ((IBinaryShared)bin).getSoName();
return (soname.length() != 0) ? soname : file.getName();
}
}
catch(IOException e) {
}
}
}
catch(CoreException e) {
}
return null;
}
// no project: for now
IPath path = new Path(file.getPath());
String name = path.lastSegment();
String extension = path.getFileExtension();
if (extension != null && (extension.compareTo("so") == 0 || extension.compareToIgnoreCase("dll") == 0)) //$NON-NLS-1$ //$NON-NLS-2$
return name;
return (name.indexOf(".so.") >= 0) ? name : null; //$NON-NLS-1$
}
protected boolean isSharedLibrary(File file) {
if (!file.isFile())
return false;
IProject project = getProject();
if (project != null) {
IPath fullPath = new Path(file.getPath());
try {
ICConfigExtensionReference[] binaryParsersExt = CCorePlugin.getDefault().getDefaultBinaryParserExtensions(project);
for(int i = 0; i < binaryParsersExt.length; i++) {
IBinaryParser parser = CoreModelUtil.getBinaryParser(binaryParsersExt[i]);
try {
IBinaryFile bin = parser.getBinary(fullPath);
return (bin instanceof IBinaryShared);
}
catch(IOException e) {
}
}
}
catch(CoreException e) {
}
return false;
}
// no project: for now
IPath path = new Path(file.getPath());
String extension = path.getFileExtension();
if (extension != null && (extension.compareTo("so") == 0 || extension.compareToIgnoreCase("dll") == 0)) //$NON-NLS-1$ //$NON-NLS-2$
return true;
String name = path.lastSegment();
return (name.indexOf(".so.") >= 0); //$NON-NLS-1$
}
public void dialogFieldChanged(DialogField field) {
setChanged();
notifyObservers();
}
}