1 package org.simantics.plant3d.dialog;
3 import java.util.Collection;
4 import java.util.HashSet;
6 import java.util.ListIterator;
9 import org.eclipse.jface.dialogs.Dialog;
10 import org.eclipse.jface.dialogs.IDialogConstants;
11 import org.eclipse.jface.dialogs.IDialogSettings;
12 import org.eclipse.jface.layout.GridDataFactory;
13 import org.eclipse.jface.layout.GridLayoutFactory;
14 import org.eclipse.jface.resource.JFaceResources;
15 import org.eclipse.jface.resource.LocalResourceManager;
16 import org.eclipse.jface.resource.ResourceLocator;
17 import org.eclipse.jface.resource.ResourceManager;
18 import org.eclipse.jface.viewers.ISelectionChangedListener;
19 import org.eclipse.jface.viewers.IStructuredSelection;
20 import org.eclipse.jface.viewers.ListViewer;
21 import org.eclipse.jface.viewers.SelectionChangedEvent;
22 import org.eclipse.jface.viewers.StructuredSelection;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.events.KeyAdapter;
25 import org.eclipse.swt.events.KeyEvent;
26 import org.eclipse.swt.events.SelectionAdapter;
27 import org.eclipse.swt.events.SelectionEvent;
28 import org.eclipse.swt.layout.GridData;
29 import org.eclipse.swt.layout.GridLayout;
30 import org.eclipse.swt.widgets.Button;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Control;
33 import org.eclipse.swt.widgets.ExpandBar;
34 import org.eclipse.swt.widgets.ExpandItem;
35 import org.eclipse.swt.widgets.Label;
36 import org.eclipse.swt.widgets.Shell;
37 import org.eclipse.swt.widgets.Text;
38 import org.simantics.Simantics;
39 import org.simantics.db.Session;
40 import org.simantics.db.exception.DatabaseException;
41 import org.simantics.plant3d.Activator;
42 import org.simantics.plant3d.ontology.Plant3D;
43 import org.simantics.plant3d.scenegraph.EndComponent;
44 import org.simantics.plant3d.scenegraph.InlineComponent;
45 import org.simantics.plant3d.scenegraph.Nozzle;
46 import org.simantics.plant3d.scenegraph.PipeRun;
47 import org.simantics.plant3d.scenegraph.PipelineComponent;
48 import org.simantics.plant3d.scenegraph.TurnComponent;
49 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
50 import org.simantics.plant3d.utils.Item;
51 import org.simantics.plant3d.utils.Item.Type;
52 import org.simantics.plant3d.utils.P3DUtil;
53 import org.simantics.utils.ui.ExceptionUtils;
55 public class ComponentSelectionDialog extends Dialog implements ISelectionChangedListener {
57 private static final String DIALOG = "ComponentSelectionDialog"; //$NON-NLS-1$
59 private IDialogSettings dialogSettings;
61 private double lengthFactor = 1.0;
63 private ResourceManager resourceManager;
65 private String libUri;
67 private Item selected;
68 private Set<PositionType> allowed;
69 private Set<PositionType> filterAllowed;
70 // private boolean positionAdjustment;
71 private PipelineComponent component;
72 private boolean insertAdjustable;
73 private boolean lenghtAdjustable;
74 private PositionType insertPosition = PositionType.NEXT;
77 private Double length;
78 private Double rotationAngle;
82 private Text nameText;
85 private Text lengthText;
87 private Text angleText;
88 // Rotated In-line, or turn component.
89 private Text rotationAngleText;
91 // Input for new PipeRun
92 private Double diameter;
93 private Double thickness;
94 private Double turnRadius;
96 private Text diameterText;
97 private Text thicknessText;
98 private Text turnRadiusText;
100 // Validation message label
101 private Label validationLabel;
103 // Position selection
104 private Button startButton;
105 private Button middleButton;
106 private Button endButton;
108 private boolean inlineSplit = false;
110 ListViewer inlineViewer;
111 ListViewer turnViewer;
112 ListViewer endViewer;
114 private Set<String> usedNames;
116 public ComponentSelectionDialog(Shell parentShell, Set<PositionType> allowed, PipelineComponent component) {
117 this(parentShell, allowed, component, Plant3D.URIs.Builtin);
120 public ComponentSelectionDialog(Shell parentShell, Set<PositionType> allowed, PipelineComponent component,
123 this.allowed = allowed;
124 this.component = component;
125 filterAllowed = new HashSet<PositionType>();
126 insertAdjustable = component instanceof InlineComponent ? ((InlineComponent) component).isVariableLength()
128 lenghtAdjustable = false;
129 this.libUri = libUri;
131 usedNames = new HashSet<>();
133 setShellStyle(getShellStyle() | SWT.RESIZE);
135 IDialogSettings settings = Activator.getDefault().getDialogSettings();
136 dialogSettings = settings.getSection(DIALOG);
137 if (dialogSettings == null)
138 dialogSettings = settings.addNewSection(DIALOG);
140 if (component.getNext() != null && component.getPrevious() != null)
141 insertPosition = PositionType.PREVIOUS;
144 private void copyPipeRunParameters() {
145 PipeRun run = component.getPipeRun();
146 if (component.getNext() == null && component.getAlternativePipeRun() != null)
147 run = component.getAlternativePipeRun();
152 diameter = run.getPipeDiameter();
153 thickness = run.getPipeThickness();
154 turnRadius = run.getTurnRadius();
156 diameterText.setText(Double.toString(diameter * lengthFactor));
157 thicknessText.setText(Double.toString(thickness * lengthFactor));
158 turnRadiusText.setText(Double.toString(turnRadius * lengthFactor));
161 public void setLengthFactor(double lengthFactor) {
162 this.lengthFactor = lengthFactor;
166 protected IDialogSettings getDialogBoundsSettings() {
167 return dialogSettings;
171 protected void configureShell(Shell newShell) {
172 super.configureShell(newShell);
173 newShell.setText("Create pipeline component");
176 public void addUsedNames(Collection<String> names) {
177 usedNames.addAll(names);
180 protected List<Item> getItems(Class<?> c, String libUri) throws DatabaseException {
181 Session session = Simantics.getSession();
182 if (InlineComponent.class.equals(c)) {
183 return P3DUtil.getInlines(session, libUri);
184 } else if (TurnComponent.class.equals(c)) {
185 return P3DUtil.getTurns(session, libUri);
186 } else if (EndComponent.class.equals(c)) {
187 return P3DUtil.getEnds(session, libUri);
194 protected Control createDialogArea(Composite parent) {
195 resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
197 Composite composite = new Composite(parent, SWT.NONE);
198 GridLayout layout = new GridLayout(2, false);
199 layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
200 layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
201 layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
202 layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
203 composite.setLayout(layout);
204 composite.setLayoutData(new GridData(GridData.FILL_BOTH));
205 applyDialogFont(composite);
207 // Grid layout data for fields that grab horizontal space
208 final GridDataFactory horizFillData = GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP);
210 // TODO : we need better classification than inline,turn, and end:
211 // * fixed length inlines
212 // * fixed angle turns
213 // * size changes (requires input for pipe run specs)
214 // * variable length inlines (input for length)
215 // * variable angle turns (input for angle)
218 List<Item> ends = null;
219 List<Item> turns = null;
220 List<Item> inlines = null;
222 ends = getItems(EndComponent.class, libUri);
223 turns = getItems(TurnComponent.class, libUri);
224 inlines = getItems(InlineComponent.class, libUri);
225 } catch (DatabaseException e) {
226 Label label = new Label(composite, SWT.NONE);
227 label.setText("Cannot load pipeline components: " + e.getMessage());
228 ExceptionUtils.logError(e);
231 ends = P3DUtil.filterUserComponents(ends);
232 turns = P3DUtil.filterUserComponents(turns);
233 inlines = P3DUtil.filterUserComponents(inlines);
235 ExpandBar expandBar = new ExpandBar(composite, SWT.V_SCROLL);
237 boolean isOpen = component.getNext() == null || component.getPrevious() == null;
238 if (!inlines.isEmpty()) {
240 // Remove variable length pipe from options
241 ListIterator<Item> it = inlines.listIterator();
242 while (it.hasNext()) {
243 Item item = it.next();
244 if (item.isVariable())
249 inlineViewer = createItemList(expandBar, inlines, "Inline");
252 // Only allow elbows and ends to be added to open ends of the pipelines
254 if (!turns.isEmpty()) {
255 turnViewer = createItemList(expandBar, turns, "Elbow");
258 if (!ends.isEmpty()) {
259 turnViewer = createItemList(expandBar, ends, "End");
263 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).span(2, 1).applyTo(expandBar);
264 GridDataFactory.fillDefaults().grab(true, true).minSize(500, 500).hint(500, 500).applyTo(composite);
266 // If there is only one item in the ExpandBar, expand it by default to avoid unnecessary clicks
267 ExpandItem[] expandBarItems = expandBar.getItems();
268 if (expandBarItems.length == 1) {
269 expandBarItems[0].setExpanded(true);
272 Label label = new Label(composite, SWT.NONE);
273 label.setText("Position");
274 Composite buttonComposite = new Composite(composite, SWT.NONE);
275 startButton = new Button(buttonComposite, SWT.TOGGLE);
276 middleButton = new Button(buttonComposite, SWT.TOGGLE);
277 endButton = new Button(buttonComposite, SWT.TOGGLE);
278 startButton.setImage(resourceManager.createImage(
279 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_start.png").get()));
280 middleButton.setImage(resourceManager.createImage(
281 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_middle.png").get()));
282 endButton.setImage(resourceManager.createImage(
283 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_end.png").get()));
284 startButton.setToolTipText("Overlapping insert");
285 middleButton.setToolTipText("Cutting insert");
286 endButton.setToolTipText("Adding insert");
287 horizFillData.applyTo(buttonComposite);
288 GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonComposite);
290 startButton.setSelection(insertPosition == PositionType.PREVIOUS);
291 middleButton.setSelection(insertPosition == PositionType.SPLIT);
292 endButton.setSelection(insertPosition == PositionType.NEXT);
294 startButton.setEnabled(false);
295 middleButton.setEnabled(false);
296 endButton.setEnabled(false);
298 startButton.addSelectionListener(new SelectionAdapter() {
300 public void widgetSelected(SelectionEvent e) {
301 updateInsertPosition(PositionType.PREVIOUS);
305 middleButton.addSelectionListener(new SelectionAdapter() {
307 public void widgetSelected(SelectionEvent e) {
308 updateInsertPosition(PositionType.SPLIT);
311 endButton.addSelectionListener(new SelectionAdapter() {
313 public void widgetSelected(SelectionEvent e) {
314 updateInsertPosition(PositionType.NEXT);
318 if (!hasInsertPosition()) {
319 label.setVisible(false);
320 buttonComposite.setVisible(false);
323 label = new Label(composite, SWT.NONE);
324 label.setText("Name");
325 nameText = new Text(composite, SWT.SINGLE | SWT.BORDER);
327 label = new Label(composite, SWT.NONE);
328 label.setText("Length");
329 lengthText = new Text(composite, SWT.SINGLE | SWT.BORDER);
330 label = new Label(composite, SWT.NONE);
331 label.setText("Angle");
332 angleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
333 label = new Label(composite, SWT.NONE);
334 label.setText("Rotation angle");
335 rotationAngleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
337 label = new Label(composite, SWT.NONE);
338 label.setText("Diameter");
339 diameterText = new Text(composite, SWT.SINGLE | SWT.BORDER);
340 label = new Label(composite, SWT.NONE);
341 label.setText("Wall thickness");
342 thicknessText = new Text(composite, SWT.SINGLE | SWT.BORDER);
343 label = new Label(composite, SWT.NONE);
344 label.setText("Elbow radius");
345 turnRadiusText = new Text(composite, SWT.SINGLE | SWT.BORDER);
347 validationLabel = new Label(composite, SWT.NONE);
348 validationLabel.setText("");
350 lengthText.setEnabled(false);
351 angleText.setEnabled(false);
352 rotationAngleText.setEnabled(false);
353 turnRadiusText.setEnabled(false);
354 diameterText.setEnabled(false);
355 thicknessText.setEnabled(false);
357 copyPipeRunParameters();
359 nameText.addKeyListener(new KeyAdapter() {
361 public void keyReleased(KeyEvent e) {
362 name = nameText.getText();
367 lengthText.addKeyListener(new KeyAdapter() {
369 public void keyReleased(KeyEvent e) {
371 length = Double.parseDouble(lengthText.getText()) / lengthFactor;
372 } catch (NumberFormatException err) {
379 angleText.addKeyListener(new KeyAdapter() {
381 public void keyReleased(KeyEvent e) {
383 angle = Double.parseDouble(angleText.getText());
384 } catch (NumberFormatException err) {
391 rotationAngleText.addKeyListener(new KeyAdapter() {
393 public void keyReleased(KeyEvent e) {
395 rotationAngle = Double.parseDouble(rotationAngleText.getText());
396 } catch (NumberFormatException err) {
397 rotationAngle = null;
403 diameterText.addKeyListener(new KeyAdapter() {
405 public void keyReleased(KeyEvent e) {
407 diameter = Double.parseDouble(diameterText.getText()) / lengthFactor;
408 } catch (NumberFormatException err) {
415 thicknessText.addKeyListener(new KeyAdapter() {
417 public void keyReleased(KeyEvent e) {
419 thickness = Double.parseDouble(thicknessText.getText()) / lengthFactor;
420 } catch (NumberFormatException err) {
427 turnRadiusText.addKeyListener(new KeyAdapter() {
429 public void keyReleased(KeyEvent e) {
431 turnRadius = Double.parseDouble(turnRadiusText.getText()) / lengthFactor;
432 } catch (NumberFormatException err) {
439 horizFillData.applyTo(nameText);
440 horizFillData.applyTo(lengthText);
441 horizFillData.applyTo(angleText);
442 horizFillData.applyTo(rotationAngleText);
443 horizFillData.applyTo(diameterText);
444 horizFillData.applyTo(thicknessText);
445 horizFillData.applyTo(turnRadiusText);
447 GridDataFactory.fillDefaults().span(2, 1).align(SWT.END, SWT.END).grab(true, false).applyTo(validationLabel);
449 if (!allowed.contains(PositionType.NEXT) && !allowed.contains(PositionType.PREVIOUS)) {
450 turnViewer.getList().setEnabled(false);
451 endViewer.getList().setEnabled(false);
458 private ListViewer createItemList(ExpandBar expandBar, List<Item> items, String name) {
459 ExpandItem expandItem = new ExpandItem(expandBar, SWT.NONE);
460 expandItem.setText(name);
461 ListViewer viewer = new ListViewer(expandBar);
462 viewer.setLabelProvider(new ComponentLabelProvider());
463 viewer.setContentProvider(new ComponentContentProvider());
465 expandItem.setControl(viewer.getList());
466 viewer.setInput(items);
467 expandItem.setHeight(viewer.getList().computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
468 viewer.addSelectionChangedListener(this);
472 private boolean hasInsertPosition() {
473 return component.getNext() == null || component.getPrevious() == null;
476 private void updateInsertPosition(PositionType type) {
477 if (insertPosition == type)
479 startButton.setSelection(type == PositionType.PREVIOUS);
480 middleButton.setSelection(type == PositionType.SPLIT);
481 endButton.setSelection(type == PositionType.NEXT);
482 insertPosition = type;
486 public void selectionChanged(SelectionChangedEvent event) {
487 IStructuredSelection sel = (IStructuredSelection) event.getSelection();
488 Item i = (Item) sel.getFirstElement();
491 if (inlineViewer != null && event.getSource() == inlineViewer) {
492 clearSelection(turnViewer);
493 clearSelection(endViewer);
494 } else if (turnViewer != null && event.getSource() == turnViewer) {
495 clearSelection(inlineViewer);
496 clearSelection(endViewer);
497 } else if (endViewer != null && event.getSource() == endViewer) {
498 clearSelection(inlineViewer);
499 clearSelection(turnViewer);
502 name = generateUniqueName(selected.getName());
503 nameText.setText(name);
509 private void clearSelection(ListViewer possibleListViewer) {
510 if (possibleListViewer != null)
511 possibleListViewer.setSelection(new StructuredSelection());
514 private void validate() {
515 filterAllowed.clear();
516 Set<PositionType> filterAllowed = new HashSet<PositionType>();
520 if (name.isEmpty() || usedNames.contains(name)) {
524 msg = "Please provide a valid name";
526 msg = "Name \"" + name + "\" is already in use";
530 if (selected == null) {
532 } else if (selected.isCode()) {// TODO : instead of disabling the button, we should filter the content.
535 lenghtAdjustable = ((selected.getType() == Type.INLINE)
536 && (selected.isVariable() || selected.isModifiable()));
538 if (!hasInsertPosition()) {
539 // We are inserting to a fully connected variable length component
540 // only allow insertion within the component
541 startButton.setEnabled(false);
542 middleButton.setEnabled(false);
543 endButton.setEnabled(false);
544 updateInsertPosition(PositionType.PREVIOUS);
545 } else if (insertAdjustable) {
546 switch (selected.getType()) {
548 startButton.setEnabled(false);
549 middleButton.setEnabled(false);
550 endButton.setEnabled(true);
551 updateInsertPosition(PositionType.NEXT);
554 if (!selected.isVariable()) {
555 startButton.setEnabled(true);
556 middleButton.setEnabled(true);
557 endButton.setEnabled(true);
559 startButton.setEnabled(false);
560 middleButton.setEnabled(false);
561 endButton.setEnabled(true);
562 updateInsertPosition(PositionType.NEXT);
566 startButton.setEnabled(false);
567 middleButton.setEnabled(false);
568 endButton.setEnabled(true);
569 updateInsertPosition(PositionType.NEXT);
572 startButton.setEnabled(false);
573 middleButton.setEnabled(true);
574 endButton.setEnabled(true);
575 if (insertPosition == PositionType.PREVIOUS)
576 updateInsertPosition(PositionType.NEXT);
579 throw new RuntimeException("Expected component, got equipment " + selected);
581 } else if (lenghtAdjustable) {
582 if (component instanceof InlineComponent) {
583 startButton.setEnabled(true);
584 middleButton.setEnabled(true);
585 endButton.setEnabled(true);
586 } else if (component instanceof TurnComponent) {
587 startButton.setEnabled(false);
588 middleButton.setEnabled(true);
589 endButton.setEnabled(true);
590 if (insertPosition == PositionType.PREVIOUS)
591 updateInsertPosition(PositionType.NEXT);
592 } else if (component instanceof EndComponent || component instanceof Nozzle) {
593 startButton.setEnabled(false);
594 middleButton.setEnabled(false);
595 endButton.setEnabled(true);
596 updateInsertPosition(PositionType.NEXT);
599 startButton.setEnabled(false);
600 middleButton.setEnabled(false);
601 endButton.setEnabled(true);
603 if (selected.isVariable() || selected.isModifiable()) {
604 if (selected.getType() == Type.INLINE) {
605 filterAllowed.add(PositionType.NEXT);
606 filterAllowed.add(PositionType.PREVIOUS);
607 if (inlineSplit && selected.isVariable()) {
608 lengthText.setEnabled(false);
609 angleText.setEnabled(false);
610 rotationAngleText.setEnabled(selected.isRotated());
612 if (msg == null) msg = "Cannot split a straight pipe with a straight pipe";
614 lengthText.setEnabled(true);
615 angleText.setEnabled(false);
616 rotationAngleText.setEnabled(selected.isRotated());
617 if (length == null || length <= 0.0) {
619 if (msg == null) msg = "Please provide a valid length";
622 } else if (selected.getType() == Type.TURN) {
623 filterAllowed.add(PositionType.NEXT);
624 filterAllowed.add(PositionType.PREVIOUS);
625 lengthText.setEnabled(false);
626 angleText.setEnabled(true);
627 rotationAngleText.setEnabled(true);
630 if (msg == null) msg = "Please provide a turn angle";
633 // this should not happen, since end components should not have variable, or
635 lengthText.setEnabled(false);
636 angleText.setEnabled(false);
637 rotationAngleText.setEnabled(false);
641 lengthText.setEnabled(false);
642 angleText.setEnabled(false);
643 rotationAngleText.setEnabled(selected.getType() == Type.TURN || selected.isRotated());
646 if (selected.isSizeChange()) {
647 turnRadiusText.setEnabled(true);
648 diameterText.setEnabled(true);
649 thicknessText.setEnabled(true);
650 if (diameter == null || diameter <= 0.0) {
652 if (msg == null) msg = "Please provide a valid diameter";
654 if (turnRadius == null || diameter != null && turnRadius < diameter / 2) {
656 if (msg == null) msg = "Please provide a valid turn radius";
658 if (thickness == null || thickness < 0.0 || diameter != null && thickness >= diameter / 2) {
660 if (msg == null) msg = "Please provide a valid wall thickness";
663 turnRadiusText.setEnabled(false);
664 diameterText.setEnabled(false);
665 thicknessText.setEnabled(false);
668 if (!selected.isVariable()) {
669 switch (selected.getType()) {
671 filterAllowed.add(PositionType.NEXT);
672 filterAllowed.add(PositionType.PREVIOUS);
678 filterAllowed.add(PositionType.NEXT);
679 filterAllowed.add(PositionType.PREVIOUS);
680 filterAllowed.add(PositionType.SPLIT);
682 filterAllowed.add(PositionType.NEXT);
683 filterAllowed.add(PositionType.PREVIOUS);
687 for (PositionType t : filterAllowed) {
688 if (allowed.contains(t))
689 this.filterAllowed.add(t);
692 validationLabel.setText(msg != null ? msg : "");
693 validationLabel.requestLayout();
695 getButton(OK).setEnabled(ok);
698 private String generateUniqueName(String name) {
701 while (usedNames.contains((newName = name + "_" + i)))
706 public Item getSelected() {
710 public String getName() {
714 public Double getAngle() {
718 public Double getLength() {
722 public Double getRotationAngle() {
723 return rotationAngle;
726 public Double getDiameter() {
730 public Double getTurnRadius() {
734 public Set<PositionType> filterAllowed() {
735 return filterAllowed;
738 public PositionType getInsertPosition() {
739 return insertPosition;
742 public boolean isInsertAdjustable() {
743 return insertAdjustable;
746 public boolean isLenghtAdjustable() {
747 return lenghtAdjustable;
750 public Double getThickness() {