1 package org.simantics.modeling.ui.componentTypeEditor;
\r
3 import java.util.ArrayList;
\r
4 import java.util.Collection;
\r
5 import java.util.Collections;
\r
6 import java.util.HashMap;
\r
7 import java.util.HashSet;
\r
8 import java.util.List;
\r
9 import java.util.Map;
\r
10 import java.util.Set;
\r
12 import org.eclipse.jface.dialogs.IDialogSettings;
\r
13 import org.eclipse.jface.layout.GridDataFactory;
\r
14 import org.eclipse.jface.layout.GridLayoutFactory;
\r
15 import org.eclipse.jface.layout.TableColumnLayout;
\r
16 import org.eclipse.jface.resource.ImageDescriptor;
\r
17 import org.eclipse.jface.viewers.ColumnWeightData;
\r
18 import org.eclipse.jface.window.Window;
\r
19 import org.eclipse.swt.SWT;
\r
20 import org.eclipse.swt.custom.TableEditor;
\r
21 import org.eclipse.swt.events.DisposeEvent;
\r
22 import org.eclipse.swt.events.DisposeListener;
\r
23 import org.eclipse.swt.events.MouseAdapter;
\r
24 import org.eclipse.swt.events.MouseEvent;
\r
25 import org.eclipse.swt.events.SelectionAdapter;
\r
26 import org.eclipse.swt.events.SelectionEvent;
\r
27 import org.eclipse.swt.graphics.Color;
\r
28 import org.eclipse.swt.graphics.Rectangle;
\r
29 import org.eclipse.swt.layout.FillLayout;
\r
30 import org.eclipse.swt.widgets.Button;
\r
31 import org.eclipse.swt.widgets.Composite;
\r
32 import org.eclipse.swt.widgets.Control;
\r
33 import org.eclipse.swt.widgets.Shell;
\r
34 import org.eclipse.swt.widgets.Table;
\r
35 import org.eclipse.swt.widgets.TableColumn;
\r
36 import org.eclipse.swt.widgets.TableItem;
\r
37 import org.eclipse.ui.PlatformUI;
\r
38 import org.eclipse.ui.forms.widgets.Form;
\r
39 import org.eclipse.ui.forms.widgets.FormToolkit;
\r
40 import org.eclipse.ui.forms.widgets.Section;
\r
41 import org.simantics.Logger;
\r
42 import org.simantics.Simantics;
\r
43 import org.simantics.databoard.Bindings;
\r
44 import org.simantics.db.ReadGraph;
\r
45 import org.simantics.db.Resource;
\r
46 import org.simantics.db.WriteGraph;
\r
47 import org.simantics.db.common.CommentMetadata;
\r
48 import org.simantics.db.common.request.ObjectsWithType;
\r
49 import org.simantics.db.common.request.PossibleIndexRoot;
\r
50 import org.simantics.db.common.request.UniqueRead;
\r
51 import org.simantics.db.common.request.WriteRequest;
\r
52 import org.simantics.db.common.utils.NameUtils;
\r
53 import org.simantics.db.exception.DatabaseException;
\r
54 import org.simantics.db.layer0.adapter.CopyHandler2;
\r
55 import org.simantics.db.layer0.adapter.Instances;
\r
56 import org.simantics.db.layer0.util.Layer0Utils;
\r
57 import org.simantics.db.layer0.variable.Variable;
\r
58 import org.simantics.db.layer0.variable.Variables;
\r
59 import org.simantics.layer0.Layer0;
\r
60 import org.simantics.modeling.ui.Activator;
\r
61 import org.simantics.modeling.ui.componentTypeEditor.LiftPropertiesDialog.LiftedProperty;
\r
62 import org.simantics.modeling.userComponent.ComponentTypeCommands;
\r
63 import org.simantics.selectionview.SelectionViewResources;
\r
64 import org.simantics.structural.stubs.StructuralResource2;
\r
65 import org.simantics.utils.datastructures.Pair;
\r
67 public class ConfigurationPropertiesSection implements ComponentTypeViewerSection {
\r
69 private static final String[] COLUMN_NAMES =
\r
70 new String[] {"Name", "Type", "Default Value", "Unit", "Range", "Label", "Description"};
\r
71 private static final int[] COLUMN_LENGTHS =
\r
72 new int[] { 120, 100, 100, 50, 100, 100, 100 };
\r
73 private static final int[] COLUMN_WEIGHTS =
\r
74 new int[] { 0, 0, 0, 0, 0, 50, 100 };
\r
77 * Configuration property table column indexes that are to be considered
\r
78 * immutable when the property relation is immutable. Note that relation
\r
79 * immutability does not make the asserted default value immutable.
\r
81 private static final int[] IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION =
\r
82 { 0, 1, 3, 4, 5, 6 };
\r
84 ComponentTypeViewerData data;
\r
86 TableColumn[] columns;
\r
89 Button removeProperty;
\r
90 Button liftProperties;
\r
92 boolean hasTypes = false;
\r
97 public ConfigurationPropertiesSection(ComponentTypeViewerData data) {
\r
99 FormToolkit tk = data.tk;
\r
100 Form form = data.form;
\r
102 section = tk.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED);
\r
103 section.setLayout(new FillLayout());
\r
104 section.setText("Configuration properties");
\r
106 Composite sectionBody = tk.createComposite(section);
\r
107 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(sectionBody);
\r
108 section.setClient(sectionBody);
\r
110 Composite tableComposite = tk.createComposite(sectionBody);
\r
111 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableComposite);
\r
112 TableColumnLayout tcl = new TableColumnLayout();
\r
113 tableComposite.setLayout(tcl);
\r
115 table = tk.createTable(tableComposite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
\r
116 table.setLinesVisible(true);
\r
117 table.setHeaderVisible(true);
\r
119 columns = new TableColumn[COLUMN_NAMES.length];
\r
120 for(int i=0;i<COLUMN_NAMES.length;++i) {
\r
121 TableColumn column = new TableColumn(table, SWT.NONE);
\r
122 columns[i] = column;
\r
123 tcl.setColumnData(column, new ColumnWeightData(COLUMN_WEIGHTS[i], COLUMN_LENGTHS[i], true));
\r
124 column.setText(COLUMN_NAMES[i]);
\r
128 editor = new TableEditor(table);
\r
129 editor.grabHorizontal = true;
\r
130 editor.grabVertical = true;
\r
131 editor.horizontalAlignment = SWT.LEFT;
\r
132 table.addMouseListener(new MouseAdapter() {
\r
134 public void mouseDown(MouseEvent e) {
\r
135 // Clean up any previous editor control
\r
136 Control oldEditor = editor.getEditor();
\r
137 if (oldEditor != null) oldEditor.dispose();
\r
142 // Relative position
\r
143 Rectangle tableBounds = table.getClientArea();
\r
144 int rx = e.x - tableBounds.x;
\r
145 int ry = e.y - tableBounds.y;
\r
148 TableItem selectedItem = null;
\r
149 int selectedColumn = -1;
\r
150 Rectangle selectedItemBounds = null;
\r
151 for(TableItem item : table.getItems()) {
\r
152 for(int column = 0;column < COLUMN_NAMES.length;++column) {
\r
153 Rectangle bounds = item.getBounds(column);
\r
154 if(bounds.contains(rx, ry)) {
\r
155 selectedItemBounds = bounds;
\r
156 selectedItem = item;
\r
157 selectedColumn = column;
\r
162 if(selectedItem == null) {
\r
167 final int column = selectedColumn;
\r
168 final ComponentTypeViewerPropertyInfo propertyInfo = (ComponentTypeViewerPropertyInfo)selectedItem.getData();
\r
169 final Resource resource = propertyInfo.resource;
\r
172 data.editName(table, editor, propertyInfo, selectedItem, column, ComponentTypeViewerData.PROPERTY_NAME_PATTERN);
\r
176 data.editType(table, editor, propertyInfo, selectedItem, column, true);
\r
180 data.editValue(table, editor, propertyInfo, selectedItem, column, data.readOnly ? null : new StringWriter() {
\r
182 public void perform(WriteGraph graph, String newValue) throws DatabaseException {
\r
183 graph.markUndoPoint();
\r
184 ComponentTypeCommands.setDefaultValue(graph, data.componentType, propertyInfo.resource, newValue);
\r
190 data.editUnit(table, editor, propertyInfo, selectedItem, column);
\r
194 data.editRange(table, editor, propertyInfo, selectedItem, selectedItemBounds, column);
\r
198 data.editValue(table, editor, propertyInfo, selectedItem, column, propertyInfo.immutable ? null : new StringWriter() {
\r
200 public void perform(WriteGraph graph, String newValue) throws DatabaseException {
\r
201 graph.markUndoPoint();
\r
202 String value = newValue.isEmpty() ? null : newValue;
\r
203 ComponentTypeCommands.setLabel(graph, resource, value);
\r
209 data.editMultilineText(table, editor, propertyInfo, selectedItem, selectedItemBounds, column, new StringWriter() {
\r
211 public void perform(WriteGraph graph, String newValue) throws DatabaseException {
\r
212 graph.markUndoPoint();
\r
213 String value = newValue.isEmpty() ? null : newValue;
\r
214 ComponentTypeCommands.setDescription(graph, resource, value);
\r
224 Composite buttons = tk.createComposite(sectionBody);
\r
225 GridDataFactory.fillDefaults().applyTo(buttons);
\r
226 GridLayoutFactory.fillDefaults().applyTo(buttons);
\r
228 newProperty = tk.createButton(buttons, "New property", SWT.PUSH);
\r
229 GridDataFactory.fillDefaults().applyTo(newProperty);
\r
230 removeProperty = tk.createButton(buttons, "Remove property", SWT.PUSH);
\r
231 GridDataFactory.fillDefaults().applyTo(removeProperty);
\r
233 liftProperties = tk.createButton(buttons, "Lift Properties", SWT.PUSH);
\r
234 GridDataFactory.fillDefaults().applyTo(liftProperties);
\r
236 hasTypes = !getTypes().isEmpty();
\r
239 setTypes = tk.createButton(buttons, "Assign Types", SWT.PUSH);
\r
240 GridDataFactory.fillDefaults().applyTo(setTypes);
\r
245 table.addSelectionListener(new SelectionAdapter() {
\r
247 public void widgetSelected(SelectionEvent e) {
\r
248 TableItem[] sel = table.getSelection();
\r
249 for (TableItem item : sel) {
\r
250 ComponentTypeViewerPropertyInfo pi = (ComponentTypeViewerPropertyInfo) item.getData();
\r
251 if (pi.immutable) {
\r
252 removeProperty.setEnabled(false);
\r
256 removeProperty.setEnabled(true);
\r
260 newProperty.addSelectionListener(new SelectionAdapter() {
\r
262 public void widgetSelected(SelectionEvent e) {
\r
263 if(editor.getEditor() != null)
\r
264 editor.getEditor().dispose();
\r
265 Simantics.getSession().async(new WriteRequest() {
\r
267 public void perform(WriteGraph graph)
\r
268 throws DatabaseException {
\r
269 ComponentTypeCommands.createPropertyWithDefaults(graph, data.componentType);
\r
275 removeProperty.addSelectionListener(new SelectionAdapter() {
\r
277 public void widgetSelected(SelectionEvent e) {
\r
278 if(editor.getEditor() != null)
\r
279 editor.getEditor().dispose();
\r
280 final List<Resource> propertiesToBeRemoved =
\r
282 for(TableItem item : table.getSelection()) {
\r
283 ComponentTypeViewerPropertyInfo info = (ComponentTypeViewerPropertyInfo) item.getData();
\r
284 if (!info.immutable)
\r
285 propertiesToBeRemoved.add(info.resource);
\r
287 System.out.println("remove " + propertiesToBeRemoved.size() + " resources");
\r
288 if(!propertiesToBeRemoved.isEmpty())
\r
289 Simantics.getSession().async(new WriteRequest() {
\r
291 public void perform(WriteGraph graph)
\r
292 throws DatabaseException {
\r
293 graph.markUndoPoint();
\r
294 for(Resource property : propertiesToBeRemoved)
\r
295 ComponentTypeCommands.removeProperty(graph, data.componentType, property);
\r
301 liftProperties.addSelectionListener(new SelectionAdapter() {
\r
303 public void widgetSelected(SelectionEvent e) {
\r
305 if(editor.getEditor() != null)
\r
306 editor.getEditor().dispose();
\r
310 Map<LiftedProperty, Pair<String, ImageDescriptor>> map = Simantics.sync(new UniqueRead<Map<LiftedProperty,Pair<String,ImageDescriptor>>>() {
\r
313 public Map<LiftedProperty, Pair<String, ImageDescriptor>> perform(ReadGraph graph) throws DatabaseException {
\r
315 Map<LiftedProperty, Pair<String,ImageDescriptor>> map = new HashMap<>();
\r
317 Layer0 L0 = Layer0.getInstance(graph);
\r
318 StructuralResource2 STR = StructuralResource2.getInstance(graph);
\r
319 SelectionViewResources SEL = SelectionViewResources.getInstance(graph);
\r
321 Resource composite = graph.getPossibleObject(data.componentType, STR.IsDefinedBy);
\r
322 if(composite == null) return map;
\r
325 Set<String> existing = new HashSet<>();
\r
326 for(Resource predicate : graph.getObjects(data.componentType, L0.DomainOf)) {
\r
327 if(graph.isSubrelationOf(predicate, L0.HasProperty)) {
\r
328 existing.add(NameUtils.getSafeName(graph, predicate));
\r
332 for(Resource component : graph.syncRequest(new ObjectsWithType(composite, L0.ConsistsOf, STR.Component))) {
\r
334 Resource type = graph.getPossibleType(component, STR.Component);
\r
335 if(type == null) continue;
\r
337 String componentName = NameUtils.getSafeName(graph, component);
\r
339 for(Resource predicate : graph.getPredicates(component)) {
\r
340 if(graph.isSubrelationOf(predicate, L0.HasProperty)) {
\r
342 // Do not list properties shown under other properties
\r
343 if(graph.hasStatement(predicate, SEL.IsShownUnder)) continue;
\r
345 // Do not list properties that are not visible in selection view
\r
346 if(!graph.hasStatement(predicate, SEL.HasStandardPropertyInfo)) continue;
\r
348 // Some properties are explicitly marked as non-liftable
\r
349 Boolean canBeLifted = graph.getPossibleRelatedValue(predicate, SEL.canBeLifted, Bindings.BOOLEAN);
\r
350 if(canBeLifted != null && !canBeLifted) continue;
\r
352 String predicateName = NameUtils.getSafeName(graph, predicate);
\r
353 if(existing.contains(predicateName)) continue;
\r
355 String name = componentName + " " + predicateName;
\r
356 map.put(new LiftedProperty(component, type, predicate), new Pair<String, ImageDescriptor>(name, null));
\r
369 Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
\r
370 LiftPropertiesDialog dialog = new LiftPropertiesDialog(shell, map, "Select properties to lift") {
\r
372 protected IDialogSettings getBaseDialogSettings() {
\r
373 return Activator.getDefault().getDialogSettings();
\r
376 if (dialog.open() == Window.OK) {
\r
377 final Collection<LiftedProperty> _result = dialog.getResultT();
\r
378 final boolean mapProperties = dialog.getMapProperties();
\r
379 if (!_result.isEmpty()) {
\r
380 Simantics.getSession().async(new WriteRequest() {
\r
381 public Resource findAssertion(ReadGraph graph, Resource sourceType, Resource predicate) throws DatabaseException {
\r
382 Collection<Resource> ass = graph.getAssertedObjects(sourceType, predicate);
\r
383 if(ass.size() == 1) return ass.iterator().next();
\r
387 public void processSubs(ReadGraph graph, Resource predicate, Resource component, Resource componentType, List<LiftedProperty> result, List<Resource> properties, List<Resource> assertions) throws DatabaseException {
\r
388 SelectionViewResources SEL = SelectionViewResources.getInstance(graph);
\r
389 for(Resource sub : graph.getObjects(predicate, SEL.UnderOf)) {
\r
390 Resource ass = findAssertion(graph, componentType, sub);
\r
391 if(ass == null) continue;
\r
392 result.add(new LiftedProperty(component, componentType, sub));
\r
393 properties.add(sub);
\r
394 assertions.add(ass);
\r
395 processSubs(graph, sub, component, componentType, result, properties, assertions);
\r
400 public void perform(WriteGraph graph) throws DatabaseException {
\r
402 Layer0 L0 = Layer0.getInstance(graph);
\r
403 graph.markUndoPoint();
\r
404 List<Resource> properties = new ArrayList<>();
\r
405 List<Resource> assertions = new ArrayList<>();
\r
407 List<LiftedProperty> result = new ArrayList<>();
\r
408 for(LiftedProperty p : _result) {
\r
409 Resource ass = findAssertion(graph, p.getComponentType(), p.getPredicate());
\r
410 if(ass == null) continue;
\r
412 properties.add(p.getPredicate());
\r
413 assertions.add(ass);
\r
414 processSubs(graph, p.getPredicate(), p.getComponent(), p.getComponentType(), result, properties, assertions);
\r
417 CopyHandler2 ch = Layer0Utils.getPossibleCopyHandler(graph, properties);
\r
418 Collection<Resource> copies = Layer0Utils.copyTo(graph, data.componentType, null, ch, null);
\r
420 for(Resource copy : copies) {
\r
421 Resource ass = assertions.get(index);
\r
422 LiftedProperty p = result.get(index);
\r
423 Collection<Resource> copyAss = Layer0Utils.copyTo(graph, null, ass);
\r
424 if(copyAss.size() == 1) {
\r
425 graph.claim(data.componentType, L0.DomainOf, copy);
\r
426 Layer0Utils.assert_(graph, data.componentType, copy, copyAss.iterator().next());
\r
427 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
428 graph.addMetadata(cm.add("Lifted property " + NameUtils.getSafeName(graph, copy) + " into "+ NameUtils.getSafeName(graph, data.componentType)));
\r
430 if(mapProperties) {
\r
431 Variable v = Variables.getVariable(graph, p.getComponent());
\r
432 Variable property = v.getProperty(graph, p.getPredicate());
\r
433 Variable displayValue = property.getProperty(graph, Variables.DISPLAY_VALUE);
\r
434 displayValue.setValue(graph, "=" + NameUtils.getSafeName(graph, p.getPredicate()), Bindings.STRING);
\r
444 } catch (DatabaseException e1) {
\r
446 Logger.defaultLogError(e1);
\r
456 setTypes.addSelectionListener(new SelectionAdapter() {
\r
458 public void widgetSelected(SelectionEvent e) {
\r
459 if(editor.getEditor() != null)
\r
460 editor.getEditor().dispose();
\r
461 final List<Resource> propertiesToSet =
\r
463 for(TableItem item : table.getSelection())
\r
464 propertiesToSet.add(((ComponentTypeViewerPropertyInfo)item.getData()).resource);
\r
466 if(propertiesToSet.size() != 1) return;
\r
468 Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
\r
469 SetTypesDialog page = new SetTypesDialog(shell, getTypes(), "Select user types for property");
\r
470 if (page.open() == Window.OK) {
\r
471 final Object[] result = page.getResult();
\r
472 if (result != null && result.length > 0) {
\r
473 Simantics.getSession().async(new WriteRequest() {
\r
475 public void perform(WriteGraph graph)
\r
476 throws DatabaseException {
\r
477 for(Object type : result) {
\r
478 Layer0 L0 = Layer0.getInstance(graph);
\r
479 graph.claim(propertiesToSet.get(0), L0.InstanceOf, null, (Resource)type);
\r
491 table.addDisposeListener(new DisposeListener() {
\r
493 public void widgetDisposed(DisposeEvent e) {
\r
499 public void update(ComponentTypePropertiesResult result) {
\r
500 if (table.isDisposed())
\r
503 // Save old selection
\r
504 Set<ComponentTypeViewerPropertyInfo> selected = new HashSet<>();
\r
505 List<TableItem> selectedItems = new ArrayList<>(selected.size());
\r
506 for (int i : table.getSelectionIndices()) {
\r
507 TableItem item = table.getItem(i);
\r
508 selected.add((ComponentTypeViewerPropertyInfo) item.getData());
\r
511 int topIndex = table.getTopIndex();
\r
515 if(editor.getEditor() != null)
\r
516 editor.getEditor().dispose();
\r
518 for(ComponentTypeViewerPropertyInfo info : result.getProperties()) {
\r
519 boolean immutable = result.isImmutable() || info.immutable;
\r
520 Color fg = immutable ? table.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY) : null;
\r
521 if(info.sectionSpecificData != null)
\r
524 TableItem item = new TableItem(table, SWT.NONE);
\r
526 item.setText(0, info.name);
\r
527 item.setText(1, info.type);
\r
528 item.setText(2, info.defaultValue);
\r
529 item.setText(3, unitStr(info));
\r
530 item.setText(4, rangeStr(info));
\r
531 item.setText(5, info.label);
\r
532 item.setText(6, info.description);
\r
534 for (int columnIndex : IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION)
\r
535 item.setForeground(columnIndex, fg);
\r
537 item.setData(info);
\r
539 if (selected.contains(info))
\r
540 selectedItems.add(item);
\r
543 // Restore old selection
\r
544 table.setTopIndex(topIndex);
\r
545 table.setSelection(selectedItems.toArray(new TableItem[selectedItems.size()]));
\r
549 private Map<Resource, Pair<String, ImageDescriptor>> getTypes() {
\r
551 return Simantics.getSession().syncRequest(new UniqueRead<Map<Resource, Pair<String, ImageDescriptor>>>() {
\r
553 public Map<Resource, Pair<String, ImageDescriptor>> perform(ReadGraph graph)
\r
554 throws DatabaseException {
\r
555 StructuralResource2 STR = StructuralResource2.getInstance(graph);
\r
556 Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(data.componentType));
\r
557 Instances query = graph.adapt(STR.UserDefinedProperty, Instances.class);
\r
558 Collection<Resource> types = query.find(graph, indexRoot);
\r
559 Map<Resource, Pair<String, ImageDescriptor>> result = new HashMap<>();
\r
560 for(Resource type : types) {
\r
561 String name = NameUtils.getSafeLabel(graph, type);
\r
562 result.put(type, new Pair<String, ImageDescriptor>(name, null));
\r
567 } catch (DatabaseException e) {
\r
568 Logger.defaultLogError(e);
\r
569 return Collections.emptyMap();
\r
573 private String unitStr(ComponentTypeViewerPropertyInfo info) {
\r
574 String unit = info.numberType == null ? null : info.numberType.getUnit();
\r
577 return unit != null ? unit : "";
\r
580 private String rangeStr(ComponentTypeViewerPropertyInfo info) {
\r
581 String range = info.numberType == null ? null : info.numberType.getRangeStr();
\r
582 return range != null ? range : "";
\r
586 public void setReadOnly(boolean readOnly) {
\r
587 boolean e = !readOnly;
\r
588 newProperty.setEnabled(e);
\r
589 removeProperty.setEnabled(e);
\r
590 liftProperties.setEnabled(e);
\r
594 public Section getSection() {
\r
599 public double getPriority() {
\r
604 public Object getSectionSpecificData(ReadGraph graph,
\r
605 ComponentTypeViewerPropertyInfo info) throws DatabaseException {
\r
610 public double getDataPriority() {
\r