diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetMetadataPanel.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetMetadataPanel.java index ad43a077396b63b0d4790b1652d893cbe816c69a..dd378dbf9a271b4229988a8481779e614b49e025 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetMetadataPanel.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetMetadataPanel.java @@ -140,7 +140,7 @@ public class DataSetMetadataPanel extends JPanel implements Observer private final JLabel ownerIdLabel; - private final JTextField sampleIdText; + private final SamplePickerPanel samplePanel; private final JTextField dataSetIdText; @@ -184,7 +184,9 @@ public class DataSetMetadataPanel extends JPanel implements Observer // Initialize the fields in the gui ownerIdLabel = new JLabel("Owner:", JLabel.TRAILING); - sampleIdText = new JTextField(); + samplePanel = + new SamplePickerPanel(mainWindow, clientModel.getExperiments(), + clientModel.getOpenBISService()); dataSetIdText = new JTextField(); experimentPicker = new ExperimentPickerPanel(mainWindow, clientModel.getExperiments()); @@ -192,7 +194,7 @@ public class DataSetMetadataPanel extends JPanel implements Observer ownerComboBox = new JComboBox(DataSetOwnerType.values()); ownerIdPanel.add(experimentPicker, DataSetOwnerType.EXPERIMENT.toString()); - ownerIdPanel.add(sampleIdText, DataSetOwnerType.SAMPLE.toString()); + ownerIdPanel.add(samplePanel, DataSetOwnerType.SAMPLE.toString()); ownerIdPanel.add(dataSetIdText, DataSetOwnerType.DATA_SET.toString()); dataSetTypeComboBox = new JComboBox(); @@ -220,7 +222,7 @@ public class DataSetMetadataPanel extends JPanel implements Observer { if (null == newDataSetInfo) { - sampleIdText.setText(EMPTY_FILE_SELECTION); + samplePanel.setText(EMPTY_FILE_SELECTION); experimentPicker.setText(EMPTY_FILE_SELECTION); updateFileLabel(); disableAllWidgets(); @@ -240,7 +242,7 @@ public class DataSetMetadataPanel extends JPanel implements Observer experimentPicker.setText(builder.getDataSetOwnerIdentifier()); break; case SAMPLE: - sampleIdText.setText(builder.getDataSetOwnerIdentifier()); + samplePanel.setText(builder.getDataSetOwnerIdentifier()); break; case DATA_SET: dataSetIdText.setText(builder.getDataSetOwnerIdentifier()); @@ -285,7 +287,7 @@ public class DataSetMetadataPanel extends JPanel implements Observer { ArrayList<JComponent> editableWidgets = new ArrayList<JComponent>(); editableWidgets.add(dataSetIdText); - editableWidgets.add(sampleIdText); + editableWidgets.add(samplePanel); editableWidgets.add(experimentPicker); editableWidgets.add(dataSetFileButton); editableWidgets.add(ownerComboBox); @@ -379,22 +381,22 @@ public class DataSetMetadataPanel extends JPanel implements Observer // The owner row ownerIdLabel.setPreferredSize(new Dimension(LABEL_WIDTH, BUTTON_HEIGHT)); - sampleIdText.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT)); - sampleIdText.addActionListener(new ActionListener() + samplePanel.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT)); + samplePanel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - setOwnerId(sampleIdText.getText()); + setOwnerId(samplePanel.getText()); } }); - sampleIdText.addFocusListener(new FocusListener() + samplePanel.addFocusListener(new FocusListener() { public void focusLost(FocusEvent e) { - setOwnerId(sampleIdText.getText()); + setOwnerId(samplePanel.getText()); } public void focusGained(FocusEvent e) @@ -666,7 +668,7 @@ public class DataSetMetadataPanel extends JPanel implements Observer case EXPERIMENT: return experimentPicker.getText(); case SAMPLE: - return sampleIdText.getText(); + return samplePanel.getText(); case DATA_SET: return dataSetIdText.getText(); } diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerDialog.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..862ef6cca1954e9fbbcdbe9efb442a14eacae3dc --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerDialog.java @@ -0,0 +1,243 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.client.api.gui; + +import java.awt.Point; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Collections; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeWillExpandListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.ExpandVetoException; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +import ch.systemsx.cisd.openbis.dss.client.api.gui.tree.FilterableMutableTreeNode; +import ch.systemsx.cisd.openbis.dss.client.api.v1.IOpenbisServiceFacade; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample; + +/** + * @author Pawel Glyzewski + */ +public class SamplePickerDialog extends JDialog implements TreeWillExpandListener +{ + private static final long serialVersionUID = 1L; + + private final JTree tree; + + private final JTextField filterField; + + private final JFrame mainWindow; + + private final JOptionPane optionPane; + + private final IOpenbisServiceFacade openbisService; + + private final Timer scheduler = new Timer(); + + /** + * @param mainWindow + * @param experiments + * @param openbisService + */ + public SamplePickerDialog(JFrame mainWindow, List<Experiment> experiments, + final IOpenbisServiceFacade openbisService) + { + super(mainWindow, "Pick a sample", true); + + this.mainWindow = mainWindow; + this.openbisService = openbisService; + + FilterableMutableTreeNode top = new FilterableMutableTreeNode("Experiments"); + createNodes(top, experiments); + tree = new JTree(top); + tree.addTreeWillExpandListener(this); + tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + + filterField = createFilterField(top, tree); + + optionPane = createOptionPane(filterField, tree, this); + this.setContentPane(optionPane); + } + + private static JOptionPane createOptionPane(JTextField filterField, final JTree tree, + final JDialog parent) + { + final JScrollPane scrollPane = new JScrollPane(tree); + + Object[] objects = new Object[] + { "Filter experiments: ", filterField, "Select Sample:", scrollPane }; + final JOptionPane optionPane = + new JOptionPane(objects, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION); + optionPane.addPropertyChangeListener(new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent evt) + { + if (evt.getPropertyName().equals(JOptionPane.VALUE_PROPERTY) + && evt.getNewValue() != null) + { + System.out.println(tree.getSelectionPath().getPath().length); + if (((Integer) evt.getNewValue()).intValue() == JOptionPane.OK_OPTION + && tree.getSelectionPath() == null) + { + JOptionPane.showMessageDialog(parent, "Sample needs to be selected!", + "No sample selected!", JOptionPane.WARNING_MESSAGE); + optionPane.setValue(optionPane.getInitialValue()); + } else if (((Integer) evt.getNewValue()).intValue() == JOptionPane.OK_OPTION + && tree.getSelectionPath().getPath().length < 3) + { + JOptionPane.showMessageDialog(parent, + "Sample should be selected, not experiment!", + "No sample selected!", JOptionPane.WARNING_MESSAGE); + optionPane.setValue(optionPane.getInitialValue()); + } else + { + parent.setVisible(false); + } + } + } + }); + + return optionPane; + } + + private static void createNodes(FilterableMutableTreeNode top, List<Experiment> experiments) + { + for (Experiment experiment : experiments) + { + DefaultMutableTreeNode category = + new DefaultMutableTreeNode(experiment.getIdentifier(), true); + category.add(new DefaultMutableTreeNode("dummy child")); + top.add(category); + } + } + + private static JTextField createFilterField(final FilterableMutableTreeNode treeNode, + final JTree tree) + { + final JTextField filterField = new JTextField(); + filterField.setEditable(true); + filterField.getDocument().addDocumentListener(new DocumentListener() + { + public void removeUpdate(DocumentEvent e) + { + treeNode.filter(filterField.getText()); + ((DefaultTreeModel) tree.getModel()).reload(); + } + + public void insertUpdate(DocumentEvent e) + { + treeNode.filter(filterField.getText()); + ((DefaultTreeModel) tree.getModel()).reload(); + } + + public void changedUpdate(DocumentEvent e) + { + treeNode.filter(filterField.getText()); + ((DefaultTreeModel) tree.getModel()).reload(); + } + }); + + return filterField; + } + + public String pickSample() + { + this.pack(); + + int height = this.getHeight() > 500 ? 500 : this.getHeight(); + int width = this.getWidth() > 600 ? 600 : this.getWidth(); + this.setSize(width, height); + + Point mwLocation = mainWindow.getLocationOnScreen(); + int x = mwLocation.x + (mainWindow.getWidth() / 2) - (this.getWidth() / 2); + int y = mwLocation.y + (mainWindow.getHeight() / 2) - (this.getHeight() / 2); + + this.setLocation(x > 0 ? x : 0, y > 0 ? y : 0); + + this.setVisible(true); + + Object value = optionPane.getValue(); + optionPane.setValue(optionPane.getInitialValue()); + if (value == null || ((Integer) value).intValue() == JOptionPane.CANCEL_OPTION) + { + return null; + } else + { + return tree.getSelectionPath().getLastPathComponent().toString(); + } + } + + public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException + { + // if top level, then finish + if (((TreeNode) event.getPath().getLastPathComponent()).getParent() == null) + { + return; + } + + List<Sample> samples = + openbisService.listSamplesForExperiments(Collections.singletonList(event.getPath() + .getLastPathComponent().toString())); + + final DefaultMutableTreeNode node = + (DefaultMutableTreeNode) event.getPath().getLastPathComponent(); + + node.removeAllChildren(); + for (Sample s : samples) + { + node.add(new DefaultMutableTreeNode(s.getIdentifier())); + } + + if (node.getChildCount() == 0) + { + scheduler.schedule(new TimerTask() + { + @Override + public void run() + { + tree.collapsePath(new TreePath(node.getPath())); + } + }, 1500l); + } + } + + public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException + { + DefaultMutableTreeNode node = + (DefaultMutableTreeNode) event.getPath().getLastPathComponent(); + + node.removeAllChildren(); + node.add(new DefaultMutableTreeNode("dummy child")); + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerPanel.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..c3093e8bec0a0dce0fe6730525c22a35033b3084 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerPanel.java @@ -0,0 +1,114 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.client.api.gui; + +import java.awt.BorderLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusListener; +import java.util.List; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import ch.systemsx.cisd.openbis.dss.client.api.v1.IOpenbisServiceFacade; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment; + +/** + * @author Pawel Glyzewski + */ +public class SamplePickerPanel extends JPanel +{ + + private static final long serialVersionUID = 1L; + + private static class JTextFieldFireActionPerformedExposed extends JTextField + { + private static final long serialVersionUID = -7656053161479466883L; + + @Override + public void fireActionPerformed() + { + super.fireActionPerformed(); + } + } + + private final JTextFieldFireActionPerformedExposed textField = + new JTextFieldFireActionPerformedExposed(); + + private final JButton button = new JButton("..."); + + private final SamplePickerDialog dialog; + + public SamplePickerPanel(final JFrame mainWindow, List<Experiment> experiments, + IOpenbisServiceFacade openbisService) + { + super(new BorderLayout()); + + dialog = new SamplePickerDialog(mainWindow, experiments, openbisService); + button.setMargin(new Insets(button.getMargin().top, 2, button.getMargin().bottom, 2)); + + button.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + String sampleId = dialog.pickSample(); + if (sampleId != null) + { + textField.setText(sampleId); + textField.fireActionPerformed(); + } + } + }); + + add(textField, BorderLayout.CENTER); + add(button, BorderLayout.EAST); + } + + public String getText() + { + return textField.getText(); + } + + public void setText(String text) + { + textField.setText(text); + } + + public void addActionListener(ActionListener actionListener) + { + textField.addActionListener(actionListener); + } + + @Override + public void addFocusListener(FocusListener focusListener) + { + textField.addFocusListener(focusListener); + button.addFocusListener(focusListener); + } + + @Override + public void setEnabled(boolean enabled) + { + textField.setEditable(enabled); + textField.setEnabled(enabled); + button.setEnabled(enabled); + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/tree/FilterableMutableTreeNode.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/tree/FilterableMutableTreeNode.java new file mode 100644 index 0000000000000000000000000000000000000000..16308fad99feae63748098c9f4d1e413b1c7bc09 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/tree/FilterableMutableTreeNode.java @@ -0,0 +1,198 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.client.api.gui.tree; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.regex.Pattern; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.MutableTreeNode; +import javax.swing.tree.TreeNode; + +/** + * @author Pawel Glyzewski + */ +public class FilterableMutableTreeNode extends DefaultMutableTreeNode +{ + private static final long serialVersionUID = 1L; + + private static class FilteredEnumerationWrapper implements Enumeration<Object> + { + private final Enumeration<Object> enumeration; + + private final Pattern pattern; + + private Object next; + + private FilteredEnumerationWrapper(final Enumeration<Object> enumeration, + final Pattern pattern) + { + this.enumeration = enumeration; + this.pattern = pattern; + } + + public boolean hasMoreElements() + { + return next != null; + } + + public Object nextElement() + { + if (next == null) + { + return enumeration.nextElement(); + } + final Object ret = next; + next = null; + + while (enumeration.hasMoreElements()) + { + final Object tmp = enumeration.nextElement(); + if (pattern.matcher(tmp.toString()).find()) + { + next = tmp; + break; + } + } + + return ret; + } + } + + private Pattern pattern = Pattern.compile(".*"); + + private ArrayList<Object> filtered = new ArrayList<Object>(); + + public FilterableMutableTreeNode(Object nodeValue) + { + super(nodeValue); + + filter(".*"); + } + + private synchronized Pattern getPattern() + { + return this.pattern; + } + + private synchronized void setPattern(Pattern pattern) + { + this.pattern = pattern; + } + + private synchronized ArrayList<Object> getFiltered() + { + return this.filtered; + } + + private synchronized void setFiltered(ArrayList<Object> filtered) + { + this.filtered = filtered; + } + + public void filter(String filter) + { + @SuppressWarnings("hiding") + Pattern pattern; + + try + { + pattern = Pattern.compile("(?i)" + filter); + } catch (RuntimeException e) + { + pattern = Pattern.compile(".*"); + } + setPattern(pattern); + + filter(pattern); + } + + public void filter(@SuppressWarnings("hiding") Pattern pattern) + { + @SuppressWarnings("hiding") + ArrayList<Object> filtered = new ArrayList<Object>(); + @SuppressWarnings("unchecked") + Enumeration<Object> enumeration = super.children(); + while (enumeration.hasMoreElements()) + { + Object o = enumeration.nextElement(); + if (pattern.matcher(o.toString()).find()) + { + filtered.add(o); + } + } + setFiltered(filtered); + } + + @Override + public TreeNode getChildAt(int childIndex) + { + @SuppressWarnings("hiding") + final ArrayList<Object> filtered = getFiltered(); + + if (filtered == null) + { + throw new ArrayIndexOutOfBoundsException("node has no children"); + } + return (TreeNode) filtered.get(childIndex); + } + + @Override + public int getChildCount() + { + @SuppressWarnings("hiding") + final ArrayList<Object> filtered = getFiltered(); + + if (filtered == null) + { + return 0; + } else + { + return filtered.size(); + } + } + + @Override + public int getIndex(TreeNode aChild) + { + if (aChild == null) + { + throw new IllegalArgumentException("argument is null"); + } + + if (!isNodeChild(aChild)) + { + return -1; + } + return getFiltered().indexOf(aChild); // linear search + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration<Object> children() + { + return new FilteredEnumerationWrapper(super.children(), getPattern()); + } + + @Override + public void add(MutableTreeNode o) + { + super.add(o); + filter(getPattern()); + } +}