blob: b6b1740aa348da564a092890470627a8c033235b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 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.listbox;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.scout.commons.CollectionUtility;
import org.eclipse.scout.commons.ConfigurationUtility;
import org.eclipse.scout.commons.TriState;
import org.eclipse.scout.commons.TypeCastUtility;
import org.eclipse.scout.commons.annotations.ClassId;
import org.eclipse.scout.commons.annotations.ConfigOperation;
import org.eclipse.scout.commons.annotations.ConfigProperty;
import org.eclipse.scout.commons.annotations.Order;
import org.eclipse.scout.commons.annotations.OrderedComparator;
import org.eclipse.scout.commons.exception.ProcessingException;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.rt.client.extension.ui.form.fields.IFormFieldExtension;
import org.eclipse.scout.rt.client.extension.ui.form.fields.listbox.IListBoxExtension;
import org.eclipse.scout.rt.client.extension.ui.form.fields.listbox.ListBoxChains.ListBoxFilterLookupResultChain;
import org.eclipse.scout.rt.client.extension.ui.form.fields.listbox.ListBoxChains.ListBoxLoadTableDataChain;
import org.eclipse.scout.rt.client.extension.ui.form.fields.listbox.ListBoxChains.ListBoxPopulateTableChain;
import org.eclipse.scout.rt.client.extension.ui.form.fields.listbox.ListBoxChains.ListBoxPrepareLookupChain;
import org.eclipse.scout.rt.client.services.lookup.FormFieldProvisioningContext;
import org.eclipse.scout.rt.client.services.lookup.ILookupCallProvisioningService;
import org.eclipse.scout.rt.client.ui.basic.cell.Cell;
import org.eclipse.scout.rt.client.ui.basic.cell.ICell;
import org.eclipse.scout.rt.client.ui.basic.table.AbstractTable;
import org.eclipse.scout.rt.client.ui.basic.table.AbstractTableRowBuilder;
import org.eclipse.scout.rt.client.ui.basic.table.ITable;
import org.eclipse.scout.rt.client.ui.basic.table.ITableRow;
import org.eclipse.scout.rt.client.ui.basic.table.ITableRowFilter;
import org.eclipse.scout.rt.client.ui.basic.table.TableAdapter;
import org.eclipse.scout.rt.client.ui.basic.table.TableEvent;
import org.eclipse.scout.rt.client.ui.basic.table.TableRow;
import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractBooleanColumn;
import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractColumn;
import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractStringColumn;
import org.eclipse.scout.rt.client.ui.basic.table.columns.IColumn;
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.AbstractFormField;
import org.eclipse.scout.rt.client.ui.form.fields.AbstractValueField;
import org.eclipse.scout.rt.client.ui.form.fields.CompositeFieldUtility;
import org.eclipse.scout.rt.client.ui.form.fields.GridData;
import org.eclipse.scout.rt.client.ui.form.fields.ICompositeField;
import org.eclipse.scout.rt.client.ui.form.fields.IFormField;
import org.eclipse.scout.rt.shared.ScoutTexts;
import org.eclipse.scout.rt.shared.data.basic.FontSpec;
import org.eclipse.scout.rt.shared.data.form.ValidationRule;
import org.eclipse.scout.rt.shared.data.form.fields.AbstractFormFieldData;
import org.eclipse.scout.rt.shared.data.form.fields.AbstractValueFieldData;
import org.eclipse.scout.rt.shared.services.common.code.ICodeType;
import org.eclipse.scout.rt.shared.services.common.exceptionhandler.IExceptionHandlerService;
import org.eclipse.scout.rt.shared.services.lookup.CodeLookupCall;
import org.eclipse.scout.rt.shared.services.lookup.ILookupCall;
import org.eclipse.scout.rt.shared.services.lookup.ILookupRow;
import org.eclipse.scout.rt.shared.services.lookup.LookupRow;
import org.eclipse.scout.service.SERVICES;
@ClassId("3dc8747d-19eb-4c0a-b5fc-c3dc2ad0783d")
public abstract class AbstractListBox<KEY> extends AbstractValueField<Set<KEY>> implements IListBox<KEY> {
private static final IScoutLogger LOG = ScoutLogManager.getLogger(AbstractListBox.class);
private ITable m_table;
private ILookupCall<KEY> m_lookupCall;
private Class<? extends ICodeType<?, KEY>> m_codeTypeClass;
private boolean m_valueTableSyncActive;
private ITableRowFilter m_checkedRowsFilter;
private ITableRowFilter m_activeRowsFilter;
// children
private List<IFormField> m_fields;
private Map<Class<? extends IFormField>, IFormField> m_movedFormFieldsByClass;
public AbstractListBox() {
this(true);
}
public AbstractListBox(boolean callInitializer) {
super(callInitializer);
}
/*
* Configuration
*/
/**
* Configure a lookup call to fill listbox with values.
*
* @return Lookup call of listbox
*/
@ConfigProperty(ConfigProperty.LOOKUP_CALL)
@Order(240)
@ValidationRule(ValidationRule.LOOKUP_CALL)
protected Class<? extends ILookupCall<KEY>> getConfiguredLookupCall() {
return null;
}
@ConfigProperty(ConfigProperty.CODE_TYPE)
@Order(250)
@ValidationRule(ValidationRule.CODE_TYPE)
protected Class<? extends ICodeType<?, KEY>> getConfiguredCodeType() {
return null;
}
@Override
@Order(210)
@ConfigProperty(ConfigProperty.BOOLEAN)
protected boolean getConfiguredAutoAddDefaultMenus() {
return false;
}
@ConfigProperty(ConfigProperty.ICON_ID)
@Order(230)
protected String getConfiguredIconId() {
return null;
}
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(260)
protected boolean getConfiguredAutoLoad() {
return true;
}
/**
* @return true: a filter is added to the listbox table that only accepts rows
* that are active or checked.<br>
* Default is true<br>
* Affects {@link ITable#getFilteredRows()}
*/
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(270)
protected boolean getConfiguredFilterActiveRows() {
return false;
}
/**
* @return true: a filter is added to the listbox table that only accepts
* checked rows<br>
* Default is false<br>
* Affects {@link ITable#getFilteredRows()}<br>
*/
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(280)
protected boolean getConfiguredFilterCheckedRows() {
return false;
}
@Override
protected double getConfiguredGridWeightY() {
return 1.0;
}
private List<Class<IFormField>> getConfiguredFields() {
Class[] dca = ConfigurationUtility.getDeclaredPublicClasses(getClass());
return ConfigurationUtility.filterClasses(dca, IFormField.class);
}
/**
* On any value change or call to {@link #checkEmpty()} this method is called
* to calculate if the field represents an empty state (semantics)
* <p>
*/
@Override
protected boolean execIsEmpty() throws ProcessingException {
return getValue().isEmpty();
}
/**
* called before any lookup is performed
*/
@ConfigOperation
@Order(250)
protected void execPrepareLookup(ILookupCall<KEY> call) throws ProcessingException {
}
/**
* @param call
* that produced this result
* @param result
* live list containing the result rows. Add, remove, set, replace
* and clear of entries in this list is supported
*/
@ConfigOperation
@Order(260)
protected void execFilterLookupResult(ILookupCall<KEY> call, List<ILookupRow<KEY>> result) throws ProcessingException {
}
@ConfigOperation
@Order(230)
protected List<? extends ILookupRow<KEY>> execLoadTableData() throws ProcessingException {
List<? extends ILookupRow<KEY>> data;
// (1) get data by service
if (getLookupCall() != null) {
ILookupCall<KEY> call = SERVICES.getService(ILookupCallProvisioningService.class).newClonedInstance(getLookupCall(), new FormFieldProvisioningContext(AbstractListBox.this));
prepareLookupCall(call);
data = call.getDataByAll();
data = filterLookupResult(call, data);
}
// (b) get data direct
else {
data = filterLookupResult(null, null);
}
return data;
}
/**
* Intercepter is called after data was fetched from LookupCall and is adding
* a table row for every LookupRow using IListBoxTable.createTableRow(row) and
* ITable.addRows()
* <p>
* For most cases the override of just {@link #interceptLoadTableData()} is sufficient
*
* <pre>
* List<ILookupRow<T>> data=execLoadTableData();
* List<ITableRow> rows=new ArrayList();
* if(data!=null){
* for(int i=0; i{@code<}data.length; i++){
* rows.add(createTableRow(data[i]));
* }
* }
* getTable().replaceRows(rows);
* </pre>
*/
@ConfigOperation
@Order(240)
protected void execPopulateTable() throws ProcessingException {
List<? extends ILookupRow<KEY>> data = null;
//sle Ticket 92'893: Listbox Master required. only run loadTable when master value is set
if (!isMasterRequired() || getMasterValue() != null) {
data = interceptLoadTableData();
}
List<ITableRow> rows = new ArrayList<ITableRow>();
if (data != null) {
for (ILookupRow<KEY> lr : data) {
rows.add(getTableRowBuilder().createTableRow(lr));
}
}
getTable().replaceRows(rows);
}
private Class<? extends ITable> getConfiguredTable() {
Class[] dca = ConfigurationUtility.getDeclaredPublicClasses(getClass());
List<Class<ITable>> tableClasses = ConfigurationUtility.filterClasses(dca, ITable.class);
if (tableClasses.size() == 1) {
return CollectionUtility.firstElement(tableClasses);
}
else {
for (Class<ITable> tableClazz : tableClasses) {
if (tableClazz.getDeclaringClass() != AbstractListBox.class) {
return tableClazz;
}
}
return null;
}
}
@Override
protected void execChangedMasterValue(Object newMasterValue) throws ProcessingException {
setValue(null);
loadListBoxData();
}
@Override
protected void initConfig() {
m_fields = CollectionUtility.emptyArrayList();
m_movedFormFieldsByClass = new HashMap<Class<? extends IFormField>, IFormField>();
super.initConfig();
setFilterActiveRows(getConfiguredFilterActiveRows());
setFilterActiveRowsValue(TriState.TRUE);
setFilterCheckedRows(getConfiguredFilterCheckedRows());
setFilterCheckedRowsValue(getConfiguredFilterCheckedRows());
try {
List<ITable> contributedTables = m_contributionHolder.getContributionsByClass(ITable.class);
m_table = CollectionUtility.firstElement(contributedTables);
if (m_table == null) {
Class<? extends ITable> configuredTable = getConfiguredTable();
if (configuredTable != null) {
m_table = ConfigurationUtility.newInnerInstance(this, configuredTable);
}
}
if (m_table != null) {
if (m_table instanceof AbstractTable) {
((AbstractTable) m_table).setContainerInternal(this);
}
updateActiveRowsFilter();
updateCheckedRowsFilter();
m_table.addTableListener(new TableAdapter() {
@Override
public void tableChanged(TableEvent e) {
switch (e.getType()) {
case TableEvent.TYPE_ROWS_SELECTED: {
if (!getTable().isCheckable()) {
syncTableToValue();
}
break;
}
case TableEvent.TYPE_ROWS_UPDATED: {
if (getTable().isCheckable()) {
syncTableToValue();
}
break;
}
}
}
});
// default icon
if (m_table.getDefaultIconId() == null && this.getConfiguredIconId() != null) {
m_table.setDefaultIconId(this.getConfiguredIconId());
}
m_table.setEnabled(isEnabled());
}
else {
LOG.warn("there is no inner class of type ITable in " + getClass().getName());
}
}
catch (Exception e) {
SERVICES.getService(IExceptionHandlerService.class).handleException(new ProcessingException("error creating instance of class '" + getConfiguredTable().getName() + "'.", e));
}
// lookup call
if (getConfiguredLookupCall() != null) {
try {
ILookupCall<KEY> call = getConfiguredLookupCall().newInstance();
setLookupCall(call);
}
catch (Exception e) {
SERVICES.getService(IExceptionHandlerService.class).handleException(new ProcessingException("error creating instance of class '" + getConfiguredLookupCall().getName() + "'.", e));
}
}
// code type
if (getConfiguredCodeType() != null) {
setCodeTypeClass(getConfiguredCodeType());
}
// local property listener
addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
if (m_table != null) {
String name = e.getPropertyName();
if (PROP_ENABLED.equals(name)) {
m_table.setEnabled(isEnabled());
}
else if (PROP_FILTER_CHECKED_ROWS_VALUE.equals(name)) {
updateCheckedRowsFilter();
}
else if (PROP_FILTER_ACTIVE_ROWS_VALUE.equals(name)) {
updateActiveRowsFilter();
}
}
}
});
// add fields
List<Class<IFormField>> fieldClasses = getConfiguredFields();
List<IFormField> contributedFields = m_contributionHolder.getContributionsByClass(IFormField.class);
List<IFormField> fieldList = new ArrayList<IFormField>(fieldClasses.size() + contributedFields.size());
for (Class<? extends IFormField> fieldClazz : fieldClasses) {
try {
IFormField f = ConfigurationUtility.newInnerInstance(this, fieldClazz);
fieldList.add(f);
}// end try
catch (Throwable t) {
SERVICES.getService(IExceptionHandlerService.class).handleException(new ProcessingException("error creating instance of class '" + fieldClazz.getName() + "'.", t));
}
}
fieldList.addAll(contributedFields);
Collections.sort(fieldList, new OrderedComparator());
for (IFormField f : fieldList) {
f.setParentFieldInternal(this);
}
m_fields = fieldList;
}
@Override
public void addField(IFormField f) {
CompositeFieldUtility.addField(f, this, m_fields);
}
@Override
public void removeField(IFormField f) {
CompositeFieldUtility.removeField(f, this, m_fields);
}
@Override
public void moveFieldTo(IFormField f, ICompositeField newContainer) {
CompositeFieldUtility.moveFieldTo(f, this, newContainer, m_movedFormFieldsByClass);
}
@Override
public Map<Class<? extends IFormField>, IFormField> getMovedFields() {
return Collections.unmodifiableMap(m_movedFormFieldsByClass);
}
@SuppressWarnings("unchecked")
public ListBoxFilterBox getListBoxFilterBox() {
return getFieldByClass(ListBoxFilterBox.class);
}
@Override
protected void initFieldInternal() throws ProcessingException {
getTable().initTable();
if (getConfiguredAutoLoad()) {
try {
setValueChangeTriggerEnabled(false);
//
loadListBoxData();
}
finally {
setValueChangeTriggerEnabled(true);
}
}
super.initFieldInternal();
}
@Override
protected void disposeFieldInternal() {
super.disposeFieldInternal();
getTable().disposeTable();
}
public AbstractTableRowBuilder<KEY> getTableRowBuilder() {
return new P_TableRowBuilder();
}
@Override
public final ITable getTable() {
return m_table;
}
@Override
public boolean isFilterCheckedRows() {
return propertySupport.getPropertyBool(PROP_FILTER_CHECKED_ROWS);
}
@Override
public void setFilterCheckedRows(boolean b) {
propertySupport.setPropertyBool(PROP_FILTER_CHECKED_ROWS, b);
}
@Override
public boolean getFilterCheckedRowsValue() {
return propertySupport.getPropertyBool(PROP_FILTER_CHECKED_ROWS_VALUE);
}
@Override
public void setFilterCheckedRowsValue(boolean b) {
propertySupport.setPropertyBool(PROP_FILTER_CHECKED_ROWS_VALUE, b);
}
@Override
public boolean isFilterActiveRows() {
return propertySupport.getPropertyBool(PROP_FILTER_ACTIVE_ROWS);
}
@Override
public void setFilterActiveRows(boolean b) {
propertySupport.setPropertyBool(PROP_FILTER_ACTIVE_ROWS, b);
}
@Override
public TriState getFilterActiveRowsValue() {
return (TriState) propertySupport.getProperty(PROP_FILTER_ACTIVE_ROWS_VALUE);
}
@Override
public void setFilterActiveRowsValue(TriState t) {
if (t == null) {
t = TriState.TRUE;
}
propertySupport.setProperty(PROP_FILTER_ACTIVE_ROWS_VALUE, t);
}
private void updateActiveRowsFilter() {
try {
getTable().setTableChanging(true);
//
if (m_activeRowsFilter != null) {
getTable().removeRowFilter(m_activeRowsFilter);
m_activeRowsFilter = null;
}
m_activeRowsFilter = new ActiveOrCheckedRowsFilter(getActiveColumnInternal(), getFilterActiveRowsValue());
getTable().addRowFilter(m_activeRowsFilter);
}
finally {
getTable().setTableChanging(false);
}
}
private void updateCheckedRowsFilter() {
try {
getTable().setTableChanging(true);
//
if (m_checkedRowsFilter != null) {
getTable().removeRowFilter(m_checkedRowsFilter);
m_checkedRowsFilter = null;
}
if (getFilterCheckedRowsValue()) {
m_checkedRowsFilter = new CheckedRowsFilter();
getTable().addRowFilter(m_checkedRowsFilter);
}
}
finally {
getTable().setTableChanging(false);
}
}
@Override
public void loadListBoxData() throws ProcessingException {
if (getTable() != null) {
try {
m_valueTableSyncActive = true;
getTable().setTableChanging(true);
//
interceptPopulateTable();
}
finally {
getTable().setTableChanging(false);
m_valueTableSyncActive = false;
}
syncValueToTable();
}
}
/**
* do not use this internal method directly
*/
@Override
public final void prepareLookupCall(ILookupCall<KEY> call) throws ProcessingException {
prepareLookupCallInternal(call);
interceptPrepareLookup(call);
}
private List<ILookupRow<KEY>> filterLookupResult(ILookupCall<KEY> call, List<? extends ILookupRow<KEY>> data) throws ProcessingException {
// create a copy for the custom filter method
List<ILookupRow<KEY>> result = CollectionUtility.arrayList(data);
interceptFilterLookupResult(call, result);
Iterator<ILookupRow<KEY>> resultIt = result.iterator();
while (resultIt.hasNext()) {
ILookupRow<KEY> row = resultIt.next();
if (row == null) {
resultIt.remove();
}
else if (row.getKey() == null) {
LOG.warn("The key of a lookup row may not be null. Row has been removed for list box '" + getClass().getName() + "'.");
resultIt.remove();
}
}
return result;
}
/**
* do not use this internal method directly
*/
private void prepareLookupCallInternal(ILookupCall<KEY> call) {
call.setActive(TriState.UNDEFINED);
//when there is a master value defined in the original call, don't set it to null when no master value is available
if (getMasterValue() != null || getLookupCall() == null || getLookupCall().getMaster() == null) {
call.setMaster(getMasterValue());
}
}
@Override
public final ILookupCall<KEY> getLookupCall() {
return m_lookupCall;
}
@Override
public void setLookupCall(ILookupCall<KEY> call) {
m_lookupCall = call;
}
@Override
public Class<? extends ICodeType<?, KEY>> getCodeTypeClass() {
return m_codeTypeClass;
}
@Override
public void setCodeTypeClass(Class<? extends ICodeType<?, KEY>> codeTypeClass) {
m_codeTypeClass = codeTypeClass;
// create lookup service call
m_lookupCall = null;
if (m_codeTypeClass != null) {
m_lookupCall = CodeLookupCall.newInstanceByService(m_codeTypeClass);
}
}
@Override
protected void valueChangedInternal() {
super.valueChangedInternal();
syncValueToTable();
}
@Override
protected String formatValueInternal(Set<KEY> validValue) {
if (!CollectionUtility.hasElements(validValue)) {
return "";
}
StringBuilder b = new StringBuilder();
List<ITableRow> rows = getKeyColumnInternal().findRows(validValue);
if (CollectionUtility.hasElements(rows)) {
Iterator<ITableRow> rowIt = rows.iterator();
b.append(getTextColumnInternal().getValue(rowIt.next()));
while (rowIt.hasNext()) {
b.append(", ");
b.append(getTextColumnInternal().getValue(rowIt.next()));
}
}
return b.toString();
}
@Override
protected final Set<KEY> validateValueInternal(Set<KEY> rawValue0) throws ProcessingException {
Set<KEY> rawValue = CollectionUtility.hashSet(rawValue0);
return doValidateValueInternal(rawValue);
}
/**
* override this method to perform detailed validation in subclasses
*/
protected Set<KEY> doValidateValueInternal(Set<KEY> rawValue) throws ProcessingException {
if (CollectionUtility.isEmpty(rawValue)) {
// fast return empty set
return rawValue;
}
ITable table = getTable();
if (table != null) {
if ((table.isCheckable() && !table.isMultiCheck()) || (!table.isCheckable() && !table.isMultiSelect())) {
//only single value
if (rawValue.size() > 1) {
LOG.warn(getClass().getName() + " only accepts a single value. Got " + CollectionUtility.format(rawValue) + ". Using only first value.");
return CollectionUtility.hashSet(CollectionUtility.firstElement(rawValue));
}
}
}
return rawValue;
}
@Override
public boolean isContentValid() {
boolean valid = super.isContentValid();
if (valid && isMandatory() && getValue().isEmpty()) {
return false;
}
return valid;
}
/**
* Value, empty {@link Set} in case of an empty value, never <code>null</code>.
*/
@Override
public Set<KEY> getValue() {
return CollectionUtility.hashSet(super.getValue());
}
/**
* Initial value, empty {@link Set} in case of an empty value, never <code>null</code>.
*/
@Override
public Set<KEY> getInitValue() {
return CollectionUtility.hashSet(super.getInitValue());
}
@Override
public KEY getSingleValue() {
return CollectionUtility.firstElement(super.getValue());
}
@Override
public void setSingleValue(KEY value) {
Set<KEY> valueSet = new HashSet<KEY>();
if (value != null) {
valueSet.add(value);
}
setValue(valueSet);
}
@Override
public int getCheckedKeyCount() {
return getValue().size();
}
@Override
public KEY getCheckedKey() {
return CollectionUtility.firstElement(getCheckedKeys());
}
@Override
public Set<KEY> getCheckedKeys() {
return getValue();
}
@Override
public ILookupRow<KEY> getCheckedLookupRow() {
return CollectionUtility.firstElement(getCheckedLookupRows());
}
@SuppressWarnings("unchecked")
@Override
public Set<ILookupRow<KEY>> getCheckedLookupRows() {
Collection<ITableRow> checkedRows = getTable().getCheckedRows();
Set<ILookupRow<KEY>> result = new HashSet<ILookupRow<KEY>>(checkedRows.size());
for (ITableRow row : checkedRows) {
ICell cell = row.getCell(1);
result.add(new LookupRow<KEY>((KEY) row.getCellValue(0), cell.getText(), cell.getIconId(), cell.getTooltipText(), cell.getBackgroundColor(), cell.getForegroundColor(), cell.getFont(), cell.isEnabled()));
}
return result;
}
@Override
public void checkKey(KEY key) {
if (key == null) {
checkKeys(null);
}
else {
checkKeys(CollectionUtility.hashSet(key));
}
}
@Override
public void checkKeys(Collection<? extends KEY> keys) {
setValue(CollectionUtility.<KEY> hashSetWithoutNullElements(keys));
}
@Override
public void setFormInternal(IForm form) {
super.setFormInternal(form);
getListBoxFilterBox().setFormInternal(form);
}
@Override
public void uncheckAllKeys() {
checkKeys(null);
}
@Override
public Set<KEY> getUncheckedKeys() {
Set<KEY> result = new HashSet<KEY>();
Set<KEY> initValue = getInitValue();
if (initValue != null) {
result.addAll(initValue);
}
Set<KEY> checkedKeys = getCheckedKeys();
if (checkedKeys != null) {
result.removeAll(checkedKeys);
}
return result;
}
@Override
public void checkAllKeys() {
checkKeys(getKeyColumnInternal().getValues());
}
@Override
public void checkAllActiveKeys() {
checkKeys(getKeyColumnInternal().getValues(getActiveColumnInternal().findRows(true)));
}
@Override
public void uncheckAllInactiveKeys() {
checkKeys(getKeyColumnInternal().getValues(getActiveColumnInternal().findRows(false)));
}
@Override
public void exportFormFieldData(AbstractFormFieldData target) throws ProcessingException {
@SuppressWarnings("unchecked")
AbstractValueFieldData<Set<KEY>> v = (AbstractValueFieldData<Set<KEY>>) target;
Set<KEY> value = getValue();
if (CollectionUtility.isEmpty(value)) {
v.setValue(null);
}
else {
v.setValue(CollectionUtility.hashSet(this.getValue()));
}
}
@SuppressWarnings("unchecked")
private IColumn<KEY> getKeyColumnInternal() {
return getTable().getColumnSet().getColumn(0);
}
@SuppressWarnings("unchecked")
private IColumn<String> getTextColumnInternal() {
return getTable().getColumnSet().getColumn(1);
}
@SuppressWarnings("unchecked")
private IColumn<Boolean> getActiveColumnInternal() {
return getTable().getColumnSet().getColumn(2);
}
private void syncValueToTable() {
if (m_valueTableSyncActive) {
return;
}
try {
m_valueTableSyncActive = true;
getTable().setTableChanging(true);
//
Set<KEY> checkedKeys = getCheckedKeys();
List<ITableRow> checkedRows = getKeyColumnInternal().findRows(checkedKeys);
for (ITableRow row : getTable().getRows()) {
row.setChecked(false);
}
for (ITableRow row : checkedRows) {
row.setChecked(true);
}
if (!getTable().isCheckable()) {
getTable().selectRows(checkedRows, false);
}
}
finally {
getTable().setTableChanging(false);
m_valueTableSyncActive = false;
}
}
private void syncTableToValue() {
if (m_valueTableSyncActive) {
return;
}
try {
m_valueTableSyncActive = true;
m_table.setTableChanging(true);
//
Collection<ITableRow> checkedRows;
if (getTable().isCheckable()) {
checkedRows = getTable().getCheckedRows();
}
else {
checkedRows = getTable().getSelectedRows();
}
checkKeys(getKeyColumnInternal().getValues(checkedRows));
if (!getTable().isCheckable()) {
//checks follow selection
for (ITableRow row : m_table.getRows()) {
row.setChecked(row.isSelected());
}
}
}
finally {
getTable().setTableChanging(false);
m_valueTableSyncActive = false;
}
// check if row filter needs to change
if (!m_table.getUIFacade().isUIProcessing()) {
updateActiveRowsFilter();
}
updateCheckedRowsFilter();
}
/*
* Implementation of ICompositeField
*/
@Override
public <F extends IFormField> F getFieldByClass(Class<F> c) {
return CompositeFieldUtility.getFieldByClass(this, c);
}
@Override
public IFormField getFieldById(String id) {
return CompositeFieldUtility.getFieldById(this, id);
}
@Override
public <X extends IFormField> X getFieldById(String id, Class<X> type) {
return CompositeFieldUtility.getFieldById(this, id, type);
}
@Override
public int getFieldCount() {
return m_fields.size();
}
@Override
public int getFieldIndex(IFormField f) {
return m_fields.indexOf(f);
}
@Override
public List<IFormField> getFields() {
return CollectionUtility.arrayList(m_fields);
}
@Override
public boolean visitFields(IFormFieldVisitor visitor, int startLevel) {
// myself
if (!visitor.visitField(this, startLevel, 0)) {
return false;
}
// children
int index = 0;
for (IFormField field : m_fields) {
if (field instanceof ICompositeField) {
if (!((ICompositeField) field).visitFields(visitor, startLevel + 1)) {
return false;
}
}
else {
if (!visitor.visitField(field, startLevel, index)) {
return false;
}
}
index++;
}
return true;
}
@Override
public final int getGridColumnCount() {
return 1;
}
@Override
public final int getGridRowCount() {
return 1;
}
@Override
public void rebuildFieldGrid() {
GridData gd = getListBoxFilterBox().getGridDataHints();
gd.x = 0;
gd.y = 0;
getListBoxFilterBox().setGridDataInternal(gd);
}
@Order(1)
@ClassId("a2e982d1-ea01-4d11-8655-d10c9935d8b9")
public class ListBoxFilterBox extends AbstractListBoxFilterBox {
@Override
protected IListBox getListBox() {
return AbstractListBox.this;
}
}
public class DefaultListBoxTable extends AbstractTable {
@Override
protected boolean getConfiguredAutoResizeColumns() {
return true;
}
@Override
protected boolean getConfiguredHeaderVisible() {
return false;
}
@Override
protected boolean getConfiguredMultiSelect() {
return false;
}
@Override
protected boolean getConfiguredCheckable() {
return true;
}
@SuppressWarnings("unchecked")
public KeyColumn getKeyColumn() {
return getColumnSet().getColumnByClass(KeyColumn.class);
}
@SuppressWarnings("unchecked")
public TextColumn getTextColumn() {
return getColumnSet().getColumnByClass(TextColumn.class);
}
@SuppressWarnings("unchecked")
public ActiveColumn getActiveColumn() {
return getColumnSet().getColumnByClass(ActiveColumn.class);
}
@Order(1)
public class KeyColumn extends AbstractColumn<KEY> {
@Override
protected boolean getConfiguredPrimaryKey() {
return true;
}
@Override
protected boolean getConfiguredDisplayable() {
return false;
}
@Override
@SuppressWarnings("unchecked")
public Class<KEY> getDataType() {
return TypeCastUtility.getGenericsParameterClass(AbstractListBox.this.getClass(), IListBox.class);
}
}
@Order(2)
public class TextColumn extends AbstractStringColumn {
}
@Order(3)
public class ActiveColumn extends AbstractBooleanColumn {
@Override
protected boolean getConfiguredDisplayable() {
return false;
}
}
}
private class P_TableRowBuilder extends AbstractTableRowBuilder<KEY> {
@Override
protected ITableRow createEmptyTableRow() {
return new TableRow(getTable().getColumnSet());
}
@Override
public ITableRow createTableRow(ILookupRow<KEY> dataRow) throws ProcessingException {
TableRow tableRow = (TableRow) super.createTableRow(dataRow);
// fill values to tableRow
getKeyColumnInternal().setValue(tableRow, dataRow.getKey());
getTextColumnInternal().setValue(tableRow, dataRow.getText());
getActiveColumnInternal().setValue(tableRow, dataRow.isActive());
//enable/disabled row
Cell cell = tableRow.getCellForUpdate(1);
cell.setEnabled(dataRow.isEnabled());
// hint for inactive codes
if (!dataRow.isActive()) {
if (cell.getFont() == null) {
cell.setFont(FontSpec.parse("italic"));
}
getTextColumnInternal().setValue(tableRow, dataRow.getText() + " (" + ScoutTexts.get("InactiveState") + ")");
}
return tableRow;
}
}
protected final void interceptPopulateTable() throws ProcessingException {
List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions();
ListBoxPopulateTableChain<KEY> chain = new ListBoxPopulateTableChain<KEY>(extensions);
chain.execPopulateTable();
}
protected final List<? extends ILookupRow<KEY>> interceptLoadTableData() throws ProcessingException {
List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions();
ListBoxLoadTableDataChain<KEY> chain = new ListBoxLoadTableDataChain<KEY>(extensions);
return chain.execLoadTableData();
}
protected final void interceptFilterLookupResult(ILookupCall<KEY> call, List<ILookupRow<KEY>> result) throws ProcessingException {
List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions();
ListBoxFilterLookupResultChain<KEY> chain = new ListBoxFilterLookupResultChain<KEY>(extensions);
chain.execFilterLookupResult(call, result);
}
protected final void interceptPrepareLookup(ILookupCall<KEY> call) throws ProcessingException {
List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions();
ListBoxPrepareLookupChain<KEY> chain = new ListBoxPrepareLookupChain<KEY>(extensions);
chain.execPrepareLookup(call);
}
protected static class LocalListBoxExtension<KEY, OWNER extends AbstractListBox<KEY>> extends LocalValueFieldExtension<Set<KEY>, OWNER> implements IListBoxExtension<KEY, OWNER> {
public LocalListBoxExtension(OWNER owner) {
super(owner);
}
@Override
public void execPopulateTable(ListBoxPopulateTableChain<KEY> chain) throws ProcessingException {
getOwner().execPopulateTable();
}
@Override
public List<? extends ILookupRow<KEY>> execLoadTableData(ListBoxLoadTableDataChain<KEY> chain) throws ProcessingException {
return getOwner().execLoadTableData();
}
@Override
public void execFilterLookupResult(ListBoxFilterLookupResultChain<KEY> chain, ILookupCall<KEY> call, List<ILookupRow<KEY>> result) throws ProcessingException {
getOwner().execFilterLookupResult(call, result);
}
@Override
public void execPrepareLookup(ListBoxPrepareLookupChain<KEY> chain, ILookupCall<KEY> call) throws ProcessingException {
getOwner().execPrepareLookup(call);
}
}
@Override
protected IListBoxExtension<KEY, ? extends AbstractListBox<KEY>> createLocalExtension() {
return new LocalListBoxExtension<KEY, AbstractListBox<KEY>>(this);
}
}