/* * SoapUI, Copyright (C) 2004-2016 SmartBear Software * * Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * * http://ec.europa.eu/idabc/eupl * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the Licence for the specific language governing permissions and limitations * under the Licence. */ package com.eviware.soapui.support.components; import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction; import com.eviware.soapui.support.UISupport; import com.eviware.soapui.support.swing.JTextComponentPopupMenu; import com.google.common.base.Preconditions; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.forms.layout.RowSpec; import org.apache.commons.lang.StringUtils; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.ComboBoxModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.Border; import javax.swing.text.JTextComponent; import java.awt.Color; import java.awt.Font; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Utility-class for creating JGoodies forms */ public class SimpleForm { public static final int DEFAULT_COMPONENT_COLUMN = 4; public static final int DEFAULT_LABEL_COLUMN = 2; public static final int SHORT_TEXT_FIELD_COLUMNS = 20; public static final int MEDIUM_TEXT_FIELD_COLUMNS = 30; public static final int LONG_TEXT_FIELD_COLUMNS = 50; public static final Color HINT_TEXT_COLOR = new Color(113, 102, 102); protected static final String DEFAULT_COMPONENT_ALIGNMENT = "left,bottom"; protected static final int DEFAULT_TEXT_FIELD_COLUMNS = MEDIUM_TEXT_FIELD_COLUMNS; protected static final String ENABLED_PROPERTY_NAME = "enabled"; private static final int DEFAULT_COLUMN_SPAN = 1; private static final String DEFAULT_COLUMN_SPECS = "5px:none,left:pref,10px,left:default,5px:grow(1.0)"; private JPanel panel; private CellConstraints cc = new CellConstraints(); private FormLayout layout; private RowSpec rowSpec; private int rowSpacing = 5; private Map<String, JComponent> components = new HashMap<String, JComponent>(); private Map<JComboBox, Object[]> comboBoxMaps = new HashMap<JComboBox, Object[]>(); private String rowAlignment = "top"; private Map<String, String> hiddenValues; private boolean appended; private Font labelFont; private int defaultTextAreaColumns = 30; private int defaultTextAreaRows = 3; private int defaultTextFieldColumns = DEFAULT_TEXT_FIELD_COLUMNS; public SimpleForm() { this(DEFAULT_COLUMN_SPECS); } public SimpleForm(String columnSpec) { this(new FormLayout(columnSpec), BorderFactory.createEmptyBorder()); } public SimpleForm(String columnSpec, Border border) { this(new FormLayout(columnSpec), border); } private SimpleForm(FormLayout layout, Border border) { this.layout = layout; panel = new JPanel(layout); panel.setBorder(border); rowSpec = new RowSpec(rowAlignment + ":pref"); } public JPanel getPanel() { return panel; } public boolean hasComponents() { return !components.isEmpty(); } public int getRowCount() { return layout.getRowCount(); } public String getRowAlignment() { return rowAlignment; } public Font getLabelFont() { return labelFont; } public void setLabelFont(Font labelFont) { this.labelFont = labelFont; } public void setRowAlignment(String rowAlignment) { this.rowAlignment = rowAlignment; rowSpec = new RowSpec(rowAlignment + ":pref"); } public void setRowAlignment(String alignment, String size, String resize) { this.rowAlignment = alignment + ":" + size + ":" + resize; rowSpec = new RowSpec(rowAlignment); } public int getRowSpacing() { return rowSpacing; } public void setRowSpacing(int rowSpacing) { this.rowSpacing = rowSpacing; } public String getComponentValue(String label) { JComponent component = getComponent(label); if (component == null) { return (String) (hiddenValues == null ? null : hiddenValues.get(label)); } if (component instanceof JTextComponent) { return ((JTextComponent) component).getText(); } if (component instanceof JComboBox) { JComboBox comboBox = ((JComboBox) component); int selectedIndex = comboBox.getSelectedIndex(); if (selectedIndex != -1) { if (comboBoxMaps.containsKey(component)) { Object[] keys = (Object[]) comboBoxMaps.get(comboBox); Object value = keys[selectedIndex]; return (String) value == null ? null : value.toString(); } else { Object value = comboBox.getSelectedItem(); return (String) value == null ? null : value.toString(); } } } if (component instanceof JList) { return (String) ((JList) component).getSelectedValue(); } if (component instanceof JCheckBox) { return String.valueOf(((JCheckBox) component).isSelected()); } else if (component instanceof JFormComponent) { return ((JFormComponent) component).getValue(); } return null; } public void setComponentValue(String label, String value) { JComponent component = getComponent(label); if (component instanceof JScrollPane) { component = (JComponent) ((JScrollPane) component).getViewport().getComponent(0); } if (component instanceof JTextComponent) { ((JTextComponent) component).setText(value); } else if (component instanceof JComboBox) { JComboBox comboBox = ((JComboBox) component); comboBox.setSelectedItem(value); } else if (component instanceof JList) { ((JList) component).setSelectedValue(value, true); } else if (component instanceof JCheckBox) { ((JCheckBox) component).setSelected(Boolean.valueOf(value)); } else if (component instanceof JFormComponent) { ((JFormComponent) component).setValue(value); } else if (component instanceof RSyntaxTextArea) { ((RSyntaxTextArea) component).setText(value); } } public void getValues(Map<String, String> values) { for (Iterator<String> i = components.keySet().iterator(); i.hasNext(); ) { String key = i.next(); values.put(key, getComponentValue(key)); } } public void setValues(Map<String, String> values) { for (Map.Entry<String, String> entry : values.entrySet()) { setComponentValue(entry.getKey(), entry.getValue()); } } public void setEnabled(boolean b) { for (JComponent component : components.values()) { if (component instanceof JScrollPane) { ((JScrollPane) component).getViewport().getView().setEnabled(b); } component.setEnabled(b); } } public JComponent getComponent(String label) { return components.get(label); } public void setBorder(Border border) { panel.setBorder(border); } public void addInputFieldHintText(String text) { JLabel label = new JLabel(text); label.setForeground(HINT_TEXT_COLOR); addComponentWithoutLabel(label); } private void setToolTip(JComponent component, String tooltip) { component.setToolTipText(StringUtils.defaultIfEmpty(tooltip, null)); } public int getDefaultTextAreaColumns() { return defaultTextAreaColumns; } /** * @param defaultTextFieldColumns Should be a constant defined in SimpleForm * @see com.eviware.soapui.support.components.SimpleForm */ public void setDefaultTextFieldColumns(int defaultTextFieldColumns) { this.defaultTextFieldColumns = defaultTextFieldColumns; } public void setDefaultTextAreaColumns(int defaultTextAreaColumns) { this.defaultTextAreaColumns = defaultTextAreaColumns; } public int getDefaultTextAreaRows() { return defaultTextAreaRows; } public void setDefaultTextAreaRows(int defaultTextAreaRows) { this.defaultTextAreaRows = defaultTextAreaRows; } // -- Custom components -- // /** * Appens a label with hyperlink behaviour that streches from the default label column to the end of the form * * @param url The URL to open when clicking on the label * @param text The text of the label */ public void appendLabelAsLink(String url, String text) { JLabel label = UISupport.createLabelLink(url, text); append(null, null, label, null, DEFAULT_LABEL_COLUMN, getColumnSpanToTheEnd(DEFAULT_LABEL_COLUMN)); } /** * Appends a heading with bold text that streches from the default label column to the end of the form * * @param text The text of the heading */ public void appendHeading(String text) { JLabel label = new JLabel(text); Font font = label.getFont(); Font fontBold = new Font(font.getName(), Font.BOLD, font.getSize()); label.setFont(fontBold); append(null, null, label, null, DEFAULT_LABEL_COLUMN, getColumnSpanToTheEnd(DEFAULT_LABEL_COLUMN)); } public void appendHeadingAndHelpButton(String text, String helpUrl) { JLabel label = new JLabel(text); Font font = label.getFont(); Font fontBold = new Font(font.getName(), Font.BOLD, font.getSize()); label.setFont(fontBold); JPanel innerPanel = new JPanel(); innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.X_AXIS)); innerPanel.add(label); innerPanel.add(Box.createHorizontalGlue()); innerPanel.add(UISupport.createFormButton(new ShowOnlineHelpAction(helpUrl))); append(null, null, innerPanel, null, DEFAULT_LABEL_COLUMN, getColumnSpanToTheEnd(DEFAULT_LABEL_COLUMN)); } // -- Standard components -- // public JComboBox appendComboBox(String label, Map<?, ?> values) { Object[] valueArray = new Object[values.size()]; Object[] keyArray = new Object[values.size()]; int ix = 0; for (Iterator<?> i = values.keySet().iterator(); i.hasNext(); ix++) { keyArray[ix] = i.next(); valueArray[ix] = values.get(keyArray[ix]); } JComboBox comboBox = new JComboBox(valueArray); comboBoxMaps.put(comboBox, keyArray); append(label, comboBox); return comboBox; } public JComboBox appendComboBox(String label, Object[] values, String tooltip) { JComboBox comboBox = new JComboBox(values); comboBox.setToolTipText(StringUtils.defaultIfEmpty(tooltip, null)); comboBox.getAccessibleContext().setAccessibleDescription(tooltip); append(label, comboBox); return comboBox; } public JComboBox appendComboBox(String label, ComboBoxModel model, String tooltip) { JComboBox comboBox = new JComboBox(model); comboBox.setToolTipText(StringUtils.defaultIfEmpty(tooltip, null)); comboBox.getAccessibleContext().setAccessibleDescription(tooltip); append(label, comboBox); return comboBox; } public JCheckBox appendCheckBox(String caption, String label, boolean selected) { JCheckBox checkBox = new JCheckBox(label, selected); checkBox.getAccessibleContext().setAccessibleDescription(caption); components.put(caption, checkBox); append(caption, checkBox); return checkBox; } public JRadioButton appendRadioButton(String caption, String label, ButtonGroup group, boolean selected) { JRadioButton radioButton = new JRadioButton(label, selected); radioButton.getAccessibleContext().setAccessibleDescription(caption); if (group != null) { group.add(radioButton); } components.put(caption, radioButton); append(caption, radioButton); return radioButton; } public JButton addRightButton(Action action) { if (rowSpacing > 0 && !components.isEmpty()) { addSpace(rowSpacing); } layout.appendRow(rowSpec); int row = layout.getRowCount(); JButton button = new JButton(action); panel.add(button, cc.xy(DEFAULT_COMPONENT_COLUMN, row, "right,bottom")); return button; } public JButton appendButtonWithoutLabel(String text, ActionListener actionListener) { JButton button = new JButton(text); button.addActionListener(actionListener); addComponentWithoutLabel(button); return button; } public JButton addButtonWithoutLabelToTheRight(String text, ActionListener actionListener) { JButton button = new JButton(text); button.addActionListener(actionListener); addComponentWithoutLabel(button, "right,bottom"); return button; } public JButton appendButton(String label, String tooltip) { JButton button = new JButton(); button.setToolTipText(StringUtils.defaultIfEmpty(tooltip, null)); button.getAccessibleContext().setAccessibleDescription(tooltip); append(label, button); return button; } public JPasswordField appendPasswordField(String label, String tooltip) { JPasswordField textField = new JPasswordField(); textField.setColumns(defaultTextFieldColumns); textField.setToolTipText(StringUtils.defaultIfEmpty(tooltip, null)); textField.getAccessibleContext().setAccessibleDescription(tooltip); append(label, textField); return textField; } public JTextArea appendTextArea(String label, String tooltip) { JTextArea textArea = new JUndoableTextArea(); textArea.setColumns(defaultTextAreaColumns); textArea.setRows(defaultTextAreaRows); textArea.setAutoscrolls(true); textArea.add(new JScrollPane()); setToolTip(textArea, tooltip); textArea.getAccessibleContext().setAccessibleDescription(tooltip); JTextComponentPopupMenu.add(textArea); append(label, new JScrollPane(textArea)); return textArea; } /** * Appends a label and a text field to the form * * @param label The value of the label. Will also be the name of the text field. * @param tooltip The value of the text field tool tip * @return The text field */ public JTextField appendTextField(String label, String tooltip) { return appendTextField(label, label, tooltip, defaultTextFieldColumns); } /** * Appends a label and a text field to the form * * @param label The value of the label * @param name The name of the text field * @param tooltip The value of the text field tool tip * @param textFieldColumns The number of columns to display for the text field. Should be a constant defined in SimpleForm * @return The text field * @see com.eviware.soapui.support.components.SimpleForm */ public JTextField appendTextField(String label, String name, String tooltip, int textFieldColumns) { JTextField textField = new JUndoableTextField(); textField.setName(name); textField.setColumns(textFieldColumns); setToolTip(textField, tooltip); textField.getAccessibleContext().setAccessibleDescription(tooltip); JTextComponentPopupMenu.add(textField); append(label, textField); return textField; } public void addHiddenValue(String name, String value) { if (hiddenValues == null) { hiddenValues = new HashMap<String, String>(); } hiddenValues.put(name, value); } public void addSpace() { addSpace(rowSpacing); } public void addSpace(int size) { if (size > 0) { layout.appendRow(new RowSpec(size + "px")); } } public void appendSeparator() { if (appended && rowSpacing > 0) { addSpace(rowSpacing); } layout.appendRow(rowSpec); int row = layout.getRowCount(); panel.add(new JSeparator(), cc.xywh(DEFAULT_LABEL_COLUMN, row, 4, 1)); appended = true; } public void addComponentWithoutLabel(JComponent component) { addComponentWithoutLabel(component, DEFAULT_COMPONENT_ALIGNMENT); } private void addComponentWithoutLabel(JComponent component, String alignment) { if (rowSpacing > 0 && !components.isEmpty()) { addSpace(rowSpacing); } layout.appendRow(rowSpec); int row = layout.getRowCount(); panel.add(component, cc.xy(DEFAULT_COMPONENT_COLUMN, row, alignment)); } public <T extends JComponent> T addLeftComponent(T component) { if (rowSpacing > 0 && !components.isEmpty()) { addSpace(rowSpacing); } layout.appendRow(rowSpec); int row = layout.getRowCount(); panel.add(component, cc.xy(DEFAULT_COMPONENT_COLUMN, row)); return component; } public void addRightComponent(JComponent component) { if (rowSpacing > 0 && !components.isEmpty()) { addSpace(rowSpacing); } layout.appendRow(rowSpec); int row = layout.getRowCount(); panel.add(component, cc.xy(DEFAULT_COMPONENT_COLUMN, row, "right,bottom")); } public void addComponent(JComponent component) { layout.appendRow(rowSpec); int row = layout.getRowCount(); panel.add(component, cc.xyw(DEFAULT_LABEL_COLUMN, row, 4)); } public void removeComponent(JComponent component) { panel.remove(component); } public void appendFixed(String label, JComponent component) { append(label, component, "left:pref"); } public void append(JComponent component) { int spaceRowIndex = -1; if (rowSpacing > 0 && appended) { addSpace(rowSpacing); spaceRowIndex = layout.getRowCount(); } layout.appendRow(rowSpec); int row = layout.getRowCount(); panel.add(component, cc.xyw(DEFAULT_LABEL_COLUMN, row, 4)); component.addComponentListener(new LabelHider(null, spaceRowIndex)); appended = true; } public void append(String label, JComponent component) { append(label, component, null); } public <T extends JComponent> T append(String label, T component, String alignments) { JLabel jlabel = null; if (label != null) { jlabel = new JLabel(label.endsWith(":") || label.isEmpty() ? label : label + ":"); jlabel.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0)); if (labelFont != null) { jlabel.setFont(labelFont); } } return append(label, jlabel, component, alignments, DEFAULT_COMPONENT_COLUMN, getColumnSpanToTheEnd(DEFAULT_COMPONENT_COLUMN)); } public <T extends JComponent> T append(String name, JLabel label, T field) { return append(name, label, field, null, DEFAULT_COMPONENT_COLUMN, getColumnSpanToTheEnd(DEFAULT_COMPONENT_COLUMN)); } /** * Appends a vararg of PropertyComponents to a single row in the form. It's assumed that every other column contains a component * and the others are used for spacing including the initial column. * * @param propertyComponents The PropertyComponents to be added */ public void appendInOneRow(PropertyComponent... propertyComponents) { Preconditions.checkArgument(propertyComponents.length * 2 <= layout.getColumnCount() - 1, "There is not enough room for the components to be added"); int currentColumn = DEFAULT_COMPONENT_COLUMN; for (int i = 0; i < propertyComponents.length; i++) { PropertyComponent propertyComponent = propertyComponents[i]; if (i == 0) { append(null, null, propertyComponent.getComponent(), "left,center", currentColumn, DEFAULT_COLUMN_SPAN); } else { addComponentToRow(null, propertyComponent.getComponent(), "left,center", currentColumn, getRowCount(), DEFAULT_COLUMN_SPAN); } currentColumn += 2; } } private <T extends JComponent> T append(String name, JComponent label, T component, String alignments, int column, int columnSpan) { int spaceRowIndex = -1; if (rowSpacing > 0 && appended) { addSpace(rowSpacing); spaceRowIndex = layout.getRowCount(); } layout.appendRow(rowSpec); int row = layout.getRowCount(); if (label != null) { panel.add(label, cc.xy(DEFAULT_LABEL_COLUMN, row)); component.addComponentListener(new LabelHider(label, spaceRowIndex)); component.addPropertyChangeListener(ENABLED_PROPERTY_NAME, new LabelEnabler(label)); if (label instanceof JLabel) { JLabel jl = ((JLabel) label); jl.setLabelFor(component); String text = jl.getText(); int ix = text.indexOf('&'); if (ix >= 0) { jl.setText(text.substring(0, ix) + text.substring(ix + 1)); jl.setDisplayedMnemonicIndex(ix); jl.setDisplayedMnemonic(text.charAt(ix + 1)); } if (component.getAccessibleContext() != null) { component.getAccessibleContext().setAccessibleName(text); } } } else { component.addComponentListener(new LabelHider(null, spaceRowIndex)); } addComponentToRow(name, component, alignments, column, row, columnSpan); return component; } private <T extends JComponent> void addComponentToRow(String name, T component, String alignments, int column, int row, int columnSpan) { if (alignments == null) { panel.add(component, cc.xyw(column, row, columnSpan)); } else { panel.add(component, cc.xyw(column, row, columnSpan, alignments)); } components.put(name, component); appended = true; } /** * Returns a column span that stretched to the last component column if using a custom layout. * It's assumed that every other column contains a component * and the others are used for spacing including the initial column. * * @param startingColumn The column from where the column span should be calculated. * Should preferly be a constant defined in SimpleForm * @see com.eviware.soapui.support.components.SimpleForm */ private int getColumnSpanToTheEnd(int startingColumn) { return layout.getColumnCount() - startingColumn; } private static class LabelEnabler implements PropertyChangeListener { private final JComponent label; public LabelEnabler(JComponent label) { this.label = label; } @Override public void propertyChange(PropertyChangeEvent evt) { label.setEnabled((Boolean) evt.getNewValue()); } } private final class LabelHider extends ComponentAdapter { private final JComponent jlabel; private final int rowIndex; public LabelHider(JComponent label, int i) { this.jlabel = label; this.rowIndex = i; } public void componentHidden(ComponentEvent e) { if (jlabel != null) { jlabel.setVisible(false); } if (rowIndex >= 0 && rowIndex < layout.getRowCount()) { layout.setRowSpec(rowIndex, new RowSpec("0px")); } } public void componentShown(ComponentEvent e) { if (jlabel != null) { jlabel.setVisible(true); } if (rowIndex >= 0 && rowIndex < layout.getRowCount()) { layout.setRowSpec(rowIndex, new RowSpec(rowSpacing + "px")); } } } }