/******************************************************************************* * Copyright (c) 2016 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.openshift.internal.ui.wizard.common; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.beans.BeanProperties; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; import org.eclipse.jface.databinding.viewers.ViewerProperties; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.wizard.IWizard; import org.eclipse.osgi.util.NLS; 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.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Table; import org.jboss.tools.common.ui.databinding.ValueBindingBuilder; import org.jboss.tools.openshift.internal.common.ui.databinding.IsNotNull2BooleanConverter; import org.jboss.tools.openshift.internal.common.ui.utils.TableViewerBuilder; import org.jboss.tools.openshift.internal.common.ui.utils.UIUtils; import org.jboss.tools.openshift.internal.common.ui.utils.TableViewerBuilder.IColumnLabelProvider; import org.jboss.tools.openshift.internal.common.ui.wizard.AbstractOpenShiftWizardPage; import org.jboss.tools.openshift.internal.common.ui.wizard.IKeyValueItem; import org.jboss.tools.openshift.internal.common.ui.wizard.IKeyValueWizardModel; import org.jboss.tools.openshift.internal.common.ui.wizard.KeyValueItem; import org.jboss.tools.openshift.internal.common.ui.wizard.KeyValueWizard; import org.jboss.tools.openshift.internal.common.ui.wizard.KeyValueWizardModelBuilder; import org.jboss.tools.openshift.internal.common.ui.wizard.OkCancelButtonWizardDialog; import org.jboss.tools.openshift.internal.ui.validator.EnvironmentVarKeyValidator; import org.jboss.tools.openshift.internal.ui.wizard.deployimage.IDeploymentConfigPageModel; /** * Base for adding env variable section to a page * @author jeff.cantrill * */ public abstract class EnvironmentVariablePage extends AbstractOpenShiftWizardPage{ private static final String ENVIRONMENT_VARIABLE_LABEL = "Environment Variable"; private static final String ENVIRONMENT_VARIABLE_KEY_LABEL = "Name"; public static final String TABLE_LABEL = "Environment variables:"; public static final String TABLE_LABEL_TOOLTIP = "Environment variables are passed to running pods for consumption by the pod containers"; private IEnvironmentVariablesPageModel model; private TableViewer envViewer; protected int heightScale = 30; protected Composite envTableContainer; protected boolean canDeleteAnyVar = false; protected EnvironmentVariablePage(String title, String description, String name, IWizard wizard, IEnvironmentVariablesPageModel model) { super(title, description, name, wizard); this.model = model; } protected void createEnvVariableControl(Composite parent, DataBindingContext dbc) { createEnvVariableControl(parent, dbc, TABLE_LABEL, TABLE_LABEL_TOOLTIP); } @SuppressWarnings("unchecked") protected void createEnvVariableControl(Composite parent, DataBindingContext dbc, String label, String tooltip) { Composite envContainer = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults() .align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(envContainer); GridLayoutFactory.fillDefaults() .numColumns(2).margins(2, 2).applyTo(envContainer); Label lblEnvVars = new Label(envContainer, SWT.NONE); lblEnvVars.setText(label); lblEnvVars.setToolTipText(tooltip); GridDataFactory.fillDefaults() .align(SWT.FILL, SWT.FILL) .span(2,1) .applyTo(lblEnvVars); Composite tableContainer = envTableContainer = new Composite(envContainer, SWT.NONE); this.envViewer = createEnvVarTable(tableContainer); GridDataFactory.fillDefaults() .span(1, 5).align(SWT.FILL, SWT.FILL).grab(true, true).hint(SWT.DEFAULT, 150).applyTo(tableContainer); ValueBindingBuilder.bind(ViewerProperties.singleSelection().observe(envViewer)) .to(BeanProperties.value(IDeploymentConfigPageModel.PROPERTY_SELECTED_ENVIRONMENT_VARIABLE) .observe(model)) .in(dbc); envViewer.setContentProvider(new ObservableListContentProvider()); envViewer.setInput(BeanProperties.list( IDeploymentConfigPageModel.PROPERTY_ENVIRONMENT_VARIABLES).observe(model)); Button addButton = new Button(envContainer, SWT.PUSH); GridDataFactory.fillDefaults() .align(SWT.FILL, SWT.BEGINNING).applyTo(addButton); addButton.setText("Add..."); addButton.setToolTipText("Add an environment variable declared by the docker image."); addButton.addSelectionListener(onAdd()); UIUtils.setDefaultButtonWidth(addButton); heightScale = addButton.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; Button editExistingButton = new Button(envContainer, SWT.PUSH); GridDataFactory.fillDefaults() .align(SWT.FILL, SWT.BEGINNING).applyTo(editExistingButton); editExistingButton.setText("Edit..."); editExistingButton.setToolTipText("Edit the environment variable declared by the docker image."); editExistingButton.addSelectionListener(new EditHandler()); ValueBindingBuilder .bind(WidgetProperties.enabled().observe(editExistingButton)) .notUpdatingParticipant() .to(BeanProperties.value(IDeploymentConfigPageModel.PROPERTY_SELECTED_ENVIRONMENT_VARIABLE).observe(model)) .converting(new IsNotNull2BooleanConverter()) .in(dbc); UIUtils.setDefaultButtonWidth(editExistingButton); Button btnReset = new Button(envContainer, SWT.PUSH); GridDataFactory.fillDefaults() .align(SWT.FILL, SWT.BEGINNING).applyTo(btnReset); btnReset.setText("Reset"); btnReset.setToolTipText("Reset to the value declared by the docker image."); btnReset.addSelectionListener(onResetEnvVar()); ValueBindingBuilder .bind(WidgetProperties.enabled().observe(btnReset)) .notUpdatingParticipant() .to(BeanProperties.value(IDeploymentConfigPageModel.PROPERTY_SELECTED_ENVIRONMENT_VARIABLE).observe(model)) .converting(new IsNotNull2BooleanConverter() { @Override public Object convert(Object fromObject) { Boolean notNull = (Boolean) super.convert(fromObject); return notNull && !((EnvironmentVariable)fromObject).isNew() && model.isEnvironmentVariableModified((EnvironmentVariable)fromObject); } }) .in(dbc); UIUtils.setDefaultButtonWidth(btnReset); Button btnResetAll = new Button(envContainer, SWT.PUSH); GridDataFactory.fillDefaults() .align(SWT.FILL, SWT.BEGINNING).applyTo(btnResetAll); btnResetAll.setText("Reset All"); btnResetAll.setToolTipText("Reset all variables to the value declared by the docker image."); btnResetAll.addSelectionListener(onResetEnvVars()); ValueBindingBuilder .bind(WidgetProperties.enabled().observe(btnResetAll)) .notUpdatingParticipant() //This may look like a hack, but this property do change at each change to variables, so that button refresh will be consistent. .to(BeanProperties.value(IDeploymentConfigPageModel.PROPERTY_SELECTED_ENVIRONMENT_VARIABLE).observe(model)) .converting(new IsNotNull2BooleanConverter() { @Override public Object convert(Object fromObject) { List<EnvironmentVariable> vars = model.getEnvironmentVariables(); return vars != null && !vars.isEmpty() && vars.stream().anyMatch(v -> !v.isNew() && model.isEnvironmentVariableModified(v)); } }) .in(dbc); UIUtils.setDefaultButtonWidth(btnResetAll); Button btnRemove = new Button(envContainer, SWT.PUSH); GridDataFactory.fillDefaults() .align(SWT.FILL, SWT.BEGINNING).applyTo(btnRemove); btnRemove.setText("Remove"); btnRemove.setToolTipText("Remove the environment variable added here."); btnRemove.addSelectionListener(onRemoveEnvVar()); ValueBindingBuilder .bind(WidgetProperties.enabled().observe(btnRemove)) .notUpdatingParticipant() .to(BeanProperties.value(IDeploymentConfigPageModel.PROPERTY_SELECTED_ENVIRONMENT_VARIABLE).observe(model)) .converting(new IsNotNull2BooleanConverter() { @Override public Object convert(Object fromObject) { Boolean notNull = (Boolean) super.convert(fromObject); return notNull && (canDeleteAnyVar || ((EnvironmentVariable)fromObject).isNew()) && !model.isEnvironmentVariableDeleted((EnvironmentVariable)fromObject); } }) .in(dbc); UIUtils.setDefaultButtonWidth(btnRemove); } private SelectionListener onResetEnvVar() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { EnvironmentVariable envVar = UIUtils.getFirstElement(envViewer.getSelection(), EnvironmentVariable.class); if(MessageDialog.openQuestion(getShell(), "Reset " + ENVIRONMENT_VARIABLE_LABEL, NLS.bind("Are you sure you want to reset the {0} {1}?", ENVIRONMENT_VARIABLE_LABEL.toLowerCase(), envVar.getKey()))) { String key = envVar.getKey(); model.resetEnvironmentVariable(envVar); selectEnvVarByKey(key); } } }; } private SelectionListener onResetEnvVars() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { List<EnvironmentVariable> vars = model.getEnvironmentVariables(); EnvironmentVariable envVar = UIUtils.getFirstElement(envViewer.getSelection(), EnvironmentVariable.class); if(MessageDialog.openQuestion(getShell(), "Reset " + ENVIRONMENT_VARIABLE_LABEL, NLS.bind("Are you sure you want to reset all {0}s?", ENVIRONMENT_VARIABLE_LABEL.toLowerCase()))) { vars.stream().forEach(v -> model.resetEnvironmentVariable(v)); if(envVar != null) { selectEnvVarByKey(envVar.getKey()); } } } }; } private SelectionListener onRemoveEnvVar() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { EnvironmentVariable envVar = UIUtils.getFirstElement(envViewer.getSelection(), EnvironmentVariable.class); if(MessageDialog.openQuestion(getShell(), "Remove " + ENVIRONMENT_VARIABLE_LABEL, NLS.bind("Are you sure you want to remove the {0} {1}?", ENVIRONMENT_VARIABLE_LABEL.toLowerCase(), envVar.getKey()))) { model.removeEnvironmentVariable(envVar); envViewer.refresh(); } } }; } private class EditHandler extends SelectionAdapter implements IDoubleClickListener { @Override public void widgetSelected(SelectionEvent e) { handleEvent(); } @Override public void doubleClick(DoubleClickEvent event) { handleEvent(); } private void handleEvent() { EnvironmentVariable var = UIUtils.getFirstElement(envViewer.getSelection(), EnvironmentVariable.class); Set<String> usedKeys = model.getEnvironmentVariables().stream() .filter(v -> !model.isEnvironmentVariableDeleted(v)) .map(v -> v.getKey()).collect(Collectors.toSet()); usedKeys.remove(var.getKey()); EnvironmentVariable copy = new EnvironmentVariable(var.getKey(), model.isEnvironmentVariableDeleted(var) ? "" : var.getValue(), var.isNew()); IKeyValueWizardModel<IKeyValueItem> dialogModel = new KeyValueWizardModelBuilder<IKeyValueItem>(copy) .windowTitle(ENVIRONMENT_VARIABLE_LABEL) .title("Edit " + ENVIRONMENT_VARIABLE_LABEL) .description(NLS.bind("Edit the {0}.", ENVIRONMENT_VARIABLE_LABEL.toLowerCase())) .keyLabel(ENVIRONMENT_VARIABLE_KEY_LABEL) .editableKey(var.isNew() || canDeleteAnyVar) .groupLabel(ENVIRONMENT_VARIABLE_LABEL) .keyAfterConvertValidator(new EnvironmentVarKeyValidator(usedKeys)) .build(); OkCancelButtonWizardDialog dialog = new OkCancelButtonWizardDialog(getShell(), new KeyValueWizard<>(var, dialogModel)); if(OkCancelButtonWizardDialog.OK == dialog.open()) { model.updateEnvironmentVariable(var, dialogModel.getKey(), dialogModel.getValue()); selectEnvVarByKey(dialogModel.getKey()); } } } private SelectionListener onAdd() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Set<String> usedKeys = model.getEnvironmentVariables().stream() .filter(v -> !model.isEnvironmentVariableDeleted(v)) .map(v -> v.getKey()).collect(Collectors.toSet()); IKeyValueWizardModel<KeyValueItem> dialogModel = new KeyValueWizardModelBuilder<KeyValueItem>() .windowTitle(ENVIRONMENT_VARIABLE_LABEL) .title("Add " + ENVIRONMENT_VARIABLE_LABEL) .description(NLS.bind("Add an {0}.", ENVIRONMENT_VARIABLE_LABEL.toLowerCase())) .keyLabel(ENVIRONMENT_VARIABLE_KEY_LABEL) .groupLabel(ENVIRONMENT_VARIABLE_LABEL) .keyAfterConvertValidator(new EnvironmentVarKeyValidator(usedKeys)) .build(); OkCancelButtonWizardDialog dialog = new OkCancelButtonWizardDialog(getShell(), new KeyValueWizard<>(dialogModel)); if(OkCancelButtonWizardDialog.OK == dialog.open()) { model.addEnvironmentVariable(dialogModel.getKey(), dialogModel.getValue()); selectEnvVarByKey(dialogModel.getKey()); } } }; } private void selectEnvVarByKey(String key) { EnvironmentVariable envVar = model.getEnvironmentVariable(key); if(envVar != null) { envViewer.setSelection(new StructuredSelection(envVar)); envViewer.reveal(envVar); } } protected TableViewer createEnvVarTable(Composite tableContainer) { Table table = new Table(tableContainer, SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL); table.setLinesVisible(true); table.setHeaderVisible(true); this.envViewer = new TableViewerBuilder(table, tableContainer) .column(new IColumnLabelProvider<IKeyValueItem>() { @Override public String getValue(IKeyValueItem label) { boolean isDeleted = model.isEnvironmentVariableDeleted(label); return !isDeleted ? label.getKey() : "[deleted] " + label.getKey(); } @Override public boolean isModified(IKeyValueItem e) { return ((EnvironmentVariable)e).isNew() || model.isEnvironmentVariableDeleted(e); } }) .name("Name").align(SWT.LEFT).weight(2).minWidth(75).buildColumn() .column(new IColumnLabelProvider<IKeyValueItem>() { @Override public String getValue(IKeyValueItem label) { if(model.isEnvironmentVariableDeleted(label)) { return ""; } return label.getValue(); } @Override public boolean isModified(IKeyValueItem e) { return model.isEnvironmentVariableModified((EnvironmentVariable)e); } }) .name("Value").align(SWT.LEFT).weight(2).minWidth(100).buildColumn() .buildViewer(); envViewer.setComparator(new ViewerComparator() { @Override public int compare(Viewer viewer, Object e1, Object e2) { IKeyValueItem first = (IKeyValueItem) e1; IKeyValueItem other = (IKeyValueItem) e2; return first.getKey().compareTo(other.getKey()); } }); envViewer.addDoubleClickListener(new EditHandler()); return envViewer; } }