| /******************************************************************************* |
| * Copyright (c) 2015, 2020 Red Hat Inc. and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Red Hat - Initial Contribution |
| *******************************************************************************/ |
| package org.eclipse.linuxtools.internal.docker.ui.launch; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.databinding.DataBindingContext; |
| import org.eclipse.core.databinding.beans.typed.BeanProperties; |
| import org.eclipse.core.databinding.observable.value.IValueChangeListener; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; |
| import org.eclipse.jface.databinding.swt.typed.WidgetProperties; |
| import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; |
| import org.eclipse.jface.databinding.viewers.ViewerSupport; |
| import org.eclipse.jface.databinding.viewers.ViewersObservables; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.layout.GridDataFactory; |
| import org.eclipse.jface.layout.GridLayoutFactory; |
| 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.TableViewer; |
| import org.eclipse.jface.viewers.TableViewerColumn; |
| import org.eclipse.linuxtools.docker.ui.Activator; |
| import org.eclipse.linuxtools.internal.docker.ui.SWTImagesFactory; |
| import org.eclipse.linuxtools.internal.docker.ui.wizards.ContainerPortDialog; |
| import org.eclipse.linuxtools.internal.docker.ui.wizards.ImageRunSelectionModel; |
| import org.eclipse.linuxtools.internal.docker.ui.wizards.ImageRunSelectionModel.ExposedPortModel; |
| import org.eclipse.linuxtools.internal.docker.ui.wizards.WizardMessages; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.graphics.Image; |
| 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.Table; |
| import org.eclipse.swt.widgets.TableColumn; |
| |
| public class RunImagePortsTab extends AbstractLaunchConfigurationTab |
| { |
| |
| private static final String TAB_NAME = "RunPortsTab.name"; //$NON-NLS-1$ |
| |
| private static final int INDENT = 30; |
| |
| private static final int COLUMNS = 3; |
| |
| private final DataBindingContext dbc = new DataBindingContext(); |
| |
| private final ImageRunSelectionModel model; |
| |
| public RunImagePortsTab(ImageRunSelectionModel model) { |
| this.model = model; |
| } |
| |
| @Override |
| public void createControl(Composite parent) { |
| final Composite container = new Composite(parent, SWT.NONE); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1) |
| .grab(true, false).applyTo(container); |
| GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(6, 6) |
| .applyTo(container); |
| if (model == null) { |
| setErrorMessage(LaunchMessages.getString("NoConnectionError.msg")); |
| } else { |
| setErrorMessage(null); |
| createPortSettingsSection(container); |
| } |
| setControl(container); |
| } |
| |
| private void createPortSettingsSection(final Composite container) { |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) |
| .grab(false, false).span(3, 1) |
| .applyTo(new Label(container, SWT.NONE)); |
| final Button publishAllPortsButton = new Button(container, SWT.CHECK); |
| publishAllPortsButton.setText(WizardMessages |
| .getString("ImageRunSelectionPage.publishAllPorts")); //$NON-NLS-1$ |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) |
| .span(COLUMNS, 1).grab(true, false) |
| .applyTo(publishAllPortsButton); |
| dbc.bindValue( |
| WidgetProperties.widgetSelection().observe(publishAllPortsButton), |
| BeanProperties |
| .value(ImageRunSelectionModel.class, |
| ImageRunSelectionModel.PUBLISH_ALL_PORTS) |
| .observe(model)); |
| // specify ports |
| final Label portSettingsLabel = new Label(container, SWT.NONE); |
| portSettingsLabel.setText( |
| WizardMessages.getString("ImageRunSelectionPage.portSettings")); //$NON-NLS-1$ |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) |
| .grab(true, false).span(COLUMNS, 1).indent(INDENT, 0) |
| .applyTo(portSettingsLabel); |
| final CheckboxTableViewer exposedPortsTableViewer = createPortSettingsTable( |
| container); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL) |
| .grab(true, true).span(COLUMNS - 1, 1).indent(INDENT, 0) |
| .hint(200, 70).applyTo(exposedPortsTableViewer.getTable()); |
| // buttons |
| final Composite buttonsContainers = new Composite(container, SWT.NONE); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP) |
| .grab(false, false).applyTo(buttonsContainers); |
| GridLayoutFactory.fillDefaults().numColumns(1).margins(0, 0) |
| .spacing(SWT.DEFAULT, 0).applyTo(buttonsContainers); |
| |
| final Button addButton = new Button(buttonsContainers, SWT.NONE); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP) |
| .grab(true, false).applyTo(addButton); |
| addButton |
| .setText(WizardMessages.getString("ImageRunSelectionPage.add")); //$NON-NLS-1$ |
| addButton.addSelectionListener(onAddPort(exposedPortsTableViewer)); |
| final Button editButton = new Button(buttonsContainers, SWT.NONE); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP) |
| .grab(true, false).applyTo(editButton); |
| editButton.setText( |
| WizardMessages.getString("ImageRunSelectionPage.editButton")); //$NON-NLS-1$ |
| editButton.setEnabled(false); |
| editButton.addSelectionListener(onEditPort(exposedPortsTableViewer)); |
| final Button removeButton = new Button(buttonsContainers, SWT.NONE); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP) |
| .grab(true, false).applyTo(removeButton); |
| removeButton.setText( |
| WizardMessages.getString("ImageRunSelectionPage.remove")); //$NON-NLS-1$ |
| removeButton |
| .addSelectionListener(onRemovePorts(exposedPortsTableViewer)); |
| BeanProperties |
| .value(ImageRunSelectionModel.class, |
| ImageRunSelectionModel.PUBLISH_ALL_PORTS) |
| .observe(model) |
| .addValueChangeListener(onPublishAllPortsChange( |
| exposedPortsTableViewer.getTable(), addButton, |
| removeButton)); |
| ViewerSupport.bind(exposedPortsTableViewer, model.getExposedPorts(), |
| BeanProperties.values(ExposedPortModel.class, |
| ExposedPortModel.CONTAINER_PORT, |
| ExposedPortModel.PORT_TYPE, |
| ExposedPortModel.HOST_ADDRESS, |
| ExposedPortModel.HOST_PORT)); |
| dbc.bindSet( |
| ViewersObservables.observeCheckedElements( |
| exposedPortsTableViewer, ExposedPortModel.class), |
| BeanProperties.set(ImageRunSelectionModel.SELECTED_PORTS) |
| .observe(model)); |
| |
| // disable the edit and removeButton if the table is empty |
| exposedPortsTableViewer.addSelectionChangedListener( |
| onSelectionChanged(editButton, removeButton)); |
| exposedPortsTableViewer.addCheckStateListener(onCheckStateChanged()); |
| |
| togglePortMappingControls(exposedPortsTableViewer.getTable(), addButton, |
| removeButton); |
| } |
| |
| private static void setControlsEnabled(final Control[] controls, |
| final boolean enabled) { |
| for (Control control : controls) { |
| control.setEnabled(enabled); |
| } |
| } |
| |
| private ISelectionChangedListener onSelectionChanged( |
| final Button... targetButtons) { |
| return e -> { |
| if (e.getSelection().isEmpty()) { |
| setControlsEnabled(targetButtons, false); |
| } else { |
| setControlsEnabled(targetButtons, true); |
| } |
| }; |
| } |
| |
| private ICheckStateListener onCheckStateChanged() { |
| return e -> { |
| ImageRunSelectionModel.ExposedPortModel element = (ImageRunSelectionModel.ExposedPortModel) e |
| .getElement(); |
| if (e.getChecked()) { |
| model.getSelectedPorts().add(element); |
| element.setSelected(true); |
| } else { |
| model.getSelectedPorts().remove(element); |
| element.setSelected(false); |
| } |
| updateLaunchConfigurationDialog(); |
| }; |
| } |
| |
| private CheckboxTableViewer createPortSettingsTable( |
| final Composite container) { |
| final Table table = new Table(container, SWT.BORDER | SWT.FULL_SELECTION |
| | SWT.V_SCROLL | SWT.H_SCROLL | SWT.CHECK); |
| final CheckboxTableViewer tableViewer = new CheckboxTableViewer(table); |
| table.setHeaderVisible(true); |
| table.setLinesVisible(true); |
| createTableViewerColumn(tableViewer, |
| WizardMessages |
| .getString("ImageRunSelectionPage.containerPortColumn"), //$NON-NLS-1$ |
| 100); |
| createTableViewerColumn(tableViewer, |
| WizardMessages.getString("ImageRunSelectionPage.typeColumn"), //$NON-NLS-1$ |
| 50); |
| createTableViewerColumn(tableViewer, |
| WizardMessages |
| .getString("ImageRunSelectionPage.hostAddressColumn"), //$NON-NLS-1$ |
| 100); |
| createTableViewerColumn(tableViewer, |
| WizardMessages |
| .getString("ImageRunSelectionPage.hostPortColumn"), //$NON-NLS-1$ |
| 100); |
| tableViewer.setContentProvider(new ObservableListContentProvider<>()); |
| return tableViewer; |
| } |
| |
| private TableViewerColumn createTableViewerColumn( |
| final TableViewer tableViewer, final String title, |
| final int width) { |
| final TableViewerColumn viewerColumn = new TableViewerColumn( |
| tableViewer, SWT.NONE); |
| final TableColumn column = viewerColumn.getColumn(); |
| if (title != null) { |
| column.setText(title); |
| } |
| column.setWidth(width); |
| return viewerColumn; |
| } |
| |
| private IValueChangeListener onPublishAllPortsChange( |
| final Control... controls) { |
| return event -> togglePortMappingControls(controls); |
| } |
| |
| private SelectionListener onAddPort( |
| final CheckboxTableViewer exposedPortsTableViewer) { |
| return SelectionListener.widgetSelectedAdapter(e -> { |
| final ContainerPortDialog dialog = new ContainerPortDialog( |
| getShell()); |
| dialog.create(); |
| if (dialog.open() == IDialogConstants.OK_ID) { |
| final ExposedPortModel port = dialog.getPort(); |
| model.addAvailablePort(port); |
| model.getSelectedPorts().add(port); |
| port.setSelected(true); |
| exposedPortsTableViewer.setChecked(port, true); |
| updateLaunchConfigurationDialog(); |
| } |
| }); |
| } |
| |
| private SelectionListener onEditPort( |
| final CheckboxTableViewer exposedPortsTableViewer) { |
| return SelectionListener.widgetSelectedAdapter(e -> { |
| final IStructuredSelection selection = exposedPortsTableViewer |
| .getStructuredSelection(); |
| final ExposedPortModel selectedContainerPort = (ExposedPortModel) selection |
| .getFirstElement(); |
| final ContainerPortDialog dialog = new ContainerPortDialog( |
| getShell(), selectedContainerPort); |
| dialog.create(); |
| if (dialog.open() == IDialogConstants.OK_ID) { |
| final ExposedPortModel configuredPort = dialog.getPort(); |
| selectedContainerPort |
| .setContainerPort(configuredPort.getContainerPort()); |
| selectedContainerPort |
| .setHostAddress(configuredPort.getHostAddress()); |
| selectedContainerPort.setHostPort(configuredPort.getHostPort()); |
| } |
| updateLaunchConfigurationDialog(); |
| }); |
| } |
| |
| private SelectionListener onRemovePorts( |
| final TableViewer portsTableViewer) { |
| return SelectionListener.widgetSelectedAdapter(e -> { |
| final IStructuredSelection selection = portsTableViewer |
| .getStructuredSelection(); |
| for (@SuppressWarnings("unchecked") |
| Iterator<ExposedPortModel> iterator = selection.iterator(); iterator |
| .hasNext();) { |
| final ExposedPortModel port = iterator.next(); |
| model.removeAvailablePort(port); |
| model.getSelectedPorts().remove(port); |
| } |
| updateLaunchConfigurationDialog(); |
| }); |
| } |
| |
| private void togglePortMappingControls(final Control... controls) { |
| for (Control control : controls) { |
| if (model.isPublishAllPorts()) { |
| control.setEnabled(false); |
| } else { |
| control.setEnabled(true); |
| } |
| updateLaunchConfigurationDialog(); |
| } |
| } |
| |
| @Override |
| public Image getImage() { |
| return SWTImagesFactory.get(SWTImagesFactory.IMG_CONTAINER_PORT); |
| } |
| |
| @Override |
| public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { |
| |
| } |
| |
| @Override |
| public void initializeFrom(ILaunchConfiguration configuration) { |
| if (model == null) |
| return; |
| try { |
| // recycle the model |
| model.removeExposedPorts(); |
| final boolean publishAllPorts = configuration.getAttribute( |
| IRunDockerImageLaunchConfigurationConstants.PUBLISH_ALL_PORTS, |
| true); |
| model.setPublishAllPorts(publishAllPorts); |
| final List<String> publishedPorts = configuration.getAttribute( |
| IRunDockerImageLaunchConfigurationConstants.PUBLISHED_PORTS, |
| new ArrayList<String>()); |
| final List<String> unusedPorts = configuration |
| .getAttribute(IRunDockerImageLaunchConfigurationConstants.UNUSED_PORTS, new ArrayList<String>()); |
| final Set<ExposedPortModel> selectedPorts = new HashSet<>(); |
| for (String port : publishedPorts) { |
| final ImageRunSelectionModel.ExposedPortModel exposedPort = ImageRunSelectionModel.ExposedPortModel |
| .createPortModel(port); |
| model.addExposedPort(exposedPort); |
| if (exposedPort.getSelected()) { |
| selectedPorts.add(exposedPort); |
| } |
| } |
| for (String port : unusedPorts) { |
| final ImageRunSelectionModel.ExposedPortModel exposedPort = ImageRunSelectionModel.ExposedPortModel |
| .createPortModel(port); |
| exposedPort.setSelected(false); |
| model.addExposedPort(exposedPort); |
| } |
| |
| // select ports |
| model.setSelectedPorts(selectedPorts); |
| |
| // update the underlying launch config working copy on model |
| // changes. |
| model.addPropertyChangeListener( |
| new LaunchConfigurationChangeListener()); |
| } catch (CoreException e) { |
| Activator.logErrorMessage( |
| LaunchMessages.getString( |
| "RunDockerImageLaunchConfiguration.load.failure"), //$NON-NLS-1$ |
| e); |
| } |
| |
| } |
| |
| @Override |
| public void performApply(ILaunchConfigurationWorkingCopy configuration) { |
| if (model == null) |
| return; |
| configuration.setAttribute( |
| IRunDockerImageLaunchConfigurationConstants.PUBLISH_ALL_PORTS, |
| model.isPublishAllPorts()); |
| List<String> portsList = new ArrayList<>(); |
| for (ImageRunSelectionModel.ExposedPortModel m : model.getExposedPorts()) { |
| portsList.add(m.toString()); |
| } |
| configuration.setAttribute( |
| IRunDockerImageLaunchConfigurationConstants.PUBLISHED_PORTS, |
| portsList); |
| } |
| |
| @Override |
| public String getName() { |
| return LaunchMessages.getString(TAB_NAME); |
| } |
| |
| private class LaunchConfigurationChangeListener |
| implements PropertyChangeListener { |
| |
| @Override |
| public void propertyChange(final PropertyChangeEvent evt) { |
| updateLaunchConfigurationDialog(); |
| } |
| } |
| |
| } |