Added editable unit for derived properties
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / componentTypeEditor / DerivedPropertiesSection.java
1 package org.simantics.modeling.ui.componentTypeEditor;
2
3 import java.util.ArrayList;
4 import java.util.HashSet;
5 import java.util.List;
6 import java.util.Set;
7
8 import org.eclipse.jface.layout.GridDataFactory;
9 import org.eclipse.jface.layout.GridLayoutFactory;
10 import org.eclipse.jface.layout.TableColumnLayout;
11 import org.eclipse.jface.viewers.ColumnWeightData;
12 import org.eclipse.swt.SWT;
13 import org.eclipse.swt.custom.TableEditor;
14 import org.eclipse.swt.events.MouseAdapter;
15 import org.eclipse.swt.events.MouseEvent;
16 import org.eclipse.swt.events.SelectionAdapter;
17 import org.eclipse.swt.events.SelectionEvent;
18 import org.eclipse.swt.graphics.Color;
19 import org.eclipse.swt.graphics.Rectangle;
20 import org.eclipse.swt.layout.FillLayout;
21 import org.eclipse.swt.widgets.Button;
22 import org.eclipse.swt.widgets.Composite;
23 import org.eclipse.swt.widgets.Control;
24 import org.eclipse.swt.widgets.Table;
25 import org.eclipse.swt.widgets.TableColumn;
26 import org.eclipse.swt.widgets.TableItem;
27 import org.eclipse.ui.forms.widgets.Form;
28 import org.eclipse.ui.forms.widgets.FormToolkit;
29 import org.eclipse.ui.forms.widgets.Section;
30 import org.simantics.Simantics;
31 import org.simantics.db.ReadGraph;
32 import org.simantics.db.RequestProcessor;
33 import org.simantics.db.Resource;
34 import org.simantics.db.WriteGraph;
35 import org.simantics.db.common.request.PossibleIndexRoot;
36 import org.simantics.db.common.request.UniqueRead;
37 import org.simantics.db.common.request.WriteRequest;
38 import org.simantics.db.exception.DatabaseException;
39 import org.simantics.layer0.Layer0;
40 import org.simantics.modeling.userComponent.ComponentTypeCommands;
41 import org.simantics.scl.runtime.function.Function4;
42 import org.simantics.structural.stubs.StructuralResource2;
43
44 public class DerivedPropertiesSection implements ComponentTypeViewerSection {
45     private static final String[] COLUMN_NAMES = 
46             new String[] {"Name", "Type", "Expression", "Unit", "Label", "Description"};
47     private static final int[] COLUMN_LENGTHS =
48             new int[] { 120, 100, 100, 70, 100, 100 };
49     private static final int[] COLUMN_WEIGHTS =
50             new int[] { 0, 0, 100, 0, 0, 0 };
51     private static Function4<RequestProcessor, Resource, Resource, String, String> VALIDATE_MONITOR_EXPRESSION =
52             new Function4<RequestProcessor, Resource, Resource, String, String>() {
53         @Override
54         public String apply(RequestProcessor p0, Resource p1, Resource p2, String p3) {
55             return validateMonitorExpression(p0, p1, p2, p3);
56         }
57     };
58     
59     ComponentTypeViewerData data;
60     Table table;
61     TableColumn[] columns;
62     TableEditor editor;
63     Button newProperty;
64     Button removeProperty;
65     
66     Section section;
67     
68     public DerivedPropertiesSection(ComponentTypeViewerData data) {
69         this.data = data;
70         FormToolkit tk = data.tk;
71         Form form = data.form;
72         section = tk.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED);
73         section.setLayout(new FillLayout());
74         section.setText("Derived properties");
75
76         Composite sectionBody = tk.createComposite(section);
77         GridLayoutFactory.fillDefaults().numColumns(2).applyTo(sectionBody);
78         section.setClient(sectionBody);
79
80         Composite tableComposite = tk.createComposite(sectionBody);
81         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableComposite);
82         TableColumnLayout tcl = new TableColumnLayout();
83         tableComposite.setLayout(tcl);
84
85         table = tk.createTable(tableComposite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
86         table.setLinesVisible(true);
87         table.setHeaderVisible(true);
88
89         columns = new TableColumn[COLUMN_NAMES.length];
90         for(int i=0;i<COLUMN_NAMES.length;++i) {
91             TableColumn column = new TableColumn(table, SWT.NONE);
92             columns[i] = column;
93             tcl.setColumnData(column, new ColumnWeightData(COLUMN_WEIGHTS[i], COLUMN_LENGTHS[i], true));
94             column.setText(COLUMN_NAMES[i]);
95         }
96
97         // Table editor
98         editor = new TableEditor(table);
99         editor.grabHorizontal = true;
100         editor.grabVertical = true;
101         editor.horizontalAlignment = SWT.LEFT;
102         table.addMouseListener(new MouseAdapter() {
103             @Override
104             public void mouseDown(MouseEvent e) {
105                 // Clean up any previous editor control
106                 Control oldEditor = editor.getEditor();
107                 if (oldEditor != null) oldEditor.dispose();
108
109                 if (data.readOnly)
110                     return;
111
112                 // Relative position
113                 Rectangle tableBounds = table.getClientArea();
114                 int rx = e.x - tableBounds.x;
115                 int ry = e.y - tableBounds.y;
116
117                 // Find cell
118                 TableItem selectedItem = null;
119                 int selectedColumn = -1;
120                 Rectangle selectedItemBounds = null;
121                 for(TableItem item : table.getItems()) {
122                     for(int column = 0;column < COLUMN_NAMES.length;++column) {
123                         Rectangle bounds = item.getBounds(column);
124                         if(bounds.contains(rx, ry)) {
125                             selectedItemBounds = bounds;
126                             selectedItem = item;
127                             selectedColumn = column;
128                             break;
129                         }
130                     }
131                 }
132                 if(selectedItem == null) {
133                     return;
134                 }
135
136                 // Table editor
137                 final int column = selectedColumn; 
138                 final ComponentTypeViewerPropertyInfo propertyInfo = (ComponentTypeViewerPropertyInfo)selectedItem.getData();
139                 final Resource resource = propertyInfo.resource;
140                 switch (column) {
141                 case 0:
142                     data.editName(table, editor, propertyInfo, selectedItem, column, ComponentTypeViewerData.PROPERTY_NAME_PATTERN);
143                     break;
144
145                 case 1:
146                     data.editType(table, editor, propertyInfo, selectedItem, column, false);
147                     break;
148
149                 case 2:
150                     data.editValue(table, editor, propertyInfo, selectedItem, column, propertyInfo.immutable ? null : new StringWriter() {
151                         @Override
152                         public void perform(WriteGraph graph, String newValue) throws DatabaseException {
153                             ComponentTypeCommands.setMonitorExpression(graph, data.componentType, resource, newValue);
154                         }
155                     }, VALIDATE_MONITOR_EXPRESSION);
156                     break;
157
158                 case 3:
159                     data.editUnit(table, editor, propertyInfo, selectedItem, column);
160                     break;
161
162                 case 4:
163                     data.editValue(table, editor, propertyInfo, selectedItem, column, propertyInfo.immutable ? null : new StringWriter() {
164                         @Override
165                         public void perform(WriteGraph graph, String newValue) throws DatabaseException {
166                             graph.markUndoPoint();
167                             String value = newValue.isEmpty() ? null : newValue;
168                             ComponentTypeCommands.setLabel(graph, resource, value);
169                         }
170                     }, null);
171                     break;
172
173                 case 5:
174                     data.editMultilineText(table, editor, propertyInfo, selectedItem, selectedItemBounds, column, new StringWriter() {
175                         @Override
176                         public void perform(WriteGraph graph, String newValue) throws DatabaseException {
177                             graph.markUndoPoint();
178                             String value = newValue.isEmpty() ? null : newValue;
179                             ComponentTypeCommands.setDescription(graph, resource, value);
180                         }
181                     });
182                     break;
183                 }
184             }
185
186         });
187
188         // Buttons
189
190         Composite buttons = tk.createComposite(sectionBody);
191         GridDataFactory.fillDefaults().applyTo(buttons);
192         GridLayoutFactory.fillDefaults().applyTo(buttons);
193
194         newProperty = tk.createButton(buttons, "New property", SWT.PUSH);
195         GridDataFactory.fillDefaults().applyTo(newProperty);
196         removeProperty = tk.createButton(buttons, "Remove property", SWT.PUSH);
197         GridDataFactory.fillDefaults().applyTo(removeProperty);
198
199         // Actions
200
201         newProperty.addSelectionListener(new SelectionAdapter() {
202             @Override
203             public void widgetSelected(SelectionEvent e) {
204                 if(editor.getEditor() != null)
205                     editor.getEditor().dispose();
206                 Simantics.getSession().async(new WriteRequest() {
207                     @Override
208                     public void perform(WriteGraph graph)
209                             throws DatabaseException {
210                         ComponentTypeCommands.createMonitorPropertyWithDefaults(graph, data.componentType);
211                     }
212                 });
213             }
214         });
215
216         removeProperty.addSelectionListener(new SelectionAdapter() {
217             @Override
218             public void widgetSelected(SelectionEvent e) {
219                 if(editor.getEditor() != null)
220                     editor.getEditor().dispose();
221                 final List<Resource> propertiesToBeRemoved = 
222                         new ArrayList<>();
223                 for(TableItem item : table.getSelection())
224                     propertiesToBeRemoved.add(((ComponentTypeViewerPropertyInfo)item.getData()).resource);
225                 //System.out.println("remove " + propertiesToBeRemoved.size() + " resources");
226                 if(!propertiesToBeRemoved.isEmpty())
227                     Simantics.getSession().async(new WriteRequest() {
228                         @Override
229                         public void perform(WriteGraph graph)
230                                 throws DatabaseException {
231                             graph.markUndoPoint();
232                             for(Resource property : propertiesToBeRemoved)
233                                 ComponentTypeCommands.removeProperty(graph, data.componentType, property);
234                         }
235                     });
236             }
237         });
238     }
239
240     @Override
241     public void setReadOnly(boolean readOnly) {
242         boolean e = !readOnly;
243         newProperty.setEnabled(e);
244         removeProperty.setEnabled(e);
245     }
246
247     public static String validateMonitorExpression(final RequestProcessor processor, final Resource componentType, final Resource relation, final String expression) {
248
249         if (expression.trim().isEmpty()) {
250             return "Expression is empty.";
251         }
252
253         if (expression.trim().isEmpty()) {
254             return "Expression is empty.";
255         }
256         try {
257             return processor.sync(new UniqueRead<String>() {
258
259                 @Override
260                 public String perform(ReadGraph graph) throws DatabaseException {
261
262                     StructuralResource2 STR = StructuralResource2.getInstance(graph);
263
264                     Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(componentType));
265
266                     for(Resource literal : graph.getAssertedObjects(componentType, relation)) {
267
268                         try {
269                             // TODO validation
270                             if(graph.isInstanceOf(componentType, STR.ProceduralComponentType)) {
271                                 //Layer0 L0 = Layer0.getInstance(graph);
272                                 //Resource environment = graph.getPossibleObject(literal, L0.SCLValue_environment); 
273                                 //ContextModule context = graph.sync(new TypeMonitorContextRequest(componentType));
274                                 //String SCLMain = graph.syncRequest(new SCLMainModuleRequest(indexRoot));
275                                 //CompilationContext cc = new CompilationContext(environment, context, SCLMain);
276                                 //graph.syncRequest(new ActualCompileRequest(expression, cc), TransientCacheListener.<CompiledExpression>instance());
277                             } else {
278                                 //CompilationContext cc = new CompileSCLMonitorRequest(literal, componentType, indexRoot).getContext(graph);
279                                 //graph.syncRequest(new ActualCompileRequest(expression, cc), TransientCacheListener.<CompiledExpression>instance());
280                                 //graph.syncRequest(new CompileSCLMonitorRequest(graph, context));
281                             }
282                             
283                         } catch (Exception e) {
284                             String msg = e.getMessage();
285                             int index = msg.indexOf(":");
286                             if(index > 0) msg = msg.substring(index);
287                             return msg;
288                         }
289                         
290                     }
291                     
292                     return null;
293                     
294                 }
295                 
296             });
297         } catch (DatabaseException e) {
298             e.printStackTrace();
299         }
300
301         return null;
302
303     }
304
305     @Override
306     public void update(ComponentTypePropertiesResult result) {
307         if (table.isDisposed())
308             return;
309
310         Set<ComponentTypeViewerPropertyInfo> selected = new HashSet<>();
311         List<TableItem> selectedItems = new ArrayList<TableItem>(selected.size());
312         for (int i : table.getSelectionIndices()) {
313             TableItem item = table.getItem(i);
314             selected.add((ComponentTypeViewerPropertyInfo) item.getData());
315         }
316         
317         int topIndex = table.getTopIndex();
318         table.removeAll();
319         if(editor.getEditor() != null)
320             editor.getEditor().dispose();
321         
322         for(ComponentTypeViewerPropertyInfo info : result.getProperties()) {
323             boolean immutable = result.isImmutable() || info.immutable;
324             Color fg = immutable ? table.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY) : null;
325             if(info.sectionSpecificData != MONITOR)
326                 continue;
327
328             TableItem item = new TableItem(table, SWT.NONE);
329
330             item.setText(0, info.valid != null ? info.name + " (!)" : info.name);
331             item.setText(1, info.type);
332             item.setText(2, info.expression);
333             item.setText(3, info.unitString());
334             item.setText(4, info.label);
335             item.setText(5, info.description);
336
337             item.setForeground(fg);
338
339             item.setData(info);
340
341             if (selected.contains(info))
342                 selectedItems.add(item);
343         }
344         
345         table.setTopIndex(topIndex);
346         table.setSelection(selectedItems.toArray(new TableItem[selectedItems.size()]));
347         table.redraw();
348     }
349
350     @Override
351     public Section getSection() {
352         return section;
353     }
354
355     @Override
356     public double getPriority() {
357         return 100.0;
358     }
359
360     private static final Object MONITOR = new Object();
361     
362     @Override
363     public Object getSectionSpecificData(ReadGraph graph,
364             ComponentTypeViewerPropertyInfo info) throws DatabaseException {
365         Layer0 L0 = Layer0.getInstance(graph);
366         StructuralResource2 STR = StructuralResource2.getInstance(graph);
367         Resource range = graph.getPossibleObject(info.resource, L0.HasRange);
368         if(range != null && graph.isInstanceOf(range, STR.MonitorValueType))
369             return MONITOR;
370         else
371             return null;
372     }
373     
374     @Override
375     public double getDataPriority() {
376         return 100.0;
377     }
378
379 }