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;
62 private String lengthUnit = "m";
64 private ResourceManager resourceManager;
66 private String libUri;
68 private Item selected;
69 private Set<PositionType> allowed;
70 private Set<PositionType> filterAllowed;
71 // private boolean positionAdjustment;
72 private PipelineComponent component;
73 private boolean insertAdjustable;
74 private boolean lenghtAdjustable;
75 private PositionType insertPosition = PositionType.NEXT;
78 private Double length;
79 private Double rotationAngle;
83 private Text nameText;
86 private Text lengthText;
88 private Text angleText;
89 // Rotated In-line, or turn component.
90 private Text rotationAngleText;
92 // Input for new PipeRun
93 private Double diameter;
94 private Double thickness;
95 private Double turnRadius;
97 private Text diameterText;
98 private Text thicknessText;
99 private Text turnRadiusText;
101 // Validation message label
102 private Label validationLabel;
104 // Position selection
105 private Button startButton;
106 private Button middleButton;
107 private Button endButton;
109 private boolean inlineSplit = false;
111 ListViewer inlineViewer;
112 ListViewer turnViewer;
113 ListViewer endViewer;
115 private Set<String> usedNames;
117 public ComponentSelectionDialog(Shell parentShell, Set<PositionType> allowed, PipelineComponent component) {
118 this(parentShell, allowed, component, Plant3D.URIs.Builtin);
121 public ComponentSelectionDialog(Shell parentShell, Set<PositionType> allowed, PipelineComponent component,
124 this.allowed = allowed;
125 this.component = component;
126 filterAllowed = new HashSet<PositionType>();
127 insertAdjustable = component instanceof InlineComponent ? ((InlineComponent) component).isVariableLength()
129 lenghtAdjustable = false;
130 this.libUri = libUri;
132 usedNames = new HashSet<>();
134 setShellStyle(getShellStyle() | SWT.RESIZE);
136 IDialogSettings settings = Activator.getDefault().getDialogSettings();
137 dialogSettings = settings.getSection(DIALOG);
138 if (dialogSettings == null)
139 dialogSettings = settings.addNewSection(DIALOG);
141 if (component.getNext() != null && component.getPrevious() != null)
142 insertPosition = PositionType.PREVIOUS;
145 private void copyPipeRunParameters() {
146 PipeRun run = component.getPipeRun();
147 if (component.getNext() == null && component.getAlternativePipeRun() != null)
148 run = component.getAlternativePipeRun();
153 diameter = run.getPipeDiameter();
154 thickness = run.getPipeThickness();
155 turnRadius = run.getTurnRadius();
157 diameterText.setText(Double.toString(diameter * lengthFactor));
158 thicknessText.setText(Double.toString(thickness * lengthFactor));
159 turnRadiusText.setText(Double.toString(turnRadius * lengthFactor));
162 public void setLengthFactor(double lengthFactor) {
163 this.lengthFactor = lengthFactor;
166 public void setLengthUnit(String lengthUnit) {
167 this.lengthUnit = lengthUnit;
171 protected IDialogSettings getDialogBoundsSettings() {
172 return dialogSettings;
176 protected void configureShell(Shell newShell) {
177 super.configureShell(newShell);
178 newShell.setText("Create pipeline component");
181 public void addUsedNames(Collection<String> names) {
182 usedNames.addAll(names);
185 protected List<Item> getItems(Class<?> c, String libUri) throws DatabaseException {
186 Session session = Simantics.getSession();
187 if (InlineComponent.class.equals(c)) {
188 return P3DUtil.getInlines(session, libUri);
189 } else if (TurnComponent.class.equals(c)) {
190 return P3DUtil.getTurns(session, libUri);
191 } else if (EndComponent.class.equals(c)) {
192 return P3DUtil.getEnds(session, libUri);
199 protected Control createDialogArea(Composite parent) {
200 resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
202 Composite composite = new Composite(parent, SWT.NONE);
203 GridLayout layout = new GridLayout(3, false);
204 layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
205 layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
206 layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
207 layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
208 composite.setLayout(layout);
209 composite.setLayoutData(new GridData(GridData.FILL_BOTH));
210 applyDialogFont(composite);
212 // Grid layout data for fields that grab horizontal space
213 final GridDataFactory horizFillData = GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP);
215 // TODO : we need better classification than inline,turn, and end:
216 // * fixed length inlines
217 // * fixed angle turns
218 // * size changes (requires input for pipe run specs)
219 // * variable length inlines (input for length)
220 // * variable angle turns (input for angle)
223 List<Item> ends = null;
224 List<Item> turns = null;
225 List<Item> inlines = null;
227 ends = getItems(EndComponent.class, libUri);
228 turns = getItems(TurnComponent.class, libUri);
229 inlines = getItems(InlineComponent.class, libUri);
230 } catch (DatabaseException e) {
231 Label label = new Label(composite, SWT.NONE);
232 label.setText("Cannot load pipeline components: " + e.getMessage());
233 ExceptionUtils.logError(e);
236 ends = P3DUtil.filterUserComponents(ends);
237 turns = P3DUtil.filterUserComponents(turns);
238 inlines = P3DUtil.filterUserComponents(inlines);
240 ExpandBar expandBar = new ExpandBar(composite, SWT.V_SCROLL);
242 boolean isOpen = component.getNext() == null || component.getPrevious() == null;
243 if (!inlines.isEmpty()) {
245 // Remove variable length pipe from options
246 ListIterator<Item> it = inlines.listIterator();
247 while (it.hasNext()) {
248 Item item = it.next();
249 if (item.isVariable())
254 inlineViewer = createItemList(expandBar, inlines, "Inline");
257 // Only allow elbows and ends to be added to open ends of the pipelines
259 if (!turns.isEmpty()) {
260 turnViewer = createItemList(expandBar, turns, "Elbow");
263 if (!ends.isEmpty()) {
264 turnViewer = createItemList(expandBar, ends, "End");
268 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).span(3, 1).applyTo(expandBar);
269 GridDataFactory.fillDefaults().grab(true, true).minSize(300, 500).hint(300, 500).applyTo(composite);
271 // If there is only one item in the ExpandBar, expand it by default to avoid unnecessary clicks
272 ExpandItem[] expandBarItems = expandBar.getItems();
273 if (expandBarItems.length == 1) {
274 expandBarItems[0].setExpanded(true);
277 Label label = new Label(composite, SWT.NONE);
278 label.setText("Position");
279 Composite buttonComposite = new Composite(composite, SWT.NONE);
280 startButton = new Button(buttonComposite, SWT.TOGGLE);
281 middleButton = new Button(buttonComposite, SWT.TOGGLE);
282 endButton = new Button(buttonComposite, SWT.TOGGLE);
283 startButton.setImage(resourceManager.createImage(
284 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_start.png").get()));
285 middleButton.setImage(resourceManager.createImage(
286 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_middle.png").get()));
287 endButton.setImage(resourceManager.createImage(
288 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_end.png").get()));
289 startButton.setToolTipText("Overlapping insert");
290 middleButton.setToolTipText("Cutting insert");
291 endButton.setToolTipText("Adding insert");
292 horizFillData.copy().span(2, 1).applyTo(buttonComposite);
293 GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonComposite);
295 startButton.setSelection(insertPosition == PositionType.PREVIOUS);
296 middleButton.setSelection(insertPosition == PositionType.SPLIT);
297 endButton.setSelection(insertPosition == PositionType.NEXT);
299 startButton.setEnabled(false);
300 middleButton.setEnabled(false);
301 endButton.setEnabled(false);
303 startButton.addSelectionListener(new SelectionAdapter() {
305 public void widgetSelected(SelectionEvent e) {
306 updateInsertPosition(PositionType.PREVIOUS);
310 middleButton.addSelectionListener(new SelectionAdapter() {
312 public void widgetSelected(SelectionEvent e) {
313 updateInsertPosition(PositionType.SPLIT);
316 endButton.addSelectionListener(new SelectionAdapter() {
318 public void widgetSelected(SelectionEvent e) {
319 updateInsertPosition(PositionType.NEXT);
323 if (!hasInsertPosition()) {
324 label.setVisible(false);
325 buttonComposite.setVisible(false);
328 label = new Label(composite, SWT.NONE);
329 label.setText("Name");
330 nameText = new Text(composite, SWT.SINGLE | SWT.BORDER);
331 label = new Label(composite, SWT.NONE);
334 label = new Label(composite, SWT.NONE);
335 label.setText("Length");
336 lengthText = new Text(composite, SWT.SINGLE | SWT.BORDER);
337 label = new Label(composite, SWT.NONE);
338 label.setText(lengthUnit);
340 label = new Label(composite, SWT.NONE);
341 label.setText("Angle");
342 angleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
343 label = new Label(composite, SWT.NONE);
344 label.setText("\u00b0");
346 label = new Label(composite, SWT.NONE);
347 label.setText("Rotation angle");
348 rotationAngleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
349 label = new Label(composite, SWT.NONE);
350 label.setText("\u00b0");
352 label = new Label(composite, SWT.NONE);
353 label.setText("Diameter");
354 diameterText = new Text(composite, SWT.SINGLE | SWT.BORDER);
355 label = new Label(composite, SWT.NONE);
356 label.setText(lengthUnit);
358 label = new Label(composite, SWT.NONE);
359 label.setText("Wall thickness");
360 thicknessText = new Text(composite, SWT.SINGLE | SWT.BORDER);
361 label = new Label(composite, SWT.NONE);
362 label.setText(lengthUnit);
364 label = new Label(composite, SWT.NONE);
365 label.setText("Elbow radius");
366 turnRadiusText = new Text(composite, SWT.SINGLE | SWT.BORDER);
367 label = new Label(composite, SWT.NONE);
368 label.setText(lengthUnit);
370 validationLabel = new Label(composite, SWT.NONE);
371 validationLabel.setText("");
373 lengthText.setEnabled(false);
374 angleText.setEnabled(false);
375 rotationAngleText.setEnabled(false);
376 turnRadiusText.setEnabled(false);
377 diameterText.setEnabled(false);
378 thicknessText.setEnabled(false);
380 copyPipeRunParameters();
382 nameText.addKeyListener(new KeyAdapter() {
384 public void keyReleased(KeyEvent e) {
385 name = nameText.getText();
390 lengthText.addKeyListener(new KeyAdapter() {
392 public void keyReleased(KeyEvent e) {
394 length = Double.parseDouble(lengthText.getText()) / lengthFactor;
395 } catch (NumberFormatException err) {
402 angleText.addKeyListener(new KeyAdapter() {
404 public void keyReleased(KeyEvent e) {
406 angle = Double.parseDouble(angleText.getText());
407 } catch (NumberFormatException err) {
414 rotationAngleText.addKeyListener(new KeyAdapter() {
416 public void keyReleased(KeyEvent e) {
418 rotationAngle = Double.parseDouble(rotationAngleText.getText());
419 } catch (NumberFormatException err) {
420 rotationAngle = null;
426 diameterText.addKeyListener(new KeyAdapter() {
428 public void keyReleased(KeyEvent e) {
430 diameter = Double.parseDouble(diameterText.getText()) / lengthFactor;
431 } catch (NumberFormatException err) {
438 thicknessText.addKeyListener(new KeyAdapter() {
440 public void keyReleased(KeyEvent e) {
442 thickness = Double.parseDouble(thicknessText.getText()) / lengthFactor;
443 } catch (NumberFormatException err) {
450 turnRadiusText.addKeyListener(new KeyAdapter() {
452 public void keyReleased(KeyEvent e) {
454 turnRadius = Double.parseDouble(turnRadiusText.getText()) / lengthFactor;
455 } catch (NumberFormatException err) {
462 horizFillData.applyTo(nameText);
463 horizFillData.applyTo(lengthText);
464 horizFillData.applyTo(angleText);
465 horizFillData.applyTo(rotationAngleText);
466 horizFillData.applyTo(diameterText);
467 horizFillData.applyTo(thicknessText);
468 horizFillData.applyTo(turnRadiusText);
470 GridDataFactory.fillDefaults().span(3, 1).align(SWT.END, SWT.END).grab(true, false).applyTo(validationLabel);
472 if (!allowed.contains(PositionType.NEXT) && !allowed.contains(PositionType.PREVIOUS)) {
473 if (turnViewer != null)
474 turnViewer.getList().setEnabled(false);
475 if (endViewer != null)
476 endViewer.getList().setEnabled(false);
483 private ListViewer createItemList(ExpandBar expandBar, List<Item> items, String name) {
484 ExpandItem expandItem = new ExpandItem(expandBar, SWT.NONE);
485 expandItem.setText(name);
486 ListViewer viewer = new ListViewer(expandBar);
487 viewer.setLabelProvider(new ComponentLabelProvider());
488 viewer.setContentProvider(new ComponentContentProvider());
490 expandItem.setControl(viewer.getList());
491 viewer.setInput(items);
492 expandItem.setHeight(viewer.getList().computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
493 viewer.addSelectionChangedListener(this);
497 private boolean hasInsertPosition() {
498 return component.getNext() == null || component.getPrevious() == null;
501 private void updateInsertPosition(PositionType type) {
502 if (insertPosition == type)
504 startButton.setSelection(type == PositionType.PREVIOUS);
505 middleButton.setSelection(type == PositionType.SPLIT);
506 endButton.setSelection(type == PositionType.NEXT);
507 insertPosition = type;
511 public void selectionChanged(SelectionChangedEvent event) {
512 IStructuredSelection sel = (IStructuredSelection) event.getSelection();
513 Item i = (Item) sel.getFirstElement();
516 if (inlineViewer != null && event.getSource() == inlineViewer) {
517 clearSelection(turnViewer);
518 clearSelection(endViewer);
519 } else if (turnViewer != null && event.getSource() == turnViewer) {
520 clearSelection(inlineViewer);
521 clearSelection(endViewer);
522 } else if (endViewer != null && event.getSource() == endViewer) {
523 clearSelection(inlineViewer);
524 clearSelection(turnViewer);
527 name = generateUniqueName(selected.getName());
528 nameText.setText(name);
534 private void clearSelection(ListViewer possibleListViewer) {
535 if (possibleListViewer != null)
536 possibleListViewer.setSelection(new StructuredSelection());
539 private void validate() {
540 filterAllowed.clear();
541 Set<PositionType> filterAllowed = new HashSet<PositionType>();
545 if (name.isEmpty() || usedNames.contains(name)) {
549 msg = "Please provide a valid name";
551 msg = "Name \"" + name + "\" is already in use";
555 if (selected == null) {
557 } else if (selected.isCode()) {// TODO : instead of disabling the button, we should filter the content.
560 lenghtAdjustable = ((selected.getType() == Type.INLINE)
561 && (selected.isVariable() || selected.isModifiable()));
563 if (!hasInsertPosition()) {
564 // We are inserting to a fully connected variable length component
565 // only allow insertion within the component
566 startButton.setEnabled(false);
567 middleButton.setEnabled(false);
568 endButton.setEnabled(false);
569 updateInsertPosition(PositionType.PREVIOUS);
570 } else if (insertAdjustable) {
571 switch (selected.getType()) {
573 startButton.setEnabled(false);
574 middleButton.setEnabled(false);
575 endButton.setEnabled(true);
576 updateInsertPosition(PositionType.NEXT);
579 if (!selected.isVariable()) {
580 startButton.setEnabled(true);
581 middleButton.setEnabled(true);
582 endButton.setEnabled(true);
584 startButton.setEnabled(false);
585 middleButton.setEnabled(false);
586 endButton.setEnabled(true);
587 updateInsertPosition(PositionType.NEXT);
591 startButton.setEnabled(false);
592 middleButton.setEnabled(false);
593 endButton.setEnabled(true);
594 updateInsertPosition(PositionType.NEXT);
597 startButton.setEnabled(false);
598 middleButton.setEnabled(true);
599 endButton.setEnabled(true);
600 if (insertPosition == PositionType.PREVIOUS)
601 updateInsertPosition(PositionType.NEXT);
604 throw new RuntimeException("Expected component, got equipment " + selected);
606 } else if (lenghtAdjustable) {
607 if (component instanceof InlineComponent) {
608 startButton.setEnabled(true);
609 middleButton.setEnabled(true);
610 endButton.setEnabled(true);
611 } else if (component instanceof TurnComponent) {
612 startButton.setEnabled(false);
613 middleButton.setEnabled(true);
614 endButton.setEnabled(true);
615 if (insertPosition == PositionType.PREVIOUS)
616 updateInsertPosition(PositionType.NEXT);
617 } else if (component instanceof EndComponent || component instanceof Nozzle) {
618 startButton.setEnabled(false);
619 middleButton.setEnabled(false);
620 endButton.setEnabled(true);
621 updateInsertPosition(PositionType.NEXT);
624 startButton.setEnabled(false);
625 middleButton.setEnabled(false);
626 endButton.setEnabled(true);
628 if (selected.isVariable() || selected.isModifiable()) {
629 if (selected.getType() == Type.INLINE) {
630 filterAllowed.add(PositionType.NEXT);
631 filterAllowed.add(PositionType.PREVIOUS);
632 if (inlineSplit && selected.isVariable()) {
633 lengthText.setEnabled(false);
634 angleText.setEnabled(false);
635 rotationAngleText.setEnabled(selected.isRotated());
637 if (msg == null) msg = "Cannot split a straight pipe with a straight pipe";
639 lengthText.setEnabled(true);
640 angleText.setEnabled(false);
641 rotationAngleText.setEnabled(selected.isRotated());
642 if (length == null || length <= 0.0) {
644 if (msg == null) msg = "Please provide a valid length";
647 } else if (selected.getType() == Type.TURN) {
648 filterAllowed.add(PositionType.NEXT);
649 filterAllowed.add(PositionType.PREVIOUS);
650 lengthText.setEnabled(false);
651 angleText.setEnabled(true);
652 rotationAngleText.setEnabled(true);
655 if (msg == null) msg = "Please provide a turn angle";
658 // this should not happen, since end components should not have variable, or
660 lengthText.setEnabled(false);
661 angleText.setEnabled(false);
662 rotationAngleText.setEnabled(false);
666 lengthText.setEnabled(false);
667 angleText.setEnabled(false);
668 rotationAngleText.setEnabled(selected.getType() == Type.TURN || selected.isRotated());
671 if (selected.isSizeChange()) {
672 turnRadiusText.setEnabled(true);
673 diameterText.setEnabled(true);
674 thicknessText.setEnabled(true);
675 if (diameter == null || diameter <= 0.0) {
677 if (msg == null) msg = "Please provide a valid diameter";
679 if (turnRadius == null || diameter != null && turnRadius < diameter / 2) {
681 if (msg == null) msg = "Please provide a valid turn radius";
683 if (thickness == null || thickness < 0.0 || diameter != null && thickness >= diameter / 2) {
685 if (msg == null) msg = "Please provide a valid wall thickness";
688 turnRadiusText.setEnabled(false);
689 diameterText.setEnabled(false);
690 thicknessText.setEnabled(false);
693 if (!selected.isVariable()) {
694 switch (selected.getType()) {
696 filterAllowed.add(PositionType.NEXT);
697 filterAllowed.add(PositionType.PREVIOUS);
703 filterAllowed.add(PositionType.NEXT);
704 filterAllowed.add(PositionType.PREVIOUS);
705 filterAllowed.add(PositionType.SPLIT);
707 filterAllowed.add(PositionType.NEXT);
708 filterAllowed.add(PositionType.PREVIOUS);
712 for (PositionType t : filterAllowed) {
713 if (allowed.contains(t))
714 this.filterAllowed.add(t);
717 validationLabel.setText(msg != null ? msg : "");
718 validationLabel.requestLayout();
720 getButton(OK).setEnabled(ok);
723 private String generateUniqueName(String name) {
726 while (usedNames.contains((newName = name + "_" + i)))
731 public Item getSelected() {
735 public String getName() {
739 public Double getAngle() {
743 public Double getLength() {
747 public Double getRotationAngle() {
748 return rotationAngle;
751 public Double getDiameter() {
755 public Double getTurnRadius() {
759 public Set<PositionType> filterAllowed() {
760 return filterAllowed;
763 public PositionType getInsertPosition() {
764 return insertPosition;
767 public boolean isInsertAdjustable() {
768 return insertAdjustable;
771 public boolean isLenghtAdjustable() {
772 return lenghtAdjustable;
775 public Double getThickness() {