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 Label label = new Label(composite, SWT.NONE);
267 label.setText("Position");
268 Composite buttonComposite = new Composite(composite, SWT.NONE);
269 startButton = new Button(buttonComposite, SWT.TOGGLE);
270 middleButton = new Button(buttonComposite, SWT.TOGGLE);
271 endButton = new Button(buttonComposite, SWT.TOGGLE);
272 startButton.setImage(resourceManager.createImage(
273 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_start.png").get()));
274 middleButton.setImage(resourceManager.createImage(
275 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_middle.png").get()));
276 endButton.setImage(resourceManager.createImage(
277 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_end.png").get()));
278 startButton.setToolTipText("Overlapping insert");
279 middleButton.setToolTipText("Cutting insert");
280 endButton.setToolTipText("Adding insert");
281 horizFillData.applyTo(buttonComposite);
282 GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonComposite);
284 startButton.setSelection(insertPosition == PositionType.PREVIOUS);
285 middleButton.setSelection(insertPosition == PositionType.SPLIT);
286 endButton.setSelection(insertPosition == PositionType.NEXT);
288 startButton.setEnabled(false);
289 middleButton.setEnabled(false);
290 endButton.setEnabled(false);
292 startButton.addSelectionListener(new SelectionAdapter() {
294 public void widgetSelected(SelectionEvent e) {
295 updateInsertPosition(PositionType.PREVIOUS);
299 middleButton.addSelectionListener(new SelectionAdapter() {
301 public void widgetSelected(SelectionEvent e) {
302 updateInsertPosition(PositionType.SPLIT);
305 endButton.addSelectionListener(new SelectionAdapter() {
307 public void widgetSelected(SelectionEvent e) {
308 updateInsertPosition(PositionType.NEXT);
312 if (!hasInsertPosition()) {
313 label.setVisible(false);
314 buttonComposite.setVisible(false);
317 label = new Label(composite, SWT.NONE);
318 label.setText("Name");
319 nameText = new Text(composite, SWT.SINGLE | SWT.BORDER);
321 label = new Label(composite, SWT.NONE);
322 label.setText("Length");
323 lengthText = new Text(composite, SWT.SINGLE | SWT.BORDER);
324 label = new Label(composite, SWT.NONE);
325 label.setText("Angle");
326 angleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
327 label = new Label(composite, SWT.NONE);
328 label.setText("Rotation angle");
329 rotationAngleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
331 label = new Label(composite, SWT.NONE);
332 label.setText("Diameter");
333 diameterText = new Text(composite, SWT.SINGLE | SWT.BORDER);
334 label = new Label(composite, SWT.NONE);
335 label.setText("Wall thickness");
336 thicknessText = new Text(composite, SWT.SINGLE | SWT.BORDER);
337 label = new Label(composite, SWT.NONE);
338 label.setText("Elbow radius");
339 turnRadiusText = new Text(composite, SWT.SINGLE | SWT.BORDER);
341 validationLabel = new Label(composite, SWT.NONE);
342 validationLabel.setText("");
344 lengthText.setEnabled(false);
345 angleText.setEnabled(false);
346 rotationAngleText.setEnabled(false);
347 turnRadiusText.setEnabled(false);
348 diameterText.setEnabled(false);
349 thicknessText.setEnabled(false);
351 copyPipeRunParameters();
353 nameText.addKeyListener(new KeyAdapter() {
355 public void keyReleased(KeyEvent e) {
356 name = nameText.getText();
361 lengthText.addKeyListener(new KeyAdapter() {
363 public void keyReleased(KeyEvent e) {
365 length = Double.parseDouble(lengthText.getText()) / lengthFactor;
366 } catch (NumberFormatException err) {
373 angleText.addKeyListener(new KeyAdapter() {
375 public void keyReleased(KeyEvent e) {
377 angle = Double.parseDouble(angleText.getText());
378 } catch (NumberFormatException err) {
385 rotationAngleText.addKeyListener(new KeyAdapter() {
387 public void keyReleased(KeyEvent e) {
389 rotationAngle = Double.parseDouble(rotationAngleText.getText());
390 } catch (NumberFormatException err) {
391 rotationAngle = null;
397 diameterText.addKeyListener(new KeyAdapter() {
399 public void keyReleased(KeyEvent e) {
401 diameter = Double.parseDouble(diameterText.getText()) / lengthFactor;
402 } catch (NumberFormatException err) {
409 thicknessText.addKeyListener(new KeyAdapter() {
411 public void keyReleased(KeyEvent e) {
413 thickness = Double.parseDouble(thicknessText.getText()) / lengthFactor;
414 } catch (NumberFormatException err) {
421 turnRadiusText.addKeyListener(new KeyAdapter() {
423 public void keyReleased(KeyEvent e) {
425 turnRadius = Double.parseDouble(turnRadiusText.getText()) / lengthFactor;
426 } catch (NumberFormatException err) {
433 horizFillData.applyTo(nameText);
434 horizFillData.applyTo(lengthText);
435 horizFillData.applyTo(angleText);
436 horizFillData.applyTo(rotationAngleText);
437 horizFillData.applyTo(diameterText);
438 horizFillData.applyTo(thicknessText);
439 horizFillData.applyTo(turnRadiusText);
441 GridDataFactory.fillDefaults().span(2, 1).align(SWT.END, SWT.END).grab(true, false).applyTo(validationLabel);
443 if (!allowed.contains(PositionType.NEXT) && !allowed.contains(PositionType.PREVIOUS)) {
444 turnViewer.getList().setEnabled(false);
445 endViewer.getList().setEnabled(false);
452 private ListViewer createItemList(ExpandBar expandBar, List<Item> items, String name) {
453 ExpandItem expandItem = new ExpandItem(expandBar, SWT.NONE);
454 expandItem.setText(name);
455 ListViewer viewer = new ListViewer(expandBar);
456 viewer.setLabelProvider(new ComponentLabelProvider());
457 viewer.setContentProvider(new ComponentContentProvider());
459 expandItem.setControl(viewer.getList());
460 viewer.setInput(items);
461 expandItem.setHeight(viewer.getList().computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
462 viewer.addSelectionChangedListener(this);
466 private boolean hasInsertPosition() {
467 return component.getNext() == null || component.getPrevious() == null;
470 private void updateInsertPosition(PositionType type) {
471 if (insertPosition == type)
473 startButton.setSelection(type == PositionType.PREVIOUS);
474 middleButton.setSelection(type == PositionType.SPLIT);
475 endButton.setSelection(type == PositionType.NEXT);
476 insertPosition = type;
480 public void selectionChanged(SelectionChangedEvent event) {
481 IStructuredSelection sel = (IStructuredSelection) event.getSelection();
482 Item i = (Item) sel.getFirstElement();
485 if (inlineViewer != null && event.getSource() == inlineViewer) {
486 clearSelection(turnViewer);
487 clearSelection(endViewer);
488 } else if (turnViewer != null && event.getSource() == turnViewer) {
489 clearSelection(inlineViewer);
490 clearSelection(endViewer);
491 } else if (endViewer != null && event.getSource() == endViewer) {
492 clearSelection(inlineViewer);
493 clearSelection(turnViewer);
496 name = generateUniqueName(selected.getName());
497 nameText.setText(name);
503 private void clearSelection(ListViewer possibleListViewer) {
504 if (possibleListViewer != null)
505 possibleListViewer.setSelection(new StructuredSelection());
508 private void validate() {
509 filterAllowed.clear();
510 Set<PositionType> filterAllowed = new HashSet<PositionType>();
514 if (name.isEmpty() || usedNames.contains(name)) {
518 msg = "Please provide a valid name";
520 msg = "Name \"" + name + "\" is already in use";
524 if (selected == null) {
526 } else if (selected.isCode()) {// TODO : instead of disabling the button, we should filter the content.
529 lenghtAdjustable = ((selected.getType() == Type.INLINE)
530 && (selected.isVariable() || selected.isModifiable()));
532 if (!hasInsertPosition()) {
533 // We are inserting to a fully connected variable length component
534 // only allow insertion within the component
535 startButton.setEnabled(false);
536 middleButton.setEnabled(false);
537 endButton.setEnabled(false);
538 updateInsertPosition(PositionType.PREVIOUS);
539 } else if (insertAdjustable) {
540 switch (selected.getType()) {
542 startButton.setEnabled(false);
543 middleButton.setEnabled(false);
544 endButton.setEnabled(true);
545 updateInsertPosition(PositionType.NEXT);
548 if (!selected.isVariable()) {
549 startButton.setEnabled(true);
550 middleButton.setEnabled(true);
551 endButton.setEnabled(true);
553 startButton.setEnabled(false);
554 middleButton.setEnabled(false);
555 endButton.setEnabled(true);
556 updateInsertPosition(PositionType.NEXT);
560 startButton.setEnabled(false);
561 middleButton.setEnabled(false);
562 endButton.setEnabled(true);
563 updateInsertPosition(PositionType.NEXT);
566 startButton.setEnabled(false);
567 middleButton.setEnabled(true);
568 endButton.setEnabled(true);
569 if (insertPosition == PositionType.PREVIOUS)
570 updateInsertPosition(PositionType.NEXT);
573 throw new RuntimeException("Expected component, got equipment " + selected);
575 } else if (lenghtAdjustable) {
576 if (component instanceof InlineComponent) {
577 startButton.setEnabled(true);
578 middleButton.setEnabled(true);
579 endButton.setEnabled(true);
580 } else if (component instanceof TurnComponent) {
581 startButton.setEnabled(false);
582 middleButton.setEnabled(true);
583 endButton.setEnabled(true);
584 if (insertPosition == PositionType.PREVIOUS)
585 updateInsertPosition(PositionType.NEXT);
586 } else if (component instanceof EndComponent || component instanceof Nozzle) {
587 startButton.setEnabled(false);
588 middleButton.setEnabled(false);
589 endButton.setEnabled(true);
590 updateInsertPosition(PositionType.NEXT);
593 startButton.setEnabled(false);
594 middleButton.setEnabled(false);
595 endButton.setEnabled(true);
597 if (selected.isVariable() || selected.isModifiable()) {
598 if (selected.getType() == Type.INLINE) {
599 filterAllowed.add(PositionType.NEXT);
600 filterAllowed.add(PositionType.PREVIOUS);
601 if (inlineSplit && selected.isVariable()) {
602 lengthText.setEnabled(false);
603 angleText.setEnabled(false);
604 rotationAngleText.setEnabled(selected.isRotated());
606 if (msg == null) msg = "Cannot split a straight pipe with a straight pipe";
608 lengthText.setEnabled(true);
609 angleText.setEnabled(false);
610 rotationAngleText.setEnabled(selected.isRotated());
611 if (length == null || length <= 0.0) {
613 if (msg == null) msg = "Please provide a valid length";
616 } else if (selected.getType() == Type.TURN) {
617 filterAllowed.add(PositionType.NEXT);
618 filterAllowed.add(PositionType.PREVIOUS);
619 lengthText.setEnabled(false);
620 angleText.setEnabled(true);
621 rotationAngleText.setEnabled(true);
624 if (msg == null) msg = "Please provide a turn angle";
627 // this should not happen, since end components should not have variable, or
629 lengthText.setEnabled(false);
630 angleText.setEnabled(false);
631 rotationAngleText.setEnabled(false);
635 lengthText.setEnabled(false);
636 angleText.setEnabled(false);
637 rotationAngleText.setEnabled(selected.getType() == Type.TURN || selected.isRotated());
640 if (selected.isSizeChange()) {
641 turnRadiusText.setEnabled(true);
642 diameterText.setEnabled(true);
643 thicknessText.setEnabled(true);
644 if (diameter == null || diameter <= 0.0) {
646 if (msg == null) msg = "Please provide a valid diameter";
648 if (turnRadius == null || diameter != null && turnRadius < diameter / 2) {
650 if (msg == null) msg = "Please provide a valid turn radius";
652 if (thickness == null || thickness < 0.0 || diameter != null && thickness >= diameter / 2) {
654 if (msg == null) msg = "Please provide a valid wall thickness";
657 turnRadiusText.setEnabled(false);
658 diameterText.setEnabled(false);
659 thicknessText.setEnabled(false);
662 if (!selected.isVariable()) {
663 switch (selected.getType()) {
665 filterAllowed.add(PositionType.NEXT);
666 filterAllowed.add(PositionType.PREVIOUS);
672 filterAllowed.add(PositionType.NEXT);
673 filterAllowed.add(PositionType.PREVIOUS);
674 filterAllowed.add(PositionType.SPLIT);
676 filterAllowed.add(PositionType.NEXT);
677 filterAllowed.add(PositionType.PREVIOUS);
681 for (PositionType t : filterAllowed) {
682 if (allowed.contains(t))
683 this.filterAllowed.add(t);
686 validationLabel.setText(msg != null ? msg : "");
687 validationLabel.requestLayout();
689 getButton(OK).setEnabled(ok);
692 private String generateUniqueName(String name) {
695 while (usedNames.contains((newName = name + "_" + i)))
700 public Item getSelected() {
704 public String getName() {
708 public Double getAngle() {
712 public Double getLength() {
716 public Double getRotationAngle() {
717 return rotationAngle;
720 public Double getDiameter() {
724 public Double getTurnRadius() {
728 public Set<PositionType> filterAllowed() {
729 return filterAllowed;
732 public PositionType getInsertPosition() {
733 return insertPosition;
736 public boolean isInsertAdjustable() {
737 return insertAdjustable;
740 public boolean isLenghtAdjustable() {
741 return lenghtAdjustable;
744 public Double getThickness() {