]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/componentTypeEditor/ComponentTypeViewerData.java
Initial version of validating derived properties formulas
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / componentTypeEditor / ComponentTypeViewerData.java
1 package org.simantics.modeling.ui.componentTypeEditor;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.concurrent.ScheduledFuture;
7 import java.util.concurrent.TimeUnit;
8 import java.util.regex.Matcher;
9 import java.util.regex.Pattern;
10
11 import org.eclipse.jface.dialogs.IDialogConstants;
12 import org.eclipse.jface.dialogs.IMessageProvider;
13 import org.eclipse.jface.layout.GridDataFactory;
14 import org.eclipse.jface.layout.GridLayoutFactory;
15 import org.eclipse.osgi.util.NLS;
16 import org.eclipse.swt.SWT;
17 import org.eclipse.swt.custom.StyledText;
18 import org.eclipse.swt.custom.TableEditor;
19 import org.eclipse.swt.events.SelectionAdapter;
20 import org.eclipse.swt.events.SelectionEvent;
21 import org.eclipse.swt.graphics.Point;
22 import org.eclipse.swt.graphics.Rectangle;
23 import org.eclipse.swt.widgets.Button;
24 import org.eclipse.swt.widgets.Combo;
25 import org.eclipse.swt.widgets.Composite;
26 import org.eclipse.swt.widgets.Display;
27 import org.eclipse.swt.widgets.Event;
28 import org.eclipse.swt.widgets.Label;
29 import org.eclipse.swt.widgets.Shell;
30 import org.eclipse.swt.widgets.Table;
31 import org.eclipse.swt.widgets.TableItem;
32 import org.eclipse.swt.widgets.Text;
33 import org.eclipse.ui.forms.widgets.Form;
34 import org.eclipse.ui.forms.widgets.FormToolkit;
35 import org.simantics.Simantics;
36 import org.simantics.databoard.type.NumberType;
37 import org.simantics.databoard.units.internal.library.UnitLibrary;
38 import org.simantics.databoard.util.Limit;
39 import org.simantics.databoard.util.Range;
40 import org.simantics.databoard.util.RangeException;
41 import org.simantics.db.RequestProcessor;
42 import org.simantics.db.Resource;
43 import org.simantics.db.WriteGraph;
44 import org.simantics.db.common.NamedResource;
45 import org.simantics.db.common.request.WriteRequest;
46 import org.simantics.db.exception.DatabaseException;
47 import org.simantics.db.function.DbConsumer;
48 import org.simantics.layer0.Layer0;
49 import org.simantics.modeling.userComponent.ComponentTypeCommands;
50 import org.simantics.scl.runtime.function.Function2;
51 import org.simantics.scl.runtime.function.Function4;
52 import org.simantics.utils.threads.ThreadUtils;
53 import org.simantics.utils.ui.ErrorLogger;
54
55 public class ComponentTypeViewerData {
56     /**
57      * Used to validate property names.
58      */
59     public static final Pattern PROPERTY_NAME_PATTERN =
60             Pattern.compile("([a-z]|_[0-9a-zA-Z_])[0-9a-zA-Z_]*"); //$NON-NLS-1$
61
62     public static final String[] PROPERTY_TYPE_SUGGESTIONS = new String[] {
63         "Double", //$NON-NLS-1$
64         "Integer", //$NON-NLS-1$
65         "Float", //$NON-NLS-1$
66         "String", //$NON-NLS-1$
67         "Boolean", //$NON-NLS-1$
68         "Long", //$NON-NLS-1$
69         "[Double]", //$NON-NLS-1$
70         "[Integer]", //$NON-NLS-1$
71         "[Float]", //$NON-NLS-1$
72         "[String]", //$NON-NLS-1$
73         "[Boolean]", //$NON-NLS-1$
74         "[Long]", //$NON-NLS-1$
75         "Vector Double", //$NON-NLS-1$
76         "Vector Integer", //$NON-NLS-1$
77         "Vector Float", //$NON-NLS-1$
78         "Vector String", //$NON-NLS-1$
79         "Vector Boolean", //$NON-NLS-1$
80         "Vector Long" //$NON-NLS-1$
81     };
82
83     public Resource componentType;
84     public FormToolkit tk;
85     public Form form;
86     public UnitLibrary unitLibrary = UnitLibrary.createDefault();
87     public boolean readOnly;
88     public NamedResource[] connectionPoints;
89     public ComponentTypeViewerPropertyInfo[] properties;
90
91     public ComponentTypeViewerData(FormToolkit tk, Resource componentType, Form form) {
92         this.tk = tk;
93         this.componentType = componentType;
94         this.form = form;
95     }
96
97     public void editName(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column,
98             Pattern namePattern) {
99         editName(table, editor, propertyInfo, selectedItem, column, namePattern, null);
100     }
101
102     public void editName(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column,
103             Pattern namePattern, DbConsumer<WriteGraph> extraWriter) {
104         editName(table, editor, propertyInfo, selectedItem, column,
105                 null,
106                 (pInfo, name) -> validatePropertyName(pInfo, name, namePattern),
107                 extraWriter);
108     }
109
110     public void editName(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column,
111             Function2<ComponentTypeViewerPropertyInfo, String, String> nameFilter, Pattern namePattern, DbConsumer<WriteGraph> extraWriter) {
112         editName(table, editor, propertyInfo, selectedItem, column, nameFilter,
113                 (pInfo, name) -> validatePropertyName(pInfo, name, namePattern),
114                 extraWriter);
115     }
116
117     public void editName(Table table, TableEditor editor, ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column,
118             Function2<ComponentTypeViewerPropertyInfo, String, String> nameValidator)
119     {
120         editName(table, editor, propertyInfo, selectedItem, column, nameValidator, null);
121     }
122
123     public void editName(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column,
124             Function2<ComponentTypeViewerPropertyInfo, String, String> nameValidator, DbConsumer<WriteGraph> extraWriter) {
125         editName(table, editor, propertyInfo, selectedItem, column, null, nameValidator, extraWriter);
126     }
127
128     public void editName(
129             Table table,
130             TableEditor editor,
131             final ComponentTypeViewerPropertyInfo propertyInfo,
132             TableItem selectedItem,
133             int column,
134             Function2<ComponentTypeViewerPropertyInfo, String, String> nameFilter,
135             Function2<ComponentTypeViewerPropertyInfo, String, String> nameValidator,
136             DbConsumer<WriteGraph> extraWriter) {
137         int extraStyle = propertyInfo.immutable ? SWT.READ_ONLY : 0;
138         final Text text = new Text(table, SWT.NONE | extraStyle);
139         org.eclipse.swt.widgets.Listener listener = 
140                 new org.eclipse.swt.widgets.Listener() {
141             @Override
142             public void handleEvent(Event e) {
143                 if (e.type == SWT.Dispose) {
144                     form.setMessage(null);
145                     return;
146                 } else if (e.type == SWT.Verify) {
147                     // Filter input if necessary
148                     e.text = nameFilter != null ? nameFilter.apply(propertyInfo, e.text) : e.text;
149                     return;
150                 } else if (e.type == SWT.Modify) {
151                     // validate current name
152                     String error = nameValidator.apply(propertyInfo, text.getText());
153                     if (error != null) {
154                         text.setBackground(text.getDisplay().getSystemColor(SWT.COLOR_RED));
155                         form.setMessage(error, IMessageProvider.ERROR);
156                     } else {
157                         text.setBackground(null);
158                         form.setMessage(null);
159                     }
160                     return;
161                 } else if (e.type == SWT.Traverse) {
162                     if (e.detail == SWT.TRAVERSE_ESCAPE) {
163                         text.dispose();
164                         e.doit = false;
165                         return;
166                     }
167                     if (e.detail == SWT.TRAVERSE_ARROW_NEXT || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS || e.detail == SWT.TRAVERSE_MNEMONIC)
168                         return;
169                     e.doit = false;
170                 }
171                 final String newValue = text.getText();
172                 text.dispose();
173
174                 String error = nameValidator.apply(propertyInfo, newValue);
175                 if (error != null)
176                     return;
177
178                 if (propertyInfo.immutable)
179                     return;
180
181                 Simantics.getSession().async(new WriteRequest() {
182                     @Override
183                     public void perform(WriteGraph graph)
184                             throws DatabaseException {
185                         graph.markUndoPoint();
186                         Layer0 L0 = Layer0.getInstance(graph);
187                         String prevName = graph.getPossibleRelatedValue2(propertyInfo.resource, L0.HasName);
188                         String oldCamelCasedLabel = prevName != null ? ComponentTypeCommands.camelCaseNameToLabel(prevName) : ""; //$NON-NLS-1$
189                         String oldLabel = graph.getPossibleRelatedValue(propertyInfo.resource, L0.HasLabel);
190                         boolean setLabel = oldLabel == null
191                                 || oldLabel.isEmpty()
192                                 || oldCamelCasedLabel.isEmpty()
193                                 || oldCamelCasedLabel.equals(oldLabel);
194
195                         ComponentTypeCommands.rename(graph, propertyInfo.resource, newValue);
196                         if (setLabel)
197                             ComponentTypeCommands.setLabel(graph, propertyInfo.resource, ComponentTypeCommands.camelCaseNameToLabel(newValue));
198
199                         if (extraWriter != null)
200                             extraWriter.accept(graph);
201                     }
202                 });
203             }
204         };
205         if (nameFilter != null)
206             text.addListener(SWT.Verify, listener);
207         text.addListener(SWT.Modify, listener);
208         text.addListener(SWT.Deactivate, listener);
209         text.addListener(SWT.Traverse, listener);
210         text.addListener(SWT.Dispose, listener);
211
212         text.setText(selectedItem.getText(column));
213         text.selectAll();
214         text.setFocus();
215
216         editor.setEditor(text, selectedItem, column);
217     }
218
219     public void editType(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column, final boolean convertDefaultValue) {
220         int extraStyle = propertyInfo.immutable ? SWT.READ_ONLY : 0;
221         final Combo combo = new Combo(table, SWT.NONE | extraStyle);
222         combo.setText(selectedItem.getText(column));
223         for(String suggestion : PROPERTY_TYPE_SUGGESTIONS)
224             combo.add(suggestion);
225         org.eclipse.swt.widgets.Listener listener = 
226                 new org.eclipse.swt.widgets.Listener() {
227             @Override
228             public void handleEvent(Event e) {
229                 if(e.type == SWT.Traverse) {
230                     if (e.detail == SWT.TRAVERSE_ESCAPE) {
231                         combo.dispose();
232                         e.doit = false;
233                         return;
234                     }
235                     if (e.detail == SWT.TRAVERSE_ARROW_NEXT
236                             || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS
237                             || e.detail == SWT.TRAVERSE_MNEMONIC)
238                         return;
239                 }
240                 final String newValue = combo.getText();
241                 if (e.type == SWT.Traverse) {
242                     e.doit = false;
243                 }
244                 combo.dispose();
245
246                 if (propertyInfo.immutable)
247                     return;
248
249                 Simantics.getSession().async(new WriteRequest() {
250                     @Override
251                     public void perform(WriteGraph graph)
252                             throws DatabaseException {
253                         graph.markUndoPoint();
254                         ComponentTypeCommands.editType(graph, componentType, propertyInfo.resource, convertDefaultValue, newValue);
255                     }
256                 });
257             }
258         };
259         combo.setFocus();
260         editor.setEditor(combo, selectedItem, column);
261         combo.addListener(SWT.FocusOut, listener);
262         combo.addListener(SWT.Traverse, listener);
263     }
264
265     public void editUnit(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, int column) {
266         // Disallow unit editing for non-numeric configuration properties
267         if (propertyInfo.numberType == null && propertyInfo.sectionSpecificData == null)
268             return;
269
270         int extraStyle = propertyInfo.immutable ? SWT.READ_ONLY : 0;
271         final Combo combo = new Combo(table, SWT.NONE | extraStyle);
272         String initialValue = selectedItem.getText(column);
273         List<String> units = new ArrayList<>( unitLibrary.getUnits() );
274         Collections.sort(units, String.CASE_INSENSITIVE_ORDER);
275         int i = -1;
276         int selected = -1;
277         for (String unit : units) {
278             combo.add(unit);
279             if (unit.equals(initialValue))
280                 combo.select(i);
281         }
282         if (selected == -1)
283             combo.setText(initialValue);
284
285         org.eclipse.swt.widgets.Listener listener = 
286                 new org.eclipse.swt.widgets.Listener() {
287             @Override
288             public void handleEvent(Event e) {
289                 if(e.type == SWT.Traverse) {
290                     if (e.detail == SWT.TRAVERSE_ESCAPE) {
291                         combo.dispose();
292                         e.doit = false;
293                         return;
294                     }
295                     if (e.detail == SWT.TRAVERSE_ARROW_NEXT
296                             || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS
297                             || e.detail == SWT.TRAVERSE_MNEMONIC)
298                         return;
299                 }
300                 final String newValue = combo.getText();
301                 if(e.type == SWT.Traverse) {
302                     e.doit = false;
303                 }
304                 combo.dispose();
305
306                 if (propertyInfo.immutable)
307                     return;
308
309                 Simantics.getSession().async(new WriteRequest() {
310                     @Override
311                     public void perform(WriteGraph graph)
312                             throws DatabaseException {
313                         graph.markUndoPoint();
314                         ComponentTypeCommands.setUnit(graph, componentType, propertyInfo.resource, newValue);
315                     }
316                 });
317             }
318         };
319         combo.setFocus();
320         editor.setEditor(combo, selectedItem, column);
321         combo.addListener(SWT.Deactivate, listener);
322         combo.addListener(SWT.Traverse, listener);
323     }
324
325     public void editValue(Table table, TableEditor editor,
326             final ComponentTypeViewerPropertyInfo propertyInfo,
327             TableItem selectedItem, int column,
328             final StringWriter writer,
329             final Function4<RequestProcessor, Resource, Resource, String, String> validator)
330     {
331         int extraStyle = writer == null ? SWT.READ_ONLY : 0;
332         final Text text = new Text(table, SWT.NONE | extraStyle);
333         text.setText(selectedItem.getText(column));
334         org.eclipse.swt.widgets.Listener listener = 
335                 new org.eclipse.swt.widgets.Listener() {
336             @Override
337             public void handleEvent(Event e) {
338                 if(e.type == SWT.Traverse) {
339                     if (e.detail == SWT.TRAVERSE_ESCAPE) {
340                         text.dispose();
341                         e.doit = false;
342                         return;
343                     }
344                     if (e.detail == SWT.TRAVERSE_ARROW_NEXT
345                             || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS
346                             || e.detail == SWT.TRAVERSE_MNEMONIC)
347                         return;
348                 }
349                 final String newValue = text.getText();
350                 if(e.type == SWT.Traverse) {
351                     e.doit = false;
352                 }
353                 text.dispose();
354
355                 if (validator != null) {
356                     String error = validator.apply(Simantics.getSession(), componentType, propertyInfo.resource, newValue);
357                     if (error != null)
358                         return;
359                 }
360
361                 if (writer != null) {
362                     Simantics.getSession().async(new WriteRequest() {
363                         @Override
364                         public void perform(WriteGraph graph) throws DatabaseException {
365                             writer.perform(graph, newValue);
366                         }
367                     });
368                 }
369             }
370         };
371         text.selectAll();
372         text.setFocus();
373         editor.setEditor(text, selectedItem, column);
374         text.addListener(SWT.FocusOut, listener);
375         text.addListener(SWT.Traverse, listener);
376
377         if (validator != null) {
378             org.eclipse.swt.widgets.Listener validationListener = new org.eclipse.swt.widgets.Listener() {
379                 
380                 private ScheduledFuture<?> future;
381                 
382                 @Override
383                 public void handleEvent(Event e) {
384                     final String newValue = text.getText();
385                     if (future != null && !future.isCancelled())
386                         future.cancel(true);
387                     future = ThreadUtils.getNonBlockingWorkExecutor().schedule(() -> {
388                         String error = validator.apply(Simantics.getSession(), componentType, propertyInfo.resource, newValue);
389                         if (!text.isDisposed()) {
390                             text.getDisplay().asyncExec(() -> {
391                                 if (!text.isDisposed()) {
392                                     if (error != null) {
393                                         text.setBackground(text.getDisplay().getSystemColor(SWT.COLOR_RED));
394                                         text.setToolTipText(error);
395                                         return;
396                                     } else {
397                                         text.setBackground(null);
398                                         text.setToolTipText(null);
399                                     }
400                                 }
401                                 
402                             });
403                         }
404                     }, 500, TimeUnit.MILLISECONDS);
405                 }
406             };
407             text.addListener(SWT.Modify, validationListener);
408         }
409     }
410
411     private Range parseRange(NumberType numberType, String minStr, String maxStr, boolean lowInclusive, boolean highInclusive) throws RangeException {
412         try {
413             String rangeStr = (lowInclusive ? "[" : "(")  + minStr + ".." + maxStr + (highInclusive ? "]" : ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
414             return Range.valueOf(rangeStr);
415         } catch (IllegalArgumentException e) {
416             // Limits are invalid
417             throw new RangeException(e.getMessage(), e);
418         }
419     }
420     
421     private static Combo createRangeInclusionCombo(Composite parent, boolean inclusive) {
422         Combo rng = new Combo(parent, SWT.READ_ONLY);
423         rng.add(Messages.ComponentTypeViewerData_Inclusive);
424         rng.add(Messages.ComponentTypeViewerData_Exclusive);
425         rng.select(inclusive ? 0 : 1);
426         return rng;
427     }
428     
429     protected void editRange(Table table, TableEditor editor, final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem, Rectangle selectedItemBounds, int column) {
430         // Disallow range editing when the property is not numeric
431         if (propertyInfo.numberType == null)
432             return;
433
434         int extraTextStyle = propertyInfo.immutable ? SWT.READ_ONLY : 0;
435
436         // Parse initial range value
437         Range range = null;
438         String rangeStr = selectedItem.getText(column);
439         try {
440             range = Range.valueOf(rangeStr);
441         } catch (RangeException ex) {
442             range = new Range(Limit.nolimit(), Limit.nolimit());
443         }
444
445         final Shell shell = new Shell(table.getShell(), SWT.ON_TOP);
446         GridLayoutFactory.fillDefaults().applyTo(shell);
447
448         Composite composite = new Composite(shell, SWT.NONE);
449         GridDataFactory.fillDefaults().grab(true, true).applyTo(composite);
450         GridLayoutFactory.swtDefaults().numColumns(3).applyTo(composite);
451
452         Label low = new Label(composite, SWT.NONE);
453         low.setText(Messages.ComponentTypeViewerData_MinimumValue);
454         final Text lowText = new Text(composite, SWT.BORDER | extraTextStyle);
455         GridDataFactory.fillDefaults().grab(true, false).hint(100, SWT.DEFAULT).applyTo(lowText);
456         final Combo lowSelector = createRangeInclusionCombo(composite, !range.getLower().isExclusive());
457         Label high = new Label(composite, SWT.NONE);
458         high.setText(Messages.ComponentTypeViewerData_MaximumValue);
459         final Text highText = new Text(composite, SWT.BORDER | extraTextStyle);
460         GridDataFactory.fillDefaults().grab(true, false).hint(100, SWT.DEFAULT).applyTo(highText);
461         final Combo highSelector = createRangeInclusionCombo(composite, !range.getUpper().isExclusive());
462
463         Composite buttonComposite = new Composite(shell, SWT.NONE);
464         GridDataFactory.fillDefaults().grab(true, false).align(SWT.TRAIL, SWT.FILL).applyTo(buttonComposite);
465         GridLayoutFactory.swtDefaults().numColumns(2).equalWidth(true).applyTo(buttonComposite);
466
467         Button ok = new Button(buttonComposite, SWT.NONE);
468         ok.setText(IDialogConstants.OK_LABEL);
469         GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).applyTo(ok);
470         Button cancel = new Button(buttonComposite, SWT.NONE);
471         cancel.setText(IDialogConstants.CANCEL_LABEL);
472         GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).applyTo(ok);
473
474         if (range.getLower().getValue() != null)
475             lowText.setText(range.getLower().getValue().toString());
476         if (range.getUpper().getValue() != null)
477             highText.setText(range.getUpper().getValue().toString());
478
479         shell.addListener(SWT.Deactivate, new org.eclipse.swt.widgets.Listener() {
480             @Override
481             public void handleEvent(Event event) {
482                 shell.dispose();
483             }
484         });
485
486         ok.addSelectionListener(new SelectionAdapter() {
487             public void widgetSelected(SelectionEvent e) {
488                 try {
489                     final Range newRange = parseRange(propertyInfo.numberType,
490                             lowText.getText().trim(),
491                             highText.getText().trim(),
492                             lowSelector.getSelectionIndex() == 0 ? true : false,
493                             highSelector.getSelectionIndex() == 0 ? true : false);
494
495                     shell.dispose();
496
497                     if (propertyInfo.immutable)
498                         return;
499
500                     Simantics.getSession().async(new WriteRequest() {
501                         @Override
502                         public void perform(WriteGraph graph)
503                                 throws DatabaseException {
504                             graph.markUndoPoint();
505                             ComponentTypeCommands.setRange(graph, componentType, propertyInfo.resource, newRange == null ? null : newRange.toString());
506                         }
507                     });
508                 } catch (RangeException ex) {
509                     ErrorLogger.defaultLogError(ex);
510                 }
511             }
512         });
513         cancel.addSelectionListener(new SelectionAdapter() {
514             public void widgetSelected(SelectionEvent e) {
515                 shell.dispose();
516             }
517         });
518
519         shell.pack();
520         Point size = shell.getSize();
521
522         Display display = table.getDisplay();
523         Rectangle clientArea = display.getClientArea();
524         Point bt = table.toDisplay(selectedItemBounds.x, selectedItemBounds.y);
525         Rectangle b = selectedItemBounds;
526         b.x = bt.x;
527         b.y = bt.y;
528         b.width = size.x;
529         b.height = size.y;
530         if ((b.x + b.width) > clientArea.width)
531             b.x -= b.x + b.width - clientArea.width;
532         if (b.height > clientArea.height)
533             b.height = clientArea.height;
534         if ((b.y + b.height) > clientArea.height)
535             b.y -= b.y + b.height - clientArea.height;
536
537         shell.setBounds(selectedItemBounds);
538         shell.open();
539     }
540
541     public void editMultilineText(Table table, TableEditor editor,
542             final ComponentTypeViewerPropertyInfo propertyInfo, TableItem selectedItem,
543             Rectangle selectedItemBounds, int column, final StringWriter writer)
544     {
545         final Shell shell = new Shell(table.getShell(), SWT.ON_TOP);
546         GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(shell);
547         final StyledText text = new StyledText(shell, SWT.MULTI | SWT.WRAP | SWT.V_SCROLL | (propertyInfo.immutable ? SWT.READ_ONLY : 0));
548         GridDataFactory.fillDefaults().grab(true, true).applyTo(text);
549         text.setText(selectedItem.getText(column));
550         org.eclipse.swt.widgets.Listener listener = 
551                 new org.eclipse.swt.widgets.Listener() {
552             @Override
553             public void handleEvent(Event e) {
554                 final String newValue = text.getText();
555
556                 if (e.type == SWT.Traverse) {
557                     if (e.detail == SWT.TRAVERSE_ESCAPE) {
558                         shell.dispose();
559                         e.doit = false;
560                         return;
561                     }
562                     if (e.detail == SWT.TRAVERSE_ARROW_NEXT
563                             || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS
564                             || e.detail == SWT.TRAVERSE_MNEMONIC)
565                         return;
566                     if ((e.stateMask & SWT.CTRL) == 0)
567                         return;
568                     e.doit = false;
569                 }
570
571                 shell.dispose();
572
573                 if (propertyInfo.immutable)
574                     return;
575
576                 if (writer != null) {
577                     Simantics.getSession().async(new WriteRequest() {
578                         @Override
579                         public void perform(WriteGraph graph) throws DatabaseException {
580                             writer.perform(graph, newValue);
581                         }
582                     });
583                 }
584             }
585         };
586
587         String helpText = propertyInfo.immutable ? Messages.ComponentTypeViewerData_ESCToClose : Messages.ComponentTypeViewerData_CtrlEnterApplyChanges;
588         Label help = tk.createLabel(shell, helpText, SWT.BORDER | SWT.FLAT);
589         GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.CENTER).applyTo(help);
590         help.setForeground( tk.getColors().createColor( "fg", tk.getColors().getSystemColor(SWT.COLOR_LIST_SELECTION) ) ); //$NON-NLS-1$
591
592         Display display = table.getDisplay();
593         Rectangle clientArea = display.getClientArea();
594         Point bt = table.toDisplay(selectedItemBounds.x, selectedItemBounds.y);
595         Rectangle b = selectedItemBounds;
596         b.x = bt.x;
597         b.y = bt.y;
598         b.height = 200;
599         if ((b.x + b.width) > clientArea.width)
600             b.x -= b.x + b.width - clientArea.width;
601         if (b.height > clientArea.height)
602             b.height = clientArea.height;
603         if ((b.y + b.height) > clientArea.height)
604             b.y -= b.y + b.height - clientArea.height;
605
606         shell.setBounds(selectedItemBounds);
607         shell.open();
608
609         text.selectAll();
610         text.setFocus();
611
612         text.addListener(SWT.Traverse, listener);
613         shell.addListener(SWT.Deactivate, listener);
614     }
615
616     private String validatePropertyName(ComponentTypeViewerPropertyInfo propertyInfo, String propertyName, Pattern namePattern) {
617         if (propertyName.equals(propertyInfo.name))
618             return null;
619         for (ComponentTypeViewerPropertyInfo info : properties) {
620             if (propertyName.equals(info.name))
621                 return NLS.bind(Messages.ComponentTypeViewerData_PropertyNameInUse, propertyName); 
622         }
623         for (NamedResource cp : connectionPoints) {
624             if (propertyName.equals(cp.getName()))
625                 return NLS.bind(Messages.ComponentTypeViewerData_NameInUse, propertyName ); 
626         }
627         Matcher m = namePattern.matcher(propertyName);
628         if (!m.matches())
629             return NLS.bind(Messages.ComponentTypeViewerData_ContainsInvalidCharacters, propertyName, namePattern.pattern());
630         return null;
631     }
632
633 }