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.PipeRun;
46 import org.simantics.plant3d.scenegraph.PipelineComponent;
47 import org.simantics.plant3d.scenegraph.TurnComponent;
48 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
49 import org.simantics.plant3d.utils.Item;
50 import org.simantics.plant3d.utils.Item.Type;
51 import org.simantics.plant3d.utils.P3DUtil;
52 import org.simantics.utils.ui.ExceptionUtils;
54 public class ComponentSelectionDialog extends Dialog implements ISelectionChangedListener {
56 private static final String DIALOG = "ComponentSelectionDialog"; //$NON-NLS-1$
58 private IDialogSettings dialogSettings;
60 private double lengthFactor = 1.0;
62 private ResourceManager resourceManager;
64 private String libUri;
66 private Item selected;
67 private Set<PositionType> allowed;
68 private Set<PositionType> filterAllowed;
69 // private boolean positionAdjustment;
70 private PipelineComponent component;
71 private boolean insertAdjustable;
72 private boolean lenghtAdjustable;
73 private PositionType insertPosition = PositionType.NEXT;
76 private Double length;
77 private Double rotationAngle;
81 private Text nameText;
84 private Text lengthText;
86 private Text angleText;
87 // Rotated In-line, or turn component.
88 private Text rotationAngleText;
90 // Input for new PipeRun
91 private Double diameter;
92 private Double thickness;
93 private Double turnRadius;
95 private Text diameterText;
96 private Text thicknessText;
97 private Text turnRadiusText;
99 // Validation message label
100 private Label validationLabel;
102 // Position selection
103 private Button startButton;
104 private Button middleButton;
105 private Button endButton;
107 private boolean inlineSplit = false;
109 ListViewer inlineViewer;
110 ListViewer turnViewer;
111 ListViewer endViewer;
113 private Set<String> usedNames;
115 public ComponentSelectionDialog(Shell parentShell, Set<PositionType> allowed, PipelineComponent component) {
116 this(parentShell, allowed, component, Plant3D.URIs.Builtin);
119 public ComponentSelectionDialog(Shell parentShell, Set<PositionType> allowed, PipelineComponent component,
122 this.allowed = allowed;
123 this.component = component;
124 filterAllowed = new HashSet<PositionType>();
125 insertAdjustable = component instanceof InlineComponent ? ((InlineComponent) component).isVariableLength()
127 lenghtAdjustable = false;
128 this.libUri = libUri;
130 usedNames = new HashSet<>();
132 setShellStyle(getShellStyle() | SWT.RESIZE);
134 IDialogSettings settings = Activator.getDefault().getDialogSettings();
135 dialogSettings = settings.getSection(DIALOG);
136 if (dialogSettings == null)
137 dialogSettings = settings.addNewSection(DIALOG);
139 if (component.getNext() != null && component.getPrevious() != null)
140 insertPosition = PositionType.PREVIOUS;
143 private void copyPipeRunParameters() {
144 PipeRun run = component.getPipeRun();
145 if (component.getNext() == null && component.getAlternativePipeRun() != null)
146 run = component.getAlternativePipeRun();
151 diameter = run.getPipeDiameter();
152 thickness = run.getPipeThickness();
153 turnRadius = run.getTurnRadius();
155 diameterText.setText(Double.toString(diameter * lengthFactor));
156 thicknessText.setText(Double.toString(thickness * lengthFactor));
157 turnRadiusText.setText(Double.toString(turnRadius * lengthFactor));
160 public void setLengthFactor(double lengthFactor) {
161 this.lengthFactor = lengthFactor;
165 protected IDialogSettings getDialogBoundsSettings() {
166 return dialogSettings;
170 protected void configureShell(Shell newShell) {
171 super.configureShell(newShell);
172 newShell.setText("Create pipeline component");
175 public void addUsedNames(Collection<String> names) {
176 usedNames.addAll(names);
179 protected List<Item> getItems(Class<?> c, String libUri) throws DatabaseException {
180 Session session = Simantics.getSession();
181 if (InlineComponent.class.equals(c)) {
182 return P3DUtil.getInlines(session, libUri);
183 } else if (TurnComponent.class.equals(c)) {
184 return P3DUtil.getTurns(session, libUri);
185 } else if (EndComponent.class.equals(c)) {
186 return P3DUtil.getEnds(session, libUri);
193 protected Control createDialogArea(Composite parent) {
194 resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
196 Composite composite = new Composite(parent, SWT.NONE);
197 GridLayout layout = new GridLayout(2, false);
198 layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
199 layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
200 layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
201 layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
202 composite.setLayout(layout);
203 composite.setLayoutData(new GridData(GridData.FILL_BOTH));
204 applyDialogFont(composite);
206 // Grid layout data for fields that grab horizontal space
207 final GridDataFactory horizFillData = GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP);
209 // TODO : we need better classification than inline,turn, and end:
210 // * fixed length inlines
211 // * fixed angle turns
212 // * size changes (requires input for pipe run specs)
213 // * variable length inlines (input for length)
214 // * variable angle turns (input for angle)
217 List<Item> ends = null;
218 List<Item> turns = null;
219 List<Item> inlines = null;
221 ends = getItems(EndComponent.class, libUri);
222 turns = getItems(TurnComponent.class, libUri);
223 inlines = getItems(InlineComponent.class, libUri);
224 } catch (DatabaseException e) {
225 Label label = new Label(composite, SWT.NONE);
226 label.setText("Cannot load pipeline components: " + e.getMessage());
227 ExceptionUtils.logError(e);
230 ends = P3DUtil.filterUserComponents(ends);
231 turns = P3DUtil.filterUserComponents(turns);
232 inlines = P3DUtil.filterUserComponents(inlines);
234 ExpandBar expandBar = new ExpandBar(composite, SWT.V_SCROLL);
236 if (!inlines.isEmpty()) {
237 inlineViewer = createItemList(expandBar, inlines, "Inline");
240 // Only allow elbows and ends to be added to open ends of the pipelines
241 boolean isOpen = component.getNext() == null || component.getPrevious() == null;
243 if (!turns.isEmpty()) {
244 turnViewer = createItemList(expandBar, turns, "Elbow");
247 if (!ends.isEmpty()) {
248 turnViewer = createItemList(expandBar, ends, "End");
252 GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).span(2, 1).applyTo(expandBar);
253 GridDataFactory.fillDefaults().grab(true, true).minSize(500, 500).hint(500, 500).applyTo(composite);
255 Label label = new Label(composite, SWT.NONE);
256 label.setText("Position");
257 Composite buttonComposite = new Composite(composite, SWT.NONE);
258 startButton = new Button(buttonComposite, SWT.TOGGLE);
259 middleButton = new Button(buttonComposite, SWT.TOGGLE);
260 endButton = new Button(buttonComposite, SWT.TOGGLE);
261 startButton.setImage(resourceManager.createImage(
262 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_start.png").get()));
263 middleButton.setImage(resourceManager.createImage(
264 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_middle.png").get()));
265 endButton.setImage(resourceManager.createImage(
266 ResourceLocator.imageDescriptorFromBundle(Activator.PLUGIN_ID, "icons/insert_end.png").get()));
267 startButton.setToolTipText("Overlapping insert");
268 middleButton.setToolTipText("Cutting insert");
269 endButton.setToolTipText("Adding insert");
270 horizFillData.applyTo(buttonComposite);
271 GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonComposite);
273 startButton.setSelection(insertPosition == PositionType.PREVIOUS);
274 middleButton.setSelection(insertPosition == PositionType.SPLIT);
275 endButton.setSelection(insertPosition == PositionType.NEXT);
277 startButton.setEnabled(false);
278 middleButton.setEnabled(false);
279 endButton.setEnabled(false);
281 startButton.addSelectionListener(new SelectionAdapter() {
283 public void widgetSelected(SelectionEvent e) {
284 updateInsertPosition(PositionType.PREVIOUS);
288 middleButton.addSelectionListener(new SelectionAdapter() {
290 public void widgetSelected(SelectionEvent e) {
291 updateInsertPosition(PositionType.SPLIT);
294 endButton.addSelectionListener(new SelectionAdapter() {
296 public void widgetSelected(SelectionEvent e) {
297 updateInsertPosition(PositionType.NEXT);
301 if (!hasInsertPosition()) {
302 label.setVisible(false);
303 buttonComposite.setVisible(false);
306 label = new Label(composite, SWT.NONE);
307 label.setText("Name");
308 nameText = new Text(composite, SWT.SINGLE | SWT.BORDER);
310 label = new Label(composite, SWT.NONE);
311 label.setText("Length");
312 lengthText = new Text(composite, SWT.SINGLE | SWT.BORDER);
313 label = new Label(composite, SWT.NONE);
314 label.setText("Angle");
315 angleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
316 label = new Label(composite, SWT.NONE);
317 label.setText("Rotation angle");
318 rotationAngleText = new Text(composite, SWT.SINGLE | SWT.BORDER);
320 label = new Label(composite, SWT.NONE);
321 label.setText("Diameter");
322 diameterText = new Text(composite, SWT.SINGLE | SWT.BORDER);
323 label = new Label(composite, SWT.NONE);
324 label.setText("Wall thickness");
325 thicknessText = new Text(composite, SWT.SINGLE | SWT.BORDER);
326 label = new Label(composite, SWT.NONE);
327 label.setText("Elbow radius");
328 turnRadiusText = new Text(composite, SWT.SINGLE | SWT.BORDER);
330 validationLabel = new Label(composite, SWT.NONE);
331 validationLabel.setText("");
333 lengthText.setEnabled(false);
334 angleText.setEnabled(false);
335 rotationAngleText.setEnabled(false);
336 turnRadiusText.setEnabled(false);
337 diameterText.setEnabled(false);
338 thicknessText.setEnabled(false);
340 copyPipeRunParameters();
342 nameText.addKeyListener(new KeyAdapter() {
344 public void keyReleased(KeyEvent e) {
345 name = nameText.getText();
350 lengthText.addKeyListener(new KeyAdapter() {
352 public void keyReleased(KeyEvent e) {
354 length = Double.parseDouble(lengthText.getText()) / lengthFactor;
355 } catch (NumberFormatException err) {
362 angleText.addKeyListener(new KeyAdapter() {
364 public void keyReleased(KeyEvent e) {
366 angle = Double.parseDouble(angleText.getText());
367 } catch (NumberFormatException err) {
374 rotationAngleText.addKeyListener(new KeyAdapter() {
376 public void keyReleased(KeyEvent e) {
378 rotationAngle = Double.parseDouble(rotationAngleText.getText());
379 } catch (NumberFormatException err) {
380 rotationAngle = null;
386 diameterText.addKeyListener(new KeyAdapter() {
388 public void keyReleased(KeyEvent e) {
390 diameter = Double.parseDouble(diameterText.getText()) / lengthFactor;
391 } catch (NumberFormatException err) {
398 thicknessText.addKeyListener(new KeyAdapter() {
400 public void keyReleased(KeyEvent e) {
402 thickness = Double.parseDouble(thicknessText.getText()) / lengthFactor;
403 } catch (NumberFormatException err) {
410 turnRadiusText.addKeyListener(new KeyAdapter() {
412 public void keyReleased(KeyEvent e) {
414 turnRadius = Double.parseDouble(turnRadiusText.getText()) / lengthFactor;
415 } catch (NumberFormatException err) {
422 horizFillData.applyTo(nameText);
423 horizFillData.applyTo(lengthText);
424 horizFillData.applyTo(angleText);
425 horizFillData.applyTo(rotationAngleText);
426 horizFillData.applyTo(diameterText);
427 horizFillData.applyTo(thicknessText);
428 horizFillData.applyTo(turnRadiusText);
430 GridDataFactory.fillDefaults().span(2, 1).align(SWT.END, SWT.END).grab(true, false).applyTo(validationLabel);
432 if (!allowed.contains(PositionType.NEXT) && !allowed.contains(PositionType.PREVIOUS)) {
433 turnViewer.getList().setEnabled(false);
434 endViewer.getList().setEnabled(false);
441 private ListViewer createItemList(ExpandBar expandBar, List<Item> items, String name) {
442 ExpandItem expandItem = new ExpandItem(expandBar, SWT.NONE);
443 expandItem.setText(name);
444 ListViewer viewer = new ListViewer(expandBar);
445 viewer.setLabelProvider(new ComponentLabelProvider());
446 viewer.setContentProvider(new ComponentContentProvider());
448 expandItem.setControl(viewer.getList());
449 viewer.setInput(items);
450 expandItem.setHeight(viewer.getList().computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
451 viewer.addSelectionChangedListener(this);
455 private boolean hasInsertPosition() {
456 return component.getNext() == null || component.getPrevious() == null;
459 private void updateInsertPosition(PositionType type) {
460 if (insertPosition == type)
462 startButton.setSelection(type == PositionType.PREVIOUS);
463 middleButton.setSelection(type == PositionType.SPLIT);
464 endButton.setSelection(type == PositionType.NEXT);
465 insertPosition = type;
469 public void selectionChanged(SelectionChangedEvent event) {
470 IStructuredSelection sel = (IStructuredSelection) event.getSelection();
471 Item i = (Item) sel.getFirstElement();
474 if (inlineViewer != null && event.getSource() == inlineViewer) {
475 clearSelection(turnViewer);
476 clearSelection(endViewer);
477 } else if (turnViewer != null && event.getSource() == turnViewer) {
478 clearSelection(inlineViewer);
479 clearSelection(endViewer);
480 } else if (endViewer != null && event.getSource() == endViewer) {
481 clearSelection(inlineViewer);
482 clearSelection(turnViewer);
485 name = generateUniqueName(selected.getName());
486 nameText.setText(name);
492 private void clearSelection(ListViewer possibleListViewer) {
493 if (possibleListViewer != null)
494 possibleListViewer.setSelection(new StructuredSelection());
497 private void validate() {
498 filterAllowed.clear();
499 Set<PositionType> filterAllowed = new HashSet<PositionType>();
503 if (name.isEmpty() || usedNames.contains(name)) {
507 msg = "Please provide a valid name";
509 msg = "Name \"" + name + "\" is already in use";
513 if (selected == null) {
515 } else if (selected.isCode()) {// TODO : instead of disabling the button, we should filter the content.
518 lenghtAdjustable = ((selected.getType() == Type.INLINE)
519 && (selected.isVariable() || selected.isModifiable()));
521 if (!hasInsertPosition()) {
522 // We are inserting to a fully connected variable length component
523 // only allow insertion within the component
524 startButton.setEnabled(false);
525 middleButton.setEnabled(false);
526 endButton.setEnabled(false);
527 updateInsertPosition(PositionType.PREVIOUS);
528 } else if (insertAdjustable) {
529 switch (selected.getType()) {
531 startButton.setEnabled(false);
532 middleButton.setEnabled(false);
533 endButton.setEnabled(true);
534 updateInsertPosition(PositionType.NEXT);
537 if (!selected.isVariable()) {
538 startButton.setEnabled(true);
539 middleButton.setEnabled(true);
540 endButton.setEnabled(true);
542 startButton.setEnabled(false);
543 middleButton.setEnabled(false);
544 endButton.setEnabled(true);
545 updateInsertPosition(PositionType.NEXT);
549 startButton.setEnabled(false);
550 middleButton.setEnabled(false);
551 endButton.setEnabled(true);
552 updateInsertPosition(PositionType.NEXT);
555 startButton.setEnabled(false);
556 middleButton.setEnabled(true);
557 endButton.setEnabled(true);
558 if (insertPosition == PositionType.PREVIOUS)
559 updateInsertPosition(PositionType.NEXT);
562 throw new RuntimeException("Expected component, got equipment " + selected);
564 } else if (lenghtAdjustable) {
565 if (component instanceof InlineComponent) {
566 startButton.setEnabled(true);
567 middleButton.setEnabled(true);
568 endButton.setEnabled(true);
569 } else if (component instanceof TurnComponent) {
570 startButton.setEnabled(false);
571 middleButton.setEnabled(true);
572 endButton.setEnabled(true);
573 if (insertPosition == PositionType.PREVIOUS)
574 updateInsertPosition(PositionType.NEXT);
575 } else if (component instanceof EndComponent || component instanceof Nozzle) {
576 startButton.setEnabled(false);
577 middleButton.setEnabled(false);
578 endButton.setEnabled(true);
579 updateInsertPosition(PositionType.NEXT);
582 startButton.setEnabled(false);
583 middleButton.setEnabled(false);
584 endButton.setEnabled(true);
586 if (selected.isVariable() || selected.isModifiable()) {
587 if (selected.getType() == Type.INLINE) {
588 filterAllowed.add(PositionType.NEXT);
589 filterAllowed.add(PositionType.PREVIOUS);
590 if (inlineSplit && selected.isVariable()) {
591 lengthText.setEnabled(false);
592 angleText.setEnabled(false);
593 rotationAngleText.setEnabled(selected.isRotated());
595 if (msg == null) msg = "Cannot split a straight pipe with a straight pipe";
597 lengthText.setEnabled(true);
598 angleText.setEnabled(false);
599 rotationAngleText.setEnabled(selected.isRotated());
600 if (length == null || length <= 0.0) {
602 if (msg == null) msg = "Please provide a valid length";
605 } else if (selected.getType() == Type.TURN) {
606 filterAllowed.add(PositionType.NEXT);
607 filterAllowed.add(PositionType.PREVIOUS);
608 lengthText.setEnabled(false);
609 angleText.setEnabled(true);
610 rotationAngleText.setEnabled(true);
613 if (msg == null) msg = "Please provide a turn angle";
616 // this should not happen, since end components should not have variable, or
618 lengthText.setEnabled(false);
619 angleText.setEnabled(false);
620 rotationAngleText.setEnabled(false);
624 lengthText.setEnabled(false);
625 angleText.setEnabled(false);
626 rotationAngleText.setEnabled(selected.getType() == Type.TURN || selected.isRotated());
629 if (selected.isSizeChange()) {
630 turnRadiusText.setEnabled(true);
631 diameterText.setEnabled(true);
632 thicknessText.setEnabled(true);
633 if (diameter == null || diameter <= 0.0) {
635 if (msg == null) msg = "Please provide a valid diameter";
637 if (turnRadius == null || diameter != null && turnRadius < diameter / 2) {
639 if (msg == null) msg = "Please provide a valid turn radius";
641 if (thickness == null || thickness < 0.0 || diameter != null && thickness >= diameter / 2) {
643 if (msg == null) msg = "Please provide a valid wall thickness";
646 turnRadiusText.setEnabled(false);
647 diameterText.setEnabled(false);
648 thicknessText.setEnabled(false);
651 if (!selected.isVariable()) {
652 switch (selected.getType()) {
654 filterAllowed.add(PositionType.NEXT);
655 filterAllowed.add(PositionType.PREVIOUS);
661 filterAllowed.add(PositionType.NEXT);
662 filterAllowed.add(PositionType.PREVIOUS);
663 filterAllowed.add(PositionType.SPLIT);
665 filterAllowed.add(PositionType.NEXT);
666 filterAllowed.add(PositionType.PREVIOUS);
670 for (PositionType t : filterAllowed) {
671 if (allowed.contains(t))
672 this.filterAllowed.add(t);
675 validationLabel.setText(msg != null ? msg : "");
676 validationLabel.requestLayout();
678 getButton(OK).setEnabled(ok);
681 private String generateUniqueName(String name) {
684 while (usedNames.contains((newName = name + "_" + i)))
689 public Item getSelected() {
693 public String getName() {
697 public Double getAngle() {
701 public Double getLength() {
705 public Double getRotationAngle() {
706 return rotationAngle;
709 public Double getDiameter() {
713 public Double getTurnRadius() {
717 public Set<PositionType> filterAllowed() {
718 return filterAllowed;
721 public PositionType getInsertPosition() {
722 return insertPosition;
725 public boolean isInsertAdjustable() {
726 return insertAdjustable;
729 public boolean isLenghtAdjustable() {
730 return lenghtAdjustable;
733 public Double getThickness() {