1 package org.simantics.modeling.ui.componentTypeEditor;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.HashSet;
12 import org.eclipse.jface.dialogs.IDialogSettings;
13 import org.eclipse.jface.layout.GridDataFactory;
14 import org.eclipse.jface.layout.GridLayoutFactory;
15 import org.eclipse.jface.layout.TableColumnLayout;
16 import org.eclipse.jface.resource.ImageDescriptor;
17 import org.eclipse.jface.viewers.ColumnWeightData;
18 import org.eclipse.jface.window.Window;
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.custom.TableEditor;
21 import org.eclipse.swt.events.DisposeEvent;
22 import org.eclipse.swt.events.DisposeListener;
23 import org.eclipse.swt.events.MouseAdapter;
24 import org.eclipse.swt.events.MouseEvent;
25 import org.eclipse.swt.events.SelectionAdapter;
26 import org.eclipse.swt.events.SelectionEvent;
27 import org.eclipse.swt.graphics.Color;
28 import org.eclipse.swt.graphics.Rectangle;
29 import org.eclipse.swt.layout.FillLayout;
30 import org.eclipse.swt.widgets.Button;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Control;
33 import org.eclipse.swt.widgets.Shell;
34 import org.eclipse.swt.widgets.Table;
35 import org.eclipse.swt.widgets.TableColumn;
36 import org.eclipse.swt.widgets.TableItem;
37 import org.eclipse.ui.PlatformUI;
38 import org.eclipse.ui.forms.widgets.Form;
39 import org.eclipse.ui.forms.widgets.FormToolkit;
40 import org.eclipse.ui.forms.widgets.Section;
41 import org.simantics.Simantics;
42 import org.simantics.databoard.Bindings;
43 import org.simantics.db.ReadGraph;
44 import org.simantics.db.Resource;
45 import org.simantics.db.WriteGraph;
46 import org.simantics.db.common.CommentMetadata;
47 import org.simantics.db.common.request.ObjectsWithType;
48 import org.simantics.db.common.request.PossibleIndexRoot;
49 import org.simantics.db.common.request.UniqueRead;
50 import org.simantics.db.common.request.WriteRequest;
51 import org.simantics.db.common.utils.NameUtils;
52 import org.simantics.db.exception.DatabaseException;
53 import org.simantics.db.layer0.adapter.CopyHandler2;
54 import org.simantics.db.layer0.adapter.Instances;
55 import org.simantics.db.layer0.util.Layer0Utils;
56 import org.simantics.db.layer0.variable.Variable;
57 import org.simantics.db.layer0.variable.Variables;
58 import org.simantics.layer0.Layer0;
59 import org.simantics.modeling.ui.Activator;
60 import org.simantics.modeling.ui.componentTypeEditor.LiftPropertiesDialog.LiftedProperty;
61 import org.simantics.modeling.userComponent.ComponentTypeCommands;
62 import org.simantics.modeling.utils.ComponentTypePropertiesResult;
63 import org.simantics.modeling.utils.ComponentTypeViewerPropertyInfo;
64 import org.simantics.selectionview.SelectionViewResources;
65 import org.simantics.structural.stubs.StructuralResource2;
66 import org.simantics.utils.datastructures.Pair;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
70 public class ConfigurationPropertiesSection implements ComponentTypeViewerSection {
72 private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationPropertiesSection.class);
74 private static final String[] COLUMN_NAMES = {
75 Messages.ConfigurationPropertiesSection_Name,
76 Messages.ConfigurationPropertiesSection_Type,
77 Messages.ConfigurationPropertiesSection_DefaultValue,
78 Messages.ConfigurationPropertiesSection_Unit,
79 Messages.ConfigurationPropertiesSection_Range,
80 Messages.ConfigurationPropertiesSection_Label,
81 Messages.ConfigurationPropertiesSection_Description
83 private static final int[] COLUMN_LENGTHS =
84 new int[] { 120, 100, 100, 50, 100, 100, 100 };
85 private static final int[] COLUMN_WEIGHTS =
86 new int[] { 0, 0, 0, 0, 0, 50, 100 };
89 * Configuration property table column indexes that are to be considered
90 * immutable when the property relation is immutable. Note that relation
91 * immutability does not make the asserted default value immutable.
93 private static final int[] IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION =
95 ComponentTypeViewerData data;
98 TableColumn[] columns;
101 Button removeProperty;
102 Button liftProperties;
104 boolean hasTypes = false;
109 public ConfigurationPropertiesSection(ComponentTypeViewerData data) {
111 FormToolkit tk = data.tk;
112 Form form = data.form;
114 section = tk.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED);
115 section.setLayout(new FillLayout());
116 section.setText(Messages.ConfigurationPropertiesSection_ConfigurationProperties);
118 Composite sectionBody = tk.createComposite(section);
119 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(sectionBody);
120 section.setClient(sectionBody);
122 Composite tableComposite = tk.createComposite(sectionBody);
123 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableComposite);
124 TableColumnLayout tcl = new TableColumnLayout();
125 tableComposite.setLayout(tcl);
127 table = tk.createTable(tableComposite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
128 table.setLinesVisible(true);
129 table.setHeaderVisible(true);
131 columns = new TableColumn[COLUMN_NAMES.length];
132 for(int i=0;i<COLUMN_NAMES.length;++i) {
133 TableColumn column = new TableColumn(table, SWT.NONE);
135 tcl.setColumnData(column, new ColumnWeightData(COLUMN_WEIGHTS[i], COLUMN_LENGTHS[i], true));
136 column.setText(COLUMN_NAMES[i]);
140 editor = new TableEditor(table);
141 editor.grabHorizontal = true;
142 editor.grabVertical = true;
143 editor.horizontalAlignment = SWT.LEFT;
144 table.addMouseListener(new MouseAdapter() {
146 public void mouseDown(MouseEvent e) {
147 // Clean up any previous editor control
148 Control oldEditor = editor.getEditor();
149 if (oldEditor != null) oldEditor.dispose();
155 Rectangle tableBounds = table.getClientArea();
156 int rx = e.x - tableBounds.x;
157 int ry = e.y - tableBounds.y;
160 TableItem selectedItem = null;
161 int selectedColumn = -1;
162 Rectangle selectedItemBounds = null;
163 for(TableItem item : table.getItems()) {
164 for(int column = 0;column < COLUMN_NAMES.length;++column) {
165 Rectangle bounds = item.getBounds(column);
166 if(bounds.contains(rx, ry)) {
167 selectedItemBounds = bounds;
169 selectedColumn = column;
174 if(selectedItem == null) {
179 final int column = selectedColumn;
180 final ComponentTypeViewerPropertyInfo propertyInfo = (ComponentTypeViewerPropertyInfo)selectedItem.getData();
181 final Resource resource = propertyInfo.resource;
184 data.editName(table, editor, propertyInfo, selectedItem, column, ComponentTypeViewerData.PROPERTY_NAME_PATTERN);
188 data.editType(table, editor, propertyInfo, selectedItem, column, selectedItem.getText(4), true);
192 data.editValue(table, editor, propertyInfo, selectedItem, column, data.readOnly ? null : new StringWriter() {
194 public void perform(WriteGraph graph, String newValue) throws DatabaseException {
195 graph.markUndoPoint();
196 ComponentTypeCommands.setDefaultValue(graph, data.componentType, propertyInfo.resource, newValue);
202 data.editUnit(table, editor, propertyInfo, selectedItem, column);
206 data.editRange(table, editor, propertyInfo, selectedItem, selectedItemBounds, column);
210 data.editValue(table, editor, propertyInfo, selectedItem, column, propertyInfo.immutable ? null : new StringWriter() {
212 public void perform(WriteGraph graph, String newValue) throws DatabaseException {
213 graph.markUndoPoint();
214 String value = newValue.isEmpty() ? null : newValue;
215 ComponentTypeCommands.setLabel(graph, resource, value);
221 data.editMultilineText(table, editor, propertyInfo, selectedItem, selectedItemBounds, column, new StringWriter() {
223 public void perform(WriteGraph graph, String newValue) throws DatabaseException {
224 graph.markUndoPoint();
225 String value = newValue.isEmpty() ? null : newValue;
226 ComponentTypeCommands.setDescription(graph, resource, value);
236 Composite buttons = tk.createComposite(sectionBody);
237 GridDataFactory.fillDefaults().applyTo(buttons);
238 GridLayoutFactory.fillDefaults().applyTo(buttons);
240 newProperty = tk.createButton(buttons, Messages.ConfigurationPropertiesSection_NewProperty, SWT.PUSH);
241 GridDataFactory.fillDefaults().applyTo(newProperty);
242 removeProperty = tk.createButton(buttons, Messages.ConfigurationPropertiesSection_RemoveProperty, SWT.PUSH);
243 GridDataFactory.fillDefaults().applyTo(removeProperty);
245 liftProperties = tk.createButton(buttons, Messages.ConfigurationPropertiesSection_LiftProperties, SWT.PUSH);
246 GridDataFactory.fillDefaults().applyTo(liftProperties);
248 hasTypes = !getTypes().isEmpty();
251 setTypes = tk.createButton(buttons, Messages.ConfigurationPropertiesSection_AssignTypes, SWT.PUSH);
252 GridDataFactory.fillDefaults().applyTo(setTypes);
257 table.addSelectionListener(new SelectionAdapter() {
259 public void widgetSelected(SelectionEvent e) {
260 TableItem[] sel = table.getSelection();
261 for (TableItem item : sel) {
262 ComponentTypeViewerPropertyInfo pi = (ComponentTypeViewerPropertyInfo) item.getData();
264 removeProperty.setEnabled(false);
268 removeProperty.setEnabled(true);
272 newProperty.addSelectionListener(new SelectionAdapter() {
274 public void widgetSelected(SelectionEvent e) {
275 if(editor.getEditor() != null)
276 editor.getEditor().dispose();
277 Simantics.getSession().async(new WriteRequest() {
279 public void perform(WriteGraph graph)
280 throws DatabaseException {
281 ComponentTypeCommands.createPropertyWithDefaults(graph, data.componentType);
287 removeProperty.addSelectionListener(new SelectionAdapter() {
289 public void widgetSelected(SelectionEvent e) {
290 if(editor.getEditor() != null)
291 editor.getEditor().dispose();
292 final List<Resource> propertiesToBeRemoved =
294 for(TableItem item : table.getSelection()) {
295 ComponentTypeViewerPropertyInfo info = (ComponentTypeViewerPropertyInfo) item.getData();
297 propertiesToBeRemoved.add(info.resource);
299 //System.out.println("remove " + propertiesToBeRemoved.size() + " resources"); //$NON-NLS-1$ //$NON-NLS-2$
300 if(!propertiesToBeRemoved.isEmpty())
301 Simantics.getSession().async(new WriteRequest() {
303 public void perform(WriteGraph graph)
304 throws DatabaseException {
305 graph.markUndoPoint();
306 for(Resource property : propertiesToBeRemoved)
307 ComponentTypeCommands.removeProperty(graph, data.componentType, property);
313 liftProperties.addSelectionListener(new SelectionAdapter() {
315 public void widgetSelected(SelectionEvent e) {
317 if(editor.getEditor() != null)
318 editor.getEditor().dispose();
322 Map<LiftedProperty, Pair<String, ImageDescriptor>> map = Simantics.sync(new UniqueRead<Map<LiftedProperty,Pair<String,ImageDescriptor>>>() {
325 public Map<LiftedProperty, Pair<String, ImageDescriptor>> perform(ReadGraph graph) throws DatabaseException {
327 Map<LiftedProperty, Pair<String,ImageDescriptor>> map = new HashMap<>();
329 Layer0 L0 = Layer0.getInstance(graph);
330 StructuralResource2 STR = StructuralResource2.getInstance(graph);
331 SelectionViewResources SEL = SelectionViewResources.getInstance(graph);
333 Resource composite = graph.getPossibleObject(data.componentType, STR.IsDefinedBy);
334 if(composite == null) return map;
337 Set<String> existing = new HashSet<>();
338 for(Resource predicate : graph.getObjects(data.componentType, L0.DomainOf)) {
339 if(graph.isSubrelationOf(predicate, L0.HasProperty)) {
340 existing.add(NameUtils.getSafeName(graph, predicate));
344 for(Resource component : graph.syncRequest(new ObjectsWithType(composite, L0.ConsistsOf, STR.Component))) {
346 Resource type = graph.getPossibleType(component, STR.Component);
347 if(type == null) continue;
349 String componentName = NameUtils.getSafeName(graph, component);
351 for(Resource predicate : graph.getPredicates(component)) {
352 if(graph.isSubrelationOf(predicate, L0.HasProperty)) {
354 // Do not list properties shown under other properties
355 if(graph.hasStatement(predicate, SEL.IsShownUnder)) continue;
357 // Do not list properties that are not visible in selection view
358 if(!graph.hasStatement(predicate, SEL.HasStandardPropertyInfo)) continue;
360 // Some properties are explicitly marked as non-liftable
361 Boolean canBeLifted = graph.getPossibleRelatedValue(predicate, SEL.canBeLifted, Bindings.BOOLEAN);
362 if(canBeLifted != null && !canBeLifted) continue;
364 String predicateName = NameUtils.getSafeName(graph, predicate);
365 if(existing.contains(predicateName)) continue;
367 String name = componentName + " " + predicateName; //$NON-NLS-1$
368 map.put(new LiftedProperty(component, type, predicate), new Pair<String, ImageDescriptor>(name, null));
381 Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
382 LiftPropertiesDialog dialog = new LiftPropertiesDialog(shell, map, Messages.ConfigurationPropertiesSection_SelectPropertiesToLift) {
384 protected IDialogSettings getBaseDialogSettings() {
385 return Activator.getDefault().getDialogSettings();
388 if (dialog.open() == Window.OK) {
389 final Collection<LiftedProperty> _result = dialog.getResultT();
390 final boolean mapProperties = dialog.getMapProperties();
391 if (!_result.isEmpty()) {
392 Simantics.getSession().async(new WriteRequest() {
393 public Resource findAssertion(ReadGraph graph, Resource sourceType, Resource predicate) throws DatabaseException {
394 Collection<Resource> ass = graph.getAssertedObjects(sourceType, predicate);
395 if(ass.size() == 1) return ass.iterator().next();
399 public void processSubs(ReadGraph graph, Resource predicate, Resource component, Resource componentType, List<LiftedProperty> result, List<Resource> properties, List<Resource> assertions) throws DatabaseException {
400 SelectionViewResources SEL = SelectionViewResources.getInstance(graph);
401 for(Resource sub : graph.getObjects(predicate, SEL.UnderOf)) {
402 Resource ass = findAssertion(graph, componentType, sub);
403 if(ass == null) continue;
404 result.add(new LiftedProperty(component, componentType, sub));
407 processSubs(graph, sub, component, componentType, result, properties, assertions);
412 public void perform(WriteGraph graph) throws DatabaseException {
414 Layer0 L0 = Layer0.getInstance(graph);
415 graph.markUndoPoint();
416 List<Resource> properties = new ArrayList<>();
417 List<Resource> assertions = new ArrayList<>();
419 List<LiftedProperty> result = new ArrayList<>();
420 for(LiftedProperty p : _result) {
421 Resource ass = findAssertion(graph, p.getComponentType(), p.getPredicate());
422 if(ass == null) continue;
424 properties.add(p.getPredicate());
426 processSubs(graph, p.getPredicate(), p.getComponent(), p.getComponentType(), result, properties, assertions);
429 CopyHandler2 ch = Layer0Utils.getPossibleCopyHandler(graph, properties);
430 Collection<Resource> copies = Layer0Utils.copyTo(graph, data.componentType, null, ch, null);
432 for(Resource copy : copies) {
433 Resource ass = assertions.get(index);
434 LiftedProperty p = result.get(index);
435 Collection<Resource> copyAss = Layer0Utils.copyTo(graph, null, ass);
436 if(copyAss.size() == 1) {
437 graph.deny(copy, L0.HasDomain);
438 graph.claim(data.componentType, L0.DomainOf, copy);
439 Layer0Utils.assert_(graph, data.componentType, copy, copyAss.iterator().next());
440 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
441 graph.addMetadata(cm.add("Lifted property " + NameUtils.getSafeName(graph, copy) + " into "+ NameUtils.getSafeName(graph, data.componentType))); //$NON-NLS-1$ //$NON-NLS-2$
444 Variable v = Variables.getVariable(graph, p.getComponent());
445 Variable property = v.getProperty(graph, p.getPredicate());
446 Variable displayValue = property.getProperty(graph, Variables.DISPLAY_VALUE);
447 displayValue.setValue(graph, "=" + NameUtils.getSafeName(graph, p.getPredicate()), Bindings.STRING); //$NON-NLS-1$
457 } catch (DatabaseException e1) {
459 LOGGER.error("Lifting properties failed", e1); //$NON-NLS-1$
469 setTypes.addSelectionListener(new SelectionAdapter() {
471 public void widgetSelected(SelectionEvent e) {
472 if(editor.getEditor() != null)
473 editor.getEditor().dispose();
474 final List<Resource> propertiesToSet =
476 for(TableItem item : table.getSelection())
477 propertiesToSet.add(((ComponentTypeViewerPropertyInfo)item.getData()).resource);
479 if(propertiesToSet.size() != 1) return;
481 Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
482 SetTypesDialog page = new SetTypesDialog(shell, getTypes(), Messages.ConfigurationPropertiesSection_SelectUserTypesForProp);
483 if (page.open() == Window.OK) {
484 final Object[] result = page.getResult();
485 if (result != null && result.length > 0) {
486 Simantics.getSession().async(new WriteRequest() {
488 public void perform(WriteGraph graph)
489 throws DatabaseException {
490 for(Object type : result) {
491 Layer0 L0 = Layer0.getInstance(graph);
492 graph.claim(propertiesToSet.get(0), L0.InstanceOf, null, (Resource)type);
504 table.addDisposeListener(new DisposeListener() {
506 public void widgetDisposed(DisposeEvent e) {
512 public void update(ComponentTypePropertiesResult result) {
513 if (table.isDisposed())
516 // Save old selection
517 Set<ComponentTypeViewerPropertyInfo> selected = new HashSet<>();
518 List<TableItem> selectedItems = new ArrayList<>(selected.size());
519 for (int i : table.getSelectionIndices()) {
520 TableItem item = table.getItem(i);
521 selected.add((ComponentTypeViewerPropertyInfo) item.getData());
524 int topIndex = table.getTopIndex();
528 if(editor.getEditor() != null)
529 editor.getEditor().dispose();
531 for(ComponentTypeViewerPropertyInfo info : result.getProperties()) {
532 boolean immutable = result.isImmutable() || info.immutable;
533 Color fg = immutable ? table.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY) : null;
534 if(info.sectionSpecificData != null)
537 TableItem item = new TableItem(table, SWT.NONE);
539 item.setText(0, info.name);
540 item.setText(1, info.type);
541 item.setText(2, info.defaultValue);
542 item.setText(3, info.unitString());
543 item.setText(4, info.rangeString());
544 item.setText(5, info.label);
545 item.setText(6, info.description);
547 for (int columnIndex : IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION)
548 item.setForeground(columnIndex, fg);
552 if (selected.contains(info))
553 selectedItems.add(item);
556 // Restore old selection
557 table.setTopIndex(topIndex);
558 table.setSelection(selectedItems.toArray(new TableItem[selectedItems.size()]));
562 private Map<Resource, Pair<String, ImageDescriptor>> getTypes() {
564 return Simantics.getSession().syncRequest(new UniqueRead<Map<Resource, Pair<String, ImageDescriptor>>>() {
566 public Map<Resource, Pair<String, ImageDescriptor>> perform(ReadGraph graph)
567 throws DatabaseException {
568 StructuralResource2 STR = StructuralResource2.getInstance(graph);
569 Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(data.componentType));
570 Instances query = graph.adapt(STR.UserDefinedProperty, Instances.class);
571 Collection<Resource> types = query.find(graph, indexRoot);
572 Map<Resource, Pair<String, ImageDescriptor>> result = new HashMap<>();
573 for(Resource type : types) {
574 String name = NameUtils.getSafeLabel(graph, type);
575 result.put(type, new Pair<String, ImageDescriptor>(name, null));
580 } catch (DatabaseException e) {
581 LOGGER.error("Finding UserDefinedProperties failed.", e); //$NON-NLS-1$
582 return Collections.emptyMap();
587 public void setReadOnly(boolean readOnly) {
588 boolean e = !readOnly;
589 newProperty.setEnabled(e);
590 removeProperty.setEnabled(e);
591 liftProperties.setEnabled(e);
595 public Section getSection() {
600 public double getPriority() {
605 public Object getSectionSpecificData(ReadGraph graph,
606 ComponentTypeViewerPropertyInfo info) throws DatabaseException {
611 public double getDataPriority() {