1 package org.simantics.modeling.ui.componentTypeEditor;
\r
3 import java.util.ArrayList;
\r
4 import java.util.Collections;
\r
5 import java.util.List;
\r
6 import java.util.regex.Matcher;
\r
7 import java.util.regex.Pattern;
\r
9 import org.eclipse.jface.dialogs.IMessageProvider;
\r
10 import org.eclipse.jface.layout.GridDataFactory;
\r
11 import org.eclipse.jface.layout.GridLayoutFactory;
\r
12 import org.eclipse.swt.SWT;
\r
13 import org.eclipse.swt.custom.StyledText;
\r
14 import org.eclipse.swt.custom.TableEditor;
\r
15 import org.eclipse.swt.events.SelectionAdapter;
\r
16 import org.eclipse.swt.events.SelectionEvent;
\r
17 import org.eclipse.swt.graphics.Point;
\r
18 import org.eclipse.swt.graphics.Rectangle;
\r
19 import org.eclipse.swt.widgets.Button;
\r
20 import org.eclipse.swt.widgets.Combo;
\r
21 import org.eclipse.swt.widgets.Composite;
\r
22 import org.eclipse.swt.widgets.Display;
\r
23 import org.eclipse.swt.widgets.Event;
\r
24 import org.eclipse.swt.widgets.Label;
\r
25 import org.eclipse.swt.widgets.Shell;
\r
26 import org.eclipse.swt.widgets.Table;
\r
27 import org.eclipse.swt.widgets.TableItem;
\r
28 import org.eclipse.swt.widgets.Text;
\r
29 import org.eclipse.ui.forms.widgets.Form;
\r
30 import org.eclipse.ui.forms.widgets.FormToolkit;
\r
31 import org.simantics.Simantics;
\r
32 import org.simantics.databoard.type.NumberType;
\r
33 import org.simantics.databoard.units.internal.library.UnitLibrary;
\r
34 import org.simantics.databoard.util.Limit;
\r
35 import org.simantics.databoard.util.Range;
\r
36 import org.simantics.databoard.util.RangeException;
\r
37 import org.simantics.db.RequestProcessor;
\r
38 import org.simantics.db.Resource;
\r
39 import org.simantics.db.WriteGraph;
\r
40 import org.simantics.db.common.NamedResource;
\r
41 import org.simantics.db.common.request.WriteRequest;
\r
42 import org.simantics.db.exception.DatabaseException;
\r
43 import org.simantics.layer0.Layer0;
\r
44 import org.simantics.modeling.userComponent.ComponentTypeCommands;
\r
45 import org.simantics.scl.runtime.function.Function4;
\r
46 import org.simantics.utils.ui.ErrorLogger;
\r
48 public class ComponentTypeViewerData {
\r
50 * Used to validate property names.
\r
52 public static final Pattern PROPERTY_NAME_PATTERN =
\r
53 Pattern.compile("([a-z]|_[0-9a-zA-Z_])[0-9a-zA-Z_]*");
\r
55 public static final String[] PROPERTY_TYPE_SUGGESTIONS = new String[] {
\r
76 public Resource componentType;
\r
77 public FormToolkit tk;
\r
79 public UnitLibrary unitLibrary = UnitLibrary.createDefault();
\r
80 public boolean readOnly;
\r
81 public NamedResource[] connectionPoints;
\r
82 public ComponentTypeViewerPropertyInfo[] properties;
\r
84 public ComponentTypeViewerData(FormToolkit tk, Resource componentType, Form form) {
\r
86 this.componentType = componentType;
\r
90 public void editName(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column,
\r
91 Pattern namePattern) {
\r
92 int extraStyle = propertyInfo.immutable ? SWT.READ_ONLY : 0;
\r
93 final Text text = new Text(table, SWT.NONE | extraStyle);
\r
94 org.eclipse.swt.widgets.Listener listener =
\r
95 new org.eclipse.swt.widgets.Listener() {
\r
97 public void handleEvent(Event e) {
\r
98 if (e.type == SWT.Dispose) {
\r
99 form.setMessage(null);
\r
103 if (e.type == SWT.Modify) {
\r
104 // validate current name
\r
105 String error = validatePropertyName(propertyInfo, text.getText(), namePattern);
\r
106 if (error != null) {
\r
107 text.setBackground(text.getDisplay().getSystemColor(SWT.COLOR_RED));
\r
108 form.setMessage(error, IMessageProvider.ERROR);
\r
110 text.setBackground(null);
\r
111 form.setMessage(null);
\r
116 if (e.type == SWT.Traverse) {
\r
117 if (e.detail == SWT.TRAVERSE_ESCAPE) {
\r
122 if (e.detail == SWT.TRAVERSE_ARROW_NEXT || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS || e.detail == SWT.TRAVERSE_MNEMONIC)
\r
126 final String newValue = text.getText();
\r
129 String error = validatePropertyName(propertyInfo, newValue, namePattern);
\r
133 if (propertyInfo.immutable)
\r
136 Simantics.getSession().async(new WriteRequest() {
\r
138 public void perform(WriteGraph graph)
\r
139 throws DatabaseException {
\r
140 graph.markUndoPoint();
\r
141 Layer0 L0 = Layer0.getInstance(graph);
\r
142 String prevName = graph.getPossibleRelatedValue2(propertyInfo.resource, L0.HasName);
\r
143 String oldCamelCasedLabel = prevName != null ? ComponentTypeCommands.camelCaseNameToLabel(prevName) : "";
\r
144 String oldLabel = graph.getPossibleRelatedValue(propertyInfo.resource, L0.HasLabel);
\r
145 boolean setLabel = oldLabel == null
\r
146 || oldLabel.isEmpty()
\r
147 || oldCamelCasedLabel.isEmpty()
\r
148 || oldCamelCasedLabel.equals(oldLabel);
\r
150 ComponentTypeCommands.rename(graph, propertyInfo.resource, newValue);
\r
152 ComponentTypeCommands.setLabel(graph, propertyInfo.resource, ComponentTypeCommands.camelCaseNameToLabel(newValue));
\r
157 text.addListener(SWT.Modify, listener);
\r
158 text.addListener(SWT.Deactivate, listener);
\r
159 text.addListener(SWT.Traverse, listener);
\r
160 text.addListener(SWT.Dispose, listener);
\r
162 text.setText(selectedItem.getText(column));
\r
166 editor.setEditor(text, selectedItem, column);
\r
169 public void editType(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column, final boolean convertDefaultValue) {
\r
170 int extraStyle = propertyInfo.immutable ? SWT.READ_ONLY : 0;
\r
171 final Combo combo = new Combo(table, SWT.NONE | extraStyle);
\r
172 combo.setText(selectedItem.getText(column));
\r
173 for(String suggestion : PROPERTY_TYPE_SUGGESTIONS)
\r
174 combo.add(suggestion);
\r
175 org.eclipse.swt.widgets.Listener listener =
\r
176 new org.eclipse.swt.widgets.Listener() {
\r
178 public void handleEvent(Event e) {
\r
179 if(e.type == SWT.Traverse) {
\r
180 if (e.detail == SWT.TRAVERSE_ESCAPE) {
\r
185 if (e.detail == SWT.TRAVERSE_ARROW_NEXT
\r
186 || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS
\r
187 || e.detail == SWT.TRAVERSE_MNEMONIC)
\r
190 final String newValue = combo.getText();
\r
191 if (e.type == SWT.Traverse) {
\r
196 if (propertyInfo.immutable)
\r
199 Simantics.getSession().async(new WriteRequest() {
\r
201 public void perform(WriteGraph graph)
\r
202 throws DatabaseException {
\r
203 graph.markUndoPoint();
\r
204 ComponentTypeCommands.editType(graph, componentType, propertyInfo.resource, convertDefaultValue, newValue);
\r
210 editor.setEditor(combo, selectedItem, column);
\r
211 combo.addListener(SWT.FocusOut, listener);
\r
212 combo.addListener(SWT.Traverse, listener);
\r
215 protected void editUnit(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column) {
\r
216 // Disallow unit editing for non-numeric configuration properties
\r
217 if (propertyInfo.numberType == null && propertyInfo.sectionSpecificData == null)
\r
220 int extraStyle = propertyInfo.immutable ? SWT.READ_ONLY : 0;
\r
221 final Combo combo = new Combo(table, SWT.NONE | extraStyle);
\r
222 String initialValue = selectedItem.getText(column);
\r
223 List<String> units = new ArrayList<>( unitLibrary.getUnits() );
\r
224 Collections.sort(units, String.CASE_INSENSITIVE_ORDER);
\r
227 for (String unit : units) {
\r
229 if (unit.equals(initialValue))
\r
232 if (selected == -1)
\r
233 combo.setText(initialValue);
\r
235 org.eclipse.swt.widgets.Listener listener =
\r
236 new org.eclipse.swt.widgets.Listener() {
\r
238 public void handleEvent(Event e) {
\r
239 if(e.type == SWT.Traverse) {
\r
240 if (e.detail == SWT.TRAVERSE_ESCAPE) {
\r
245 if (e.detail == SWT.TRAVERSE_ARROW_NEXT
\r
246 || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS
\r
247 || e.detail == SWT.TRAVERSE_MNEMONIC)
\r
250 final String newValue = combo.getText();
\r
251 if(e.type == SWT.Traverse) {
\r
256 if (propertyInfo.immutable)
\r
259 Simantics.getSession().async(new WriteRequest() {
\r
261 public void perform(WriteGraph graph)
\r
262 throws DatabaseException {
\r
263 graph.markUndoPoint();
\r
264 ComponentTypeCommands.setUnit(graph, componentType, propertyInfo.resource, newValue);
\r
270 editor.setEditor(combo, selectedItem, column);
\r
271 combo.addListener(SWT.Deactivate, listener);
\r
272 combo.addListener(SWT.Traverse, listener);
\r
275 public void editValue(Table table, TableEditor editor,
\r
276 final ComponentTypeViewerPropertyInfo propertyInfo,
\r
277 TableItem selectedItem, int column,
\r
278 final StringWriter writer,
\r
279 final Function4<RequestProcessor, Resource, Resource, String, String> validator)
\r
281 int extraStyle = writer == null ? SWT.READ_ONLY : 0;
\r
282 final Text text = new Text(table, SWT.NONE | extraStyle);
\r
283 text.setText(selectedItem.getText(column));
\r
284 org.eclipse.swt.widgets.Listener listener =
\r
285 new org.eclipse.swt.widgets.Listener() {
\r
287 public void handleEvent(Event e) {
\r
288 if(e.type == SWT.Traverse) {
\r
289 if (e.detail == SWT.TRAVERSE_ESCAPE) {
\r
294 if (e.detail == SWT.TRAVERSE_ARROW_NEXT
\r
295 || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS
\r
296 || e.detail == SWT.TRAVERSE_MNEMONIC)
\r
299 final String newValue = text.getText();
\r
300 if(e.type == SWT.Traverse) {
\r
305 if (writer != null) {
\r
306 Simantics.getSession().async(new WriteRequest() {
\r
308 public void perform(WriteGraph graph) throws DatabaseException {
\r
309 writer.perform(graph, newValue);
\r
317 editor.setEditor(text, selectedItem, column);
\r
318 text.addListener(SWT.FocusOut, listener);
\r
319 text.addListener(SWT.Traverse, listener);
\r
321 if (validator != null) {
\r
322 org.eclipse.swt.widgets.Listener validationListener = new org.eclipse.swt.widgets.Listener() {
\r
324 public void handleEvent(Event e) {
\r
325 final String newValue = text.getText();
\r
326 String error = validator.apply(Simantics.getSession(), componentType, propertyInfo.resource, newValue);
\r
327 if (error != null) {
\r
328 text.setBackground(text.getDisplay().getSystemColor(SWT.COLOR_RED));
\r
329 text.setToolTipText(error);
\r
332 text.setBackground(null);
\r
333 text.setToolTipText(null);
\r
337 text.addListener(SWT.Modify, validationListener);
\r
341 private Range parseRange(NumberType numberType, String minStr, String maxStr, boolean lowInclusive, boolean highInclusive) throws RangeException {
\r
343 String rangeStr = (lowInclusive ? "[" : "(") + minStr + ".." + maxStr + (highInclusive ? "]" : ")");
\r
344 return Range.valueOf(rangeStr);
\r
345 } catch (IllegalArgumentException e) {
\r
346 // Limits are invalid
\r
347 throw new RangeException(e.getMessage(), e);
\r
351 private static Combo createRangeInclusionCombo(Composite parent, boolean inclusive) {
\r
352 Combo rng = new Combo(parent, SWT.READ_ONLY);
\r
353 rng.add("Inclusive");
\r
354 rng.add("Exclusive");
\r
355 rng.select(inclusive ? 0 : 1);
\r
359 protected void editRange(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, Rectangle selectedItemBounds, int column) {
\r
360 // Disallow range editing when the property is not numeric
\r
361 if (propertyInfo.numberType == null)
\r
364 int extraTextStyle = propertyInfo.immutable ? SWT.READ_ONLY : 0;
\r
366 // Parse initial range value
\r
367 Range range = null;
\r
368 String rangeStr = selectedItem.getText(column);
\r
370 range = Range.valueOf(rangeStr);
\r
371 } catch (RangeException ex) {
\r
372 range = new Range(Limit.nolimit(), Limit.nolimit());
\r
375 final Shell shell = new Shell(table.getShell(), SWT.ON_TOP);
\r
376 GridLayoutFactory.fillDefaults().applyTo(shell);
\r
378 Composite composite = new Composite(shell, SWT.NONE);
\r
379 GridDataFactory.fillDefaults().grab(true, true).applyTo(composite);
\r
380 GridLayoutFactory.swtDefaults().numColumns(3).applyTo(composite);
\r
382 Label low = new Label(composite, SWT.NONE);
\r
383 low.setText("Minimum Value:");
\r
384 final Text lowText = new Text(composite, SWT.BORDER | extraTextStyle);
\r
385 GridDataFactory.fillDefaults().grab(true, false).hint(100, SWT.DEFAULT).applyTo(lowText);
\r
386 final Combo lowSelector = createRangeInclusionCombo(composite, !range.getLower().isExclusive());
\r
387 Label high = new Label(composite, SWT.NONE);
\r
388 high.setText("Maximum Value:");
\r
389 final Text highText = new Text(composite, SWT.BORDER | extraTextStyle);
\r
390 GridDataFactory.fillDefaults().grab(true, false).hint(100, SWT.DEFAULT).applyTo(highText);
\r
391 final Combo highSelector = createRangeInclusionCombo(composite, !range.getUpper().isExclusive());
\r
393 Composite buttonComposite = new Composite(shell, SWT.NONE);
\r
394 GridDataFactory.fillDefaults().grab(true, false).align(SWT.TRAIL, SWT.FILL).applyTo(buttonComposite);
\r
395 GridLayoutFactory.swtDefaults().numColumns(2).equalWidth(true).applyTo(buttonComposite);
\r
397 Button ok = new Button(buttonComposite, SWT.NONE);
\r
399 GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).applyTo(ok);
\r
400 Button cancel = new Button(buttonComposite, SWT.NONE);
\r
401 cancel.setText("&Cancel");
\r
402 GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).applyTo(ok);
\r
404 if (range.getLower().getValue() != null)
\r
405 lowText.setText(range.getLower().getValue().toString());
\r
406 if (range.getUpper().getValue() != null)
\r
407 highText.setText(range.getUpper().getValue().toString());
\r
409 shell.addListener(SWT.Deactivate, new org.eclipse.swt.widgets.Listener() {
\r
411 public void handleEvent(Event event) {
\r
416 ok.addSelectionListener(new SelectionAdapter() {
\r
417 public void widgetSelected(SelectionEvent e) {
\r
419 final Range newRange = parseRange(propertyInfo.numberType,
\r
420 lowText.getText().trim(),
\r
421 highText.getText().trim(),
\r
422 lowSelector.getSelectionIndex() == 0 ? true : false,
\r
423 highSelector.getSelectionIndex() == 0 ? true : false);
\r
427 if (propertyInfo.immutable)
\r
430 Simantics.getSession().async(new WriteRequest() {
\r
432 public void perform(WriteGraph graph)
\r
433 throws DatabaseException {
\r
434 graph.markUndoPoint();
\r
435 ComponentTypeCommands.setRange(graph, componentType, propertyInfo.resource, newRange == null ? null : newRange.toString());
\r
438 } catch (RangeException ex) {
\r
439 ErrorLogger.defaultLogError(ex);
\r
443 cancel.addSelectionListener(new SelectionAdapter() {
\r
444 public void widgetSelected(SelectionEvent e) {
\r
450 Point size = shell.getSize();
\r
452 Display display = table.getDisplay();
\r
453 Rectangle clientArea = display.getClientArea();
\r
454 Point bt = table.toDisplay(selectedItemBounds.x, selectedItemBounds.y);
\r
455 Rectangle b = selectedItemBounds;
\r
460 if ((b.x + b.width) > clientArea.width)
\r
461 b.x -= b.x + b.width - clientArea.width;
\r
462 if (b.height > clientArea.height)
\r
463 b.height = clientArea.height;
\r
464 if ((b.y + b.height) > clientArea.height)
\r
465 b.y -= b.y + b.height - clientArea.height;
\r
467 shell.setBounds(selectedItemBounds);
\r
471 protected void editMultilineText(Table table, TableEditor editor,
\r
472 final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem,
\r
473 Rectangle selectedItemBounds, int column, final StringWriter writer)
\r
475 final Shell shell = new Shell(table.getShell(), SWT.ON_TOP);
\r
476 GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(shell);
\r
477 final StyledText text = new StyledText(shell, SWT.MULTI | SWT.WRAP | SWT.V_SCROLL | (propertyInfo.immutable ? SWT.READ_ONLY : 0));
\r
478 GridDataFactory.fillDefaults().grab(true, true).applyTo(text);
\r
479 text.setText(selectedItem.getText(column));
\r
480 org.eclipse.swt.widgets.Listener listener =
\r
481 new org.eclipse.swt.widgets.Listener() {
\r
483 public void handleEvent(Event e) {
\r
484 final String newValue = text.getText();
\r
486 if (e.type == SWT.Traverse) {
\r
487 if (e.detail == SWT.TRAVERSE_ESCAPE) {
\r
492 if (e.detail == SWT.TRAVERSE_ARROW_NEXT
\r
493 || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS
\r
494 || e.detail == SWT.TRAVERSE_MNEMONIC)
\r
496 if ((e.stateMask & SWT.CTRL) == 0)
\r
503 if (propertyInfo.immutable)
\r
506 if (writer != null) {
\r
507 Simantics.getSession().async(new WriteRequest() {
\r
509 public void perform(WriteGraph graph) throws DatabaseException {
\r
510 writer.perform(graph, newValue);
\r
517 String helpText = propertyInfo.immutable ? "ESC to close." : "Ctrl+Enter to apply changes, ESC to cancel.";
\r
518 Label help = tk.createLabel(shell, helpText, SWT.BORDER | SWT.FLAT);
\r
519 GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.CENTER).applyTo(help);
\r
520 help.setForeground( tk.getColors().createColor( "fg", tk.getColors().getSystemColor(SWT.COLOR_LIST_SELECTION) ) );
\r
522 Display display = table.getDisplay();
\r
523 Rectangle clientArea = display.getClientArea();
\r
524 Point bt = table.toDisplay(selectedItemBounds.x, selectedItemBounds.y);
\r
525 Rectangle b = selectedItemBounds;
\r
529 if ((b.x + b.width) > clientArea.width)
\r
530 b.x -= b.x + b.width - clientArea.width;
\r
531 if (b.height > clientArea.height)
\r
532 b.height = clientArea.height;
\r
533 if ((b.y + b.height) > clientArea.height)
\r
534 b.y -= b.y + b.height - clientArea.height;
\r
536 shell.setBounds(selectedItemBounds);
\r
542 text.addListener(SWT.Traverse, listener);
\r
543 shell.addListener(SWT.Deactivate, listener);
\r
546 private String validatePropertyName(ComponentTypeViewerPropertyInfo propertyInfo, String propertyName, Pattern namePattern) {
\r
547 if (propertyName.equals(propertyInfo.name))
\r
549 for (ComponentTypeViewerPropertyInfo info : properties) {
\r
550 if (propertyName.equals(info.name))
\r
551 return "Property name '" + propertyName + "' is already in use.";
\r
553 for (NamedResource cp : connectionPoints) {
\r
554 if (propertyName.equals(cp.getName()))
\r
555 return "Name '" + propertyName + "' is already used for a terminal.";
\r
557 Matcher m = namePattern.matcher(propertyName);
\r
559 return "Property name '" + propertyName + "' contains invalid characters, does not match pattern "
\r
560 + namePattern.pattern() + ".";
\r