/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.activiti.designer.property.extension.field;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.activiti.designer.Activator;
import org.activiti.designer.PluginImage;
import org.activiti.designer.integration.servicetask.PropertyType;
import org.activiti.designer.integration.servicetask.annotation.DataGridProperty;
import org.activiti.designer.integration.servicetask.annotation.Help;
import org.activiti.designer.integration.servicetask.annotation.Property;
import org.activiti.designer.integration.servicetask.validator.RequiredFieldValidator;
import org.activiti.designer.property.PropertyCustomServiceTaskSection;
import org.activiti.designer.property.custom.MultilineTextDialog;
import org.activiti.designer.property.custom.PeriodDialog;
import org.activiti.designer.property.extension.FormToolTip;
import org.apache.commons.lang.StringUtils;
import org.eclipse.bpmn2.Bpmn2Factory;
import org.eclipse.bpmn2.ComplexDataType;
import org.eclipse.bpmn2.DataGrid;
import org.eclipse.bpmn2.DataGridField;
import org.eclipse.bpmn2.DataGridRow;
import org.eclipse.bpmn2.ServiceTask;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
/**
* @author Tiese Barrell
* @since 0.6.1
* @version 1
*/
public class CustomPropertyDataGridField extends AbstractCustomPropertyField {
private static final int DEFAULT_ADDITIONAL_COLUMNS = 1;
private static final int ORDERABLE_ADDITIONAL_COLUMNS = 3;
private Composite parent;
private Composite dataGridControl;
private FocusListener sharedFocusListener;
private DataGrid dataGrid;
private DataGridProperty dataGridAnnotation;
private List<FieldInfo> gridFields;
private TabbedPropertySheetWidgetFactory factory;
public CustomPropertyDataGridField(final PropertyCustomServiceTaskSection section, final ServiceTask serviceTask, final Field field) {
super(section, serviceTask, field);
}
@Override
public PropertyType getPrimaryPropertyType() {
return PropertyType.DATA_GRID;
}
@Override
public void refresh() {
if (dataGrid == null) {
final ComplexDataType value = getComplexValueFromModel();
if (value == null) {
dataGrid = Bpmn2Factory.eINSTANCE.createDataGrid();
} else {
dataGrid = (DataGrid) value;
}
}
refreshGrid();
}
@Override
public boolean isComplex() {
return true;
}
@Override
public ComplexDataType getComplexValue() {
return dataGrid;
}
@Override
public Composite render(Composite parent, TabbedPropertySheetWidgetFactory factory, final FocusListener listener) {
Composite result = factory.createFlatFormComposite(parent);
this.parent = result;
this.factory = factory;
this.sharedFocusListener = listener;
recreateDataGridControl();
return result;
}
private void recreateDataGridControl() {
if (dataGridControl != null) {
dataGridControl.dispose();
}
dataGridControl = factory.createFlatFormComposite(parent);
if (gridFields == null && List.class.isAssignableFrom(getField().getType())) {
gridFields = new ArrayList<FieldInfo>();
dataGridAnnotation = getField().getAnnotation(DataGridProperty.class);
if (dataGridAnnotation != null) {
final Class< ? extends Object> itemClass = dataGridAnnotation.itemClass();
final Field[] itemClassDeclaredFields = itemClass.getDeclaredFields();
for (final Field itemClassField : itemClassDeclaredFields) {
if (itemClassField.isAnnotationPresent(Property.class)) {
try {
final FieldInfo info = new FieldInfo(itemClassField);
gridFields.add(info);
} catch (IllegalArgumentException e) {
// fail silently, field doesn't contain required info.
}
}
}
// Sort the list
Collections.sort(gridFields);
}
}
// Set the layout for the contents of the listParent to a
// grid
int additionalColumns = DEFAULT_ADDITIONAL_COLUMNS;
if (dataGridAnnotation.orderable()) {
additionalColumns += ORDERABLE_ADDITIONAL_COLUMNS;
}
final GridLayout layout = new GridLayout(gridFields.size() + additionalColumns, false);
dataGridControl.setLayout(layout);
FormData dataGridData = new FormData();
dataGridData.left = new FormAttachment();
dataGridData.right = new FormAttachment(100);
dataGridControl.setLayoutData(dataGridData);
}
private void refreshGrid() {
recreateDataGridControl();
createHeader();
createBody();
createFooter();
parent.getParent().getParent().getParent().layout(true, true);
}
private void createHeader() {
if (dataGridAnnotation.orderable()) {
// Empty label for the header of order column
final CLabel orderColumnLabel = factory.createCLabel(dataGridControl, "", SWT.WRAP);
}
// Create a row with headers
for (final FieldInfo fieldInfo : gridFields) {
final Property propertyAnnotation = fieldInfo.getPropertyAnnotation();
Composite headerGroup = factory.createFlatFormComposite(dataGridControl);
final GridLayout headerLayout = new GridLayout(2, false);
headerGroup.setLayout(headerLayout);
final CLabel headerLabel = factory.createCLabel(headerGroup, propertyAnnotation.displayName());
GridData headerLabelData = new GridData();
headerLabelData.grabExcessHorizontalSpace = true;
headerLabelData.horizontalAlignment = GridData.FILL;
headerLabel.setLayoutData(headerLabelData);
headerLabel.setFont(PropertyCustomServiceTaskSection.boldFont);
final Help help = getHelpAnnotation();
if (help != null) {
final Button propertyHelp = factory.createButton(headerGroup, "", SWT.BUTTON1);
propertyHelp.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_LCL_LINKTO_HELP));
GridData headerButtonData = new GridData();
headerButtonData.horizontalAlignment = SWT.END;
headerLabel.setLayoutData(headerButtonData);
// create a tooltip
final ToolTip tooltip = new FormToolTip(propertyHelp, String.format("Help for field %s",
propertyAnnotation.displayName().equals("") ? fieldInfo.getFieldName() : propertyAnnotation.displayName()), help.displayHelpShort(),
help.displayHelpLong());
tooltip.setHideOnMouseDown(false);
propertyHelp.addMouseListener(new MouseListener() {
@Override
public void mouseUp(MouseEvent e) {
}
@Override
public void mouseDown(MouseEvent e) {
tooltip.show(new Point(0, 0));
}
@Override
public void mouseDoubleClick(MouseEvent e) {
}
});
}
final GridData gridData = new GridData();
gridData.grabExcessHorizontalSpace = true;
gridData.horizontalAlignment = GridData.FILL;
headerGroup.setLayoutData(gridData);
}
if (dataGridAnnotation.orderable()) {
// Empty label for the header of the two ordering columns
final CLabel upColumnLabel = factory.createCLabel(dataGridControl, "", SWT.WRAP);
final CLabel downColumnLabel = factory.createCLabel(dataGridControl, "", SWT.WRAP);
}
// Empty label for the header of the final column
final CLabel finalColumnLabel = factory.createCLabel(dataGridControl, "", SWT.WRAP);
}
private void createBody() {
if (dataGrid.getRow().size() == 0) {
final CLabel label = factory.createCLabel(dataGridControl, "There are no rows at the moment.", SWT.WRAP);
//
} else {
// Create rows with fields
for (final DataGridRow dataGridRow : dataGrid.getRow()) {
if (dataGridAnnotation.orderable()) {
final CLabel orderLabel = factory.createCLabel(dataGridControl, dataGridRow.getIndex() + ". ", SWT.WRAP);
}
for (final FieldInfo fieldInfo : gridFields) {
final DataGridField field = getFieldByName(dataGridRow, fieldInfo.getFieldName());
final Property propertyAnnotation = fieldInfo.getPropertyAnnotation();
switch (propertyAnnotation.type()) {
case TEXT:
final Text propertyText = factory.createText(dataGridControl, "", SWT.BORDER_SOLID);
propertyText.setEnabled(true);
propertyText.setText(getSimpleValueOrDefault(field, propertyAnnotation));
propertyText.addFocusListener(new GridFieldListener(propertyText, field));
propertyText.addFocusListener(sharedFocusListener);
if (propertyAnnotation.required()) {
addFieldValidator(propertyText, RequiredFieldValidator.class);
}
if (propertyAnnotation.fieldValidator() != null) {
addFieldValidator(propertyText, propertyAnnotation.fieldValidator());
}
final GridData textGridData = new GridData();
textGridData.grabExcessHorizontalSpace = true;
textGridData.horizontalAlignment = GridData.FILL;
propertyText.setLayoutData(textGridData);
break;
case MULTILINE_TEXT:
final Text multiControl = factory.createText(dataGridControl, "", SWT.BORDER_SOLID);
multiControl.setText(getSimpleValueOrDefault(field, propertyAnnotation));
multiControl.setToolTipText("Double-click this field to edit");
multiControl.setEnabled(true);
final GridData multiGridData = new GridData();
multiGridData.grabExcessHorizontalSpace = false;
multiGridData.horizontalAlignment = GridData.FILL;
multiControl.setLayoutData(multiGridData);
multiControl.addFocusListener(new GridFieldListener(multiControl, field));
multiControl.addFocusListener(sharedFocusListener);
if (propertyAnnotation.required()) {
addFieldValidator(multiControl, RequiredFieldValidator.class);
}
if (propertyAnnotation.fieldValidator() != null) {
addFieldValidator(multiControl, propertyAnnotation.fieldValidator());
}
multiControl.addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(MouseEvent e) {
MultilineTextDialog dialog = new MultilineTextDialog(dataGridControl.getDisplay().getActiveShell(), getHelpAnnotation(), multiControl.getText());
// open dialog and wait for return status
// code.
if (dialog.open() == IStatus.OK) {
// If user clicks ok display message box
String value = dialog.getValue();
multiControl.setText(value);
} else {
}
}
@Override
public void mouseDown(MouseEvent e) {
}
@Override
public void mouseUp(MouseEvent e) {
}
});
break;
case PERIOD:
final Text periodControl = factory.createText(dataGridControl, "", SWT.BORDER_SOLID);
periodControl.setText(getSimpleValueOrDefault(field, propertyAnnotation));
periodControl.setToolTipText("Double-click this field to edit");
final String fieldName = propertyAnnotation.displayName();
periodControl.setEnabled(true);
final GridData periodGridData = new GridData();
periodGridData.grabExcessHorizontalSpace = true;
periodGridData.horizontalAlignment = GridData.FILL;
periodControl.setLayoutData(periodGridData);
periodControl.addFocusListener(new GridFieldListener(periodControl, field));
periodControl.addFocusListener(sharedFocusListener);
if (propertyAnnotation.required()) {
addFieldValidator(periodControl, RequiredFieldValidator.class);
}
if (propertyAnnotation.fieldValidator() != null) {
addFieldValidator(periodControl, propertyAnnotation.fieldValidator());
}
periodControl.addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(MouseEvent e) {
PeriodDialog dialog = new PeriodDialog(dataGridControl.getDisplay().getActiveShell(), getHelpAnnotation(), periodControl.getText());
// open dialog and wait for return status code.
if (dialog.open() == IStatus.OK) {
// If user clicks ok display message box
String value = dialog.getValue();
periodControl.setText(value);
} else {
}
}
@Override
public void mouseDown(MouseEvent e) {
}
@Override
public void mouseUp(MouseEvent e) {
}
});
break;
}
}
if (dataGridAnnotation.orderable()) {
// if the row is not the first row
if (dataGridRow.getIndex() > 1) {
final Button orderUpButton = factory.createButton(dataGridControl, "", SWT.BUTTON1);
orderUpButton.setImage(Activator.getImage(PluginImage.ACTION_UP));
orderUpButton.addMouseListener(new MouseListener() {
@Override
public void mouseUp(MouseEvent e) {
final Runnable runnable = new Runnable() {
public void run() {
final int currentPosition = dataGridRow.getIndex();
if (currentPosition != 1) {
for (final DataGridRow currentDataGridRow : dataGrid.getRow()) {
if (currentDataGridRow.equals(dataGridRow)) {
currentDataGridRow.setIndex(currentDataGridRow.getIndex() - 1);
} else if (currentDataGridRow.getIndex() == currentPosition - 1) {
currentDataGridRow.setIndex(currentDataGridRow.getIndex() + 1);
}
}
ECollections.sort((EList<DataGridRow>) dataGrid.getRow(), new DataGridRowComparator());
}
}
};
runModelChange(runnable);
refreshGrid();
}
@Override
public void mouseDown(MouseEvent e) {
}
@Override
public void mouseDoubleClick(MouseEvent e) {
}
});
} else {
final CLabel placeHolderLabel = factory.createCLabel(dataGridControl, "", SWT.WRAP);
}
// if the row is not the last row
if (dataGridRow.getIndex() < dataGrid.getRow().size()) {
final Button orderDownButton = factory.createButton(dataGridControl, "", SWT.BUTTON1);
orderDownButton.setImage(Activator.getImage(PluginImage.ACTION_DOWN));
orderDownButton.addMouseListener(new MouseListener() {
@Override
public void mouseUp(MouseEvent e) {
final Runnable runnable = new Runnable() {
public void run() {
final int currentPosition = dataGridRow.getIndex();
if (currentPosition != dataGrid.getRow().size()) {
for (final DataGridRow currentDataGridRow : dataGrid.getRow()) {
if (currentDataGridRow.equals(dataGridRow)) {
currentDataGridRow.setIndex(currentDataGridRow.getIndex() + 1);
} else if (currentDataGridRow.getIndex() == currentPosition + 1) {
currentDataGridRow.setIndex(currentDataGridRow.getIndex() - 1);
}
}
ECollections.sort((EList<DataGridRow>) dataGrid.getRow(), new DataGridRowComparator());
}
}
};
runModelChange(runnable);
refreshGrid();
}
@Override
public void mouseDown(MouseEvent e) {
}
@Override
public void mouseDoubleClick(MouseEvent e) {
}
});
} else {
final CLabel placeHolderLabel = factory.createCLabel(dataGridControl, "", SWT.WRAP);
}
}
final Button deleteButton = factory.createButton(dataGridControl, "", SWT.BUTTON1);
deleteButton.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_ETOOL_DELETE));
deleteButton.addMouseListener(new MouseListener() {
@Override
public void mouseUp(MouseEvent e) {
final Runnable runnable = new Runnable() {
public void run() {
final int currentPosition = dataGridRow.getIndex();
boolean success = dataGrid.getRow().remove(dataGridRow);
if (success) {
for (final DataGridRow currentDataGridRow : dataGrid.getRow()) {
if (currentDataGridRow.getIndex() > currentPosition) {
currentDataGridRow.setIndex(currentDataGridRow.getIndex() - 1);
}
}
ECollections.sort((EList<DataGridRow>) dataGrid.getRow(), new DataGridRowComparator());
}
}
};
runModelChange(runnable);
refreshGrid();
}
@Override
public void mouseDown(MouseEvent e) {
}
@Override
public void mouseDoubleClick(MouseEvent e) {
}
});
}
}
}
private String getSimpleValueOrDefault(DataGridField field, Property propertyAnnotation) {
String value = field.getSimpleValue();
if (value == null) {
if (StringUtils.isNotBlank(propertyAnnotation.defaultValue())) {
value = propertyAnnotation.defaultValue();
} else {
value = "";
}
}
return value;
}
private void createFooter() {
final Button addButton = factory.createButton(dataGridControl, "Add item", SWT.BUTTON1);
addButton.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ADD));
final GridData gridData = new GridData();
gridData.horizontalSpan = gridFields.size() + 1;
addButton.setLayoutData(gridData);
addButton.addMouseListener(new MouseListener() {
@Override
public void mouseUp(MouseEvent e) {
final DataGridRow newRow = Bpmn2Factory.eINSTANCE.createDataGridRow();
newRow.setIndex(dataGrid.getRow().size() + 1);
for (final FieldInfo fieldInfo : gridFields) {
final DataGridField newField = Bpmn2Factory.eINSTANCE.createDataGridField();
newField.setName(fieldInfo.getFieldName());
newField.setSimpleValue(null);
newRow.getField().add(newField);
}
final Runnable runnable = new Runnable() {
public void run() {
dataGrid.getRow().add(newRow);
}
};
runModelChange(runnable);
refreshGrid();
}
@Override
public void mouseDown(MouseEvent e) {
}
@Override
public void mouseDoubleClick(MouseEvent e) {
}
});
}
private DataGridField getFieldByName(final DataGridRow dataGridRow, final String name) {
DataGridField result = null;
final List<DataGridField> fields = dataGridRow.getField();
for (final DataGridField field : fields) {
if (StringUtils.equalsIgnoreCase(name, field.getName())) {
result = field;
break;
}
}
return result;
}
private class GridFieldListener implements FocusListener {
private Text textControl;
private DataGridField dataGridField;
public GridFieldListener(final Text textControl, final DataGridField dataGridField) {
this.textControl = textControl;
this.dataGridField = dataGridField;
}
@Override
public void focusGained(FocusEvent e) {
}
@Override
public void focusLost(FocusEvent e) {
final Runnable runnable = new Runnable() {
@Override
public void run() {
dataGridField.setSimpleValue(textControl.getText());
}
};
runModelChange(runnable);
}
}
private class DataGridRowComparator implements Comparator<DataGridRow> {
@Override
public int compare(DataGridRow thisDataGridRow, DataGridRow otherDataGridRow) {
return new Integer(thisDataGridRow.getIndex()).compareTo(new Integer(otherDataGridRow.getIndex()));
}
}
}