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 if (turnViewer != null)
451 turnViewer.getList().setEnabled(false);
452 if (endViewer != null)
453 endViewer.getList().setEnabled(false);
460 private ListViewer createItemList(ExpandBar expandBar, List<Item> items, String name) {
461 ExpandItem expandItem = new ExpandItem(expandBar, SWT.NONE);
462 expandItem.setText(name);
463 ListViewer viewer = new ListViewer(expandBar);
464 viewer.setLabelProvider(new ComponentLabelProvider());
465 viewer.setContentProvider(new ComponentContentProvider());
467 expandItem.setControl(viewer.getList());
468 viewer.setInput(items);
469 expandItem.setHeight(viewer.getList().computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
470 viewer.addSelectionChangedListener(this);
474 private boolean hasInsertPosition() {
475 return component.getNext() == null || component.getPrevious() == null;
478 private void updateInsertPosition(PositionType type) {
479 if (insertPosition == type)
481 startButton.setSelection(type == PositionType.PREVIOUS);
482 middleButton.setSelection(type == PositionType.SPLIT);
483 endButton.setSelection(type == PositionType.NEXT);
484 insertPosition = type;
488 public void selectionChanged(SelectionChangedEvent event) {
489 IStructuredSelection sel = (IStructuredSelection) event.getSelection();
490 Item i = (Item) sel.getFirstElement();
493 if (inlineViewer != null && event.getSource() == inlineViewer) {
494 clearSelection(turnViewer);
495 clearSelection(endViewer);
496 } else if (turnViewer != null && event.getSource() == turnViewer) {
497 clearSelection(inlineViewer);
498 clearSelection(endViewer);
499 } else if (endViewer != null && event.getSource() == endViewer) {
500 clearSelection(inlineViewer);
501 clearSelection(turnViewer);
504 name = generateUniqueName(selected.getName());
505 nameText.setText(name);
511 private void clearSelection(ListViewer possibleListViewer) {
512 if (possibleListViewer != null)
513 possibleListViewer.setSelection(new StructuredSelection());
516 private void validate() {
517 filterAllowed.clear();
518 Set<PositionType> filterAllowed = new HashSet<PositionType>();
522 if (name.isEmpty() || usedNames.contains(name)) {
526 msg = "Please provide a valid name";
528 msg = "Name \"" + name + "\" is already in use";
532 if (selected == null) {
534 } else if (selected.isCode()) {// TODO : instead of disabling the button, we should filter the content.
537 lenghtAdjustable = ((selected.getType() == Type.INLINE)
538 && (selected.isVariable() || selected.isModifiable()));
540 if (!hasInsertPosition()) {
541 // We are inserting to a fully connected variable length component
542 // only allow insertion within the component
543 startButton.setEnabled(false);
544 middleButton.setEnabled(false);
545 endButton.setEnabled(false);
546 updateInsertPosition(PositionType.PREVIOUS);
547 } else if (insertAdjustable) {
548 switch (selected.getType()) {
550 startButton.setEnabled(false);
551 middleButton.setEnabled(false);
552 endButton.setEnabled(true);
553 updateInsertPosition(PositionType.NEXT);
556 if (!selected.isVariable()) {
557 startButton.setEnabled(true);
558 middleButton.setEnabled(true);
559 endButton.setEnabled(true);
561 startButton.setEnabled(false);
562 middleButton.setEnabled(false);
563 endButton.setEnabled(true);
564 updateInsertPosition(PositionType.NEXT);
568 startButton.setEnabled(false);
569 middleButton.setEnabled(false);
570 endButton.setEnabled(true);
571 updateInsertPosition(PositionType.NEXT);
574 startButton.setEnabled(false);
575 middleButton.setEnabled(true);
576 endButton.setEnabled(true);
577 if (insertPosition == PositionType.PREVIOUS)
578 updateInsertPosition(PositionType.NEXT);
581 throw new RuntimeException("Expected component, got equipment " + selected);
583 } else if (lenghtAdjustable) {
584 if (component instanceof InlineComponent) {
585 startButton.setEnabled(true);
586 middleButton.setEnabled(true);
587 endButton.setEnabled(true);
588 } else if (component instanceof TurnComponent) {
589 startButton.setEnabled(false);
590 middleButton.setEnabled(true);
591 endButton.setEnabled(true);
592 if (insertPosition == PositionType.PREVIOUS)
593 updateInsertPosition(PositionType.NEXT);
594 } else if (component instanceof EndComponent || component instanceof Nozzle) {
595 startButton.setEnabled(false);
596 middleButton.setEnabled(false);
597 endButton.setEnabled(true);
598 updateInsertPosition(PositionType.NEXT);
601 startButton.setEnabled(false);
602 middleButton.setEnabled(false);
603 endButton.setEnabled(true);
605 if (selected.isVariable() || selected.isModifiable()) {
606 if (selected.getType() == Type.INLINE) {
607 filterAllowed.add(PositionType.NEXT);
608 filterAllowed.add(PositionType.PREVIOUS);
609 if (inlineSplit && selected.isVariable()) {
610 lengthText.setEnabled(false);
611 angleText.setEnabled(false);
612 rotationAngleText.setEnabled(selected.isRotated());
614 if (msg == null) msg = "Cannot split a straight pipe with a straight pipe";
616 lengthText.setEnabled(true);
617 angleText.setEnabled(false);
618 rotationAngleText.setEnabled(selected.isRotated());
619 if (length == null || length <= 0.0) {
621 if (msg == null) msg = "Please provide a valid length";
624 } else if (selected.getType() == Type.TURN) {
625 filterAllowed.add(PositionType.NEXT);
626 filterAllowed.add(PositionType.PREVIOUS);
627 lengthText.setEnabled(false);
628 angleText.setEnabled(true);
629 rotationAngleText.setEnabled(true);
632 if (msg == null) msg = "Please provide a turn angle";
635 // this should not happen, since end components should not have variable, or
637 lengthText.setEnabled(false);
638 angleText.setEnabled(false);
639 rotationAngleText.setEnabled(false);
643 lengthText.setEnabled(false);
644 angleText.setEnabled(false);
645 rotationAngleText.setEnabled(selected.getType() == Type.TURN || selected.isRotated());
648 if (selected.isSizeChange()) {
649 turnRadiusText.setEnabled(true);
650 diameterText.setEnabled(true);
651 thicknessText.setEnabled(true);
652 if (diameter == null || diameter <= 0.0) {
654 if (msg == null) msg = "Please provide a valid diameter";
656 if (turnRadius == null || diameter != null && turnRadius < diameter / 2) {
658 if (msg == null) msg = "Please provide a valid turn radius";
660 if (thickness == null || thickness < 0.0 || diameter != null && thickness >= diameter / 2) {
662 if (msg == null) msg = "Please provide a valid wall thickness";
665 turnRadiusText.setEnabled(false);
666 diameterText.setEnabled(false);
667 thicknessText.setEnabled(false);
670 if (!selected.isVariable()) {
671 switch (selected.getType()) {
673 filterAllowed.add(PositionType.NEXT);
674 filterAllowed.add(PositionType.PREVIOUS);
680 filterAllowed.add(PositionType.NEXT);
681 filterAllowed.add(PositionType.PREVIOUS);
682 filterAllowed.add(PositionType.SPLIT);
684 filterAllowed.add(PositionType.NEXT);
685 filterAllowed.add(PositionType.PREVIOUS);
689 for (PositionType t : filterAllowed) {
690 if (allowed.contains(t))
691 this.filterAllowed.add(t);
694 validationLabel.setText(msg != null ? msg : "");
695 validationLabel.requestLayout();
697 getButton(OK).setEnabled(ok);
700 private String generateUniqueName(String name) {
703 while (usedNames.contains((newName = name + "_" + i)))
708 public Item getSelected() {
712 public String getName() {
716 public Double getAngle() {
720 public Double getLength() {
724 public Double getRotationAngle() {
725 return rotationAngle;
728 public Double getDiameter() {
732 public Double getTurnRadius() {
736 public Set<PositionType> filterAllowed() {
737 return filterAllowed;
740 public PositionType getInsertPosition() {
741 return insertPosition;
744 public boolean isInsertAdjustable() {
745 return insertAdjustable;
748 public boolean isLenghtAdjustable() {
749 return lenghtAdjustable;
752 public Double getThickness() {