Java tutorial
/* * Copyright 2014 The Depan Project Authors * * 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 com.google.devtools.depan.view_doc.eclipse.ui.widgets; import com.google.devtools.depan.graph.api.Relation; import com.google.devtools.depan.graph.registry.RelationRegistry; import com.google.devtools.depan.platform.AlphabeticSorter; import com.google.devtools.depan.platform.Colors; import com.google.devtools.depan.platform.InverseSorter; import com.google.devtools.depan.platform.LabelProviderToString; import com.google.devtools.depan.platform.eclipse.ui.tables.EditColTableDef; import com.google.devtools.depan.platform.eclipse.ui.widgets.Selections; import com.google.devtools.depan.platform.eclipse.ui.widgets.Widgets; import com.google.devtools.depan.view_doc.model.EdgeDisplayProperty; import com.google.devtools.depan.view_doc.model.RelationDisplayRepository; import org.eclipse.jface.resource.StringConverter; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.ColorCellEditor; import org.eclipse.jface.viewers.ComboBoxCellEditor; import org.eclipse.jface.viewers.ICellModifier; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import java.awt.Color; import java.util.Collection; /** * Show a table of the relations with their {@link EdgeDisplayProperty} * rendering properties. * * [Jun 2016] Given the number of columns shared with * {@link EdgeDisplayTableControl}, these classes should be sharing more * of their implementation. See also {@link NodeDisplayTableControl} for * additional columns to consider. */ public class RelationDisplayTableControl extends Composite { public static final String COL_NAME = "Name"; public static final String COL_SOURCE = "Source"; public static final String COL_COLOR = "Color"; public static final String COL_WIDTH = "Width"; public static final String COL_STYLE = "Style"; public static final String COL_SHAPE = "Shape"; public static final String COL_ARROWHEAD = "Arrowhead"; public static final int INDEX_NAME = 0; public static final int INDEX_SOURCE = 1; public static final int INDEX_COLOR = 2; public static final int INDEX_STYLE = 3; public static final int INDEX_ARROWHEAD = 4; private static final EditColTableDef[] TABLE_DEF = new EditColTableDef[] { new EditColTableDef(COL_NAME, false, COL_NAME, 320), new EditColTableDef(COL_SOURCE, false, COL_SOURCE, 160), new EditColTableDef(COL_COLOR, true, COL_COLOR, 80), // new EditColTableDef(COL_WIDTH, false, COL_WIDTH, 180), new EditColTableDef(COL_STYLE, true, COL_STYLE, 120), new EditColTableDef(COL_ARROWHEAD, true, COL_ARROWHEAD, 160), // new EditColTableDef(COL_SHAPE, false, COL_SHAPE, 180), }; private static final String[] LINE_WIDTHS = { "0", "1", "2", "3", "4" }; private static final String[] LINE_SHAPES = { "arched", "straight" }; ///////////////////////////////////// // EdgeDisplayProperty integration private static final String[] UPDATE_COLUMNS = new String[] { COL_COLOR, COL_WIDTH, COL_ARROWHEAD }; private class ControlChangeListener implements RelationDisplayRepository.ChangeListener { @Override public void edgeDisplayChanged(Relation relation, EdgeDisplayProperty props) { updateRelationColumns(relation, UPDATE_COLUMNS); } } private ControlChangeListener propListener; private RelationDisplayRepository propRepo; ///////////////////////////////////// // UX Elements private TableViewer propViewer; ///////////////////////////////////// // Public methods public RelationDisplayTableControl(Composite parent) { super(parent, SWT.NONE); setLayout(Widgets.buildContainerLayout(1)); propViewer = new TableViewer(this, SWT.FULL_SELECTION | SWT.MULTI | SWT.BORDER | SWT.V_SCROLL); // Layout embedded table Table propTable = propViewer.getTable(); propTable.setLayoutData(Widgets.buildGrabFillData()); // initialize the table propTable.setHeaderVisible(true); propTable.setToolTipText("Relations Display Properties"); EditColTableDef.setupTable(TABLE_DEF, propTable); // Configure cell editing CellEditor[] cellEditors = new CellEditor[TABLE_DEF.length]; cellEditors[INDEX_NAME] = null; cellEditors[INDEX_SOURCE] = null; cellEditors[INDEX_COLOR] = new ColorCellEditor(propTable); cellEditors[INDEX_STYLE] = new ComboBoxCellEditor(propTable, toString(EdgeDisplayProperty.LineStyle.values(), true)); cellEditors[INDEX_ARROWHEAD] = new ComboBoxCellEditor(propTable, toString(EdgeDisplayProperty.ArrowheadStyle.values(), true)); propViewer.setCellEditors(cellEditors); propViewer.setLabelProvider(new EdgeDisplayLabelProvider()); propViewer.setColumnProperties(EditColTableDef.getProperties(TABLE_DEF)); propViewer.setCellModifier(new EdgeDisplayCellModifier()); // TODO: Add column sorters, filters? configSorters(propTable); // Configure content last: use updateTable() to render relations propViewer.setContentProvider(ArrayContentProvider.getInstance()); } private String[] toString(Object[] objs, boolean lowercase) { String[] s = new String[objs.length]; int i = 0; for (Object o : objs) { s[i++] = lowercase ? o.toString().toLowerCase() : o.toString(); } return s; } /** * Fill the list with {@link Relation}s. * Since rendering depends on propRepo, set input after * the propRepo is installed. */ public void setInput(Collection<Relation> relations) { propViewer.setInput(relations); } /** * Provide the set of currently selected {@link Relation} rows in * the {@link TableViewer}. */ public Collection<Relation> getSelection() { ISelection selection = propViewer.getSelection(); return Selections.getSelection(selection, Relation.class); } public void setEdgeDisplayRepository(RelationDisplayRepository propRepo) { this.propRepo = propRepo; propListener = new ControlChangeListener(); propRepo.addChangeListener(propListener); } public void removeEdgeDisplayRepository(RelationDisplayRepository edgeDisplayRepo) { if (null != propListener) { this.propRepo.removeChangeListener(propListener); propListener = null; } this.propRepo = null; } private void updateRelationColumns(Relation relation, String[] cols) { propViewer.update(relation, cols); } ///////////////////////////////////// // Property repository methods /** * Acquire properties directly, avoid setting up a default. */ private void saveDisplayProperty(Relation relation, EdgeDisplayProperty props) { propRepo.setDisplayProperty(relation, props); } /** * Acquire properties directly, avoid setting up a default. */ private EdgeDisplayProperty loadDisplayProperty(Relation relation) { return propRepo.getDisplayProperty(relation); } /** * Utility method for both the label provider and cell modifier. * Note that the default constructor for {@link EdgeDisplayProperty} * uses the default values for all member elements. */ private EdgeDisplayProperty getDisplayProperty(Relation relation) { EdgeDisplayProperty relationProp = loadDisplayProperty(relation); if (null != relationProp) { return relationProp; } // Provide the default if none are persisted. return new EdgeDisplayProperty(); } ///////////////////////////////////// // Column sorting private void configSorters(Table table) { int index = 0; for (TableColumn column : table.getColumns()) { final int colIndex = index++; column.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent event) { updateSortColumn((TableColumn) event.widget, colIndex); } }); } } private void updateSortColumn(TableColumn column, int colIndex) { setSortColumn(column, colIndex, getSortDirection(column)); } private int getSortDirection(TableColumn column) { Table tableControl = (Table) propViewer.getControl(); if (column != tableControl.getSortColumn()) { return SWT.DOWN; } // If it is unsorted (SWT.NONE), assume down sort return (SWT.DOWN == tableControl.getSortDirection()) ? SWT.UP : SWT.DOWN; } private void setSortColumn(TableColumn column, int colIndex, int direction) { ViewerComparator sorter = buildColumnSorter(colIndex); if (SWT.UP == direction) { sorter = new InverseSorter(sorter); } Table tableControl = (Table) propViewer.getControl(); propViewer.setComparator(sorter); tableControl.setSortColumn(column); tableControl.setSortDirection(direction); } private ViewerComparator buildColumnSorter(int colIndex) { // By default, use an alphabetic sort over the column labels. ITableLabelProvider labelProvider = (ITableLabelProvider) propViewer.getLabelProvider(); ViewerComparator result = new AlphabeticSorter(new LabelProviderToString(labelProvider, colIndex)); return result; } ///////////////////////////////////// // Label provider for table cell text private class EdgeDisplayLabelProvider extends LabelProvider implements ITableLabelProvider { @Override public String getColumnText(Object element, int columnIndex) { if (element instanceof Relation) { Relation relation = (Relation) element; EdgeDisplayProperty prop = getDisplayProperty(relation); switch (columnIndex) { case INDEX_NAME: return relation.toString(); case INDEX_SOURCE: return getSourceLabelForRelation(relation); case INDEX_COLOR: return getColorName(prop); // case INDEX_WIDTH: case INDEX_STYLE: if (null != prop) { return prop.getLineStyle().toString().toLowerCase(); } return null; case INDEX_ARROWHEAD: if (null != prop) { return prop.getArrowhead().toString().toLowerCase(); } return null; } } return null; } private String getSourceLabelForRelation(Relation relation) { return RelationRegistry.getRegistryRelationSource(relation); } private String getColorName(EdgeDisplayProperty prop) { if (null == prop) { return null; } Color color = prop.getColor(); if (null == color) { return null; } String result = StringConverter.asString(Colors.rgbFromColor(color)); return "(" + result + ")"; } @Override public Image getColumnImage(Object element, int columnIndex) { return null; } } ///////////////////////////////////// // Value provider/modifier for edit cells private class EdgeDisplayCellModifier implements ICellModifier { @Override public boolean canModify(Object element, String property) { return EditColTableDef.get(TABLE_DEF, property).isEditable(); } @Override public Object getValue(Object element, String property) { if (!(element instanceof Relation)) { return null; } Relation relation = (Relation) element; EdgeDisplayProperty relProp = getDisplayProperty(relation); if (COL_COLOR.equals(property)) { Color relColor = relProp.getColor(); if (null == relColor) { return new RGB(0, 0, 0); } RGB result = Colors.rgbFromColor(relColor); return result; } if (COL_ARROWHEAD.equals(property)) { return relProp.getArrowhead().ordinal(); } if (COL_STYLE.equals(property)) { return relProp.getLineStyle().ordinal(); } if (COL_SHAPE.equals(property)) { } return null; } @Override public void modify(Object element, String property, Object value) { if (!(element instanceof TableItem)) { return; } Object modifiedObject = ((TableItem) element).getData(); if (!(modifiedObject instanceof Relation)) { return; } Relation relation = (Relation) modifiedObject; EdgeDisplayProperty relProp = loadDisplayProperty(relation); if (null == relProp) { return; // For example, when there is no editor. } if (property.equals(COL_STYLE) && (value instanceof Integer)) { relProp.setLineStyle(EdgeDisplayProperty.LineStyle.values()[(Integer) value]); } else if (property.equals(COL_ARROWHEAD) && (value instanceof Integer)) { relProp.setArrowhead(EdgeDisplayProperty.ArrowheadStyle.values()[(Integer) value]); } else if (property.equals(COL_COLOR) && (value instanceof RGB)) { Color newColor = Colors.colorFromRgb((RGB) value); relProp.setColor(newColor); } saveDisplayProperty(relation, relProp); // Viewer update via ChangeListener } } }