blob: 4822502638b76f2f13d5b74db211fd50833fbf8a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 Red Hat 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:
* Red Hat - Initial Contribution
*******************************************************************************/
package org.eclipse.linuxtools.internal.docker.ui.launch;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.beans.IBeanValueProperty;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.databinding.observable.map.IObservableMap;
import org.eclipse.core.databinding.observable.set.IObservableSet;
import org.eclipse.core.databinding.property.Properties;
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.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
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.StructuredViewer;
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.ContainerDataVolumeDialog;
import org.eclipse.linuxtools.internal.docker.ui.wizards.DataVolumeModel;
import org.eclipse.linuxtools.internal.docker.ui.wizards.ImageRunResourceVolumesVariablesModel;
import org.eclipse.linuxtools.internal.docker.ui.wizards.ImageRunResourceVolumesVariablesModel.MountType;
import org.eclipse.linuxtools.internal.docker.ui.wizards.WizardMessages;
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.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 RunImageVolumesTab extends AbstractLaunchConfigurationTab {
private static final String TAB_NAME = "RunVolumesTab.name"; //$NON-NLS-1$
private static final int COLUMNS = 3;
private final DataBindingContext dbc = new DataBindingContext();
private ImageRunResourceVolumesVariablesModel model = null;
public RunImageVolumesTab(ImageRunResourceVolumesVariablesModel model) {
this.model = model;
}
public ImageRunResourceVolumesVariablesModel getModel() {
return model;
}
@Override
public void createControl(Composite parent) {
final Composite container = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(6, 6)
.applyTo(container);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)
.applyTo(container);
if (model == null) {
setErrorMessage(LaunchMessages.getString("NoConnectionError.msg"));
} else {
setErrorMessage(null);
createVolumeSettingsContainer(container);
}
setControl(container);
}
private void createVolumeSettingsContainer(final Composite container) {
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).span(3, 1)
.applyTo(new Label(container, SWT.NONE));
final Label volumesLabel = new Label(container, SWT.NONE);
volumesLabel.setText(WizardMessages
.getString("ImageRunResourceVolVarPage.dataVolumesLabel")); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).span(COLUMNS, 1).applyTo(volumesLabel);
final CheckboxTableViewer dataVolumesTableViewer = createVolumesTable(
container);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).hint(200, 100)
.applyTo(dataVolumesTableViewer.getTable());
// update table content when selected image changes
bind(dataVolumesTableViewer, model.getDataVolumes(),
BeanProperties.values(DataVolumeModel.class,
new String[] { DataVolumeModel.CONTAINER_PATH,
DataVolumeModel.MOUNT,
DataVolumeModel.READ_ONLY_VOLUME }));
final IObservableSet selectedVolumesObservable = BeanProperties
.set(ImageRunResourceVolumesVariablesModel.SELECTED_DATA_VOLUMES)
.observe(model);
dbc.bindSet(
ViewersObservables.observeCheckedElements(
dataVolumesTableViewer, DataVolumeModel.class),
selectedVolumesObservable);
dataVolumesTableViewer.addCheckStateListener(onCheckStateChanged());
// initializes the checkboxes selection upon loading the model.
// remove ?
// selectedVolumesObservable.addChangeListener(new
// IChangeListener() {
//
// @Override
// public void handleChange(ChangeEvent event) {
// final IObservableSet observable = (IObservableSet) event
// .getObservable();
// for (Iterator<?> iterator = observable.iterator(); iterator
// .hasNext();) {
// final DataVolumeModel volume = (DataVolumeModel) iterator
// .next();
// dataVolumesTableViewer.setChecked(volume, true);
// }
// updateLaunchConfigurationDialog();
// }
// });
// 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("ImageRunResourceVolVarPage.addButton")); //$NON-NLS-1$
addButton.addSelectionListener(onAddDataVolume(dataVolumesTableViewer));
final Button editButton = new Button(buttonsContainers, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).applyTo(editButton);
editButton.setText(WizardMessages
.getString("ImageRunResourceVolVarPage.editButton")); //$NON-NLS-1$
editButton
.addSelectionListener(onEditDataVolume(dataVolumesTableViewer));
editButton.setEnabled(false);
final Button removeButton = new Button(buttonsContainers, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).applyTo(removeButton);
removeButton.setText(WizardMessages
.getString("ImageRunResourceVolVarPage.removeButton")); //$NON-NLS-1$
removeButton.addSelectionListener(
onRemoveDataVolumes(dataVolumesTableViewer));
removeButton.setEnabled(false);
// disable the edit and removeButton if the table is empty
dataVolumesTableViewer.addSelectionChangedListener(
onSelectionChanged(editButton, removeButton));
}
private ICheckStateListener onCheckStateChanged() {
return e -> {
DataVolumeModel element = (DataVolumeModel) e.getElement();
if (e.getChecked()) {
model.getSelectedDataVolumes().add(element);
element.setSelected(true);
} else {
model.getSelectedDataVolumes().remove(element);
element.setSelected(false);
}
updateLaunchConfigurationDialog();
};
}
/**
* Same as
* {@link org.eclipse.jface.databinding.viewers.ViewerSupport#bind(StructuredViewer, IObservableList, org.eclipse.core.databinding.property.value.IValueProperty[])
* but with a custom LabelProvider, DataVolumesLabelProvider
*
* @param viewer
* @param input
* @param labelProperties
*/
private void bind(final StructuredViewer viewer,
final IObservableList input,
final IBeanValueProperty[] labelProperties) {
final ObservableListContentProvider contentProvider = new ObservableListContentProvider();
if (viewer.getInput() != null) {
viewer.setInput(null);
}
viewer.setContentProvider(contentProvider);
viewer.setLabelProvider(
new DataVolumesLabelProvider(Properties.observeEach(
contentProvider.getKnownElements(), labelProperties)));
if (input != null) {
viewer.setInput(input);
}
}
private ISelectionChangedListener onSelectionChanged(
final Button... targetButtons) {
return e -> {
if (e.getSelection().isEmpty()) {
setControlsEnabled(targetButtons, false);
updateLaunchConfigurationDialog();
} else {
setControlsEnabled(targetButtons, true);
updateLaunchConfigurationDialog();
}
};
}
private SelectionListener onAddDataVolume(
final CheckboxTableViewer dataVolumesTableViewer) {
return new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final ContainerDataVolumeDialog dialog = new ContainerDataVolumeDialog(
getShell(), model.getConnection());
dialog.create();
if (dialog.open() == IDialogConstants.OK_ID) {
model.getDataVolumes().add(dialog.getDataVolume());
model.getSelectedDataVolumes().add(dialog.getDataVolume());
dialog.getDataVolume().setSelected(true);
dataVolumesTableViewer.setChecked(dialog.getDataVolume(),
true);
updateLaunchConfigurationDialog();
}
}
};
}
private SelectionListener onEditDataVolume(
final CheckboxTableViewer dataVolumesTableViewer) {
return new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final IStructuredSelection selection = (IStructuredSelection) dataVolumesTableViewer
.getSelection();
if (selection.isEmpty()) {
return;
}
final DataVolumeModel selectedDataVolume = (DataVolumeModel) selection
.getFirstElement();
final ContainerDataVolumeDialog dialog = new ContainerDataVolumeDialog(
getShell(), model.getConnection(), selectedDataVolume);
dialog.create();
if (dialog.open() == IDialogConstants.OK_ID) {
final DataVolumeModel dialogDataVolume = dialog
.getDataVolume();
selectedDataVolume.setContainerMount(
dialogDataVolume.getContainerMount());
selectedDataVolume
.setMountType(dialogDataVolume.getMountType());
selectedDataVolume.setHostPathMount(
dialogDataVolume.getHostPathMount());
selectedDataVolume.setContainerMount(
dialogDataVolume.getContainerMount());
selectedDataVolume
.setReadOnly(dialogDataVolume.isReadOnly());
model.getSelectedDataVolumes().add(selectedDataVolume);
dataVolumesTableViewer.setChecked(selectedDataVolume, true);
updateLaunchConfigurationDialog();
}
}
};
}
private SelectionListener onRemoveDataVolumes(
final TableViewer dataVolumesTableViewer) {
return new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final IStructuredSelection selection = dataVolumesTableViewer
.getStructuredSelection();
for (@SuppressWarnings("unchecked")
Iterator<DataVolumeModel> iterator = selection
.iterator(); iterator.hasNext();) {
final DataVolumeModel volume = iterator.next();
model.removeDataVolume(volume);
model.getSelectedDataVolumes().remove(volume);
}
updateLaunchConfigurationDialog();
}
};
}
private CheckboxTableViewer createVolumesTable(final Composite container) {
final Table table = new Table(container, SWT.CHECK | SWT.BORDER
| SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL);
final CheckboxTableViewer tableViewer = new CheckboxTableViewer(table);
table.setHeaderVisible(true);
table.setLinesVisible(true);
addTableViewerColumn(tableViewer,
WizardMessages.getString(
"ImageRunResourceVolVarPage.containerPathColumn"), //$NON-NLS-1$
150);
addTableViewerColumn(tableViewer,
WizardMessages
.getString("ImageRunResourceVolVarPage.mountColumn"), //$NON-NLS-1$
150);
addTableViewerColumn(tableViewer,
WizardMessages
.getString("ImageRunResourceVolVarPage.readonlyColumn"), //$NON-NLS-1$
60);
return tableViewer;
}
private TableViewerColumn addTableViewerColumn(
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 static void setControlsEnabled(final Control[] controls,
final boolean enabled) {
for (Control control : controls) {
control.setEnabled(enabled);
}
}
private static final class DataVolumesLabelProvider
extends ObservableMapLabelProvider {
private Image CONTAINER_IMAGE = SWTImagesFactory.DESC_CONTAINER
.createImage();
private Image FOLDER_CLOSED_IMAGE = SWTImagesFactory.DESC_FOLDER_CLOSED
.createImage();
private Image FILE_IMAGE = SWTImagesFactory.DESC_FILE.createImage();
public DataVolumesLabelProvider(final IObservableMap[] attributeMaps) {
super(attributeMaps);
}
@Override
public void dispose() {
CONTAINER_IMAGE.dispose();
FOLDER_CLOSED_IMAGE.dispose();
FILE_IMAGE.dispose();
super.dispose();
}
@Override
public Image getColumnImage(Object element, int columnIndex) {
final DataVolumeModel dataVolume = ((DataVolumeModel) element);
if (dataVolume.getMountType() != null && columnIndex == 1) {
switch (dataVolume.getMountType()) {
case CONTAINER:
return CONTAINER_IMAGE;
case HOST_FILE_SYSTEM:
final File hostFile = new File(dataVolume.getMount());
if (!hostFile.exists() || hostFile.isDirectory()) {
return FOLDER_CLOSED_IMAGE;
} else {
return FILE_IMAGE;
}
default:
return null;
}
}
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
final DataVolumeModel dataVolume = ((DataVolumeModel) element);
switch (columnIndex) {
case 0:
return dataVolume.getContainerPath();
case 1:
return dataVolume.getMount();
case 2:
if (dataVolume.getMountType() != MountType.HOST_FILE_SYSTEM) {
return null;
} else if (dataVolume.isReadOnly()) {
return WizardMessages
.getString("ImageRunResourceVolVarPage.true"); //$NON-NLS-1$
}
return WizardMessages
.getString("ImageRunResourceVolVarPage.false"); //$NON-NLS-1$
default:
return null;
}
}
}
@Override
public Image getImage() {
return SWTImagesFactory.get(SWTImagesFactory.IMG_CONTAINER_VOLUME);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
}
@Override
public void initializeFrom(ILaunchConfiguration configuration) {
if (model == null)
return;
final List<DataVolumeModel> volumes = new ArrayList<>();
try {
final List<String> volumesList = configuration.getAttribute(
IRunDockerImageLaunchConfigurationConstants.DATA_VOLUMES,
new ArrayList<String>());
final Set<DataVolumeModel> selectedVolumes = new HashSet<>();
for (String volume : volumesList) {
DataVolumeModel volumeModel = DataVolumeModel
.parseString(volume);
volumes.add(volumeModel);
if (volumeModel.getSelected()) {
selectedVolumes.add(volumeModel);
}
}
model.setDataVolumes(volumes);
model.setSelectedDataVolumes(selectedVolumes);
} catch (CoreException e) {
Activator.logErrorMessage(
LaunchMessages.getString(
"RunDockerImageLaunchConfiguration.load.failure"), //$NON-NLS-1$
e);
}
// update the underlying launch config working copy on model
// changes.
model.addPropertyChangeListener(
new LaunchConfigurationChangeListener());
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
if (model == null)
return;
WritableList<DataVolumeModel> volumes = model.getDataVolumes();
Set<DataVolumeModel> selectedVolumes = model.getSelectedDataVolumes();
ArrayList<String> binds = new ArrayList<>();
ArrayList<String> volumesFrom = new ArrayList<>();
ArrayList<String> volumesList = new ArrayList<>();
Set<String> selectedVolumesSet = new TreeSet<>();
for (Iterator<DataVolumeModel> iterator = volumes.iterator(); iterator
.hasNext();) {
DataVolumeModel volume = iterator.next();
StringBuffer buffer = new StringBuffer();
volumesList.add(volume.toString());
switch (volume.getMountType()) {
case HOST_FILE_SYSTEM:
buffer.append(volume.getHostPathMount() + ","
+ volume.getHostPathMount() + ","
+ volume.isReadOnly());
if (selectedVolumes.contains(volume)) {
selectedVolumesSet.add(volume.toString());
String bind = LaunchConfigurationUtils
.convertToUnixPath(volume.getHostPathMount())
+ ':' + volume.getContainerPath() + ':' + 'Z';
if (volume.isReadOnly()) {
bind += ",ro"; //$NON-NLS-1$
}
binds.add(bind);
}
break;
case CONTAINER:
if (selectedVolumes.contains(volume)) {
selectedVolumesSet.add(volume.toString());
volumesFrom.add(volume.getContainerMount());
}
break;
default:
break;
}
}
configuration.setAttribute(
IRunDockerImageLaunchConfigurationConstants.BINDS, binds);
configuration.setAttribute(
IRunDockerImageLaunchConfigurationConstants.VOLUMES_FROM,
volumesFrom);
configuration.setAttribute(
IRunDockerImageLaunchConfigurationConstants.DATA_VOLUMES,
volumesList);
}
@Override
public String getName() {
return LaunchMessages.getString(TAB_NAME);
}
private class LaunchConfigurationChangeListener
implements PropertyChangeListener {
@Override
public void propertyChange(final PropertyChangeEvent evt) {
updateLaunchConfigurationDialog();
}
}
}