| /******************************************************************************* |
| * Copyright (c) 2010-2015 BSI Business Systems Integration AG. |
| * 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: |
| * BSI Business Systems Integration AG - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.scout.rt.client.ui.form.fields; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.security.Permission; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.scout.rt.client.dto.FormData; |
| import org.eclipse.scout.rt.client.dto.FormData.SdkCommand; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.FormFieldChains.FormFieldAddSearchTermsChain; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.FormFieldChains.FormFieldCalculateVisibleChain; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.FormFieldChains.FormFieldChangedMasterValueChain; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.FormFieldChains.FormFieldDataChangedChain; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.FormFieldChains.FormFieldDisposeFieldChain; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.FormFieldChains.FormFieldInitFieldChain; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.FormFieldChains.FormFieldIsEmptyChain; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.FormFieldChains.FormFieldIsSaveNeededChain; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.FormFieldChains.FormFieldMarkSavedChain; |
| import org.eclipse.scout.rt.client.extension.ui.form.fields.IFormFieldExtension; |
| import org.eclipse.scout.rt.client.services.common.search.ISearchFilterService; |
| import org.eclipse.scout.rt.client.ui.DataChangeListener; |
| import org.eclipse.scout.rt.client.ui.WeakDataChangeListener; |
| import org.eclipse.scout.rt.client.ui.action.ActionUtility; |
| import org.eclipse.scout.rt.client.ui.action.keystroke.IKeyStroke; |
| import org.eclipse.scout.rt.client.ui.desktop.IDesktop; |
| import org.eclipse.scout.rt.client.ui.form.IForm; |
| import org.eclipse.scout.rt.client.ui.form.IFormFieldVisitor; |
| import org.eclipse.scout.rt.client.ui.form.fields.groupbox.AbstractGroupBox; |
| import org.eclipse.scout.rt.client.ui.form.fields.groupbox.IGroupBox; |
| import org.eclipse.scout.rt.client.ui.form.fields.sequencebox.AbstractSequenceBox; |
| import org.eclipse.scout.rt.platform.BEANS; |
| import org.eclipse.scout.rt.platform.IOrdered; |
| import org.eclipse.scout.rt.platform.Order; |
| import org.eclipse.scout.rt.platform.Replace; |
| import org.eclipse.scout.rt.platform.annotations.ConfigOperation; |
| import org.eclipse.scout.rt.platform.annotations.ConfigProperty; |
| import org.eclipse.scout.rt.platform.classid.ClassId; |
| import org.eclipse.scout.rt.platform.exception.ExceptionHandler; |
| import org.eclipse.scout.rt.platform.exception.PlatformError; |
| import org.eclipse.scout.rt.platform.exception.ProcessingException; |
| import org.eclipse.scout.rt.platform.reflect.AbstractPropertyObserver; |
| import org.eclipse.scout.rt.platform.reflect.BasicPropertySupport; |
| import org.eclipse.scout.rt.platform.reflect.ConfigurationUtility; |
| import org.eclipse.scout.rt.platform.status.IMultiStatus; |
| import org.eclipse.scout.rt.platform.status.IStatus; |
| import org.eclipse.scout.rt.platform.status.MultiStatus; |
| import org.eclipse.scout.rt.platform.util.CollectionUtility; |
| import org.eclipse.scout.rt.platform.util.XmlUtility; |
| import org.eclipse.scout.rt.shared.data.basic.FontSpec; |
| import org.eclipse.scout.rt.shared.data.basic.NamedBitMaskHelper; |
| import org.eclipse.scout.rt.shared.data.form.fields.AbstractFormFieldData; |
| import org.eclipse.scout.rt.shared.dimension.IDimensions; |
| import org.eclipse.scout.rt.shared.extension.AbstractExtension; |
| import org.eclipse.scout.rt.shared.extension.ContributionComposite; |
| import org.eclipse.scout.rt.shared.extension.IContributionOwner; |
| import org.eclipse.scout.rt.shared.extension.IExtensibleObject; |
| import org.eclipse.scout.rt.shared.extension.IExtension; |
| import org.eclipse.scout.rt.shared.extension.ObjectExtensions; |
| import org.eclipse.scout.rt.shared.services.common.jdbc.SearchFilter; |
| import org.eclipse.scout.rt.shared.services.common.security.IAccessControlService; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| |
| @ClassId("cb3204c4-71bf-4dc6-88a4-3a8f81a7ca10") |
| @FormData(value = AbstractFormFieldData.class, sdkCommand = SdkCommand.USE) |
| public abstract class AbstractFormField extends AbstractPropertyObserver implements IFormField, IContributionOwner, IExtensibleObject { |
| |
| private static final String ENABLED_SLAVE = "ENABLED_SLAVE"; |
| private static final String INITIALIZED = "INITIALIZED"; |
| private static final String TOUCHED = "TOUCHED"; |
| private static final String LABEL_VISIBLE = "LABEL_VISIBLE"; |
| private static final String MASTER_REQUIRED = "MASTER_REQUIRED"; |
| |
| private static final Logger LOG = LoggerFactory.getLogger(AbstractFormField.class); |
| private static final NamedBitMaskHelper VISIBLE_BIT_HELPER = new NamedBitMaskHelper(IDimensions.VISIBLE, IDimensions.VISIBLE_GRANTED); |
| private static final NamedBitMaskHelper LABEL_VISIBLE_BIT_HELPER = new NamedBitMaskHelper(LABEL_VISIBLE); |
| private static final NamedBitMaskHelper ENABLED_BIT_HELPER = new NamedBitMaskHelper(IDimensions.ENABLED, IDimensions.ENABLED_GRANTED, ENABLED_SLAVE); |
| private static final NamedBitMaskHelper FLAGS_BIT_HELPER = new NamedBitMaskHelper(INITIALIZED, TOUCHED, MASTER_REQUIRED); |
| |
| /** |
| * Provides 8 dimensions for enabled state.<br> |
| * Internally used: {@link IDimensions#ENABLED}, {@link IDimensions#ENABLED_GRANTED}, {@link #ENABLED_SLAVE}.<br> |
| * 5 dimensions remain for custom use. This FormField is enabled, if all dimensions are enabled (all bits set). |
| */ |
| private byte m_enabled; |
| |
| /** |
| * Provides 8 dimensions for visibility.<br> |
| * Internally used: {@link IDimensions#VISIBLE}, {@link IDimensions#VISIBLE_GRANTED}.<br> |
| * 6 dimensions remain for custom use. This FormField is visible, if all dimensions are visible (all bits set). |
| */ |
| private byte m_visible; |
| |
| /** |
| * Provides 8 dimensions for label visibility.<br> |
| * Internally used: {@link #LABEL_VISIBLE} and one level by the {@link AbstractSequenceBox}.<br> |
| * 6 dimensions remain for custom use. This FormField's label is visible, if all dimensions are visible (all bits |
| * set). |
| */ |
| private byte m_labelVisible; |
| |
| /** |
| * Provides 8 boolean flags.<br> |
| * Currently used: {@link #INITIALIZED}, {@link #TOUCHED}, {@link #MASTER_REQUIRED} |
| */ |
| private byte m_flags; |
| |
| private IForm m_form; |
| private byte m_labelPosition; |
| private byte m_labelHorizontalAlignment; |
| private int m_labelWidthInPixel; |
| private Permission m_visiblePermission; |
| private Permission m_enabledPermission; |
| private IValueField<?> m_masterField; |
| protected int m_valueChangeTriggerEnabled = 1;// >=1 is true |
| private BasicPropertySupport m_subtreePropertyChangeSupport; |
| private P_MasterListener m_currentMasterListener;// my master |
| private DataChangeListener m_internalDataChangeListener; |
| protected ContributionComposite m_contributionHolder; |
| private String m_initialLabel; |
| private final ObjectExtensions<AbstractFormField, IFormFieldExtension<? extends AbstractFormField>> m_objectExtensions; |
| |
| public AbstractFormField() { |
| this(true); |
| } |
| |
| public AbstractFormField(boolean callInitializer) { |
| m_enabled = NamedBitMaskHelper.ALL_BITS_SET; // default enabled |
| m_visible = NamedBitMaskHelper.ALL_BITS_SET; // default visible |
| m_labelVisible = NamedBitMaskHelper.ALL_BITS_SET; // default label visible |
| m_objectExtensions = new ObjectExtensions<AbstractFormField, IFormFieldExtension<? extends AbstractFormField>>(this, false); |
| if (callInitializer) { |
| callInitializer(); |
| } |
| } |
| |
| @Override |
| public final List<Object> getAllContributions() { |
| return m_contributionHolder.getAllContributions(); |
| } |
| |
| @Override |
| public final <T> List<T> getContributionsByClass(Class<T> type) { |
| return m_contributionHolder.getContributionsByClass(type); |
| } |
| |
| @Override |
| public final <T> T getContribution(Class<T> contribution) { |
| return m_contributionHolder.getContribution(contribution); |
| } |
| |
| @Override |
| public final <T> T optContribution(Class<T> contribution) { |
| return m_contributionHolder.optContribution(contribution); |
| } |
| |
| protected final void callInitializer() { |
| if (isInitialized()) { |
| return; |
| } |
| |
| try { |
| setValueChangeTriggerEnabled(false); |
| interceptInitConfig(); |
| } |
| finally { |
| setValueChangeTriggerEnabled(true); |
| } |
| setInitialized(); |
| } |
| |
| @Override |
| public final List<? extends IFormFieldExtension<? extends AbstractFormField>> getAllExtensions() { |
| return m_objectExtensions.getAllExtensions(); |
| } |
| |
| protected IFormFieldExtension<? extends AbstractFormField> createLocalExtension() { |
| return new LocalFormFieldExtension<AbstractFormField>(this); |
| } |
| |
| @Override |
| public <T extends IExtension<?>> T getExtension(Class<T> c) { |
| return m_objectExtensions.getExtension(c); |
| } |
| |
| /** |
| * @return The text to show as the label of the current {@link IFormField}. |
| */ |
| @ConfigProperty(ConfigProperty.TEXT) |
| @Order(10) |
| protected String getConfiguredLabel() { |
| return null; |
| } |
| |
| /** |
| * One of the LABEL_POSITION_* constants or a custom constants interpreted by the ui. |
| * |
| * @since 17.11.2009 |
| */ |
| @Order(15) |
| @ConfigProperty(ConfigProperty.LABEL_POSITION) |
| protected byte getConfiguredLabelPosition() { |
| return LABEL_POSITION_DEFAULT; |
| } |
| |
| /** |
| * @since 19.11.2009 |
| * @return the fixed label witdh >0 or LABEL_WIDTH_DEFAULT or LABEL_WIDTH_UI for ui-dependent label width |
| */ |
| @ConfigProperty(ConfigProperty.INTEGER) |
| @Order(16) |
| protected int getConfiguredLabelWidthInPixel() { |
| return LABEL_WIDTH_DEFAULT; |
| } |
| |
| /** |
| * @since 19.11.2009 |
| * @return one of the following {@link IFormField#LABEL_HORIZONTAL_ALIGNMENT_LEFT}, |
| * {@link IFormField#LABEL_HORIZONTAL_ALIGNMENT_CENTER}, {@link IFormField#LABEL_HORIZONTAL_ALIGNMENT_RIGHT} |
| * or {@link IFormField#LABEL_HORIZONTAL_ALIGNMENT_DEFAULT}. |
| */ |
| @Order(17) |
| @ConfigProperty(ConfigProperty.LABEL_HORIZONTAL_ALIGNMENT) |
| protected byte getConfiguredLabelHorizontalAlignment() { |
| return LABEL_HORIZONTAL_ALIGNMENT_DEFAULT; |
| } |
| |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(20) |
| protected boolean getConfiguredLabelVisible() { |
| return true; |
| } |
| |
| /** |
| * Specifies if the form field is enabled initially. |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(30) |
| protected boolean getConfiguredEnabled() { |
| return true; |
| } |
| |
| @ConfigProperty(ConfigProperty.INTEGER) |
| @Order(35) |
| protected int getConfiguredDisabledStyle() { |
| return DISABLED_STYLE_DEFAULT; |
| } |
| |
| /** |
| * Specifies if the form field is visible initially.<br> |
| * Affects only the field itself. In case of a composite field the property does not get broadcasted initially. |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(40) |
| protected boolean getConfiguredVisible() { |
| return true; |
| } |
| |
| /** |
| * Specifies if the form field is mandatory (required) initially.<br> |
| * Affects only the field itself. In case of a composite field the property does not get broadcasted initially. |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(45) |
| protected boolean getConfiguredMandatory() { |
| return false; |
| } |
| |
| /** |
| * Configures the view order of this form field. The view order determines the order in which the field appears.<br> |
| * The view order of field with no configured view order ({@code < 0}) is initialized based on the {@link Order} |
| * annotation of the form field class. |
| * <p> |
| * Subclasses can override this method. The default is {@link IOrdered#DEFAULT_ORDER}. |
| * |
| * @return View order of this form field. |
| */ |
| @ConfigProperty(ConfigProperty.DOUBLE) |
| @Order(145) |
| protected double getConfiguredViewOrder() { |
| return IOrdered.DEFAULT_ORDER; |
| } |
| |
| @ConfigProperty(ConfigProperty.TEXT) |
| @Order(50) |
| protected String getConfiguredTooltipText() { |
| return null; |
| } |
| |
| /** |
| * Configures the css class(es) of this field. |
| * <p> |
| * Subclasses can override this method. Default is {@code null}. |
| * |
| * @return a string containing one or more classes separated by space, or null if no class should be set. |
| */ |
| @ConfigProperty(ConfigProperty.STRING) |
| @Order(55) |
| protected String getConfiguredCssClass() { |
| return null; |
| } |
| |
| @ConfigProperty(ConfigProperty.COLOR) |
| @Order(60) |
| protected String getConfiguredForegroundColor() { |
| return null; |
| } |
| |
| @ConfigProperty(ConfigProperty.COLOR) |
| @Order(70) |
| protected String getConfiguredBackgroundColor() { |
| return null; |
| } |
| |
| @ConfigProperty(ConfigProperty.FONT) |
| @Order(80) |
| protected String getConfiguredFont() { |
| return null; |
| } |
| |
| /** |
| * Configures the foreground color of the label. The color is represented by the HEX value (e.g. FFFFFF). |
| * <p> |
| * Subclasses can override this method. Default is {@code null}. |
| * |
| * @return Foreground color HEX value of the label. |
| */ |
| @ConfigProperty(ConfigProperty.COLOR) |
| @Order(60) |
| protected String getConfiguredLabelForegroundColor() { |
| return null; |
| } |
| |
| /** |
| * Configures the background color of the label. The color is represented by the HEX value (e.g. FFFFFF). |
| * <p> |
| * Subclasses can override this method. Default is {@code null}. |
| * |
| * @return Background color HEX value of the label. |
| */ |
| @ConfigProperty(ConfigProperty.COLOR) |
| @Order(70) |
| protected String getConfiguredLabelBackgroundColor() { |
| return null; |
| } |
| |
| /** |
| * Configures the font of the label. See {@link FontSpec#parse(String)} for the appropriate format. |
| * <p> |
| * Subclasses can override this method. Default is {@code null}. |
| * |
| * @return Font of the label. |
| */ |
| @ConfigProperty(ConfigProperty.FONT) |
| @Order(80) |
| protected String getConfiguredLabelFont() { |
| return null; |
| } |
| |
| /** |
| * Configures the horizontal alignment of the fields inside this group box.<br> |
| * This property typically only has an effect if fill horizontal is set to false which can be configured by |
| * {@link #getConfiguredFillHorizontal()}. |
| * <p> |
| * Subclasses can override this method. Default alignment is left. |
| * |
| * @return -1 for left, 0 for center and 1 for right alignment |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.HORIZONTAL_ALIGNMENT) |
| @Order(85) |
| protected int getConfiguredHorizontalAlignment() { |
| return -1; |
| } |
| |
| /** |
| * Configures the vertical alignment of the fields inside this group box.<br> |
| * This property typically only has an effect if fill vertical is set to false which can be configured by |
| * {@link #getConfiguredFillVertical()}. |
| * <p> |
| * Subclasses can override this method. Default alignment is top. |
| * |
| * @return -1 for top, 0 for center and 1 for bottom alignment |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.VERTICAL_ALIGNMENT) |
| @Order(86) |
| protected int getConfiguredVerticalAlignment() { |
| return -1; |
| } |
| |
| /** |
| * Configures whether this field should horizontally fill the grid cell.<br> |
| * If the property is set to true, the field takes all the horizontal space and therefore is as width as the grid |
| * cell.<br> |
| * If it's set to false, the width is computed based on the properties {@link #getConfiguredGridUseUiWidth()} and |
| * {@link #getConfiguredWidthInPixel()}. If non of these are set, a default value is used which typically is the width |
| * of a logical grid column. |
| * <p> |
| * Subclasses can override this method. Default is true. |
| * |
| * @return {@code true} if this field should horizontally fill the grid cell, {@code false} otherwise |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(87) |
| protected boolean getConfiguredFillHorizontal() { |
| return true; |
| } |
| |
| /** |
| * Configures whether this field should vertically fill the grid cell.<br> |
| * If the property is set to true, the field takes all the vertical space and therefore is as height as the grid cell. |
| * <br> |
| * If it's set to false, the height is computed based on the properties {@link #getConfiguredGridUseUiHeight()} and |
| * {@link #getConfiguredHeightInPixel()}. If non of these are set, a default value is used which typically is the |
| * height of a logical grid row. |
| * <p> |
| * Subclasses can override this method. Default is true. |
| * |
| * @return {@code true} if this field should vertically fill the grid cell, {@code false} otherwise |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(88) |
| protected boolean getConfiguredFillVertical() { |
| return true; |
| } |
| |
| /** |
| * Configures the x position (horizontal) of this field in the logical grid of the surrounding group box.<br> |
| * If the value is set to -1, the property will be ignored. If the value is >= 0, it's considered as grid column. <br> |
| * It is not necessary to explicitly set a column count by {@link AbstractGroupBox#getConfiguredGridColumnCount()}. |
| * <p> |
| * This property only has an effect if every field inside the group box has a fix position which means every field |
| * inside the group box need to have x and y to be set which can be configured by {@link #getConfiguredGridX()} and |
| * {@link #getConfiguredGridY()}. |
| * <p> |
| * Subclasses can override this method. Default is -1. |
| * |
| * @return the x position in the grid. |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.INTEGER) |
| @Order(90) |
| protected int getConfiguredGridX() { |
| return -1; |
| } |
| |
| /** |
| * Configures the y (vertical) position of this field in the logical grid of the surrounding group box.<br> |
| * If the value is set to -1, the property will be ignored. If the value is >= 0, it's considered as grid row. <br> |
| * <p> |
| * This property only has an effect if every field inside the group box has a fix position which means every field |
| * inside the group box need to have x and y to be set which can be configured by {@link #getConfiguredGridX()} and |
| * {@link #getConfiguredGridY()}. |
| * <p> |
| * Subclasses can override this method. Default is -1. |
| * |
| * @return the y position in the grid. |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.INTEGER) |
| @Order(95) |
| protected int getConfiguredGridY() { |
| return -1; |
| } |
| |
| /** |
| * Configures the column span of this field.<br> |
| * The value defined by this property refers to the number of columns defined by the group box which contains this |
| * field. This column count can be configured by {@link AbstractGroupBox#getConfiguredGridColumnCount()}. |
| * <p> |
| * <b>Example:</b><br> |
| * If the column count of the group box is set to 3 and a column span of this field is set to 2 it means 2/3 of the |
| * group box width is used for this field:<br> |
| * <table border="1"> |
| * <colgroup align="center" width="40"/> <colgroup align="center" width="40"/> <colgroup align="center" width="40"/> |
| * <tr> |
| * <td colspan="2">this</td> |
| * <td>...</td> |
| * </tr> |
| * <tr> |
| * <td>...</td> |
| * <td>...</td> |
| * <td>...</td> |
| * </tr> |
| * </table> |
| * <p> |
| * Subclasses can override this method. Default is 1. |
| * |
| * @return the number of columns to span |
| * @see #getConfiguredGridWeightX(), {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.INTEGER) |
| @Order(100) |
| protected int getConfiguredGridW() { |
| return 1; |
| } |
| |
| /** |
| * Configures the row span of this field.<br> |
| * Compared to the number of columns, which is a configurable value and therefore static, the number of rows is |
| * dynamic. That number depends on the number of fields used in the group box which contains this field, as well as |
| * the number of columns defined by that group box. |
| * <p> |
| * <b>Example:</b> A group box with 2 columns contains 3 fields: The first 2 fields have gridW = 1 and gridH = 1 and |
| * the third field has gridW = 1 and gridH = 2. In this case the third field would be as height as the other 2 fields |
| * together because it spans two rows:<br> |
| * <table border="1"> |
| * <colgroup align="center" width="40"/> <colgroup align="center" width="40"/> |
| * <tr> |
| * <td>1</td> |
| * <td rowspan="2">3</td> |
| * </tr> |
| * <tr> |
| * <td>2</td> |
| * </tr> |
| * </table> |
| * <p> |
| * Note that this value is actually the minimum logical height if the field is scalable (see |
| * {@link #getConfiguredGridWeightY()}. |
| * <p> |
| * Subclasses can override this method. Default is 1. |
| * |
| * @return the number of rows to span |
| * @see {@link #getConfiguredGridWeightY()} comment about weightY logic which depends on the gridH value configured |
| * here |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.INTEGER) |
| @Order(105) |
| protected int getConfiguredGridH() { |
| return 1; |
| } |
| |
| /** |
| * Configures how much a grid cell should horizontally grow or shrink.<br> |
| * <p> |
| * The value for this property can either be -1 or between 0 and 1. |
| * <ul> |
| * <li>0 means fixed width and the grid cell won't grow or shrink.</li> |
| * <li>Greater 0 means the grid cell will grab the excess horizontal space and therefore grow or shrink. If the group |
| * box contains more than one field with weightX > 0, the weight is used to specify how strong the width of the grid |
| * cell should be adjusted.</li> |
| * <li>-1 means the ui computes the optimal value so that the fields proportionally grab the excess space.</li> |
| * </ul> |
| * <b>Examples:</b> |
| * <ul> |
| * <li>A group box with 3 columns contains 3 fields: Every field has gridW = 1 and weightX = -1. This leads to 1 row |
| * and 3 grid cells which would grow and shrink proportionally because weightX is automatically set to > 0.</li> |
| * <li>If the weight of these 3 fields were set to 0.1, 0.1 and 1, the first two fields would adjust the size very |
| * slowly and would mostly be as big as a logical grid column (because gridW is set to 1), whereas the third field |
| * would adjust it's size very fast.</li> |
| * </ul> |
| * <p> |
| * Subclasses can override this method. Default is -1. |
| * |
| * @return a value between 0 and 1, or -1 |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.DOUBLE) |
| @Order(130) |
| protected double getConfiguredGridWeightX() { |
| return -1; |
| } |
| |
| /** |
| * Configures how much a grid cell should vertically grow or shrink.<br> |
| * <p> |
| * The value for this property can either be -1 or between 0 and 1. |
| * <ul> |
| * <li>0 means fixed height and the grid cell won't grow or shrink.</li> |
| * <li>Greater 0 means the grid cell will grab the excess vertical space and therefore grow or shrink. If the group |
| * box contains more than one field with weightY > 0, the weight is used to specify how strong the height of the grid |
| * cell should be adjusted.</li> |
| * <li>-1 means the ui computes the optimal value so that the fields proportionally grab the excess space, but only if |
| * gridH is > 1. If gridH is 1 a weight of 0 is set and the grid cell does not grow or shrink.</li> |
| * </ul> |
| * <b>Examples:</b> |
| * <ul> |
| * <li>A group box with 1 column contains 3 fields: Every field has gridH = 1 and weightY = -1. This leads to 3 rows |
| * with fixed height, no additional space is grabbed, because weightY will automatically be set to 0.</li> |
| * <li>If the weight of these 3 fields were set to 1, the fields would grow and shrink proportionally.</li> |
| * <li>If the weight of these 3 fields were set to 0.1, 0.1 and 1, the first two fields would adjust the size very |
| * slowly and would mostly be a as big as one logical grid row (because gridH is set to 1), whereas the third field |
| * would adjust it's size very fast.</li> |
| * </ul> |
| * <p> |
| * Subclasses can override this method. Default is -1. |
| * |
| * @return a value between 0 and 1, or -1 |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.DOUBLE) |
| @Order(140) |
| protected double getConfiguredGridWeightY() { |
| return -1; |
| } |
| |
| /** |
| * Configures whether the field should be as width as preferred by the ui. The preferred width normally is the |
| * computed width of the child fields.<br> |
| * This property typically has less priority than {@link #getConfiguredWidthInPixel()} and therefore only has an |
| * effect if no explicit width is set. |
| * <p> |
| * Subclasses can override this method. Default is false. |
| * |
| * @return {@code true} if this field should be as width as preferred by the ui, {@code false} otherwise |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(142) |
| protected boolean getConfiguredGridUseUiWidth() { |
| return false; |
| } |
| |
| /** |
| * Configures whether the field should be as height as preferred by the ui. The preferred height normally is the |
| * computed height of the child fields.<br> |
| * This property typically has less priority than {@link #getConfiguredHeightInPixel()} and therefore only has an |
| * effect if no explicit height is set. |
| * <p> |
| * Subclasses can override this method. Default is false. |
| * |
| * @return {@code true} if this field should be as height as preferred by the ui, {@code false} otherwise |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(142) |
| protected boolean getConfiguredGridUseUiHeight() { |
| return false; |
| } |
| |
| /** |
| * Configures the preferred width of the field. <br> |
| * If the value is <=0 the property will be ignored by the ui layout manager. |
| * <p> |
| * Subclasses can override this method. Default is 0. |
| * |
| * @return the preferred width in pixel |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.INTEGER) |
| @Order(150) |
| protected int getConfiguredWidthInPixel() { |
| return 0; |
| } |
| |
| /** |
| * Configures the preferred height of the field. <br> |
| * If the value is <=0 the property will be ignored by the ui layout manager. |
| * <p> |
| * Subclasses can override this method. Default is 0. |
| * |
| * @return the preferred height in pixel |
| * @see {@link #getGridData()}, {@link #getGridDataHints()} |
| */ |
| @ConfigProperty(ConfigProperty.INTEGER) |
| @Order(160) |
| protected int getConfiguredHeightInPixel() { |
| return 0; |
| } |
| |
| @ConfigProperty(ConfigProperty.MASTER_FIELD) |
| @Order(170) |
| protected Class<? extends IValueField> getConfiguredMasterField() { |
| return null; |
| } |
| |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(180) |
| protected boolean getConfiguredMasterRequired() { |
| return false; |
| } |
| |
| /** |
| * @deprecated will be removed with 7.1, property is no longer supported by Html UI |
| * @return |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(190) |
| @Deprecated |
| protected boolean getConfiguredFocusable() { |
| return true; |
| } |
| |
| /** |
| * @return <code>false</code> if this field can get the initial focus when the form is opened (default). Set to |
| * <code>true</code> to prevent this field from getting the initial focus. In both cases, the field will still |
| * be manually focusable by the user. |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(195) |
| protected boolean getConfiguredPreventInitialFocus() { |
| return false; |
| } |
| |
| /** |
| * Configures whether or not the space for the status is visible. |
| * <p> |
| * If set to false, the space is not visible unless there is a status message, tooltip or menu. <br> |
| * If set to true the space is always visible, even if there is no status message, no tooltip and no menu. |
| * |
| * @return {@code true} if the space for the status should always be visible, {@code false} otherwise |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(200) |
| protected boolean getConfiguredStatusVisible() { |
| return true; |
| } |
| |
| /** |
| * Configures the position of the status. |
| * <p> |
| * Subclasses can override this method. Default is {@value IFormField#STATUS_POSITION_DEFAULT}. |
| * |
| * @since 6.0 |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(200) |
| protected String getConfiguredStatusPosition() { |
| return STATUS_POSITION_DEFAULT; |
| } |
| |
| private List<Class<? extends IKeyStroke>> getConfiguredKeyStrokes() { |
| Class[] dca = ConfigurationUtility.getDeclaredPublicClasses(getClass()); |
| List<Class<IKeyStroke>> fca = ConfigurationUtility.filterClasses(dca, IKeyStroke.class); |
| return ConfigurationUtility.removeReplacedClasses(fca); |
| } |
| |
| @ConfigOperation |
| @Order(10) |
| protected void execInitField() { |
| } |
| |
| /** |
| * On any value change or call to {@link #checkSaveNeeded()} this method is called to calculate if the field needs |
| * save |
| */ |
| @ConfigOperation |
| @Order(11) |
| protected boolean execIsSaveNeeded() { |
| return false; |
| } |
| |
| /** |
| * Make field saved, for example a table is maring all rows as non-changed |
| */ |
| @ConfigOperation |
| @Order(12) |
| protected void execMarkSaved() { |
| } |
| |
| /** |
| * on any value change or call to {@link #checkEmpty()} this method is called to calculate if the field represents an |
| * empty state (semantics) |
| */ |
| @ConfigOperation |
| @Order(13) |
| protected boolean execIsEmpty() { |
| return true; |
| } |
| |
| /** |
| * see {@link IDesktop#dataChanged(Object...)} |
| */ |
| @ConfigOperation |
| @Order(14) |
| protected void execDataChanged(Object... dataTypes) { |
| } |
| |
| @ConfigOperation |
| @Order(15) |
| protected void execDisposeField() { |
| } |
| |
| @Override |
| public final void applySearch(SearchFilter search) { |
| interceptAddSearchTerms(search); |
| } |
| |
| /** |
| * add verbose information to the search filter |
| */ |
| @ConfigOperation |
| protected void execAddSearchTerms(SearchFilter search) { |
| applySearchInternal(search); |
| } |
| |
| /** |
| * override this method to apply new default handling |
| */ |
| protected void applySearchInternal(final SearchFilter search) { |
| ISearchFilterService sfs = BEANS.get(ISearchFilterService.class); |
| if (sfs != null) { |
| sfs.applySearchDelegate(this, search, true); |
| } |
| } |
| |
| /** |
| * AFTER a new valid master value was stored, this method is called |
| */ |
| @ConfigOperation |
| @Order(50) |
| protected void execChangedMasterValue(Object newMasterValue) { |
| } |
| |
| protected final void interceptInitConfig() { |
| m_objectExtensions.initConfigAndBackupExtensionContext(createLocalExtension(), new Runnable() { |
| @Override |
| public void run() { |
| initConfig(); |
| } |
| }); |
| } |
| |
| protected void initConfig() { |
| setGridDataInternal(new GridData(-1, -1, 1, 1, -1, -1)); |
| setGridDataHints(new GridData(-1, -1, 1, 1, -1, -1)); |
| propertySupport.setPropertyBool(PROP_EMPTY, true); |
| m_contributionHolder = new ContributionComposite(this); |
| |
| setEnabled(getConfiguredEnabled()); |
| setDisabledStyle(getConfiguredDisabledStyle()); |
| setVisible(getConfiguredVisible()); |
| setMandatory(getConfiguredMandatory()); |
| setOrder(calculateViewOrder()); |
| setTooltipText(getConfiguredTooltipText()); |
| setInitialLabel(getConfiguredLabel()); |
| setLabel(getConfiguredLabel()); |
| setLabelPosition(getConfiguredLabelPosition()); |
| setLabelWidthInPixel(getConfiguredLabelWidthInPixel()); |
| setLabelHorizontalAlignment(getConfiguredLabelHorizontalAlignment()); |
| setLabelVisible(getConfiguredLabelVisible()); |
| setStatusVisible(getConfiguredStatusVisible()); |
| setStatusPosition(getConfiguredStatusPosition()); |
| setCssClass((getConfiguredCssClass())); |
| if (getConfiguredBackgroundColor() != null) { |
| setBackgroundColor((getConfiguredBackgroundColor())); |
| } |
| if (getConfiguredForegroundColor() != null) { |
| setForegroundColor((getConfiguredForegroundColor())); |
| } |
| if (getConfiguredFont() != null) { |
| setFont(FontSpec.parse(getConfiguredFont())); |
| } |
| if (getConfiguredLabelBackgroundColor() != null) { |
| setLabelBackgroundColor((getConfiguredLabelBackgroundColor())); |
| } |
| if (getConfiguredLabelForegroundColor() != null) { |
| setLabelForegroundColor((getConfiguredLabelForegroundColor())); |
| } |
| if (getConfiguredLabelFont() != null) { |
| setLabelFont(FontSpec.parse(getConfiguredLabelFont())); |
| } |
| setFocusable(getConfiguredFocusable()); |
| setPreventInitialFocus(getConfiguredPreventInitialFocus()); |
| setGridDataHints(new GridData(getConfiguredGridX(), getConfiguredGridY(), getConfiguredGridW(), getConfiguredGridH(), getConfiguredGridWeightX(), getConfiguredGridWeightY(), getConfiguredGridUseUiWidth(), getConfiguredGridUseUiHeight(), |
| getConfiguredHorizontalAlignment(), getConfiguredVerticalAlignment(), getConfiguredFillHorizontal(), getConfiguredFillVertical(), getConfiguredWidthInPixel(), getConfiguredHeightInPixel())); |
| setMasterRequired(getConfiguredMasterRequired()); |
| // private listener for subtree property change events |
| addPropertyChangeListener(new PropertyChangeListener() { |
| @Override |
| public void propertyChange(PropertyChangeEvent e) { |
| fireSubtreePropertyChange(e); |
| } |
| }); |
| } |
| |
| /** |
| * Calculates the formfield's view order, e.g. if the @Order annotation is set to 30.0, the method will return 30.0. |
| * If no {@link Order} annotation is set, the method checks its super classes for an @Order annotation. |
| * |
| * @since 4.0.1 |
| */ |
| @SuppressWarnings("squid:S1244") // Floating point numbers should not be tested for equality |
| protected double calculateViewOrder() { |
| double viewOrder = getConfiguredViewOrder(); |
| if (viewOrder == IOrdered.DEFAULT_ORDER) { |
| Class<?> cls = getClass(); |
| while (cls != null && IFormField.class.isAssignableFrom(cls)) { |
| if (cls.isAnnotationPresent(Order.class)) { |
| Order order = (Order) cls.getAnnotation(Order.class); |
| return order.value(); |
| } |
| cls = cls.getSuperclass(); |
| } |
| } |
| return viewOrder; |
| } |
| |
| @Override |
| public boolean isInitialized() { |
| return FLAGS_BIT_HELPER.isBitSet(INITIALIZED, m_flags); |
| } |
| |
| private void setInitialized() { |
| m_flags = FLAGS_BIT_HELPER.setBit(INITIALIZED, m_flags); |
| } |
| |
| /** |
| * do not use this method |
| */ |
| @Override |
| public void postInitConfig() { |
| // key strokes, now all inner fields are built |
| updateKeyStrokes(); |
| // master listener, now the inner field is available |
| if (getConfiguredMasterField() != null) { |
| IValueField master = findNearestFieldByClass(getConfiguredMasterField()); |
| setMasterField(master); |
| } |
| } |
| |
| /** |
| * Searching the nearest field implementing the specified class by processing the enclosing field list bottom-up. |
| * |
| * @since 3.8.1 |
| */ |
| private <T extends IFormField> T findNearestFieldByClass(final Class<T> c) { |
| List<ICompositeField> enclosingFields = getEnclosingFieldList(); |
| if (enclosingFields.isEmpty()) { |
| // there are no enclosing fields (i.e. this field is not part of a field template) |
| return getForm().getFieldByClass(c); |
| } |
| |
| // search requested field within critical parent field |
| Collections.reverse(enclosingFields); |
| for (ICompositeField parentField : enclosingFields) { |
| T field = parentField.getFieldByClass(c); |
| if (field != null) { |
| return field; |
| } |
| } |
| |
| // field has not been found in a critical parent field |
| return getForm().getFieldByClass(c); |
| } |
| |
| /** |
| * This is the init of the runtime model after the form and fields are built and configured |
| */ |
| @Override |
| public final void initField() { |
| try { |
| setValueChangeTriggerEnabled(false); |
| // |
| initFieldInternal(); |
| interceptInitField(); |
| // init key strokes |
| ActionUtility.initActions(getKeyStrokes()); |
| } |
| finally { |
| setValueChangeTriggerEnabled(true); |
| } |
| } |
| |
| protected void initFieldInternal() { |
| checkSaveNeeded(); |
| checkEmpty(); |
| } |
| |
| @Override |
| public final void disposeField() { |
| try { |
| disposeFieldInternal(); |
| } |
| catch (Exception e) { |
| LOG.warn("Field [{}]", getClass().getName(), e); |
| } |
| try { |
| interceptDisposeField(); |
| } |
| catch (Exception e) { |
| LOG.warn("Field [{}]", getClass().getName(), e); |
| } |
| } |
| |
| protected void disposeFieldInternal() { |
| } |
| |
| /** |
| * Register a {@link DataChangeListener} on the desktop for these dataTypes<br> |
| * Example: |
| * |
| * <pre> |
| * registerDataChangeListener(CRMEnum.Company, CRMEnum.Project, CRMEnum.Task); |
| * </pre> |
| */ |
| public void registerDataChangeListener(Object... dataTypes) { |
| if (m_internalDataChangeListener == null) { |
| m_internalDataChangeListener = new WeakDataChangeListener() { |
| @Override |
| public void dataChanged(Object... innerDataTypes) { |
| interceptDataChanged(innerDataTypes); |
| } |
| }; |
| } |
| IDesktop.CURRENT.get().addDataChangeListener(m_internalDataChangeListener, dataTypes); |
| } |
| |
| /** |
| * Unregister the {@link DataChangeListener} from the desktop for these dataTypes<br> |
| * Example: |
| * |
| * <pre> |
| * unregisterDataChangeListener(CRMEnum.Company, CRMEnum.Project, CRMEnum.Task); |
| * </pre> |
| */ |
| public void unregisterDataChangeListener(Object... dataTypes) { |
| if (m_internalDataChangeListener != null) { |
| IDesktop.CURRENT.get().removeDataChangeListener(m_internalDataChangeListener, dataTypes); |
| } |
| } |
| |
| protected void fireSubtreePropertyChange(PropertyChangeEvent e) { |
| // fire up the tree |
| IFormField parentField = getParentField(); |
| if (parentField instanceof AbstractFormField) { |
| ((AbstractFormField) parentField).fireSubtreePropertyChange(e); |
| } |
| // fire my level |
| if (m_subtreePropertyChangeSupport != null) { |
| m_subtreePropertyChangeSupport.firePropertyChange(e); |
| } |
| } |
| |
| @Override |
| public boolean acceptVisitor(IFormFieldVisitor visitor, int level, int fieldIndex, boolean includeThis) { |
| if (!includeThis) { |
| return true; |
| } |
| return CompositeFieldUtility.applyFormFieldVisitor(visitor, this, null, level, fieldIndex); |
| } |
| |
| @Override |
| public IForm getForm() { |
| return m_form; |
| } |
| |
| @Override |
| public IGroupBox getParentGroupBox() { |
| ICompositeField f = getParentField(); |
| while (f != null && !(f instanceof IGroupBox)) { |
| f = f.getParentField(); |
| } |
| return (IGroupBox) f; |
| } |
| |
| @Override |
| public ICompositeField getParentField() { |
| return (ICompositeField) propertySupport.getProperty(PROP_PARENT_FIELD); |
| } |
| |
| @Override |
| public void setParentFieldInternal(ICompositeField f) { |
| propertySupport.setProperty(PROP_PARENT_FIELD, f); |
| } |
| |
| /** |
| * do not use this internal method |
| */ |
| @Override |
| public void setFormInternal(IForm form) { |
| m_form = form; |
| } |
| |
| @Override |
| public String toString() { |
| return getLabel() + "/" + getFieldId() + " (" + getClass().getName() + ")"; |
| } |
| |
| @Override |
| public void setView(boolean visible, boolean enabled, boolean mandatory) { |
| setVisible(visible); |
| setEnabled(enabled); |
| setMandatory(mandatory); |
| } |
| |
| @Override |
| public boolean isValueChangeTriggerEnabled() { |
| return m_valueChangeTriggerEnabled >= 1; |
| } |
| |
| @Override |
| public void setValueChangeTriggerEnabled(boolean b) { |
| if (b) { |
| m_valueChangeTriggerEnabled++; |
| } |
| else { |
| m_valueChangeTriggerEnabled--; |
| } |
| } |
| |
| @Override |
| public void addSubtreePropertyChangeListener(PropertyChangeListener listener) { |
| if (listener == null) { |
| return; |
| } |
| if (m_subtreePropertyChangeSupport == null) { |
| m_subtreePropertyChangeSupport = new BasicPropertySupport(this); |
| } |
| m_subtreePropertyChangeSupport.addPropertyChangeListener(listener); |
| } |
| |
| @Override |
| public void addSubtreePropertyChangeListener(String propName, PropertyChangeListener listener) { |
| if (listener == null) { |
| return; |
| } |
| if (m_subtreePropertyChangeSupport == null) { |
| m_subtreePropertyChangeSupport = new BasicPropertySupport(this); |
| } |
| m_subtreePropertyChangeSupport.addPropertyChangeListener(propName, listener); |
| } |
| |
| @Override |
| public void removeSubtreePropertyChangeListener(PropertyChangeListener listener) { |
| if (m_subtreePropertyChangeSupport != null) { |
| m_subtreePropertyChangeSupport.removePropertyChangeListener(listener); |
| } |
| } |
| |
| @Override |
| public void removeSubtreePropertyChangeListener(String propName, PropertyChangeListener listener) { |
| if (m_subtreePropertyChangeSupport != null) { |
| m_subtreePropertyChangeSupport.removePropertyChangeListener(propName, listener); |
| } |
| } |
| |
| @Override |
| public boolean hasProperty(String name) { |
| return propertySupport.hasProperty(name); |
| } |
| |
| @Override |
| public boolean isFieldChanging() { |
| return propertySupport.isPropertiesChanging(); |
| } |
| |
| @Override |
| public void setFieldChanging(boolean b) { |
| propertySupport.setPropertiesChanging(b); |
| } |
| |
| @Override |
| public String getFieldId() { |
| Class<?> c = getClass(); |
| while (c.isAnnotationPresent(Replace.class)) { |
| c = c.getSuperclass(); |
| } |
| return c.getSimpleName(); |
| } |
| |
| /** |
| * Computes a unique class id. |
| * <p> |
| * If the class is annotated with {@link ClassId}, the annotation value is used. If the field is defined in a template |
| * (outside of its form class), the ids of the enclosing fields are appended, if necessary, to make the id unique. |
| * <p> |
| * If the class is not annotated with {@link ClassId}, a unique id is computed using the simple class name. |
| * <p> |
| * For dynamically injected fields this method needs to be overridden to make sure it is unique. |
| */ |
| @Override |
| public String classId() { |
| StringBuilder classId = new StringBuilder(computeClassId()); |
| |
| boolean appendFormId = !getClass().isAnnotationPresent(ClassId.class); |
| if (appendFormId && getForm() != null) { |
| classId.append(ID_CONCAT_SYMBOL).append(getForm().classId()); |
| } |
| boolean duplicate = existsDuplicateClassId(); |
| if (duplicate) { |
| LOG.warn("Found a duplicate classid for {}, adding field index. Override classId for dynamically injected fields.", classId); |
| int fieldIndex = (getParentField() == null ? 0 : getParentField().getFieldIndex(this)); |
| classId.append(ID_CONCAT_SYMBOL).append(fieldIndex); |
| } |
| |
| return classId.toString(); |
| } |
| |
| /** |
| * Computes a class id by considering the enclosing field list. |
| * <p> |
| * Does not consider the complete path for lenient support. For dynamically injected fields {@link #classid()} needs |
| * to be overridden. |
| * </p> |
| */ |
| private String computeClassId() { |
| StringBuilder fieldId = new StringBuilder(); |
| String simpleClassId = ConfigurationUtility.getAnnotatedClassIdWithFallback(getClass(), true); |
| fieldId.append(simpleClassId); |
| List<ICompositeField> enclosingFieldList = getEnclosingFieldList(); |
| for (int i = enclosingFieldList.size() - 1; i >= 0; --i) { |
| ICompositeField enclosingField = enclosingFieldList.get(i); |
| String enclosingClassId = ConfigurationUtility.getAnnotatedClassIdWithFallback(enclosingField.getClass(), true); |
| fieldId.append(ID_CONCAT_SYMBOL).append(enclosingClassId); |
| } |
| return fieldId.toString(); |
| } |
| |
| /** |
| * Sanity check for class ids. Scans all fields in a form to find duplicate class ids. |
| * |
| * @return <code>true</code>, if another field with the same id is found. <code>false</code> otherwise. |
| */ |
| private boolean existsDuplicateClassId() { |
| IForm form = getForm(); |
| String currentClassId = computeClassId(); |
| if (form != null) { |
| List<String> classIds = new ArrayList<String>(); |
| for (IFormField f : form.getAllFields()) { |
| if (f != this) { |
| String fClassId = ConfigurationUtility.getAnnotatedClassIdWithFallback(f.getClass(), true); |
| classIds.add(fClassId); |
| } |
| } |
| if (classIds.contains(currentClassId)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * Data i/o |
| */ |
| @Override |
| public void exportFormFieldData(AbstractFormFieldData target) { |
| } |
| |
| @Override |
| public void importFormFieldData(AbstractFormFieldData source, boolean valueChangeTriggersEnabled) { |
| } |
| |
| /* |
| * XML i/o |
| */ |
| @Override |
| public void storeToXml(Element x) { |
| List<ICompositeField> enclosingFieldList = getEnclosingFieldList(); |
| for (ICompositeField field : enclosingFieldList) { |
| Element enclosingField = x.getOwnerDocument().createElement("enclosingField"); |
| setXmlFormFieldId(enclosingField, field); |
| // Enclosing fields are traversed from outside to inside. Hence add XML child at the end. |
| x.appendChild(enclosingField); |
| } |
| // set field ids |
| setXmlFormFieldId(x, this); |
| } |
| |
| @Override |
| public List<ICompositeField> getEnclosingFieldList() { |
| List<ICompositeField> enclosingFieldList = new ArrayList<ICompositeField>(); |
| // compute enclosing field path |
| ICompositeField p = getParentField(); |
| while (p != null) { |
| if (!(p instanceof AbstractCompositeField) || ((AbstractCompositeField) p).isTemplateField()) { |
| enclosingFieldList.add(0, p); |
| } |
| p = p.getParentField(); |
| } |
| return enclosingFieldList; |
| } |
| |
| protected final void setXmlFormFieldId(Element x, IFormField f) { |
| x.setAttribute("fieldId", f.getFieldId()); |
| x.setAttribute("fieldQname", f.getClass().getName()); |
| } |
| |
| @Override |
| public void loadFromXml(Element x) { |
| } |
| |
| @Override |
| public final void loadFromXmlString(String xml) { |
| if (xml == null) { |
| return; |
| } |
| try { |
| Document doc = XmlUtility.getXmlDocument(xml); |
| Element root = doc.getDocumentElement(); |
| loadFromXml(root); |
| } |
| catch (Exception e) { |
| throw new ProcessingException("Error in AbstractFormField.setXML: ", e); |
| } |
| } |
| |
| @Override |
| public final String storeToXmlString() { |
| Document x = XmlUtility.createNewXmlDocument("field"); |
| storeToXml(x.getDocumentElement()); |
| return XmlUtility.wellformDocument(x); |
| } |
| |
| @Override |
| public String getInitialLabel() { |
| return m_initialLabel; |
| } |
| |
| @Override |
| public void setInitialLabel(String name) { |
| m_initialLabel = name; |
| } |
| |
| @Override |
| public String getLabel() { |
| return propertySupport.getPropertyString(PROP_LABEL); |
| } |
| |
| @Override |
| public void setLabel(String name) { |
| propertySupport.setPropertyString(PROP_LABEL, name); |
| } |
| |
| @Override |
| public byte getLabelPosition() { |
| return m_labelPosition; |
| } |
| |
| @Override |
| public void setLabelPosition(byte position) { |
| m_labelPosition = position; |
| } |
| |
| @Override |
| public int getLabelWidthInPixel() { |
| return m_labelWidthInPixel; |
| } |
| |
| @Override |
| public void setLabelWidthInPixel(int w) { |
| m_labelWidthInPixel = w; |
| } |
| |
| @Override |
| public byte getLabelHorizontalAlignment() { |
| return m_labelHorizontalAlignment; |
| } |
| |
| @Override |
| public void setLabelHorizontalAlignment(byte a) { |
| m_labelHorizontalAlignment = a; |
| } |
| |
| @Override |
| public String getFullyQualifiedLabel(String separator) { |
| StringBuilder b = new StringBuilder(); |
| IFormField p = getParentField(); |
| if (p != null) { |
| String s = p.getFullyQualifiedLabel(separator); |
| if (s != null) { |
| b.append(s); |
| } |
| } |
| String s = getLabel(); |
| if (s != null) { |
| if (b.length() > 0) { |
| b.append(separator); |
| } |
| b.append(s); |
| } |
| return b.toString(); |
| } |
| |
| @Override |
| public boolean isLabelVisible() { |
| return propertySupport.getPropertyBool(PROP_LABEL_VISIBLE); |
| } |
| |
| @Override |
| public void setLabelVisible(boolean labelVisible) { |
| setLabelVisible(labelVisible, LABEL_VISIBLE); |
| } |
| |
| @Override |
| public void setLabelVisible(boolean visible, String dimension) { |
| m_labelVisible = LABEL_VISIBLE_BIT_HELPER.changeBit(dimension, visible, m_labelVisible); |
| calculateLabelVisibleInternal(); |
| } |
| |
| @Override |
| public boolean isLabelVisible(String dimension) { |
| return LABEL_VISIBLE_BIT_HELPER.isBitSet(dimension, m_labelVisible); |
| } |
| |
| /** |
| * Do not use this internal method |
| */ |
| protected void calculateLabelVisibleInternal() { |
| propertySupport.setPropertyBool(PROP_LABEL_VISIBLE, NamedBitMaskHelper.allBitsSet(m_labelVisible)); |
| } |
| |
| @Override |
| public Object getProperty(String name) { |
| return propertySupport.getProperty(name); |
| } |
| |
| @Override |
| public void setProperty(String name, Object value) { |
| propertySupport.setProperty(name, value); |
| } |
| |
| @Override |
| public Permission getEnabledPermission() { |
| return m_enabledPermission; |
| } |
| |
| @Override |
| public void setEnabledPermission(Permission p) { |
| m_enabledPermission = p; |
| boolean b = true; |
| if (p != null) { |
| b = BEANS.get(IAccessControlService.class).checkPermission(p); |
| } |
| setEnabledGranted(b); |
| } |
| |
| @Override |
| public boolean isEnabledGranted() { |
| return isEnabled(IDimensions.ENABLED_GRANTED); |
| } |
| |
| @Override |
| public void setEnabledGranted(boolean enabled) { |
| setEnabledGranted(enabled, false); |
| } |
| |
| @Override |
| public void setEnabledGranted(boolean enabled, boolean updateParents) { |
| setEnabledGranted(enabled, updateParents, false); |
| } |
| |
| @Override |
| public void setEnabledGranted(boolean enabled, boolean updateParents, boolean updateChildren) { |
| setEnabled(enabled, updateParents, updateChildren, IDimensions.ENABLED_GRANTED); |
| } |
| |
| private void setEnabledSlave(boolean enabled) { |
| setEnabled(enabled, ENABLED_SLAVE); |
| } |
| |
| @Override |
| public boolean getEnabledProperty() { |
| return isEnabled(IDimensions.ENABLED); |
| } |
| |
| @Override |
| public void setEnabled(boolean enabled) { |
| setEnabled(enabled, false); |
| if (enabled) { |
| setEnabledSlave(true); |
| } |
| } |
| |
| @Override |
| public void setEnabled(final boolean enabled, final boolean updateParents) { |
| setEnabled(enabled, updateParents, false); |
| } |
| |
| @Override |
| public void setEnabled(final boolean enabled, final boolean updateParents, final boolean updateChildren) { |
| setEnabled(enabled, updateParents, updateChildren, IDimensions.ENABLED); |
| } |
| |
| @Override |
| public void setEnabled(boolean enabled, String dimension) { |
| setEnabled(enabled, false, dimension); |
| } |
| |
| @Override |
| public void setEnabled(final boolean enabled, final boolean updateParents, final String dimension) { |
| setEnabled(enabled, updateParents, false, dimension); |
| } |
| |
| @Override |
| public void setEnabled(final boolean enabled, final boolean updateParents, final boolean updateChildren, final String dimension) { |
| m_enabled = ENABLED_BIT_HELPER.changeBit(dimension, enabled, m_enabled); |
| calculateEnabledInternal(); |
| |
| if (enabled && updateParents) { |
| // also enable all parents |
| visitParents(new IFormFieldVisitor() { |
| @Override |
| public boolean visitField(IFormField field, int level, int fieldIndex) { |
| field.setEnabled(true, dimension); |
| return true; |
| } |
| }); |
| } |
| |
| if (updateChildren) { |
| // propagate change to children |
| acceptVisitor(new IFormFieldVisitor() { |
| @Override |
| public boolean visitField(IFormField field, int level, int fieldIndex) { |
| field.setEnabled(enabled, dimension); |
| return true; |
| } |
| }, 0, 0, false); |
| } |
| } |
| |
| @Override |
| public boolean isEnabled(String dimension) { |
| return ENABLED_BIT_HELPER.isBitSet(dimension, m_enabled); |
| } |
| |
| @Override |
| public boolean isEnabledIncludingParents() { |
| if (!isEnabled()) { |
| return false; |
| } |
| |
| return visitParents(new IFormFieldVisitor() { |
| @Override |
| public boolean visitField(IFormField field, int level, int fieldIndex) { |
| return field.isEnabled(); |
| } |
| }); |
| } |
| |
| @Override |
| public boolean visitParents(IFormFieldVisitor v) { |
| return visitParents(v, -1); |
| } |
| |
| protected boolean visitParents(IFormFieldVisitor v, int level) { |
| IFormField curField = this.getParentField(); |
| IFormField lastField = this; |
| if (curField == null) { |
| curField = getOuterFormField(lastField); |
| if (curField == null) { |
| return true; |
| } |
| } |
| |
| do { |
| lastField = curField; |
| boolean continueVisiting = v.visitField(curField, level, 0); |
| if (!continueVisiting) { |
| return false; |
| } |
| curField = curField.getParentField(); |
| if (curField == null) { |
| curField = getOuterFormField(lastField); |
| } |
| level--; |
| } |
| while (curField != null); |
| return true; |
| } |
| |
| protected IFormField getOuterFormField(IFormField owner) { |
| if (owner == null) { |
| return null; |
| } |
| IForm innerForm = owner.getForm(); |
| if (innerForm == null) { |
| return null; |
| } |
| return innerForm.getOuterFormField(); |
| } |
| |
| /** |
| * Do not use this internal method |
| */ |
| protected void calculateEnabledInternal() { |
| final boolean enabled = NamedBitMaskHelper.allBitsSet(m_enabled); |
| boolean changed = propertySupport.setPropertyBool(PROP_ENABLED, enabled); |
| if (!changed || !isInitialized()) { |
| return; |
| } |
| |
| // notify myself and children that their inherited value might have changed |
| acceptVisitor(new IFormFieldVisitor() { |
| @Override |
| public boolean visitField(IFormField field, int level, int fieldIndex) { |
| if (field instanceof AbstractFormField) { |
| boolean b = (enabled ? field.isEnabledIncludingParents() : false); |
| ((AbstractFormField) field).propertySupport.firePropertyChange(PROP_ENABLED_COMPUTED, !b, b); |
| } |
| return true; |
| } |
| }, 0, 0, true); |
| } |
| |
| @Override |
| public boolean isEnabled() { |
| return propertySupport.getPropertyBool(PROP_ENABLED); |
| } |
| |
| @Override |
| public void setDisabledStyle(int disabledStyle) { |
| propertySupport.setPropertyInt(PROP_DISABLED_STYLE, disabledStyle); |
| } |
| |
| @Override |
| public int getDisabledStyle() { |
| return propertySupport.getPropertyInt(PROP_DISABLED_STYLE); |
| } |
| |
| @Override |
| public Permission getVisiblePermission() { |
| return m_visiblePermission; |
| } |
| |
| @Override |
| public void setVisiblePermission(Permission p) { |
| m_visiblePermission = p; |
| boolean b = true; |
| if (p != null) { |
| b = BEANS.get(IAccessControlService.class).checkPermission(p); |
| } |
| setVisibleGranted(b); |
| } |
| |
| /** |
| * for thread-safety-reason this method is final |
| */ |
| @Override |
| public final boolean isSaveNeeded() { |
| return propertySupport.getPropertyBool(PROP_SAVE_NEEDED); |
| } |
| |
| /** |
| * Default implementation just calls {@link #interceptIsSaveNeeded()}<br> |
| * For thread-safety-reason this method is final |
| */ |
| @Override |
| public final void checkSaveNeeded() { |
| if (isInitialized()) { |
| try { |
| propertySupport.setPropertyBool(PROP_SAVE_NEEDED, isTouched() || interceptIsSaveNeeded()); |
| } |
| catch (RuntimeException | PlatformError e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| } |
| |
| private boolean isTouched() { |
| return FLAGS_BIT_HELPER.isBitSet(TOUCHED, m_flags); |
| } |
| |
| private void setTouched(boolean touched) { |
| m_flags = FLAGS_BIT_HELPER.changeBit(TOUCHED, touched, m_flags); |
| } |
| |
| @Override |
| public void touch() { |
| setTouched(true); |
| checkSaveNeeded(); |
| } |
| |
| /** |
| * Default implementation does nothing |
| */ |
| @Override |
| public final void markSaved() { |
| try { |
| setTouched(false); |
| interceptMarkSaved(); |
| checkSaveNeeded(); |
| } |
| catch (RuntimeException | PlatformError e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| |
| @Override |
| public final boolean isEmpty() { |
| return propertySupport.getPropertyBool(PROP_EMPTY); |
| } |
| |
| /** |
| * Default implementation just calls {@link #interceptIsEmpty()} |
| */ |
| protected final void checkEmpty() { |
| if (isInitialized()) { |
| try { |
| propertySupport.setPropertyBool(PROP_EMPTY, interceptIsEmpty()); |
| } |
| catch (RuntimeException | PlatformError e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isVisibleGranted() { |
| return isVisible(IDimensions.VISIBLE_GRANTED); |
| } |
| |
| @Override |
| public void setVisibleGranted(boolean visible) { |
| setVisibleGranted(visible, false); |
| } |
| |
| @Override |
| public void setVisibleGranted(boolean visible, boolean updateParents) { |
| setVisibleGranted(visible, updateParents, false); |
| } |
| |
| @Override |
| public void setVisibleGranted(boolean visible, boolean updateParents, boolean updateChildren) { |
| setVisible(visible, updateParents, updateChildren, IDimensions.VISIBLE_GRANTED); |
| } |
| |
| @Override |
| public boolean isVisible() { |
| return propertySupport.getPropertyBool(PROP_VISIBLE); |
| } |
| |
| @Override |
| public void setVisible(boolean visible) { |
| setVisible(visible, false); |
| } |
| |
| @Override |
| public void setVisible(boolean visible, boolean updateParents) { |
| setVisible(visible, updateParents, false); |
| } |
| |
| @Override |
| public void setVisible(boolean visible, boolean updateParents, boolean updateChildren) { |
| setVisible(visible, updateParents, updateChildren, IDimensions.VISIBLE); |
| } |
| |
| @Override |
| public void setVisible(boolean visible, boolean updateParents, final String dimension) { |
| setVisible(visible, updateParents, false, dimension); |
| } |
| |
| @Override |
| public void setVisible(final boolean visible, final boolean updateParents, final boolean updateChildren, final String dimension) { |
| m_visible = VISIBLE_BIT_HELPER.changeBit(dimension, visible, m_visible); |
| calculateVisibleInternal(); |
| |
| if (visible && updateParents) { |
| // also enable all parents |
| visitParents(new IFormFieldVisitor() { |
| @Override |
| public boolean visitField(IFormField field, int level, int fieldIndex) { |
| field.setVisible(true, dimension); |
| return true; |
| } |
| }); |
| } |
| |
| if (updateChildren) { |
| // propagate change to children |
| acceptVisitor(new IFormFieldVisitor() { |
| @Override |
| public boolean visitField(IFormField field, int level, int fieldIndex) { |
| field.setVisible(visible, dimension); |
| return true; |
| } |
| }, 0, 0, false); |
| } |
| } |
| |
| @Override |
| public void setVisible(boolean visible, String dimension) { |
| setVisible(visible, false, dimension); |
| } |
| |
| @ConfigOperation |
| protected boolean execCalculateVisible() { |
| return true; |
| } |
| |
| @Override |
| public boolean isVisible(String dimension) { |
| return VISIBLE_BIT_HELPER.isBitSet(dimension, m_visible); |
| } |
| |
| @Override |
| public boolean isVisibleIncludingParents() { |
| if (!isVisible()) { |
| return false; |
| } |
| |
| return visitParents(new IFormFieldVisitor() { |
| @Override |
| public boolean visitField(IFormField field, int level, int fieldIndex) { |
| return field.isVisible(); |
| } |
| }); |
| } |
| |
| /** |
| * Do not use this internal method |
| */ |
| protected void calculateVisibleInternal() { |
| boolean changed = propertySupport.setPropertyBool(PROP_VISIBLE, NamedBitMaskHelper.allBitsSet(m_visible) && interceptCalculateVisible()); |
| if (!changed) { |
| return; |
| } |
| |
| IForm form = getForm(); |
| if (form != null) { |
| form.structureChanged(this); |
| } |
| } |
| |
| @Override |
| public boolean isMandatory() { |
| return propertySupport.getPropertyBool(PROP_MANDATORY); |
| } |
| |
| @Override |
| public void setMandatory(boolean b) { |
| propertySupport.setPropertyBool(PROP_MANDATORY, b); |
| } |
| |
| @Override |
| public double getOrder() { |
| return propertySupport.getPropertyDouble(PROP_ORDER); |
| } |
| |
| @Override |
| public void setOrder(double order) { |
| propertySupport.setPropertyDouble(PROP_ORDER, order); |
| } |
| |
| @Override |
| public IMultiStatus getErrorStatus() { |
| final IMultiStatus ms = getErrorStatusInternal(); |
| return (ms == null) ? null : new MultiStatus(ms); |
| } |
| |
| /** |
| * @return the live error status |
| */ |
| protected MultiStatus getErrorStatusInternal() { |
| return (MultiStatus) propertySupport.getProperty(PROP_ERROR_STATUS); |
| } |
| |
| @Override |
| public void setErrorStatus(IMultiStatus status) { |
| setErrorStatusInternal(new MultiStatus(status)); |
| } |
| |
| protected void setErrorStatusInternal(MultiStatus status) { |
| propertySupport.setProperty(PROP_ERROR_STATUS, status); |
| } |
| |
| @Override |
| public void clearErrorStatus() { |
| propertySupport.setProperty(PROP_ERROR_STATUS, null); |
| } |
| |
| @Override |
| public void addErrorStatus(String message) { |
| addErrorStatus(new DefaultFieldStatus(message)); |
| } |
| |
| /** |
| * Adds an error status |
| */ |
| @Override |
| public void addErrorStatus(IStatus newStatus) { |
| final MultiStatus status = ensureMultiStatus(getErrorStatusInternal()); |
| // Create a copy, otherwise no PropertyChange event is fired |
| final MultiStatus copy = new MultiStatus(status); |
| copy.add(newStatus); |
| setErrorStatus(copy); |
| } |
| |
| /** |
| * Remove IStatus of a specific type |
| */ |
| @Override |
| public void removeErrorStatus(Class<? extends IStatus> statusClazz) { |
| final MultiStatus ms = getErrorStatusInternal(); |
| if (ms != null && ms.containsStatus(statusClazz)) { |
| // Create a copy, otherwise no PropertyChange event is fired |
| final MultiStatus copy = new MultiStatus(ms); |
| copy.removeAll(statusClazz); |
| if (copy.getChildren().isEmpty()) { |
| clearErrorStatus(); |
| } |
| else { |
| setErrorStatusInternal(copy); |
| } |
| } |
| } |
| |
| private MultiStatus ensureMultiStatus(IStatus s) { |
| if (s instanceof MultiStatus) { |
| return (MultiStatus) s; |
| } |
| final MultiStatus ms = new MultiStatus(); |
| if (s != null) { |
| ms.add(s); |
| } |
| return ms; |
| } |
| |
| @Override |
| public IValidateContentDescriptor validateContent() { |
| if (!isContentValid()) { |
| return new ValidateFormFieldDescriptor(this); |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean isContentValid() { |
| return !hasError() && isMandatoryFulfilled(); |
| } |
| |
| @Override |
| public boolean isMandatoryFulfilled() { |
| return !isMandatory() || !isEmpty(); |
| } |
| |
| /** |
| * @return true, if it contains an error status with severity >= IStatus.ERROR |
| */ |
| protected boolean hasError() { |
| IStatus errorStatus = getErrorStatus(); |
| return errorStatus != null && (errorStatus.getSeverity() >= IStatus.ERROR); |
| } |
| |
| @Override |
| public void setTooltipText(String text) { |
| propertySupport.setPropertyString(PROP_TOOLTIP_TEXT, text); |
| } |
| |
| @Override |
| public String getTooltipText() { |
| return propertySupport.getPropertyString(PROP_TOOLTIP_TEXT); |
| } |
| |
| @Override |
| public String getCssClass() { |
| return propertySupport.getPropertyString(PROP_CSS_CLASS); |
| } |
| |
| @Override |
| public void setCssClass(String cssClass) { |
| propertySupport.setPropertyString(PROP_CSS_CLASS, cssClass); |
| } |
| |
| @Override |
| public void setForegroundColor(String c) { |
| propertySupport.setProperty(PROP_FOREGROUND_COLOR, c); |
| } |
| |
| @Override |
| public String getForegroundColor() { |
| return (String) propertySupport.getProperty(PROP_FOREGROUND_COLOR); |
| } |
| |
| @Override |
| public void setBackgroundColor(String c) { |
| propertySupport.setProperty(PROP_BACKGROUND_COLOR, c); |
| } |
| |
| @Override |
| public String getBackgroundColor() { |
| return (String) propertySupport.getProperty(PROP_BACKGROUND_COLOR); |
| } |
| |
| @Override |
| public void setFont(FontSpec f) { |
| propertySupport.setProperty(PROP_FONT, f); |
| } |
| |
| @Override |
| public FontSpec getFont() { |
| return (FontSpec) propertySupport.getProperty(PROP_FONT); |
| } |
| |
| @Override |
| public void setLabelForegroundColor(String c) { |
| propertySupport.setProperty(PROP_LABEL_FOREGROUND_COLOR, c); |
| } |
| |
| @Override |
| public String getLabelForegroundColor() { |
| return (String) propertySupport.getProperty(PROP_LABEL_FOREGROUND_COLOR); |
| } |
| |
| @Override |
| public void setLabelBackgroundColor(String c) { |
| propertySupport.setProperty(PROP_LABEL_BACKGROUND_COLOR, c); |
| } |
| |
| @Override |
| public String getLabelBackgroundColor() { |
| return (String) propertySupport.getProperty(PROP_LABEL_BACKGROUND_COLOR); |
| } |
| |
| @Override |
| public void setLabelFont(FontSpec f) { |
| propertySupport.setProperty(PROP_LABEL_FONT, f); |
| } |
| |
| @Override |
| public FontSpec getLabelFont() { |
| return (FontSpec) propertySupport.getProperty(PROP_LABEL_FONT); |
| } |
| |
| @Override |
| public GridData getGridData() { |
| return (GridData) propertySupport.getProperty(PROP_GRID_DATA); |
| } |
| |
| @Override |
| public void setGridDataInternal(GridData data) { |
| propertySupport.setProperty(PROP_GRID_DATA, data); |
| } |
| |
| @Override |
| public GridData getGridDataHints() { |
| return new GridData((GridData) propertySupport.getProperty(PROP_GRID_DATA_HINTS)); |
| } |
| |
| @Override |
| public void setGridDataHints(GridData hints) { |
| propertySupport.setProperty(PROP_GRID_DATA_HINTS, new GridData((GridData) hints)); |
| } |
| |
| @Override |
| public void requestFocus() { |
| IForm form = getForm(); |
| if (form != null) { |
| form.requestFocus(this); |
| } |
| } |
| |
| @Override |
| @SuppressWarnings("deprecation") |
| public void setFocusable(boolean b) { |
| propertySupport.setPropertyBool(PROP_FOCUSABLE, b); |
| } |
| |
| @Override |
| @SuppressWarnings("deprecation") |
| public boolean isFocusable() { |
| return propertySupport.getPropertyBool(PROP_FOCUSABLE); |
| } |
| |
| @Override |
| public void requestInput() { |
| IForm form = getForm(); |
| if (form != null) { |
| form.requestInput(this); |
| } |
| } |
| |
| @Override |
| public void setPreventInitialFocus(boolean preventInitialFocus) { |
| propertySupport.setPropertyBool(PROP_PREVENT_INITIAL_FOCUS, preventInitialFocus); |
| } |
| |
| @Override |
| public boolean isPreventInitialFocus() { |
| return propertySupport.getPropertyBool(PROP_PREVENT_INITIAL_FOCUS); |
| } |
| |
| @Override |
| public void setMasterField(IValueField field) { |
| IValueField oldMasterField = getMasterField(); |
| // remove old listener |
| if (oldMasterField != null && m_currentMasterListener != null) { |
| oldMasterField.removeMasterListener(m_currentMasterListener); |
| m_currentMasterListener = null; |
| } |
| // add new listener and set enabling |
| if (field != null) { |
| m_currentMasterListener = new P_MasterListener(); |
| field.addMasterListener(m_currentMasterListener); |
| setEnabledSlave(field.getValue() != null || !isMasterRequired()); |
| } |
| m_masterField = field; |
| } |
| |
| @Override |
| public IValueField getMasterField() { |
| return m_masterField; |
| } |
| |
| // commodity helper |
| @Override |
| public Object getMasterValue() { |
| if (getMasterField() != null) { |
| return getMasterField().getValue(); |
| } |
| return null; |
| } |
| |
| @Override |
| public void setMasterRequired(boolean masterRequired) { |
| m_flags = FLAGS_BIT_HELPER.changeBit(MASTER_REQUIRED, masterRequired, m_flags); |
| } |
| |
| @Override |
| public boolean isMasterRequired() { |
| return FLAGS_BIT_HELPER.isBitSet(MASTER_REQUIRED, m_flags); |
| } |
| |
| @Override |
| public void updateKeyStrokes() { |
| m_objectExtensions.runInExtensionContext(new Runnable() { |
| @Override |
| public void run() { |
| m_contributionHolder.resetContributionsByClass(AbstractFormField.this, IKeyStroke.class); |
| List<IKeyStroke> keyStrokes = initLocalKeyStrokes(); |
| propertySupport.setPropertyList(PROP_KEY_STROKES, keyStrokes); |
| } |
| }); |
| } |
| |
| protected List<IKeyStroke> initLocalKeyStrokes() { |
| List<Class<? extends IKeyStroke>> configuredKeyStrokes = getConfiguredKeyStrokes(); |
| List<IKeyStroke> contributedKeyStrokes = m_contributionHolder.getContributionsByClass(IKeyStroke.class); |
| |
| Map<String, IKeyStroke> ksMap = new HashMap<String, IKeyStroke>(configuredKeyStrokes.size() + contributedKeyStrokes.size()); |
| for (Class<? extends IKeyStroke> keystrokeClazz : configuredKeyStrokes) { |
| IKeyStroke ks = ConfigurationUtility.newInnerInstance(this, keystrokeClazz); |
| ks.initAction(); |
| if (ks.getKeyStroke() != null) { |
| ksMap.put(ks.getKeyStroke().toUpperCase(), ks); |
| } |
| } |
| |
| for (IKeyStroke ks : contributedKeyStrokes) { |
| try { |
| ks.initAction(); |
| if (ks.getKeyStroke() != null) { |
| ksMap.put(ks.getKeyStroke().toUpperCase(), ks); |
| } |
| } |
| catch (Exception e) { |
| BEANS.get(ExceptionHandler.class).handle(new ProcessingException("error initializing key stroke '" + ks.getClass().getName() + "'.", e)); |
| } |
| } |
| return CollectionUtility.arrayListWithoutNullElements(ksMap.values()); |
| } |
| |
| @Override |
| public List<IKeyStroke> getKeyStrokes() { |
| return CollectionUtility.arrayList(propertySupport.<IKeyStroke> getPropertyList(PROP_KEY_STROKES)); |
| } |
| |
| @Override |
| public boolean isStatusVisible() { |
| return propertySupport.getPropertyBool(PROP_STATUS_VISIBLE); |
| } |
| |
| @Override |
| public void setStatusVisible(boolean statusVisible) { |
| propertySupport.setPropertyBool(PROP_STATUS_VISIBLE, statusVisible); |
| } |
| |
| @Override |
| public String getStatusPosition() { |
| return propertySupport.getPropertyString(PROP_STATUS_POSITION); |
| } |
| |
| @Override |
| public void setStatusPosition(String statusPosition) { |
| propertySupport.setPropertyString(PROP_STATUS_POSITION, statusPosition); |
| } |
| |
| @Override |
| public void setLoading(boolean loading) { |
| propertySupport.setPropertyBool(PROP_LOADING, loading); |
| } |
| |
| @Override |
| public boolean isLoading() { |
| return propertySupport.getPropertyBool(PROP_LOADING); |
| } |
| |
| private class P_MasterListener implements MasterListener { |
| @Override |
| public void masterChanged(Object newMasterValue) { |
| // only active if the unique listener itself |
| if (this == m_currentMasterListener) { |
| setEnabledSlave(newMasterValue != null || !isMasterRequired()); |
| try { |
| interceptChangedMasterValue(newMasterValue); |
| } |
| catch (RuntimeException | PlatformError e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| } |
| } |
| |
| /** |
| * The extension delegating to the local methods. This Extension is always at the end of the chain and will not call |
| * any further chain elements. |
| */ |
| protected static class LocalFormFieldExtension<OWNER extends AbstractFormField> extends AbstractExtension<OWNER> implements IFormFieldExtension<OWNER> { |
| |
| public LocalFormFieldExtension(OWNER owner) { |
| super(owner); |
| } |
| |
| @Override |
| public void execDataChanged(FormFieldDataChangedChain chain, Object... dataTypes) { |
| getOwner().execDataChanged(dataTypes); |
| } |
| |
| @Override |
| public void execAddSearchTerms(FormFieldAddSearchTermsChain chain, SearchFilter search) { |
| getOwner().execAddSearchTerms(search); |
| } |
| |
| @Override |
| public void execChangedMasterValue(FormFieldChangedMasterValueChain chain, Object newMasterValue) { |
| getOwner().execChangedMasterValue(newMasterValue); |
| } |
| |
| @Override |
| public void execDisposeField(FormFieldDisposeFieldChain chain) { |
| getOwner().execDisposeField(); |
| } |
| |
| @Override |
| public void execInitField(FormFieldInitFieldChain chain) { |
| getOwner().execInitField(); |
| } |
| |
| @Override |
| public boolean execCalculateVisible(FormFieldCalculateVisibleChain chain) { |
| return getOwner().execCalculateVisible(); |
| } |
| |
| @Override |
| public void execMarkSaved(FormFieldMarkSavedChain chain) { |
| getOwner().execMarkSaved(); |
| } |
| |
| @Override |
| public boolean execIsEmpty(FormFieldIsEmptyChain chain) { |
| return getOwner().execIsEmpty(); |
| } |
| |
| @Override |
| public boolean execIsSaveNeeded(FormFieldIsSaveNeededChain chain) { |
| return getOwner().execIsSaveNeeded(); |
| } |
| |
| } |
| |
| protected final void interceptDataChanged(Object... dataTypes) { |
| List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); |
| FormFieldDataChangedChain chain = new FormFieldDataChangedChain(extensions); |
| chain.execDataChanged(dataTypes); |
| } |
| |
| protected final void interceptAddSearchTerms(SearchFilter search) { |
| List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); |
| FormFieldAddSearchTermsChain chain = new FormFieldAddSearchTermsChain(extensions); |
| chain.execAddSearchTerms(search); |
| } |
| |
| protected final void interceptChangedMasterValue(Object newMasterValue) { |
| List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); |
| FormFieldChangedMasterValueChain chain = new FormFieldChangedMasterValueChain(extensions); |
| chain.execChangedMasterValue(newMasterValue); |
| } |
| |
| protected final void interceptDisposeField() { |
| List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); |
| FormFieldDisposeFieldChain chain = new FormFieldDisposeFieldChain(extensions); |
| chain.execDisposeField(); |
| } |
| |
| protected final void interceptInitField() { |
| List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); |
| FormFieldInitFieldChain chain = new FormFieldInitFieldChain(extensions); |
| chain.execInitField(); |
| } |
| |
| protected final boolean interceptCalculateVisible() { |
| List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); |
| FormFieldCalculateVisibleChain chain = new FormFieldCalculateVisibleChain(extensions); |
| return chain.execCalculateVisible(); |
| } |
| |
| protected final void interceptMarkSaved() { |
| List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); |
| FormFieldMarkSavedChain chain = new FormFieldMarkSavedChain(extensions); |
| chain.execMarkSaved(); |
| } |
| |
| protected final boolean interceptIsEmpty() { |
| List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); |
| FormFieldIsEmptyChain chain = new FormFieldIsEmptyChain(extensions); |
| return chain.execIsEmpty(); |
| } |
| |
| protected final boolean interceptIsSaveNeeded() { |
| List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); |
| FormFieldIsSaveNeededChain chain = new FormFieldIsSaveNeededChain(extensions); |
| return chain.execIsSaveNeeded(); |
| } |
| } |