/** * Copyright (C) 2015 Valkyrie RCP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.valkyriercp.form.builder; import com.jgoodies.forms.factories.FormFactory; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.forms.layout.RowSpec; import org.valkyriercp.binding.form.FormModel; import org.valkyriercp.binding.form.support.NestedPropertyChangeListener; import org.valkyriercp.form.binding.Binding; import org.valkyriercp.form.binding.BindingFactory; import org.valkyriercp.form.binding.swing.SwingBindingFactory; import javax.swing.*; import javax.swing.border.Border; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; /** * <p> * Layout builder based on the <a href="http://www.jgoodies.com">JGoodies</a> Framework. * </p> * <p/> * <p> * Shortcuts have been provided to directly place a local and a component. For this, you provide the coordinates * for the label (x, y), and the component will be places (x + 2, y). If width or height spanning is provided, * this will be applied to the component. * </p> * <p/> * <pre> * FormBuilder builder = new FormBuilder(getBindingFactory(), myLayout); * //... add everything ... * JPanel myFirstPanel = builder.getPanel(); * // set new layout, new panel * builder.setPanel(mySecondLayout); * //... add everything ... * JPanel mySecondPanel = builder.getPanel(); * </pre> * * @author jh */ public class FormLayoutFormBuilder extends AbstractFormBuilder { public static final String ALIGN_LEFT_TOP = "left, top"; public static final String ALIGN_LEFT_CENTER = "left, center"; public static final String ALIGN_LEFT_BOTTOM = "left, bottom"; public static final String ALIGN_RIGHT_TOP = "right, top"; public static final String ALIGN_RIGHT_CENTER = "right, center"; public static final String ALIGN_RIGHT_BOTTOM = "right, bottom"; private FormLayout layout; private JPanel panel; private CellConstraints cc; private String labelAttributes = ALIGN_LEFT_CENTER; private String componentAttributes = null; private Map<String, Map<Object, Object>> bindingContexts = new HashMap<String, Map<Object, Object>>(); private int row; private static final int DEFAULT_ROW_INCREMENT = 2; public Map<String, Map<Object, Object>> getBindingContexts() { return bindingContexts; } public void setBindingContexts(Map<String, Map<Object, Object>> bindingContexts) { this.bindingContexts = bindingContexts; } public void addBindingContextParameter(String propertyPath, Object key, Object value) { if(!getBindingContexts().containsKey(propertyPath)) getBindingContexts().put(propertyPath, new HashMap<Object, Object>()); Map<Object, Object> context = getBindingContexts().get(propertyPath); context.put(key, value); } /** * Constructor. * * @param bindingFactory BindingFactory. * @param layout JGoodies FormLayout */ public FormLayoutFormBuilder(BindingFactory bindingFactory, FormLayout layout) { this(bindingFactory, layout, new JPanel()); } /** * Constructor. * * @param bindingFactory BindingFactory. * @param layout JGoodies FormLayout * @param panel JPanel on which the builder will place the components. */ public FormLayoutFormBuilder(BindingFactory bindingFactory, FormLayout layout, JPanel panel) { super(bindingFactory); setLayout(layout, panel); } /** * Move to the next line, minding the line gap * * @return This FormBuilder */ public FormLayoutFormBuilder nextRow() { return nextRow(FormFactory.DEFAULT_ROWSPEC); } public FormLayoutFormBuilder nextRow(String rowSpec) { return nextRow(RowSpec.decode(rowSpec)); } public FormLayoutFormBuilder nextRow(RowSpec rowSpec) { row += DEFAULT_ROW_INCREMENT; correctNumberOfRows(row, rowSpec); return this; } private void correctNumberOfRows(int requiredNrOfRows) { correctNumberOfRows(requiredNrOfRows, FormFactory.DEFAULT_ROWSPEC); } private void correctNumberOfRows(int requiredNrOfRows, RowSpec rowSpec) { int rowCount = layout.getRowCount(); // if not initialized, set on first row if (row == -1 && requiredNrOfRows == -1) { requiredNrOfRows = 1; row = 1; } if (requiredNrOfRows > rowCount) { for (int i = rowCount; i < requiredNrOfRows; i = i + DEFAULT_ROW_INCREMENT) { if (i != 0) layout.appendRow(FormFactory.LINE_GAP_ROWSPEC); layout.appendRow(rowSpec); } } } /** * Returns the current row. If the layout is initialized, the row is set to -1. Calling getRow() will * result in setting the row to 1 to allow direct usage of getRow(). * * @return the current row */ public int getRow() { if (row == -1) // init row row = 1; return row; } /** * Sets the current row * * @param row The current row */ public void setRow(int row) { this.row = row; correctNumberOfRows(row); } /** * Create a new panel with the provided layout en default DIALOG_BORDER. * * @param layout JGoodies FormLayout */ public void setLayout(FormLayout layout) { setLayout(layout, new JPanel()); } /** * Set a panel with the provided layout layout. * * @param layout JGoodies FormLayout * @param panel JPanel on which the builder will place the components. */ public void setLayout(FormLayout layout, JPanel panel) { this.panel = panel; this.layout = layout; panel.setLayout(layout); cc = new CellConstraints(); row = -1; } /** * Set the border for the panel. * * @param border The border for the panel */ public void setBorder(Border border) { this.panel.setBorder(border); } /** * Add a binder to column 1 and the current row. Equals to builder.addBinding(component, 1, * builder.getRow()). * * @param binding The binding to add * @see #addBinding(Binding, int, int) */ public JComponent addBinding(Binding binding) { return addBinding(binding, 1, row); } /** * Add a binder to a column and the current row. Equals to builder.addBinding(component, column, * builder.getRow()). * * @param binding The binding to add * @param column The column on which the binding must be added * @return The component produced by the binding * @see #addBinding(Binding, int, int) */ public JComponent addBinding(Binding binding, int column) { return addBinding(binding, column, row); } /** * Add a binder to a column and a row. Equals to builder.addBinding(component, column, * row). * * @param binding The binding to add * @param column The column on which the binding must be added * @param column The row on which the binding must be added * @return The component produced by the binding * @see #addBinding(Binding, int, int, int, int) */ public JComponent addBinding(Binding binding, int column, int row) { return this.addBinding(binding, column, row, 1, 1); } /** * Add a binder to a column and a row with width and height spanning. */ public JComponent addBinding(Binding binding, int column, int row, int widthSpan, int heightSpan) { ((SwingBindingFactory) getBindingFactory()).interceptBinding(binding); JComponent component = binding.getControl(); addComponent(component, column, row, widthSpan, heightSpan); return component; } /** * Add a property to column 1 and the current row. Equals to builder.addBinding(component, 1, * builder.getRow()). * * @see #addProperty(String, int, int) */ public JComponent addProperty(String property) { return addProperty(property, 1, row); } /** * Add a property to a column and the current row. Equals to builder.addBinding(component, column, * builder.getRow()). * * @see #addProperty(String, int, int) */ public JComponent addProperty(String property, int column) { return addProperty(property, column, row); } public JComponent addProperty(String property, String binderId) { return addProperty(property, binderId, 1, row); } public JComponent addProperty(String property, String binderId, Map<?, ?> contextMap) { return addProperty(property, binderId, contextMap, 1, row); } public JComponent addProperty(String property, String binderId, int column) { return addProperty(property, binderId, column, row); } public JComponent addProperty(String property, String binderId, Map<?, ?> contextMap, int column) { return addProperty(property, binderId, contextMap, column, row); } public JComponent addProperty(String property, int column, int row) { return this.addProperty(property, column, row, 1, 1); } public JComponent addProperty(String property, String binderId, int column, int row) { return this.addProperty(property, binderId, column, row, 1, 1); } public JComponent addProperty(String property, String binderId, Map<?, ?> contextMap, int column, int row) { return this.addProperty(property, binderId, contextMap, column, row, 1, 1); } public JComponent addProperty(String property, int column, int row, int widthSpan, int heightSpan) { JComponent propertyComponent; if(bindingContexts.get(property) != null) propertyComponent = getBindingFactory().createBinding(property, bindingContexts.get(property)).getControl(); else propertyComponent = getBindingFactory().createBinding(property).getControl(); addComponent(propertyComponent, column, row, widthSpan, heightSpan); return propertyComponent; } public JComponent addProperty(String property, String binderId, int column, int row, int widthSpan, int heightSpan) { return addProperty(property, binderId, Collections.EMPTY_MAP, column, row, widthSpan, heightSpan); } public JComponent addProperty(String property, String binderId, Map<?, ?> contextMap, int column, int row, int widthSpan, int heightSpan) { JComponent propertyComponent = ((SwingBindingFactory) getBindingFactory()).createBinding(property, binderId, contextMap).getControl(); addComponent(propertyComponent, column, row, widthSpan, heightSpan); return propertyComponent; } public JLabel addLabel(String property) { return addLabel(property, 1, row); } public JLabel addLabel(String property, int column) { return addLabel(property, column, row); } public JLabel addLabel(String property, int column, int row) { return addLabel(property, null, column, row); } public JLabel addLabel(String property, int column, int row, String attributes) { return addLabel(property, null, column, row, 1, 1, attributes); } public JLabel addLabel(String property, JComponent forComponent, int column, int row) { return addLabel(property, forComponent, column, row, 1, 1, this.labelAttributes); } public JLabel addLabel(String property, JComponent forComponent, int column, int row, int widthSpan, int heightSpan, String attributes) { JLabel labelComponent = createLabelFor(property, forComponent); this.row = row; if (this.row == -1) { this.row = 1; } correctNumberOfRows(this.row); this.panel.add(labelComponent, cc.xywh(column, this.row, widthSpan, heightSpan, attributes)); return labelComponent; } public void addComponent(JComponent component) { addComponent(component, 1, row, 1, 1); } public void addComponent(JComponent component, int column) { addComponent(component, column, row, 1, 1); } public void addComponent(JComponent component, int column, int row) { addComponent(component, column, row, 1, 1); } public void addComponent(JComponent component, int column, int row, int widthSpan, int heightSpan) { addComponent(component, column, row, widthSpan, heightSpan, this.componentAttributes); } public void addComponent(JComponent component, int column, int row, int widthSpan, int heightSpan, String attributes) { this.row = row; if (this.row == -1) { this.row = 1; } correctNumberOfRows(this.row); if (attributes == null) this.panel.add(component, cc.xywh(column, this.row, widthSpan, heightSpan)); else this.panel.add(component, cc.xywh(column, this.row, widthSpan, heightSpan, attributes)); } public JComponent[] addPropertyAndLabel(String property) { return addPropertyAndLabel(property, 1, row, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, int column) { return addPropertyAndLabel(property, column, row, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, String binderId) { return addPropertyAndLabel(property, binderId, 1, row, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, String binderId, Map<?, ?> context) { return addPropertyAndLabel(property, binderId, context, 1, row, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, int column, String binderId) { return addPropertyAndLabel(property, binderId, column, row, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, int column, int row) { return addPropertyAndLabel(property, column, row, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row) { return addPropertyAndLabel(property, binderId, column, row, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, int column, int row, String attributes) { return addPropertyAndLabel(property, column, row, 1, attributes); } public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row, String attributes) { return addPropertyAndLabel(property, binderId, column, row, 1, attributes); } public JComponent[] addPropertyAndLabel(String property, String binderId, Map<?, ?> context, int column, int row, String attributes) { return addPropertyAndLabel(property, binderId, context, column, row, 1, attributes); } public JComponent[] addPropertyAndLabel(String property, int column, int row, int widthSpan) { return addPropertyAndLabel(property, column, row, widthSpan, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, int column, int row, int widthSpan, String attributes) { return addPropertyAndLabel(property, column, row, widthSpan, 1, attributes); } public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row, int widthSpan, String attributes) { return addPropertyAndLabel(property, binderId, column, row, widthSpan, 1, attributes); } public JComponent[] addPropertyAndLabel(String property, String binderId, Map<?, ?> context, int column, int row, int widthSpan, String attributes) { return addPropertyAndLabel(property, binderId, context, column, row, widthSpan, 1, attributes); } public JComponent[] addPropertyAndLabel(String property, int column, int row, int widthSpan, int heightSpan) { return addPropertyAndLabel(property, column, row, widthSpan, heightSpan, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row, int widthSpan, int heightSpan) { return addPropertyAndLabel(property, binderId, column, row, widthSpan, heightSpan, this.labelAttributes); } public JComponent[] addPropertyAndLabel(String property, int column, int row, int widthSpan, int heightSpan, String attributes) { JComponent component = addProperty(property, column + 2, row, widthSpan, heightSpan); JLabel label = addLabel(property, component, column, row, 1, 1, attributes); return new JComponent[]{label, component}; } public JComponent[] addPropertyAndLabel(String property, String binderId, Map<?, ?> context, int column, int row, int widthSpan, int heightSpan, String attributes) { JComponent component = addProperty(property, binderId, context, column + 2, row, widthSpan, heightSpan); JLabel label = addLabel(property, component, column, row, 1, 1, attributes); return new JComponent[]{label, component}; } public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row, int widthSpan, int heightSpan, String attributes) { JComponent component = addProperty(property, binderId, column + 2, row, widthSpan, heightSpan); JLabel label = addLabel(property, component, column, row, 1, 1, attributes); return new JComponent[]{label, component}; } public JPanel getPanel() { getBindingFactory().getFormModel().revert(); return this.panel; } public void setLabelAttributes(String attributes) { this.labelAttributes = attributes; } public void setComponentAttributes(String attributes) { this.componentAttributes = attributes; } public JComponent[] addTextArea(String propertyName, int column, int row) { return addTextArea(propertyName, column, row, 1, 1); } public JComponent[] addTextArea(String propertyName, int column) { return addTextArea(propertyName, column, row, 1, 1); } public JComponent[] addTextArea(String propertyName) { return addTextArea(propertyName, 1, row, 1, 1); } public JComponent[] addTextArea(String propertyName, int column, int row, int widthSpan, int heightSpan) { return addTextArea(propertyName, column, row, widthSpan, heightSpan, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); } public JComponent[] addTextArea(String propertyName, int column, int row, int widthSpan, int heightSpan, int vsbPolicy, int hsbPolicy) { JComponent textArea = createTextArea(propertyName); createBinding(propertyName, textArea); JScrollPane scrollPane = new JScrollPane(textArea, vsbPolicy, hsbPolicy); addComponent(scrollPane, column, row, widthSpan, heightSpan); return new JComponent[]{textArea, scrollPane}; } public JComponent[] addTextAreaAndLabel(String propertyName, int column, int row) { return addTextAreaAndLabel(propertyName, column, row, 1, 1, this.labelAttributes); } public JComponent[] addTextAreaAndLabel(String propertyName, int column, int row, String attributes) { return addTextAreaAndLabel(propertyName, column, row, 1, 1, attributes); } public JComponent[] addTextAreaAndLabel(String propertyName, int column, int row, int widthSpan, int heightSpan) { return addTextAreaAndLabel(propertyName, column, row, widthSpan, heightSpan, this.labelAttributes); } public JComponent[] addTextAreaAndLabel(String propertyName, int column, int row, int widthSpan, int heightSpan, String attributes) { JLabel label = addLabel(propertyName, column, row, attributes); JComponent[] components = addTextArea(propertyName, column + 2, row, widthSpan, heightSpan); return new JComponent[]{label, components[0], components[1]}; } public JComponent addPasswordField(String propertyName, int column) { return addPasswordField(propertyName, column, row, 1, 1); } public JComponent addPasswordField(String propertyName) { return addPasswordField(propertyName, 1, row); } public JComponent addPasswordField(String propertyName, int column, int row) { return addPasswordField(propertyName, column, row, 1, 1); } public JComponent addPasswordField(String propertyName, int column, int row, int widthSpan, int heightSpan) { JComponent passwordField = createBinding(propertyName, createPasswordField(propertyName)) .getControl(); addComponent(passwordField, column, row, widthSpan, heightSpan); return passwordField; } public JComponent[] addPasswordFieldAndLabel(String propertyName, int column) { return addPasswordFieldAndLabel(propertyName, column, row, 1, 1); } public JComponent[] addPasswordFieldAndLabel(String propertyName) { return addPasswordFieldAndLabel(propertyName, 1, row); } public JComponent[] addPasswordFieldAndLabel(String propertyName, int column, int row) { return addPasswordFieldAndLabel(propertyName, column, row, 1, 1, this.labelAttributes); } public JComponent[] addPasswordFieldAndLabel(String propertyName, int column, int row, String attributes) { return addPasswordFieldAndLabel(propertyName, column, row, 1, 1, attributes); } public JComponent[] addPasswordFieldAndLabel(String propertyName, int column, int row, int widthSpan, int heightSpan) { return addPasswordFieldAndLabel(propertyName, column, row, widthSpan, heightSpan, this.labelAttributes); } public JComponent[] addPasswordFieldAndLabel(String propertyName, int column, int row, int widthSpan, int heightSpan, String attributes) { JLabel label = addLabel(propertyName, column, row, attributes); JComponent component = addPasswordField(propertyName, column + 2, row, widthSpan, heightSpan); return new JComponent[]{label, component}; } public void addHorizontalSeparator(int column, int row, int widthSpan) { addComponent(new JSeparator(), column, row, widthSpan, 1); } public void addHorizontalSeparator() { addComponent(new JSeparator(), 1, row, 1, 1); } public void addHorizontalSeparator(int widthSpan) { addComponent(new JSeparator(), 1, row, widthSpan, 1); } public void addHorizontalSeparator(int column, int widthSpan) { addComponent(new JSeparator(), column, row, widthSpan, 1); } public void addHorizontalSeparator(String text) { addComponent(getComponentFactory().createLabeledSeparator(text), 1, row, 1, 1); } public void addHorizontalSeparator(String text, int widthSpan) { addComponent(getComponentFactory().createLabeledSeparator(text), 1, row, widthSpan, 1); } public void addHorizontalSeparator(String text, int column, int widthSpan) { addComponent(getComponentFactory().createLabeledSeparator(text), column, row, widthSpan, 1); } public void addVerticalSeparator(int column, int row, int heightSpan) { addComponent(new JSeparator(SwingConstants.VERTICAL), column, row, 1, heightSpan); } public void addVerticalSeparator() { addComponent(new JSeparator(SwingConstants.VERTICAL), 1, row, 1, 1); } public void addVerticalSeparator(int heightSpan) { addComponent(new JSeparator(SwingConstants.VERTICAL), 1, row, 1, heightSpan); } public void addVerticalSeparator(int column, int heightSpan) { addComponent(new JSeparator(SwingConstants.VERTICAL), column, row, 1, heightSpan); } public JComponent addNestedPropertyReadOnly(String property, String nestedProperty) { return addNestedPropertyReadOnly(property, nestedProperty, 1, row); } public JComponent addNestedPropertyReadOnly(String property, String nestedProperty, int column) { return addNestedPropertyReadOnly(property, nestedProperty, column, row); } public JComponent addNestedPropertyReadOnly(String property, String nestedProperty, int column, int row) { return addNestedPropertyReadOnly(property, nestedProperty, column, row, 1, 1); } public JComponent addNestedPropertyReadOnly(String property, String nestedProperty, int column, int row, int widthSpan, int heightSpan) { final JTextField nestedPropertyField = new JTextField(); nestedPropertyField.setEditable(false); getFormModel().getValueModel(property).addValueChangeListener( new NestedPropertyChangeListener(nestedPropertyField, nestedProperty)); getFormModelEnabledListener().add(nestedPropertyField); addComponent(nestedPropertyField, column, row, widthSpan, heightSpan); return nestedPropertyField; } public JComponent[] addNestedPropertyReadOnlyAndLabel(String property, String nestedProperty) { return addNestedPropertyReadOnlyAndLabel(property, nestedProperty, 1, row); } public JComponent[] addNestedPropertyReadOnlyAndLabel(String property, String nestedProperty, int column) { return addNestedPropertyReadOnlyAndLabel(property, nestedProperty, column, row); } public JComponent[] addNestedPropertyReadOnlyAndLabel(String property, String nestedProperty, int column, int row) { return addNestedPropertyReadOnlyAndLabel(property, nestedProperty, column, row, 1, 1); } public JComponent[] addNestedPropertyReadOnlyAndLabel(String property, String nestedProperty, int column, int row, int widthSpan, int heightSpan) { final JTextField nestedPropertyField = new JTextField(); nestedPropertyField.setEditable(false); getFormModel().getValueModel(property).addValueChangeListener( new NestedPropertyChangeListener(nestedPropertyField, nestedProperty)); getFormModelEnabledListener().add(nestedPropertyField); JComponent comp = addNestedPropertyReadOnly(property, nestedProperty, column + 2, row, widthSpan, heightSpan); JLabel label = addLabel(property + "." + nestedProperty, comp, column, row, 1, 1, this.labelAttributes); return new JComponent[] { label, comp }; } private FormModelEnabledListener formModelEnabledListener; public FormModelEnabledListener getFormModelEnabledListener() { if (formModelEnabledListener == null) { formModelEnabledListener = new FormModelEnabledListener(); getFormModel().addPropertyChangeListener(FormModel.ENABLED_PROPERTY, formModelEnabledListener); } return formModelEnabledListener; } public static class FormModelEnabledListener implements PropertyChangeListener { private List<JComponent> components = new ArrayList<JComponent>(); public void propertyChange(PropertyChangeEvent evt) { for (JComponent component : components) { component.setEnabled((Boolean) evt.getNewValue()); } } public void add(JComponent component) { components.add(component); } } }