/******************************************************************************* * Copyright (c) 2011-2016 Medevit OG, Medelexis 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: * Marco Descher, initial API and implementaion * Lucia Amman, bug fixes and improvements * Sponsors: M. + P. Richter *******************************************************************************/ package at.medevit.elexis.gdt.ui.table.util; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnPixelData; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; /** * ColumnBuilder is responsible to build a column for {@link TableViewerBuilder} Methods are * chainable so you can construct table columns in a single line. After customizing the column by * calling methods, call build() once to create the actual column. * * @author Ralf Ebert <info@ralfebert.de> */ @SuppressWarnings("unchecked") public class ColumnBuilder { private final TableViewer viewer; private final String columnHeaderText; private IValue valueHandler; private IValueFormatter valueFormatter; private ICellFormatter cellFormatter; private CellLabelProvider customLabelProvider; private Integer widthPixel; private Integer widthPercent; private int align = SWT.LEFT; private CellEditor editor; private IValue sortBy; private boolean defaultSort; private IValueFormatter editorFormat; public ColumnBuilder(TableViewer viewer, String columnHeaderText){ this.viewer = viewer; this.columnHeaderText = columnHeaderText; } /** * Binds this column to the given property. */ public ColumnBuilder bindToProperty(String propertyName){ return bindToValue(new PropertyValue(propertyName)); } /** * Binds the column to an arbitrary value. */ public ColumnBuilder bindToValue(IValue valueHandler){ this.valueHandler = valueHandler; return this; } /** * Sets a formatter for this column that is responsible to convert the value into a String. The * 'parse' method of the CellFormatter is not required for this. See {@link Formatter} for * commonly-used formatters. */ public ColumnBuilder format(IValueFormatter valueFormatter){ this.valueFormatter = valueFormatter; return this; } /** * A cell formatter allows to format the cell besides the textual value, for example to * customize colors or set images. */ public ColumnBuilder format(ICellFormatter cellFormatter){ this.cellFormatter = cellFormatter; return this; } /** * If your column is not text based (for example a column with images that are owner-drawn), you * can use a custom CellLabelProvider instead of a value and a value formatter. */ public ColumnBuilder setCustomLabelProvider(CellLabelProvider customLabelProvider){ this.customLabelProvider = customLabelProvider; return this; } /** * Sets column width in percent */ public ColumnBuilder setPercentWidth(int width){ this.widthPercent = width; return this; } /** * Sets column width in pixel */ public ColumnBuilder setPixelWidth(int width){ this.widthPixel = width; return this; } /** * Sets alignment of column cell texts to be centered. */ public ColumnBuilder alignCenter(){ this.align = SWT.CENTER; return this; } /** * Sets alignment of column cell texts to be right-aligned. */ public ColumnBuilder alignRight(){ this.align = SWT.RIGHT; return this; } /** * Makes this column editable. Using this method you get a text editor without any formatting * applied, to the value type needs to be String. */ public ColumnBuilder makeEditable(){ return makeEditable(new TextCellEditor(viewer.getTable()), StringValueFormatter.INSTANCE); } /** * Makes this column editable. Using this method you get a text editor. The given valueFormatter * will be responsible for formatting the value to a String and parsing it back to a new value. */ public ColumnBuilder makeEditable(IValueFormatter valueFormatter){ return makeEditable(new TextCellEditor(viewer.getTable()), valueFormatter); } /** * Makes the column cells editable using a custom cell editor. No formatting is applied, the * editor will see the value as it is. */ public ColumnBuilder makeEditable(CellEditor cellEditor){ return makeEditable(cellEditor, null); } /** * Makes the column cells editable using a custom cell editor. The given valueFormatter will be * responsible for formatting the value for the editor and converting it back to a new value. */ public ColumnBuilder makeEditable(CellEditor cellEditor, IValueFormatter valueFormatter){ if (cellEditor.getControl().getParent() != viewer.getTable()) throw new RuntimeException("Parent of cell editor needs to be the table!"); this.editor = cellEditor; this.editorFormat = valueFormatter; return this; } /** * Sets a custom value to sort by. Implement yourself our use PropertyValue to sort by a custom * property value. */ public ColumnBuilder sortBy(IValue sortBy){ this.sortBy = sortBy; return this; } /** * Sets this column as default sort column */ public ColumnBuilder useAsDefaultSortColumn(){ this.defaultSort = true; return this; } /** * Builds the column and returns the TableViewerColumn */ public TableViewerColumn build(){ // create column TableViewerColumn viewerColumn = new TableViewerColumn(viewer, align); TableColumn column = viewerColumn.getColumn(); column.setText(columnHeaderText); // set label provider if (customLabelProvider != null) { if (cellFormatter != null) { throw new RuntimeException( "If you specify a custom label provider, it is not allowed " + "to specify a cell formatter. You need to do the formatting in your labelprovider!"); } viewerColumn.setLabelProvider(customLabelProvider); } else { viewerColumn.setLabelProvider(new PropertyCellLabelProvider(valueHandler, valueFormatter, cellFormatter)); } // activate column sorting if (sortBy == null) { sortBy = valueHandler; } if (sortBy != null) { column.setData(SortColumnComparator.SORT_BY, sortBy); column.addSelectionListener(new ColumnSortSelectionListener(viewer)); if (defaultSort) { viewer.getTable().setSortColumn(column); viewer.getTable().setSortDirection(SWT.UP); } } // set column layout data if (widthPixel != null && widthPercent != null) { throw new RuntimeException( "You can specify a width in pixel OR in percent, but not both!"); } if (widthPercent == null) { // default width of 100px if nothing specified ((TableColumnLayout) viewer.getTable().getParent().getLayout()).setColumnData(column, new ColumnPixelData(widthPixel == null ? 100 : widthPixel)); } else { ((TableColumnLayout) viewer.getTable().getParent().getLayout()).setColumnData(column, new ColumnWeightData(widthPercent)); } // set editing support if (editor != null) { if (valueHandler == null) { throw new RuntimeException( "makeEditable() requires that the column is bound to some value using bindTo...()"); } viewerColumn.setEditingSupport(new PropertyEditingSupport(viewer, valueHandler, editorFormat, editor)); } return viewerColumn; } /** * ColumnSortSelectionListener is a selection listener for {@link TableColumn} objects. When a * column is selected (= header is clicked), it switches the sort direction if the column is * already active sort column, otherwise it sets the active sort column. * * @author Ralf Ebert <info@ralfebert.de> */ public class ColumnSortSelectionListener extends SelectionAdapter { private final TableViewer viewer; public ColumnSortSelectionListener(TableViewer viewer){ this.viewer = viewer; } @Override public void widgetSelected(SelectionEvent e){ TableColumn column = (TableColumn) e.getSource(); Table table = column.getParent(); boolean alreadyActiveSortColumn = (column == table.getSortColumn()); if (alreadyActiveSortColumn) { table.setSortDirection(table.getSortDirection() == SWT.DOWN ? SWT.UP : SWT.DOWN); } else { table.setSortColumn(column); table.setSortDirection(SWT.UP); } viewer.refresh(); } } /** * An ICellFormatter is responsible for formatting a cell. Should be used to apply additional * formatting to the cell, like setting colors / images. * * @author Ralf Ebert <info@ralfebert.de> */ public interface ICellFormatter { public void formatCell(ViewerCell cell, Object value); } /** * PropertyCellLabelProvider is a CellLabelProvider that gets cell labels using a nested bean * property string like "company.country.name". * * @author Ralf Ebert <info@ralfebert.de> */ @SuppressWarnings("unchecked") public class PropertyCellLabelProvider extends CellLabelProvider { private final IValue valueHandler; private IValueFormatter valueFormatter; private final ICellFormatter cellFormatter; public PropertyCellLabelProvider(String propertyName){ this.valueHandler = new PropertyValue(propertyName); this.cellFormatter = null; } public PropertyCellLabelProvider(IValue valueHandler, IValueFormatter valueFormatter, ICellFormatter cellFormatter){ this.valueHandler = valueHandler; this.valueFormatter = valueFormatter; this.cellFormatter = cellFormatter; } @Override public void update(ViewerCell cell){ try { Object rawValue = null; if (valueHandler != null) { rawValue = valueHandler.getValue(cell.getElement()); Object formattedValue = rawValue; if (valueFormatter != null) { formattedValue = valueFormatter.format(rawValue); } cell.setText(String.valueOf(formattedValue)); } if (cellFormatter != null) { cellFormatter.formatCell(cell, rawValue); } } catch (Exception e) { System.out.println(e.getMessage()); } } } /** * EditingSupport for JFace viewers that gets and sets the value using a nested bean property * string like "company.country.name". * * @author Ralf Ebert <info@ralfebert.de> */ @SuppressWarnings("unchecked") public class PropertyEditingSupport extends EditingSupport { private final CellEditor cellEditor; private final IValue valueHandler; private final IValueFormatter valueFormatter; public PropertyEditingSupport(ColumnViewer viewer, String propertyName, CellEditor cellEditor){ this(viewer, new PropertyValue(propertyName), null, cellEditor); } public PropertyEditingSupport(ColumnViewer viewer, IValue valueHandler, IValueFormatter valueFormatter, CellEditor cellEditor){ super(viewer); this.valueHandler = valueHandler; this.valueFormatter = valueFormatter; this.cellEditor = cellEditor; } @Override protected CellEditor getCellEditor(Object element){ return cellEditor; } @Override protected boolean canEdit(Object element){ return true; } @Override protected Object getValue(Object element){ try { Object value = valueHandler.getValue(element); if (valueFormatter != null) { value = valueFormatter.format(value); } return value; } catch (Exception e) { return null; } } @Override protected void setValue(Object element, Object value){ try { Object parsedValue = value; if (valueFormatter != null) { parsedValue = valueFormatter.parse(value); } valueHandler.setValue(element, parsedValue); getViewer().refresh(); } catch (Exception e) {} } } }