/* * Copyright 2015 Nokia Solutions and Networks * Licensed under the Apache License, Version 2.0, * see license.txt file for details. */ package org.robotframework.ide.eclipse.main.plugin.project.editor.general; import java.util.Arrays; import java.util.List; import java.util.function.Supplier; import javax.inject.Inject; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.tools.services.IDirtyProviderService; import org.eclipse.e4.ui.di.UIEventTopic; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.RowExposingTableViewer; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.ViewerColumnsFactory; import org.eclipse.jface.viewers.ViewersConfigurator; 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.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.forms.widgets.ExpandableComposite; import org.eclipse.ui.forms.widgets.Section; import org.rf.ide.core.project.RobotProjectConfig; import org.rf.ide.core.project.RobotProjectConfig.VariableMapping; import org.robotframework.ide.eclipse.main.plugin.project.RobotProjectConfigEvents; import org.robotframework.ide.eclipse.main.plugin.project.editor.Environments; import org.robotframework.ide.eclipse.main.plugin.project.editor.RedProjectEditorInput; import org.robotframework.ide.eclipse.main.plugin.project.editor.general.VariableMappingsDetailsEditingSupport.VariableMappingNameEditingSupport; import org.robotframework.ide.eclipse.main.plugin.project.editor.general.VariableMappingsDetailsEditingSupport.VariableMappingValueEditingSupport; import org.robotframework.ide.eclipse.main.plugin.tableeditor.CellsActivationStrategy; import org.robotframework.ide.eclipse.main.plugin.tableeditor.CellsActivationStrategy.RowTabbingStrategy; import org.robotframework.ide.eclipse.main.plugin.tableeditor.HeaderFilterMatchesCollection; import org.robotframework.ide.eclipse.main.plugin.tableeditor.ISectionFormFragment; import org.robotframework.red.forms.RedFormToolkit; import org.robotframework.red.viewers.ElementAddingToken; import org.robotframework.red.viewers.RedCommonLabelProvider; import org.robotframework.red.viewers.StructuredContentProvider; import org.robotframework.red.viewers.Viewers; public class VariableMappingsFormFragment implements ISectionFormFragment { private static final String CONTEXT_ID = "org.robotframework.ide.eclipse.redxmleditor.varmapping.context"; @Inject private IEditorSite site; @Inject private RedFormToolkit toolkit; @Inject private IDirtyProviderService dirtyProviderService; @Inject private RedProjectEditorInput editorInput; private RowExposingTableViewer viewer; TableViewer getViewer() { return viewer; } @Override public void initialize(final Composite parent) { final Section section = createSection(parent); final Composite internalComposite = toolkit.createComposite(section); section.setClient(internalComposite); GridDataFactory.fillDefaults().applyTo(internalComposite); GridLayoutFactory.fillDefaults().applyTo(internalComposite); createViewer(internalComposite); createColumns(); createContextMenu(); setInput(); } private Section createSection(final Composite parent) { final Section section = toolkit.createSection(parent, ExpandableComposite.EXPANDED | ExpandableComposite.TITLE_BAR | Section.DESCRIPTION); section.setText("Variables mappings"); section.setDescription("Define variable values. Those mappings will " + "be used by RED in order to resolve parameterized paths in Library, Resource and " + "Variable settings."); GridDataFactory.fillDefaults().grab(true, true).applyTo(section); return section; } private void createViewer(final Composite parent) { viewer = new RowExposingTableViewer(parent, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); CellsActivationStrategy.addActivationStrategy(viewer, RowTabbingStrategy.MOVE_TO_NEXT); GridDataFactory.fillDefaults().grab(true, true).indent(0, 10).applyTo(viewer.getTable()); viewer.setUseHashlookup(true); viewer.getTable().setEnabled(false); viewer.getTable().setLinesVisible(true); viewer.getTable().setHeaderVisible(true); viewer.setContentProvider(new VariableMappingsContentProvider()); ViewersConfigurator.enableDeselectionPossibility(viewer); ViewersConfigurator.disableContextMenuOnHeader(viewer); Viewers.boundViewerWithContext(viewer, site, CONTEXT_ID); } private void createColumns() { final Supplier<VariableMapping> elementsCreator = newElementsCreator(); ViewerColumnsFactory.newColumn("Variable name").withWidth(150) .withMinWidth(50) .editingEnabledOnlyWhen(editorInput.isEditable()) .editingSupportedBy(new VariableMappingNameEditingSupport(viewer, elementsCreator)) .labelsProvidedBy(new VariableMappingsNameLabelProvider()) .createFor(viewer); ViewerColumnsFactory.newColumn("Value").withWidth(100) .shouldGrabAllTheSpaceLeft(true).withMinWidth(50) .editingEnabledOnlyWhen(editorInput.isEditable()) .editingSupportedBy(new VariableMappingValueEditingSupport(viewer, elementsCreator)) .labelsProvidedBy(new VariableMappingsValueLabelProvider()) .createFor(viewer); } private Supplier<VariableMapping> newElementsCreator() { return () -> { final VariableMappingDialog dialog = new VariableMappingDialog(viewer.getTable().getShell()); if (dialog.open() == Window.OK) { final VariableMapping mapping = dialog.getMapping(); @SuppressWarnings("unchecked") final List<VariableMapping> mappings = (List<VariableMapping>) viewer.getInput(); if (!mappings.contains(mapping)) { editorInput.getProjectConfiguration().addVariableMapping(mapping); setDirty(true); } return mapping; } return null; }; } private void createContextMenu() { final String menuId = "org.robotframework.ide.eclipse.redxmleditor.variablesmapping.contextMenu"; final MenuManager manager = new MenuManager("Red.xml file editor variables mapping context menu", menuId); final Table control = viewer.getTable(); final Menu menu = manager.createContextMenu(control); control.setMenu(menu); site.registerContextMenu(menuId, manager, viewer, false); } private void setInput() { final List<VariableMapping> variableMappings = editorInput.getProjectConfiguration().getVariableMappings(); viewer.setInput(variableMappings); } @Override public void setFocus() { viewer.getTable().setFocus(); } private void setDirty(final boolean isDirty) { dirtyProviderService.setDirtyState(isDirty); } @Override public HeaderFilterMatchesCollection collectMatches(final String filter) { return null; } @Inject @Optional private void whenEnvironmentLoadingStarted( @UIEventTopic(RobotProjectConfigEvents.ROBOT_CONFIG_ENV_LOADING_STARTED) final RobotProjectConfig config) { setInput(); viewer.getTable().setEnabled(false); } @Inject @Optional private void whenEnvironmentsWereLoaded( @UIEventTopic(RobotProjectConfigEvents.ROBOT_CONFIG_ENV_LOADED) final Environments envs) { final boolean isEditable = editorInput.isEditable(); viewer.getTable().setEnabled(isEditable); } @Inject @Optional private void whenMappingDetailChanged( @UIEventTopic(RobotProjectConfigEvents.ROBOT_CONFIG_VAR_MAP_DETAIL_CHANGED) final VariableMapping mapping) { setDirty(true); viewer.refresh(); } @Inject @Optional private void whenMappingsChanged( @UIEventTopic(RobotProjectConfigEvents.ROBOT_CONFIG_VAR_MAP_STRUCTURE_CHANGED) final List<VariableMapping> mappings) { setDirty(true); viewer.refresh(); } private class VariableMappingsContentProvider extends StructuredContentProvider { @Override public Object[] getElements(final Object inputElement) { final Object[] elements = ((List<?>) inputElement).toArray(); if (editorInput.isEditable()) { final Object[] newElements = Arrays.copyOf(elements, elements.length + 1, Object[].class); newElements[elements.length] = new ElementAddingToken("mapping", true); return newElements; } else { return elements; } } } private static class VariableMappingsNameLabelProvider extends RedCommonLabelProvider { @Override public StyledString getStyledText(final Object element) { if (element instanceof VariableMapping) { final VariableMapping mapping = (VariableMapping) element; return new StyledString(mapping.getName()); } else if (element instanceof ElementAddingToken) { return ((ElementAddingToken) element).getStyledText(); } else { return new StyledString(); } } @Override public Image getImage(final Object element) { if (element instanceof ElementAddingToken) { return ((ElementAddingToken) element).getImage(); } else { return null; } } } private static class VariableMappingsValueLabelProvider extends RedCommonLabelProvider { @Override public StyledString getStyledText(final Object element) { if (element instanceof VariableMapping) { final VariableMapping mapping = (VariableMapping) element; return new StyledString(mapping.getValue()); } else { return new StyledString(); } } } public static class VariableMappingDialog extends Dialog { private boolean isVariablePredefined; private VariableMapping mapping; private Label exceptionLabel; private Text nameText; private Text valueText; private final String initialVariableName; public VariableMappingDialog(final Shell parentShell) { this(parentShell, "${var}"); this.isVariablePredefined = false; } public VariableMappingDialog(final Shell parentShell, final String variableName) { super(parentShell); this.isVariablePredefined = true; this.initialVariableName = variableName; } @Override public void create() { super.create(); getShell().setText("Add variable mapping"); } @Override protected Control createDialogArea(final Composite parent) { final Composite dialogComposite = (Composite) super.createDialogArea(parent); GridLayoutFactory.fillDefaults().numColumns(2).margins(10, 10).applyTo(dialogComposite); final Label infoLabel = new Label(dialogComposite, SWT.WRAP); infoLabel.setText("Specify name and value of variable which will be used in parameterized imports."); GridDataFactory.fillDefaults().hint(200, SWT.DEFAULT).span(2, 1).applyTo(infoLabel); final Label nameLabel = new Label(dialogComposite, SWT.NONE); nameLabel.setText("Name"); nameText = new Text(dialogComposite, SWT.SINGLE | SWT.BORDER); GridDataFactory.fillDefaults().grab(true, false).hint(300, SWT.DEFAULT).applyTo(nameText); nameText.setText(initialVariableName); nameText.addModifyListener(new ModifyListener() { @Override public void modifyText(final ModifyEvent event) { validate(); } }); final Label valueLabel = new Label(dialogComposite, SWT.NONE); valueLabel.setText("Value"); valueText = new Text(dialogComposite, SWT.SINGLE | SWT.BORDER); GridDataFactory.fillDefaults().grab(true, false).hint(300, SWT.DEFAULT).applyTo(valueText); valueText.setText("value"); valueText.addModifyListener(new ModifyListener() { @Override public void modifyText(final ModifyEvent event) { validate(); } }); exceptionLabel = new Label(dialogComposite, SWT.NONE); exceptionLabel.setText(""); GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(exceptionLabel); final Text controlToFocus = isVariablePredefined ? valueText : nameText; controlToFocus.setFocus(); controlToFocus.setSelection(0, controlToFocus.getText().length()); return dialogComposite; } private void validate() { boolean hasError = false; String errorMsg = ""; if (nameText.getText().isEmpty()) { errorMsg += "Name cannot be empty"; nameText.setBackground(nameText.getDisplay().getSystemColor(SWT.COLOR_RED)); hasError = true; } else { nameText.setBackground(nameText.getDisplay().getSystemColor(SWT.COLOR_WHITE)); } if (valueText.getText().isEmpty()) { errorMsg += "\nValue cannot be empty"; valueText.setBackground(valueText.getDisplay().getSystemColor(SWT.COLOR_RED)); hasError = true; } else { valueText.setBackground(valueText.getDisplay().getSystemColor(SWT.COLOR_WHITE)); } exceptionLabel.setText(errorMsg); getButton(IDialogConstants.OK_ID).setEnabled(!hasError); } @Override protected void okPressed() { mapping = VariableMapping.create(nameText.getText(), valueText.getText()); super.okPressed(); } public VariableMapping getMapping() { return mapping; } } }