1 package org.simantics.plant3d.dialog;
3 import java.util.Collection;
4 import java.util.HashSet;
8 import org.eclipse.jface.dialogs.Dialog;
9 import org.eclipse.jface.dialogs.IDialogConstants;
10 import org.eclipse.jface.dialogs.IDialogSettings;
11 import org.eclipse.jface.layout.GridDataFactory;
12 import org.eclipse.jface.layout.GridLayoutFactory;
13 import org.eclipse.jface.resource.JFaceResources;
14 import org.eclipse.jface.resource.LocalResourceManager;
15 import org.eclipse.jface.resource.ResourceLocator;
16 import org.eclipse.jface.resource.ResourceManager;
17 import org.eclipse.jface.viewers.ISelectionChangedListener;
18 import org.eclipse.jface.viewers.IStructuredSelection;
19 import org.eclipse.jface.viewers.ListViewer;
20 import org.eclipse.jface.viewers.SelectionChangedEvent;
21 import org.eclipse.jface.viewers.StructuredSelection;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.events.KeyAdapter;
24 import org.eclipse.swt.events.KeyEvent;
25 import org.eclipse.swt.events.SelectionAdapter;
26 import org.eclipse.swt.events.SelectionEvent;
27 import org.eclipse.swt.layout.GridData;
28 import org.eclipse.swt.layout.GridLayout;
29 import org.eclipse.swt.widgets.Button;
30 import org.eclipse.swt.widgets.Composite;
31 import org.eclipse.swt.widgets.Control;
32 import org.eclipse.swt.widgets.ExpandBar;
33 import org.eclipse.swt.widgets.ExpandItem;
34 import org.eclipse.swt.widgets.Label;
35 import org.eclipse.swt.widgets.Shell;
36 import org.eclipse.swt.widgets.Text;
37 import org.simantics.Simantics;
38 import org.simantics.db.Session;
39 import org.simantics.db.exception.DatabaseException;
40 import org.simantics.plant3d.Activator;
41 import org.simantics.plant3d.ontology.Plant3D;
42 import org.simantics.plant3d.scenegraph.EndComponent;
43 import org.simantics.plant3d.scenegraph.InlineComponent;
44 import org.simantics.plant3d.scenegraph.Nozzle;
45 import org.simantics.plant3d.scenegraph.PipelineComponent;
46 import org.simantics.plant3d.scenegraph.TurnComponent;
47 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
48 import org.simantics.plant3d.utils.Item;
49 import org.simantics.plant3d.utils.Item.Type;
50 import org.simantics.plant3d.utils.P3DUtil;
51 import org.simantics.utils.ui.ExceptionUtils;
53 public class ComponentSelectionDialog extends Dialog implements ISelectionChangedListener {
55 private static final String DIALOG = "ComponentSelectionDialog"; //$NON-NLS-1$
57 private IDialogSettings dialogSettings;
59 private double lengthFactor = 1.0;
61 private ResourceManager resourceManager;
63 private String libUri;
65 private Item selected;
66 private Set<PositionType> allowed;
67 private Set<PositionType> filterAllowed;
68 // private boolean positionAdjustment;
69 private PipelineComponent component;
70 private boolean insertAdjustable;
71 private boolean lenghtAdjustable;
72 private PositionType insertPosition = PositionType.NEXT;
75 private Double length;
76 private Double rotationAngle;
80 private Text nameText;
83 private Text lengthText;
85 private Text angleText;
86 // Rotated In-line, or turn component.
87 private Text rotationAngleText;
89 // Input for new PipeRun
90 private Double diameter;
91 private Double thickness;
92 private Double turnRadius;
94 private Text diameterText;
95 private Text thicknessText;
96 private Text turnRadiusText;
98 // Validation message label
99 private Label validationLabel;
101 // Position selection
102 private Button startButton;
103 private Button middleButton;
104 private Button endButton;
106 private boolean inlineSplit = false;
108 ListViewer inlineViewer;
109 ListViewer turnViewer;
110 ListViewer endViewer;
112 private Set<String> usedNames;
114 public ComponentSelectionDialog(Shell parentShell, Set<PositionType> allowed, PipelineComponent component) {
115 this(parentShell, allowed, component, Plant3D.URIs.Builtin);
118 public ComponentSelectionDialog(Shell parentShell, Set<PositionType> allowed, PipelineComponent component,
121 this.allowed = allowed;
122 this.component = component;
123 filterAllowed = new HashSet<PositionType>();
124 insertAdjustable = component instanceof InlineComponent ? ((InlineComponent) component).isVariableLength()
126 lenghtAdjustable = false;
127 this.libUri = libUri;
129 usedNames = new HashSet<>();
131 setShellStyle(getShellStyle() | SWT.RESIZE);
133 IDialogSettings settings = Activator.getDefault().getDialogSettings();
134 dialogSettings = settings.getSection(DIALOG);
135 if (dialogSettings == null)
136 dialogSettings = settings.addNewSection(DIALOG);
138 if (component.getNext() != null && component.getPrevious() != null)
139 insertPosition = PositionType.PREVIOUS;
142 public void setLengthFactor(double lengthFactor) {
143 this.lengthFactor = lengthFactor;
147 protected IDialogSettings getDialogBoundsSettings() {
148 return dialogSettings;
152 protected void configureShell(Shell newShell) {
153 super.configureShell(newShell);
154 newShell.setText("Create pipeline component");
157 public void addUsedNames(Collection<String> names) {
158 usedNames.addAll(names);
161 protected List<Item> getItems(Class<?> c, String libUri) throws DatabaseException {
162 Session session = Simantics.getSession();
163 if (InlineComponent.class.equals(c)) {
164 return P3DUtil.getInlines(session, libUri);
165 } else if (TurnComponent.class.equals(c)) {
166 return P3DUtil.getTurns(session, libUri);
167 } else if (EndComponent.class.equals(c)) {
168 return P3DUtil.getEnds(session, libUri);
175 protected Control createDialogArea(Composite parent) {
176 resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
178 Composite composite = new Composite(parent, SWT.NONE);
179 GridLayout layout = new GridLayout(2, false);
180 layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
181 layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
182 layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
183 layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
184 composite.setLayout(layout);
185 composite.setLayoutData(new GridData(GridData.FILL_BOTH));
186 applyDialogFont(composite);
188 // Grid layout data for fields that grab horizontal space
189 final GridDataFactory horizFillData = GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP);
191 // TODO : we need better classification than inline,turn, and end:
192 // * fixed length inlines
193 // * fixed angle turns
194 // * size changes (requires input for pipe run specs)
195 // * variable length inlines (input for length)
196 // * variable angle turns (input for angle)
199 List<Item> ends = null;
200 List<Item> turns = null;
201 List<Item> inlines = null;
203 ends = getItems(EndComponent.class, libUri);
204 turns = getItems(TurnComponent.class, libUri);
205 inlines = getItems(InlineComponent.class, libUri);
206 } catch (DatabaseException e) {
207 Label label = new Label(composite, SWT.NONE);
208 label.setText("Cannot load pipeline components: " + e.getMessage());
209 ExceptionUtils.logError(e);
212 ends = P3DUtil.filterUserComponents(ends);
213 turns = P3DUtil.filterUserComponents(turns);
214 inlines = P3DUtil.filterUserComponents(inlines);
216 ExpandBar expandBar = new ExpandBar(composite, SWT.V_SCROLL);
218 if (!inlines.isEmpty()) {
219 inlineViewer = createItemList(expandBar, inlines, "Inline");
222 // Only allow elbows and ends to be added to open ends of the pipelines
223 boolean isOpen = component.getNext() == null || component.getPrevious() == null;
225 if (!turns.isEmpty()) {
226 turnViewer = createItemList(expandBar, turns, "Elbow");
229 if (!ends.isEmpty()) {
230 turnViewer = createItemList(expandBar, ends, "End");
234 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).span(2, 1).applyTo(expandBar);
235 GridDataFactory.fillDefaults().grab(true, true).minSize(500, 500).hint(500, 500).applyTo(composite);
237 Label label = new Label(composite, SWT.NONE);
238 label.setText("Position");
239 Composite buttonComposite = new Composite(composite, SWT.NONE);
240 startButton = new Button(buttonComposite, SWT.TOGGLE);
241 middleButton = new Button(buttonComposite, SWT.TOGGLE);
242 endButton = new Button(buttonComposite, SWT.TOGGLE);
243 startButton.setImage(resourceManager.createImage(
244 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_start.png").get()));
245 middleButton.setImage(resourceManager.createImage(
246 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_middle.png").get()));
247 endButton.setImage(resourceManager.createImage(
248 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_end.png").get()));
249 startButton.setToolTipText("Overlapping insert");
250 middleButton.setToolTipText("Cutting insert");
251 endButton.setToolTipText("Adding insert");
252 horizFillData.applyTo(buttonComposite);
253 GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonComposite);
255 startButton.setSelection(insertPosition == PositionType.PREVIOUS);
256 middleButton.setSelection(insertPosition == PositionType.SPLIT);
257 endButton.setSelection(insertPosition == PositionType.NEXT);
259 startButton.setEnabled(false);
260 middleButton.setEnabled(false);
261 endButton.setEnabled(false);
263 startButton.addSelectionListener(new SelectionAdapter() {
265 public void widgetSelected(SelectionEvent e) {
266 updateInsertPosition(PositionType.PREVIOUS);
270 middleButton.addSelectionListener(new SelectionAdapter() {
272 public void widgetSelected(SelectionEvent e) {
273 updateInsertPosition(PositionType.SPLIT);
276 endButton.addSelectionListener(new SelectionAdapter() {
278 public void widgetSelected(SelectionEvent e) {
279 updateInsertPosition(PositionType.NEXT);
283 if (!hasInsertPosition()) {
284 label.setVisible(false);
285 buttonComposite.setVisible(false);
288 label = new Label(composite, SWT.NONE);
289 label.setText("Name");
290 nameText = new Text(composite, SWT.SINGLE | SWT.BORDER);
292 label = new Label(composite, SWT.NONE);
293 label.setText("Length");
294 lengthText = new Text(composite, SWT.SINGLE | SWT.BORDER);
295 label = new Label(composite, SWT.NONE);
296 label.setText("Angle");
297 angleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
298 label = new Label(composite, SWT.NONE);
299 label.setText("Rotation angle");
300 rotationAngleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
302 label = new Label(composite, SWT.NONE);
303 label.setText("Diameter");
304 diameterText = new Text(composite, SWT.SINGLE | SWT.BORDER);
305 label = new Label(composite, SWT.NONE);
306 label.setText("Wall thickness");
307 thicknessText = new Text(composite, SWT.SINGLE | SWT.BORDER);
308 label = new Label(composite, SWT.NONE);
309 label.setText("Elbow radius");
310 turnRadiusText = new Text(composite, SWT.SINGLE | SWT.BORDER);
312 validationLabel = new Label(composite, SWT.NONE);
313 validationLabel.setText("");
315 lengthText.setEnabled(false);
316 angleText.setEnabled(false);
317 rotationAngleText.setEnabled(false);
318 turnRadiusText.setEnabled(false);
319 diameterText.setEnabled(false);
320 thicknessText.setEnabled(false);
322 nameText.addKeyListener(new KeyAdapter() {
324 public void keyReleased(KeyEvent e) {
325 name = nameText.getText();
330 lengthText.addKeyListener(new KeyAdapter() {
332 public void keyReleased(KeyEvent e) {
334 length = Double.parseDouble(lengthText.getText()) / lengthFactor;
335 } catch (NumberFormatException err) {
342 angleText.addKeyListener(new KeyAdapter() {
344 public void keyReleased(KeyEvent e) {
346 angle = Double.parseDouble(angleText.getText());
347 } catch (NumberFormatException err) {
354 rotationAngleText.addKeyListener(new KeyAdapter() {
356 public void keyReleased(KeyEvent e) {
358 rotationAngle = Double.parseDouble(rotationAngleText.getText());
359 } catch (NumberFormatException err) {
360 rotationAngle = null;
366 diameterText.addKeyListener(new KeyAdapter() {
368 public void keyReleased(KeyEvent e) {
370 diameter = Double.parseDouble(diameterText.getText()) / lengthFactor;
371 } catch (NumberFormatException err) {
378 thicknessText.addKeyListener(new KeyAdapter() {
380 public void keyReleased(KeyEvent e) {
382 thickness = Double.parseDouble(thicknessText.getText()) / lengthFactor;
383 } catch (NumberFormatException err) {
390 turnRadiusText.addKeyListener(new KeyAdapter() {
392 public void keyReleased(KeyEvent e) {
394 turnRadius = Double.parseDouble(turnRadiusText.getText()) / lengthFactor;
395 } catch (NumberFormatException err) {
402 horizFillData.applyTo(nameText);
403 horizFillData.applyTo(lengthText);
404 horizFillData.applyTo(angleText);
405 horizFillData.applyTo(rotationAngleText);
406 horizFillData.applyTo(diameterText);
407 horizFillData.applyTo(thicknessText);
408 horizFillData.applyTo(turnRadiusText);
410 GridDataFactory.fillDefaults().span(2, 1).align(SWT.END, SWT.END).grab(true, false).applyTo(validationLabel);
412 if (!allowed.contains(PositionType.NEXT) && !allowed.contains(PositionType.PREVIOUS)) {
413 turnViewer.getList().setEnabled(false);
414 endViewer.getList().setEnabled(false);
421 private ListViewer createItemList(ExpandBar expandBar, List<Item> items, String name) {
422 ExpandItem expandItem = new ExpandItem(expandBar, SWT.NONE);
423 expandItem.setText(name);
424 ListViewer viewer = new ListViewer(expandBar);
425 viewer.setLabelProvider(new ComponentLabelProvider());
426 viewer.setContentProvider(new ComponentContentProvider());
428 expandItem.setControl(viewer.getList());
429 viewer.setInput(items);
430 expandItem.setHeight(viewer.getList().computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
431 viewer.addSelectionChangedListener(this);
435 private boolean hasInsertPosition() {
436 return component.getNext() == null || component.getPrevious() == null;
439 private void updateInsertPosition(PositionType type) {
440 if (insertPosition == type)
442 startButton.setSelection(type == PositionType.PREVIOUS);
443 middleButton.setSelection(type == PositionType.SPLIT);
444 endButton.setSelection(type == PositionType.NEXT);
445 insertPosition = type;
449 public void selectionChanged(SelectionChangedEvent event) {
450 IStructuredSelection sel = (IStructuredSelection) event.getSelection();
451 Item i = (Item) sel.getFirstElement();
454 if (inlineViewer != null && event.getSource() == inlineViewer) {
455 clearSelection(turnViewer);
456 clearSelection(endViewer);
457 } else if (turnViewer != null && event.getSource() == turnViewer) {
458 clearSelection(inlineViewer);
459 clearSelection(endViewer);
460 } else if (endViewer != null && event.getSource() == endViewer) {
461 clearSelection(inlineViewer);
462 clearSelection(turnViewer);
465 name = generateUniqueName(selected.getName());
466 nameText.setText(name);
472 private void clearSelection(ListViewer possibleListViewer) {
473 if (possibleListViewer != null)
474 possibleListViewer.setSelection(new StructuredSelection());
477 private void validate() {
478 filterAllowed.clear();
479 Set<PositionType> filterAllowed = new HashSet<PositionType>();
483 if (name.isEmpty() || usedNames.contains(name)) {
487 msg = "Please provide a valid name";
489 msg = "Name \"" + name + "\" is already in use";
493 if (selected == null) {
495 } else if (selected.isCode()) {// TODO : instead of disabling the button, we should filter the content.
498 lenghtAdjustable = ((selected.getType() == Type.INLINE)
499 && (selected.isVariable() || selected.isModifiable()));
501 if (!hasInsertPosition()) {
502 // We are inserting to a fully connected variable length component
503 // only allow insertion within the component
504 startButton.setEnabled(false);
505 middleButton.setEnabled(false);
506 endButton.setEnabled(false);
507 updateInsertPosition(PositionType.PREVIOUS);
508 } else if (insertAdjustable) {
509 switch (selected.getType()) {
511 startButton.setEnabled(false);
512 middleButton.setEnabled(false);
513 endButton.setEnabled(true);
514 updateInsertPosition(PositionType.NEXT);
517 if (!selected.isVariable()) {
518 startButton.setEnabled(true);
519 middleButton.setEnabled(true);
520 endButton.setEnabled(true);
522 startButton.setEnabled(false);
523 middleButton.setEnabled(false);
524 endButton.setEnabled(true);
525 updateInsertPosition(PositionType.NEXT);
529 startButton.setEnabled(false);
530 middleButton.setEnabled(false);
531 endButton.setEnabled(true);
532 updateInsertPosition(PositionType.NEXT);
535 startButton.setEnabled(false);
536 middleButton.setEnabled(true);
537 endButton.setEnabled(true);
538 if (insertPosition == PositionType.PREVIOUS)
539 updateInsertPosition(PositionType.NEXT);
542 throw new RuntimeException("Expected component, got equipment " + selected);
544 } else if (lenghtAdjustable) {
545 if (component instanceof InlineComponent) {
546 startButton.setEnabled(true);
547 middleButton.setEnabled(true);
548 endButton.setEnabled(true);
549 } else if (component instanceof TurnComponent) {
550 startButton.setEnabled(false);
551 middleButton.setEnabled(true);
552 endButton.setEnabled(true);
553 if (insertPosition == PositionType.PREVIOUS)
554 updateInsertPosition(PositionType.NEXT);
555 } else if (component instanceof EndComponent || component instanceof Nozzle) {
556 startButton.setEnabled(false);
557 middleButton.setEnabled(false);
558 endButton.setEnabled(true);
559 updateInsertPosition(PositionType.NEXT);
562 startButton.setEnabled(false);
563 middleButton.setEnabled(false);
564 endButton.setEnabled(true);
566 if (selected.isVariable() || selected.isModifiable()) {
567 if (selected.getType() == Type.INLINE) {
568 filterAllowed.add(PositionType.NEXT);
569 filterAllowed.add(PositionType.PREVIOUS);
570 if (inlineSplit && selected.isVariable()) {
571 lengthText.setEnabled(false);
572 angleText.setEnabled(false);
573 rotationAngleText.setEnabled(selected.isRotated());
575 if (msg == null) msg = "Cannot split a straight pipe with a straight pipe";
577 lengthText.setEnabled(true);
578 angleText.setEnabled(false);
579 rotationAngleText.setEnabled(selected.isRotated());
580 if (length == null || length <= 0.0) {
582 if (msg == null) msg = "Please provide a valid length";
585 } else if (selected.getType() == Type.TURN) {
586 filterAllowed.add(PositionType.NEXT);
587 filterAllowed.add(PositionType.PREVIOUS);
588 lengthText.setEnabled(false);
589 angleText.setEnabled(true);
590 rotationAngleText.setEnabled(true);
593 if (msg == null) msg = "Please provide a turn angle";
596 // this should not happen, since end components should not have variable, or
598 lengthText.setEnabled(false);
599 angleText.setEnabled(false);
600 rotationAngleText.setEnabled(false);
604 lengthText.setEnabled(false);
605 angleText.setEnabled(false);
606 rotationAngleText.setEnabled(selected.getType() == Type.TURN || selected.isRotated());
609 if (selected.isSizeChange()) {
610 turnRadiusText.setEnabled(true);
611 diameterText.setEnabled(true);
612 thicknessText.setEnabled(true);
613 if (diameter == null || diameter <= 0.0) {
615 if (msg == null) msg = "Please provide a valid diameter";
617 if (turnRadius == null || diameter != null && turnRadius < diameter / 2) {
619 if (msg == null) msg = "Please provide a valid turn radius";
621 if (thickness == null || thickness < 0.0 || diameter != null && thickness >= diameter / 2) {
623 if (msg == null) msg = "Please provide a valid wall thickness";
626 turnRadiusText.setEnabled(false);
627 diameterText.setEnabled(false);
630 if (!selected.isVariable()) {
631 switch (selected.getType()) {
633 filterAllowed.add(PositionType.NEXT);
634 filterAllowed.add(PositionType.PREVIOUS);
640 filterAllowed.add(PositionType.NEXT);
641 filterAllowed.add(PositionType.PREVIOUS);
642 filterAllowed.add(PositionType.SPLIT);
644 filterAllowed.add(PositionType.NEXT);
645 filterAllowed.add(PositionType.PREVIOUS);
649 for (PositionType t : filterAllowed) {
650 if (allowed.contains(t))
651 this.filterAllowed.add(t);
654 validationLabel.setText(msg != null ? msg : "");
655 validationLabel.requestLayout();
657 getButton(OK).setEnabled(ok);
660 private String generateUniqueName(String name) {
663 while (usedNames.contains((newName = name + "_" + i)))
668 public Item getSelected() {
672 public String getName() {
676 public Double getAngle() {
680 public Double getLength() {
684 public Double getRotationAngle() {
685 return rotationAngle;
688 public Double getDiameter() {
692 public Double getTurnRadius() {
696 public Set<PositionType> filterAllowed() {
697 return filterAllowed;
700 public PositionType getInsertPosition() {
701 return insertPosition;
704 public boolean isInsertAdjustable() {
705 return insertAdjustable;
708 public boolean isLenghtAdjustable() {
709 return lenghtAdjustable;
712 public Double getThickness() {