package uihelpers; import helpers.GH; import helpers.PreferenceChangedAdapter; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.viewers.ITableColorProvider; import org.eclipse.jface.viewers.ITableFontProvider; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.swt.IFocusService; /** * helper class that will take most of the work of managing a TableViewer/TreeViewer * * it will handle : * - creating columns /make them movable * - persisting order and size of the columns * - handle sorting * * it is no: * - ContentProvider! must be set separately * * @author Quicksilver * */ public class TableViewerAdministrator<T> { public static final String ExtensionpointID = "eu.jucy.helpers.ui.tablevieweradministrator"; public static final int NoSorting = Integer.MIN_VALUE; private final StructuredViewer viewer; private Table table; private Tree tree; private final List<IColumnDescriptor<T>> originalDescriptors = new ArrayList<IColumnDescriptor<T>>(); private final List<IColumnDescriptor<T>> descriptors = new ArrayList<IColumnDescriptor<T>>(); private PreferenceChangedAdapter changedColsWatcher; private final String tableID ; private final int defaultSortCol; private TableTreePersister persister; private final boolean sortAllowed; private TableViewerAdministrator(StructuredViewer viewer, List<? extends IColumnDescriptor<T>> columns, String tableID,int defaultSortCol,boolean allowSorting) { this.viewer = viewer; this.originalDescriptors.addAll(columns); this.tableID = tableID; this.sortAllowed = allowSorting; this.defaultSortCol = defaultSortCol; loadDescriptors(); viewer.getControl().addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { descriptors.clear(); persister.dispose(); changedColsWatcher.dispose(); } }); } /** * * @param tableviewer - the Viewer on which this manager will work * @param columns - the Columns that should be added to the viewer * @param tableID - used for storing values and for contributing new columns to the table * @param allowSorting - allow to use the sorting function provided by the columns? * default is true * @param defaultSortCol for which column should be sorted by default.. Integer.MinValue for none * -columnNumber -1 for inverted.. */ public TableViewerAdministrator(TableViewer tableviewer, List<? extends IColumnDescriptor<T>> columns, String tableID,int defaultSortCol,boolean allowSorting) { this((StructuredViewer)tableviewer,columns,tableID,defaultSortCol,allowSorting); this.table = tableviewer.getTable(); } public TableViewerAdministrator(TableViewer tableviewer, List<? extends IColumnDescriptor<T>> columns, String tableID,int defaultSortCol) { this(tableviewer,columns,tableID,defaultSortCol,true ); } public TableViewerAdministrator(TreeViewer treeviewer, List<? extends IColumnDescriptor<T>> columns, String tableID,int defaultSortCol,boolean allowSorting) { this((StructuredViewer)treeviewer,columns,tableID,defaultSortCol,allowSorting); this.tree = treeviewer.getTree(); } public TableViewerAdministrator(TreeViewer treeviewer, List<? extends IColumnDescriptor<T>> columns, String tableID,int defaultSortCol) { this(treeviewer,columns,tableID,defaultSortCol,true ); } private void refresh() { //first delete everything for (Item tc: (table== null?tree.getColumns():table.getColumns())) { tc.dispose(); } descriptors.clear(); persister.dispose(); changedColsWatcher.dispose(); loadDescriptors(); apply(); } @SuppressWarnings("unchecked") private void loadDescriptors() { IExtensionRegistry reg = Platform.getExtensionRegistry(); IConfigurationElement[] configElements = reg .getConfigurationElementsFor(ExtensionpointID); List<String> allIDs = new ArrayList<String>(); descriptors.addAll(originalDescriptors); for (IConfigurationElement element : configElements) { try { if ("table".equals(element.getName()) && tableID.equals(element.getAttribute("id")) ) { for (IConfigurationElement newColumns : element.getChildren("table_column")) { String fullID = TVAPI.IDForTableColumn(tableID, newColumns.getAttribute("id"), false); allIDs.add(fullID); if (TVAPI.get(fullID)) { IColumnDescriptor<T> col = (IColumnDescriptor<T>)newColumns.createExecutableExtension("class"); descriptors.add(col); } } List<IColumnDescriptor<T>> descriptorsCopy = new ArrayList<IColumnDescriptor<T>>(descriptors); for (IConfigurationElement decorators : element.getChildren("table_column_decorator")) { String fullID = TVAPI.IDForTableColumn(tableID,decorators.getAttribute("id"), true); allIDs.add(fullID); if (TVAPI.get(fullID)) { TableColumnDecorator<T> tcd = (TableColumnDecorator<T>)decorators.createExecutableExtension("class"); String classname = decorators.getAttribute("columnToDecorate"); for (int i =0; i < descriptors.size(); i++) { String foundclassanme = descriptorsCopy.get(i).getClass().getName(); if (foundclassanme.equals(classname)) { tcd.init(descriptors.get(i)); descriptors.set(i, tcd); } } } } } } catch (CoreException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } changedColsWatcher = new PreferenceChangedAdapter(InstanceScope.INSTANCE .getNode(TVAPI.PLUGIN_ID),allIDs.toArray(new String[0])) { @Override public void preferenceChanged(String preference, String oldValue,String newValue) { new SUIJob() { @Override public void run() { refresh(); } }.scheduleIfNotRunning(1000,this); } }; } public void apply() { if (table == null) { for (IColumnDescriptor<T> col: descriptors ) { final TreeColumn treeColumn = new TreeColumn(tree, col.getStyle()); treeColumn.setText(col.getColumnName()); treeColumn.setWidth(col.getDefaultColumnSize()); if (sortAllowed) { new TableColumnSorter<T>((TreeViewer)viewer,treeColumn, col); } treeColumn.setMoveable(true); } persister = new TableTreePersister(tree, tableID, extractColWidths()); } else { for (IColumnDescriptor<T> col: descriptors ) { final TableColumn tableColumn = new TableColumn(table, col.getStyle()); tableColumn.setText(col.getColumnName()); tableColumn.setWidth(col.getDefaultColumnSize()); if (sortAllowed) { new TableColumnSorter<T>((TableViewer)viewer,tableColumn, col); } tableColumn.setMoveable(true); } persister = new TableTreePersister(table, tableID, extractColWidths()); } persister.load(); viewer.setLabelProvider(new AdminLabelProvider()); if (defaultSortCol != NoSorting) { boolean inverted = defaultSortCol < 0 ; int col = inverted? (-defaultSortCol)-1: defaultSortCol; viewer.setComparator(descriptors.get(col).getComparator(inverted)); } IFocusService ifc = (IFocusService)PlatformUI.getWorkbench().getService(IFocusService.class); if (ifc != null) { ifc.addFocusTracker(viewer.getControl(), tableID); } } private int[] extractColWidths() { int[] widths = new int[descriptors.size()]; for (int i=0; i < widths.length; i++) { widths[i] = descriptors.get(i).getDefaultColumnSize(); } return widths; } /** * content provider utilising the ColumnDescriptors to provide content * @author Quicksilver * */ @SuppressWarnings("unchecked") class AdminLabelProvider extends LabelProvider implements ITableLabelProvider,ITableColorProvider,ITableFontProvider { public Image getColumnImage(Object element, int columnIndex) { return descriptors.get(columnIndex).getImage((T)element); } public String getColumnText(Object element, int columnIndex) { return descriptors.get(columnIndex).getText((T)element); } public Color getBackground(Object element, int columnIndex) { return descriptors.get(columnIndex).getBackground((T)element); } public Color getForeground(Object element, int columnIndex) { return descriptors.get(columnIndex).getForeground((T)element); } public Font getFont(Object element, int columnIndex) { return descriptors.get(columnIndex).getFont((T)element); } } public abstract static class ColumnDescriptor<X> implements IColumnDescriptor<X> { private final int defaultColumnSize; private final String columnName; private final int style; public ColumnDescriptor(int defaultColumnSize, String columnName, int style) { this.defaultColumnSize = defaultColumnSize; this.columnName = columnName; this.style = style; } public ColumnDescriptor(int defaultColumnSize, String columnName) { this(defaultColumnSize, columnName , SWT.LEAD); } /** * A text from the object * @param x - the object that is presented * @return a String to be shown in the Column */ public abstract String getText(X x); /** * an image for the provided object * @param x * @return null if no image should be shown.. */ public Image getImage(X x) { return null; } public Color getForeground(X x) { return null; } public Color getBackground(X x) { return null; } public Font getFont(X x) { return null; } /** * @return a simple comparator that compares * lexicographically by using getText() * subclasses should override if this is not enough */ public Comparator<X> getComparator() { return new Comparator<X>() { public int compare(X o1, X o2) { String t1 = null,t2 = null; if (o1 == null || o2 == null || (t1=getText(o1))==null || (t2=getText(o2))==null ) { // if (o1 != null && t1 == null && Platform.inDevelopmentMode()) { // System.err.println("TableViewerAdministrator getComparator() failed: "+o1); // } return 0; } return t1.compareTo(t2); } }; } /** * returns the normal reverse comparator.. */ public Comparator<X> getReverseComparator() { if (getComparator() == null) { return null; } return Collections.reverseOrder(getComparator()); } /** * * @return an instance of viewer Comparator for this column * based on getComparator() */ @SuppressWarnings("unchecked") public final ViewerComparator getComparator(boolean inverted) { final Comparator<X> comparator = inverted ? getReverseComparator():getComparator(); if (comparator == null) { return null; } return new ViewerComparator() { public int compare(Viewer viewer, Object e1, Object e2) { return comparator.compare((X) e1, (X)e2); } }; } public final int getDefaultColumnSize() { return defaultColumnSize; } public final String getColumnName() { return columnName; } public final int getStyle() { return style; } } public abstract static class NumberColumnDescriptor<X> extends ColumnDescriptor<X> { public NumberColumnDescriptor(int defaultColumnSize, String columnName, int style) { super(defaultColumnSize, columnName, style); } public NumberColumnDescriptor(int defaultColumnSize, String columnName) { super(defaultColumnSize, columnName); } public abstract long getNumber(X x); public abstract String getTextFromNumber(long num); @Override public String getText(X x) { return getTextFromNumber(getNumber(x)); } @Override public Comparator<X> getComparator() { return new Comparator<X>() { public int compare(X o1, X o2) { return GH.compareTo(getNumber(o1), getNumber(o2)); } }; } } public static interface IColumnDescriptor<X> { /** * * @return a text to be presented in the line */ String getText(X x); /** * * @return an icon to be presented in the line */ Image getImage(X x); /** * Colour for Foreground */ Color getForeground(X x); /** * Colour for the Background */ Color getBackground(X x); /** * Font for the table Column * * @param x - the item * @return which font */ Font getFont(X x); /** * @return comparator used to sort the line */ Comparator<X> getComparator(); /** * * @return the reverse comparator.. that might defer from * just the normal comparators inversion */ Comparator<X> getReverseComparator(); /** * * @return the default width for columns * */ int getDefaultColumnSize(); /** * @return how the column is called * */ String getColumnName(); /** * * @return what SWT style the column should use */ int getStyle(); /** * * @param inverted - if the comparator should be inverted.. * @return a Comparator for this column.. * (this is automatically implemented by ColumnDscriptor based on getComparator()) */ ViewerComparator getComparator(boolean inverted); } public static abstract class TableColumnDecorator<T> implements IColumnDescriptor<T> { public TableColumnDecorator() {} private IColumnDescriptor<T> parent; protected void init(IColumnDescriptor<T> parent) { this.parent = parent; } public final Color getBackground(T x) { return getBackground(x,parent.getBackground(x)); } public Color getBackground(T t,Color parentcolor) { return parentcolor; } public final Color getForeground(T x) { return getForeground(x,parent.getForeground(x)); } public Color getForeground(T t,Color parentcolor) { return parentcolor; } public final Font getFont(T x) { return getFont(x,parent.getFont(x)); } public Font getFont(T t,Font parentfont) { return parentfont; } public final Image getImage(T x) { return getImage(x,parent.getImage(x)); } public Image getImage(T t,Image parent) { return parent; } public final String getText(T x) { return getText(x,parent.getText(x)); } public String getText(T t, String parent) { return parent; } public String getColumnName() { return parent.getColumnName(); } public Comparator<T> getComparator() { return parent.getComparator(); } public ViewerComparator getComparator(boolean inverted) { return parent.getComparator(inverted); } public int getDefaultColumnSize() { return parent.getDefaultColumnSize(); } public Comparator<T> getReverseComparator() { return parent.getReverseComparator(); } public int getStyle() { return parent.getStyle(); } } }