/******************************************************************************* * Copyright (c) 2015, 2016 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.te.tcf.launch.cdt.tabs; import java.io.File; import java.net.URI; import java.nio.file.Files; import java.nio.file.Paths; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.dsf.gdb.internal.ui.launching.CMainTab; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.URIUtil; import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; 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.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.tcf.te.runtime.services.ServiceManager; import org.eclipse.tcf.te.tcf.core.interfaces.IPathMapResolverService; import org.eclipse.tcf.te.tcf.filesystem.core.internal.FSTreeNode; import org.eclipse.tcf.te.tcf.filesystem.ui.dialogs.FSOpenFileDialog; import org.eclipse.tcf.te.tcf.launch.cdt.controls.TCFPeerSelector; import org.eclipse.tcf.te.tcf.launch.cdt.interfaces.IRemoteTEConfigurationConstants; import org.eclipse.tcf.te.tcf.launch.cdt.nls.Messages; import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode; import org.eclipse.tcf.te.ui.controls.validator.NumberVerifyListener; /** * Abstract custom main tab implementation. */ @SuppressWarnings("restriction") public abstract class TEAbstractMainTab extends CMainTab { /* Labels and Error Messages */ private static final String REMOTE_PROG_LABEL_TEXT = Messages.RemoteCMainTab_Program; private static final String SKIP_DOWNLOAD_BUTTON_TEXT = Messages.RemoteCMainTab_SkipDownload; private static final String REMOTE_PROG_TEXT_ERROR = Messages.RemoteCMainTab_ErrorNoProgram; private static final String CONNECTION_TEXT_ERROR = Messages.RemoteCMainTab_ErrorNoConnection; private static final String PRE_RUN_LABEL_TEXT = Messages.RemoteCMainTab_Prerun; private static final String PID_LABEL_TEXT = Messages.RemoteCMainTab_Pid; private static final String REMOTE_PROG_SYMBOLIC_TEXT_ERROR = Messages.RemoteCMainTab_ErrorSymbolicLink; private static final String REMOTE_USER_ID_LABEL_TEXT = Messages.RemoteCMainTab_RemoteUser_Label; protected TCFPeerSelector peerSelector; protected Label remoteProgLabel; protected Text remoteProgText; protected Button remoteBrowseButton; protected boolean remoteProgVisible = true; protected Button skipDownloadButton; protected boolean skipDownloadButtonVisible = true; protected boolean progTextFireNotification; protected boolean remoteProgTextFireNotification; protected boolean remoteProgValidation = true; protected Text preRunText; private Label preRunLabel; private Button preRunEditButton; private boolean preRunVisible = true; private Text userIdText; private Button userIdButton; private Label pidLabel; private Text pidText; private boolean pidVisible = false; public static final int NO_DOWNLOAD_GROUP = 2 << 8; public static final int NO_PRERUN_GROUP = 4 << 8; public static final int NO_REMOTE_PATH = 8 << 8; public static final int PID_GROUP = 16 << 8; /** * Constructor */ public TEAbstractMainTab() { super(); } /** * Constructor * * @param flags The flags to configure the main tab. */ public TEAbstractMainTab(int flags) { super(flags); if ((flags & DONT_CHECK_PROGRAM) != 0) { remoteProgValidation = false; } if ((flags & NO_DOWNLOAD_GROUP) != 0) { skipDownloadButtonVisible = false; } if ((flags & NO_PRERUN_GROUP) != 0) { preRunVisible = false; } if ((flags & NO_REMOTE_PATH) != 0) { remoteProgVisible = false; } if ((flags & PID_GROUP) != 0) { pidVisible = true; } } @Override public void createControl(Composite parent) { super.createControl(parent); Composite comp = (Composite) getControl(); /* The TE Connection dropdown */ createVerticalSpacer(comp, 1); createRemoteConnectionGroup(comp); /* The remote binary location and skip download option */ if (remoteProgVisible || preRunVisible || pidVisible) createVerticalSpacer(comp, 1); createTargetExePathGroup(comp); /* If the local binary path changes, modify the remote binary location */ fProgText.addModifyListener(new ModifyListener() { @SuppressWarnings("synthetic-access") @Override public void modifyText(ModifyEvent evt) { if (progTextFireNotification) { setRemotePathFromLocalPath(); } } }); progTextFireNotification = true; } /* * isValid * @see org.eclipse.debug.ui.ILaunchConfigurationTab#isValid */ @Override public boolean isValid(ILaunchConfiguration config) { boolean retVal = super.isValid(config); if (retVal == true) { setErrorMessage(null); if (peerSelector.getPeerId() == null) { setErrorMessage(CONNECTION_TEXT_ERROR); retVal = false; } if (retVal && remoteProgValidation && remoteProgVisible) { String name = remoteProgText.getText().trim(); if (name.length() == 0) { setErrorMessage(REMOTE_PROG_TEXT_ERROR); retVal = false; } } } else { try { if ( Files.isSymbolicLink(Paths.get(fProgText.getText())) ) { setErrorMessage(NLS.bind(REMOTE_PROG_SYMBOLIC_TEXT_ERROR, Files.readSymbolicLink(Paths.get(fProgText.getText())))); } } catch (Exception e) { /* Omitted on purpose */ } } return retVal; } protected void createRemoteConnectionGroup(Composite parent) { peerSelector = new TCFPeerSelector(parent, SWT.NONE, 2); peerSelector.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); peerSelector.addModifyListener(new ModifyListener() { @SuppressWarnings("synthetic-access") @Override public void modifyText(ModifyEvent e) { setDirty(true); updateLaunchConfigurationDialog(); } }); } /* * createTargetExePath This creates the remote path user-editable textfield on the Main Tab. */ protected void createTargetExePathGroup(Composite parent) { if (!remoteProgVisible && !preRunVisible) return; Composite mainComp = new Composite(parent, SWT.NONE); GridLayout mainLayout = new GridLayout(); mainLayout.numColumns = 2; mainLayout.marginHeight = 0; mainLayout.marginWidth = 0; mainComp.setLayout(mainLayout); GridData gd = new GridData(GridData.FILL_HORIZONTAL); mainComp.setLayoutData(gd); if (remoteProgVisible) { remoteProgLabel = new Label(mainComp, SWT.NONE); remoteProgLabel.setText(REMOTE_PROG_LABEL_TEXT); gd = new GridData(); gd.horizontalSpan = 2; remoteProgLabel.setLayoutData(gd); remoteProgText = new Text(mainComp, SWT.SINGLE | SWT.BORDER); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 1; remoteProgText.setLayoutData(gd); remoteProgText.addModifyListener(new ModifyListener() { @SuppressWarnings("synthetic-access") @Override public void modifyText(ModifyEvent evt) { if (remoteProgTextFireNotification) { setLocalPathFromRemotePath(); } updateLaunchConfigurationDialog(); } }); remoteProgTextFireNotification = true; remoteBrowseButton = createPushButton(mainComp, Messages.RemoteCMainTab_Remote_Path_Browse_Button, null); remoteBrowseButton.addSelectionListener(new SelectionAdapter() { @SuppressWarnings("synthetic-access") @Override public void widgetSelected(SelectionEvent evt) { handleRemoteBrowseSelected(); updateLaunchConfigurationDialog(); } }); } if (skipDownloadButtonVisible) createDownloadOption(mainComp); if (pidVisible) { pidLabel = new Label(mainComp, SWT.NONE); pidLabel.setText(PID_LABEL_TEXT); gd = new GridData(); gd.horizontalSpan = 2; pidLabel.setLayoutData(gd); pidText = new Text(mainComp, SWT.SINGLE | SWT.BORDER); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 2; pidText.setLayoutData(gd); pidText.addModifyListener(new ModifyListener() { @SuppressWarnings("synthetic-access") @Override public void modifyText(ModifyEvent evt) { updateLaunchConfigurationDialog(); } }); pidText.addVerifyListener(new NumberVerifyListener()); } if (preRunVisible) { createVerticalSpacer(mainComp, 2); // Launch with remote user Composite userIdComp = new Composite(mainComp, SWT.NONE); GridLayout userIdCompLayout = new GridLayout(); userIdCompLayout.numColumns = 2; userIdCompLayout.marginHeight = 0; userIdCompLayout.marginWidth = 0; userIdComp.setLayout(userIdCompLayout); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 2; userIdComp.setLayoutData(gd); userIdButton = createCheckButton(userIdComp, REMOTE_USER_ID_LABEL_TEXT); userIdButton.addSelectionListener(new SelectionAdapter() { @SuppressWarnings("synthetic-access") @Override public void widgetSelected(SelectionEvent evt) { updateLaunchRemoteUserControls(); updateLaunchConfigurationDialog(); } }); userIdButton.setEnabled(true); userIdText = new Text(userIdComp, SWT.SINGLE | SWT.BORDER); gd = new GridData(); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = SWT.FILL; userIdText.setLayoutData(gd); userIdText.addModifyListener(new ModifyListener() { @SuppressWarnings("synthetic-access") @Override public void modifyText(ModifyEvent evt) { updateLaunchConfigurationDialog(); } }); createVerticalSpacer(mainComp, 2); // Commands to run before execution preRunLabel = new Label(mainComp, SWT.NONE); preRunLabel.setText(PRE_RUN_LABEL_TEXT); gd = new GridData(); gd.horizontalSpan = 2; preRunLabel.setLayoutData(gd); preRunText = new Text(mainComp, SWT.MULTI | SWT.BORDER); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 1; gd.heightHint = 3 * preRunText.getLineHeight(); preRunText.setLayoutData(gd); preRunText.addModifyListener(new ModifyListener() { @SuppressWarnings("synthetic-access") @Override public void modifyText(ModifyEvent evt) { updateLaunchConfigurationDialog(); } }); preRunEditButton = createPushButton(mainComp, Messages.RemoteCMainTab_Prerun_Edit_Button, null); gd = new GridData(SWT.FILL, SWT.BEGINNING, false, false); preRunEditButton.setLayoutData(gd); preRunEditButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent evt) { showCommandsEditor(); } }); } } /* * createDownloadOption This creates the skip download check button. */ protected void createDownloadOption(Composite parent) { Composite mainComp = new Composite(parent, SWT.NONE); GridLayout mainLayout = new GridLayout(); mainLayout.marginHeight = 0; mainLayout.marginWidth = 0; mainComp.setLayout(mainLayout); GridData gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 2; mainComp.setLayoutData(gd); skipDownloadButton = createCheckButton(mainComp, SKIP_DOWNLOAD_BUTTON_TEXT); skipDownloadButton.addSelectionListener(new SelectionAdapter() { @SuppressWarnings("synthetic-access") @Override public void widgetSelected(SelectionEvent evt) { updateLaunchConfigurationDialog(); } }); skipDownloadButton.setEnabled(true); } protected void handleRemoteBrowseSelected() { IPeerNode connection = peerSelector.getPeerNode(); if (connection != null) { FSOpenFileDialog dialog = new FSOpenFileDialog(getShell()); dialog.setInput(connection); if (dialog.open() == Window.OK) { Object candidate = dialog.getFirstResult(); if (candidate instanceof FSTreeNode) { String absPath = ((FSTreeNode) candidate).getLocation(); if (absPath != null) { remoteProgText.setText(absPath); } } } } } protected void updateTargetProgFromConfig(ILaunchConfiguration config) { if (remoteProgText != null) { String targetPath = ""; //$NON-NLS-1$ try { targetPath = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_PATH, ""); //$NON-NLS-1$ } catch (CoreException e) { /* ignored on purpose */ } boolean prevRemoteProgTextFireNotification = remoteProgTextFireNotification; remoteProgTextFireNotification = false; remoteProgText.setText(targetPath); remoteProgTextFireNotification = prevRemoteProgTextFireNotification; } if (pidText != null) { String pid = ""; //$NON-NLS-1$ try { pid = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_PID, ""); //$NON-NLS-1$ } catch (CoreException e) { /* ignored on purpose */ } pidText.setText(pid); } if (preRunText != null) { String prelaunchCmd = ""; //$NON-NLS-1$ try { prelaunchCmd = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_PRERUN_COMMANDS, ""); //$NON-NLS-1$ } catch (CoreException e) { /* ignored on purpose */ } preRunText.setText(prelaunchCmd); } if (userIdText != null) { String user = ""; //$NON-NLS-1$ try { user = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_USER_ID, ""); //$NON-NLS-1$ } catch (CoreException e) { /* ignored on purpose */ } userIdText.setText(user); } } protected void updateSkipDownloadFromConfig(ILaunchConfiguration config) { if (skipDownloadButton != null) { boolean downloadToTarget = true; try { downloadToTarget = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET, IRemoteTEConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET_DEFAULT); } catch (CoreException e) { /* ignored on purpose */ } skipDownloadButton.setSelection(downloadToTarget); } } protected void updateLaunchRemoteUserFromConfig(ILaunchConfiguration config) { if (userIdButton != null) { boolean launchRemoteUser = true; try { launchRemoteUser = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_LAUNCH_REMOTE_USER, IRemoteTEConfigurationConstants.ATTR_LAUNCH_REMOTE_USER_DEFAULT); } catch (CoreException e) { /* ignored on purpose */ } userIdButton.setSelection(launchRemoteUser); updateLaunchRemoteUserControls(); } } private void updateLaunchRemoteUserControls() { if (userIdText != null && userIdButton != null) { userIdText.setEnabled(userIdButton.getSelection()); } } /** * Sets the remote path from the local path. Apply path mappings before * setting the remote path. */ private void setRemotePathFromLocalPath() { String programName = fProgText.getText().trim(); if (programName != null && !"".equals(programName) && remoteProgText != null) { //$NON-NLS-1$ IPeerNode connection = peerSelector.getPeerNode(); if (connection != null) { IPathMapResolverService svc = ServiceManager.getInstance().getService(connection, IPathMapResolverService.class); if (svc != null) { String remoteName = null; // The program name may contain variables, resolve them first try { programName = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(programName); } catch (CoreException e) { // Silently ignore substitution failure (for consistency with "Arguments" and "Work directory" fields) } // The path might be project relative IPath p = new Path(programName); if (!p.isAbsolute()) { ICProject project = getCProject(); if (project != null && project.isOpen()) { URI uri = project.getLocationURI(); File f = URIUtil.toFile(uri); if (f != null) { programName = new File(f, p.toString()).getAbsolutePath(); } } } // Perform the path resolution remoteName = svc.findTargetPath(connection, programName); if (remoteName != null) { boolean prevRemoteProgTextFireNotification = remoteProgTextFireNotification; remoteProgTextFireNotification = false; remoteProgText.setText(remoteName); remoteProgTextFireNotification = prevRemoteProgTextFireNotification; } } } } } /** * Sets the local path from the remote path. Apply path mappings before * setting the local path. */ private void setLocalPathFromRemotePath() { String remoteName = remoteProgText.getText().trim(); if (remoteName != null && !"".equals(remoteName)) { //$NON-NLS-1$ IPeerNode connection = peerSelector.getPeerNode(); if (connection != null) { IPathMapResolverService svc = ServiceManager.getInstance().getService(connection, IPathMapResolverService.class); if (svc != null) { String programName = svc.findHostPath(connection, remoteName); if (programName == null) { // If there is no match, try to use the parent directory String programParent = svc.findHostPath(connection, Path.fromPortableString(remoteName).removeLastSegments(1).toPortableString()); if (programParent != null) { programName = new File(programParent, Path.fromPortableString(remoteName).lastSegment()).toString(); } else { programName = ""; //$NON-NLS-1$ } } boolean prevProgTextFireNotification = progTextFireNotification; progTextFireNotification = false; fProgText.setText(programName); progTextFireNotification = prevProgTextFireNotification; } } } } @Override public void initializeFrom(ILaunchConfiguration config) { String remoteConnection = null; try { remoteConnection = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_CONNECTION, ""); //$NON-NLS-1$ } catch (CoreException ce) { // Ignore } peerSelector.updateSelectionFrom(remoteConnection); super.initializeFrom(config); updateTargetProgFromConfig(config); updateSkipDownloadFromConfig(config); updateLaunchRemoteUserFromConfig(config); } /* * performApply * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply */ @Override public void performApply(ILaunchConfigurationWorkingCopy config) { String currentSelection = peerSelector.getPeerId(); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_CONNECTION, currentSelection != null ? currentSelection : null); if (remoteProgText != null) { String value = remoteProgText.getText(); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_PATH, !"".equals(value.trim()) ? value.trim() : (String)null); //$NON-NLS-1$ } if (pidText != null) { String value = pidText.getText(); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_PID, !"".equals(value.trim()) ? value.trim() : (String)null); //$NON-NLS-1$ } if (skipDownloadButton != null) { config.setAttribute(IRemoteTEConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET, skipDownloadButton.getSelection()); } if (preRunText != null) { String value = preRunText.getText(); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_PRERUN_COMMANDS, !"".equals(value.trim()) ? value.trim() : (String)null); //$NON-NLS-1$ } if (userIdText != null) { String value = userIdText.getText(); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_USER_ID, !"".equals(value.trim()) ? value.trim() : (String)null); //$NON-NLS-1$ } if (userIdButton != null) { config.setAttribute(IRemoteTEConfigurationConstants.ATTR_LAUNCH_REMOTE_USER, userIdButton.getSelection()); } super.performApply(config); } @Override public void setDefaults(ILaunchConfigurationWorkingCopy config) { super.setDefaults(config); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_CONNECTION, (String)null); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_PATH, (String)null); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_PID, (String)null); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET, IRemoteTEConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET_DEFAULT); config.setAttribute(IRemoteTEConfigurationConstants.ATTR_PRERUN_COMMANDS, (String)null); } protected void showCommandsEditor() { Dialog dialog = new Dialog(getShell()) { private StyledText textArea = null; @Override protected Control createDialogArea(Composite parent) { Composite baseComposite = (Composite) super.createDialogArea(parent); baseComposite.getShell().setText(Messages.RemoteCMainTab_Prerun_Edit_Dialog_Title); textArea = new StyledText(baseComposite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); textArea.setAlwaysShowScrollBars(false); GridData textAreaGD = new GridData(GridData.FILL_BOTH); textAreaGD.heightHint = 180; textAreaGD.widthHint = 300; textArea.setLayoutData(textAreaGD); textArea.setText(preRunText.getText()); return baseComposite; } @Override protected void okPressed() { if (preRunText != null && textArea != null) { preRunText.setText(textArea.getText()); } super.okPressed(); } @Override protected boolean isResizable() { return true; } }; dialog.open(); } }