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