]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTable.java
8ee627a57989c09a79732b4773281c1a1b493352
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / techtype / table / TechTypeTable.java
1 package org.simantics.district.network.ui.techtype.table;
2
3 import java.io.IOException;
4 import java.nio.charset.Charset;
5 import java.nio.file.Files;
6 import java.nio.file.Paths;
7 import java.util.Set;
8 import java.util.stream.Collectors;
9
10 import org.eclipse.jface.layout.GridDataFactory;
11 import org.eclipse.jface.layout.GridLayoutFactory;
12 import org.eclipse.nebula.widgets.nattable.NatTable;
13 import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
14 import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
15 import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
16 import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
17 import org.eclipse.nebula.widgets.nattable.copy.command.CopyDataCommandHandler;
18 import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
19 import org.eclipse.nebula.widgets.nattable.freeze.CompositeFreezeLayer;
20 import org.eclipse.nebula.widgets.nattable.freeze.FreezeLayer;
21 import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
22 import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
23 import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
24 import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
25 import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultColumnHeaderDataLayer;
26 import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
27 import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
28 import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
29 import org.eclipse.nebula.widgets.nattable.group.ColumnGroupHeaderLayer;
30 import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel;
31 import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
32 import org.eclipse.nebula.widgets.nattable.hover.HoverLayer;
33 import org.eclipse.nebula.widgets.nattable.hover.config.BodyHoverStylingBindings;
34 import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
35 import org.eclipse.nebula.widgets.nattable.layer.ILayer;
36 import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
37 import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
38 import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelAccumulator;
39 import org.eclipse.nebula.widgets.nattable.reorder.RowReorderLayer;
40 import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
41 import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
42 import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
43 import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
44 import org.eclipse.nebula.widgets.nattable.style.Style;
45 import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
46 import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
47 import org.eclipse.swt.SWT;
48 import org.eclipse.swt.dnd.Clipboard;
49 import org.eclipse.swt.events.ModifyEvent;
50 import org.eclipse.swt.events.ModifyListener;
51 import org.eclipse.swt.widgets.Composite;
52 import org.eclipse.swt.widgets.Text;
53 import org.simantics.Simantics;
54 import org.simantics.db.Resource;
55 import org.simantics.db.WriteGraph;
56 import org.simantics.db.common.request.WriteRequest;
57 import org.simantics.db.exception.DatabaseException;
58 import org.simantics.db.layer0.request.PossibleActiveModel;
59 import org.simantics.district.network.techtype.requests.PossibleTechTypeKeyName;
60 import org.simantics.district.network.techtype.requests.PossibleTechTypeTable;
61 import org.simantics.district.network.techtype.requests.WriteTechTypeTable;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 public class TechTypeTable extends Composite {
66
67         private static final String INVALID_LABEL = "INVALID";
68
69         private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTable.class);
70
71         NatTable table;
72         private TechTypeTableDataProvider bodyDataProvider;
73         DataLayer bodyDataLayer;
74         private IConfigRegistry summaryConfigRegistry;
75         private IUniqueIndexLayer summaryRowLayer;
76         private ViewportLayer viewportLayer;
77         private CompositeFreezeLayer compositeFreezeLayer;
78         private FreezeLayer freezeLayer;
79         // private TableDataSortModel sortModel;
80         private ColumnHideShowLayer columnHideShowLayer;
81         private ColumnGroupModel columnGroupModel = new ColumnGroupModel();
82         private TechTypeColumnHeaderTableDataProvider columnHeaderDataProvider;
83         Clipboard cpb;
84         public SelectionLayer selectionLayer;
85         private TechTypeTableSortModel sortModel;
86
87         private Resource componentType;
88         private Resource tableResource;
89
90         protected Set<String> validationResult;
91
92         public TechTypeTable(Composite parent, int style, Resource componentType, Resource tableResource, String data) {
93                 super(parent, style);
94                 this.componentType = componentType;
95                 this.tableResource = tableResource;
96
97                 defaultInitializeUI(data);
98         }
99
100         private void defaultInitializeUI(String data) {
101                 GridDataFactory.fillDefaults().grab(true, true).applyTo(this);
102                 GridLayoutFactory.fillDefaults().numColumns(1).applyTo(this);
103
104                 Composite filterComposite = new Composite(this, SWT.NONE);
105                 GridDataFactory.fillDefaults().grab(true, false).applyTo(filterComposite);
106                 GridLayoutFactory.fillDefaults().numColumns(1).applyTo(filterComposite);
107
108                 createFilterBar(filterComposite);
109
110                 Composite tableComposite = new Composite(this, SWT.NONE);
111                 GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite);
112                 GridLayoutFactory.fillDefaults().numColumns(1).applyTo(tableComposite);
113
114                 createTable(tableComposite, data);
115         }
116
117         private void createFilterBar(Composite filterComposite) {
118
119                 Text filterText = new Text(filterComposite, SWT.BORDER);
120                 GridDataFactory.fillDefaults().grab(true, true).applyTo(filterText);
121                 filterText.addModifyListener(new ModifyListener() {
122
123                         @Override
124                         public void modifyText(ModifyEvent e) {
125                                 System.out.println("text modified");
126                                 bodyDataProvider.setFilter(filterText.getText());
127                                 table.refresh(true);
128                         }
129                 });
130
131         }
132
133         private void createTable(Composite parent, String data) {
134
135                 // build the body layer stack
136                 // Usually you would create a new layer stack by extending
137                 // AbstractIndexLayerTransform and
138                 // setting the ViewportLayer as underlying layer. But in this case using the
139                 // ViewportLayer
140                 // directly as body layer is also working.
141                 bodyDataProvider = new TechTypeTableDataProvider(data);
142                 bodyDataLayer = new DataLayer(bodyDataProvider);
143                 
144                 RowReorderLayer rowReorderLayer = new RowReorderLayer(
145                                 columnHideShowLayer = new ColumnHideShowLayer(bodyDataLayer));
146
147                 HoverLayer hoverLayer = new HoverLayer(rowReorderLayer, false);
148                 // we need to ensure that the hover styling is removed when the mouse
149                 // cursor moves out of the cell area
150                 hoverLayer.addConfiguration(new BodyHoverStylingBindings(hoverLayer));
151
152                 selectionLayer = new SelectionLayer(hoverLayer);
153
154                 viewportLayer = new ViewportLayer(selectionLayer);
155                 viewportLayer.setRegionName(GridRegion.BODY);
156                 freezeLayer = new FreezeLayer(selectionLayer);
157                 compositeFreezeLayer = new CompositeFreezeLayer(freezeLayer, viewportLayer, selectionLayer);
158
159                 // build the column header layer
160                 columnHeaderDataProvider = new TechTypeColumnHeaderTableDataProvider(bodyDataProvider);
161                 DataLayer columnHeaderDataLayer = new DefaultColumnHeaderDataLayer(columnHeaderDataProvider);
162                 columnHeaderDataLayer.setRowsResizableByDefault(false);
163                 columnHeaderDataLayer.setColumnsResizableByDefault(true);
164                 ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, compositeFreezeLayer,
165                                 selectionLayer);
166                 ColumnGroupHeaderLayer columnGroupHeaderLayer = new ColumnGroupHeaderLayer(columnHeaderLayer, selectionLayer,
167                                 columnGroupModel);
168                 columnGroupHeaderLayer.setCalculateHeight(true);
169                 SortHeaderLayer<String> columnSortHeaderLayer = new SortHeaderLayer<>(columnGroupHeaderLayer,
170                                 sortModel = new TechTypeTableSortModel(bodyDataProvider));
171
172                 // build the row header layer
173                 IDataProvider rowHeaderDataProvider = new TechTypeRowHeaderTableDataProvider(bodyDataProvider);
174                 DataLayer rowHeaderDataLayer = new DefaultRowHeaderDataLayer(rowHeaderDataProvider);
175                 rowHeaderDataLayer.setRowsResizableByDefault(false);
176                 rowHeaderDataLayer.setColumnsResizableByDefault(false);
177                 RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, compositeFreezeLayer, selectionLayer);
178
179                 // build the corner layer
180                 IDataProvider cornerDataProvider = new DefaultCornerDataProvider(columnHeaderDataProvider,
181                                 rowHeaderDataProvider);
182                 DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
183                 ILayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, columnSortHeaderLayer);
184
185                 // build the grid layer
186                 GridLayer gridLayer = new GridLayer(compositeFreezeLayer, columnSortHeaderLayer, rowHeaderLayer, cornerLayer);
187
188                 table = new NatTable(parent, NatTable.DEFAULT_STYLE_OPTIONS | SWT.BORDER, gridLayer, false);
189                 GridDataFactory.fillDefaults().grab(true, true).applyTo(table);
190                 
191                 // Show entries labeled "INVALID" with red text
192                 table.addConfiguration(new AbstractRegistryConfiguration() {
193                         @Override
194                         public void configureRegistry(IConfigRegistry configRegistry) {
195                                 Style cellStyle = new Style();
196                                 cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, GUIHelper.COLOR_RED);
197                                 configRegistry.registerConfigAttribute(
198                                                 CellConfigAttributes.CELL_STYLE,
199                                                 cellStyle,
200                                                 DisplayMode.NORMAL,
201                                                 INVALID_LABEL
202                                         );
203                         }
204                 });
205
206                 // Register a CopyDataCommandHandler that also copies the headers and
207                 // uses the configured IDisplayConverters
208                 CopyDataCommandHandler copyHandler = new CopyDataCommandHandler(selectionLayer, columnHeaderDataLayer,
209                                 rowHeaderDataLayer);
210                 copyHandler.setCopyFormattedText(true);
211                 gridLayer.registerCommandHandler(copyHandler);
212
213                 // initialize paste handler with SWT clipboard
214                 cpb = new Clipboard(getDisplay());
215                 // PasteDataCommandHandler pasteHandler = new
216                 // PasteDataCommandHandler(bodyDataProvider, bodyDataLayer, selectionLayer,
217                 // cpb);
218                 // bodyDataLayer.registerCommandHandler(pasteHandler);
219
220                 table.addConfiguration(new DefaultNatTableStyleConfiguration());
221                 // table.addConfiguration(new EditingSupportConfiguration(bodyDataProvider));
222                 table.configure();
223         }
224
225         private static String getKeyColumnName(Resource componentType) {
226                 String keyName = null;
227                 if (componentType != null) {
228                         try {
229                                 keyName = Simantics.getSession().syncRequest(new PossibleTechTypeKeyName(componentType));
230                         } catch (DatabaseException e) {
231                                 LOGGER.error("Failed to read possible tech type key name for {}", componentType, e);
232                         }
233                 }
234                 return keyName.startsWith("_") ? keyName.substring(1) : keyName;
235         }
236
237         @Override
238         public void dispose() {
239                 cpb.dispose();
240                 super.dispose();
241         }
242
243         public void setTechTypePath(String path) {
244                 String data;
245                 try {
246                         data = Files.lines(Paths.get(path), Charset.defaultCharset()).collect(Collectors.joining("\n"));
247                 } catch (IOException e) {
248                         LOGGER.error("Failed to read contents of file '{}' as {}", path, Charset.defaultCharset(), e);
249                         return;
250                 }
251
252                 try {
253                         Simantics.getSession().syncRequest(new WriteRequest() {
254                                 @Override
255                                 public void perform(WriteGraph graph) throws DatabaseException {
256                                         graph.syncRequest(new WriteTechTypeTable(componentType, data));
257                                         Resource model = graph.syncRequest(new PossibleActiveModel(Simantics.getProjectResource()));
258                                         if (model != null) {
259                                                 tableResource = graph.syncRequest(new PossibleTechTypeTable(model, componentType));
260                                         }
261                                 }
262                         });
263                 } catch (DatabaseException e) {
264                         LOGGER.error("Failed to write tech type table data to model", e);
265                 }
266
267                 setTechTypeData(data);
268                 setValidationResult(null);
269         }
270
271         public void setTechTypeData(String data) {
272                 bodyDataProvider.setData(data);
273                 table.refresh(true);
274         }
275
276         public void setComponentType(Resource componentType) {
277                 this.componentType = componentType;
278         }
279
280         /**
281          * Set results of a validation operation
282          * 
283          * Invalid entries are designated by a string of the form "<type_code>/<property_name>".
284          * 
285          * This method must be called in the SWT thread.
286          * 
287          * @param result  A set of strings representing invalid entries
288          */
289         public void setValidationResult(Set<String> result) {
290                 if (result != null && result.isEmpty())
291                         result = null;
292                 
293                 this.validationResult = result;
294                 if (result != null) {
295                         String keyName = getKeyColumnName(componentType);
296                         
297                         bodyDataLayer.setConfigLabelAccumulator(new IConfigLabelAccumulator() {
298                                 @Override
299                                 public void accumulateConfigLabels(LabelStack configLabels, int columnPosition, int rowPosition) {
300                                         if (keyName != null) {
301                                                 int keyColumn = bodyDataProvider.getVariableIndex(keyName);
302                                                 if (keyColumn >= 0) {
303                                                         String key = (String) bodyDataProvider.getDataValue(keyColumn, rowPosition);
304                                                         String columnName = bodyDataProvider.getVariableName(columnPosition);
305                                                         
306                                                         if (validationResult.contains(key + "/" + columnName)) {
307                                                                 configLabels.addLabel(INVALID_LABEL);
308                                                         }
309                                                 }
310                                         }
311                                 }
312                         });
313                 } else {
314                         bodyDataLayer.setConfigLabelAccumulator(null);
315                 }
316                 
317                 table.refresh();
318         }
319
320         /**
321          * Get a resource representation of the currently open table, or null if
322          * table is not stored in the model.
323          */
324         public Resource getCurrentTable() {
325                 return tableResource;
326         }
327 }