/* *------------------------------------------------------------------------------ * Copyright (C) 2006-2013 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.agents.imviewer.util.proj; import java.awt.BorderLayout; import java.awt.Container; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JTextField; import javax.swing.WindowConstants; import javax.swing.border.TitledBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import info.clearthought.layout.TableLayout; import org.apache.commons.collections.CollectionUtils; import org.openmicroscopy.shoola.agents.imviewer.IconManager; import org.openmicroscopy.shoola.agents.imviewer.ImViewerAgent; import org.openmicroscopy.shoola.agents.util.ComboBoxToolTipRenderer; import org.openmicroscopy.shoola.agents.util.EditorUtil; import org.openmicroscopy.shoola.agents.util.ViewerSorter; import org.openmicroscopy.shoola.agents.util.browser.DataNode; import org.openmicroscopy.shoola.env.LookupNames; import org.openmicroscopy.shoola.env.data.model.ProjectionParam; import org.openmicroscopy.shoola.util.ui.TitlePanel; import org.openmicroscopy.shoola.util.ui.UIUtilities; import org.openmicroscopy.shoola.util.ui.filechooser.CreateFolderDialog; import org.openmicroscopy.shoola.util.ui.slider.TextualTwoKnobsSlider; import omero.gateway.model.DataObject; import omero.gateway.model.DatasetData; import omero.gateway.model.ExperimenterData; import omero.gateway.model.ProjectData; /** * Dialog used to set the extra parameters required to project the image. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @version 3.0 * <small> * (<b>Internal version:</b> $Revision: $Date: $) * </small> * @since 3.0-Beta4 */ public class ProjSavingDialog extends JDialog implements ActionListener, DocumentListener, PropertyChangeListener { /** Bound property indicating to project the image. */ public static final String PROJECTION_PROPERTY = "projection"; /** Bound property indicating to load all the datasets. */ public static final String LOAD_ALL_PROPERTY = "loadAll"; /** The default text. */ private static final String PROJECT_TXT = "Project"; /** The default text. */ private static final String DATASET_TXT = "Dataset"; /** The title of the dialog. */ private static final String TITLE = "Projection"; /** The suggested extension for the projected image. */ private static final String DEFAULT_EXTENSION = "_proj"; /** Action id to close the dialog. */ private static final int CLOSE = 0; /** Action id to close the dialog. */ private static final int PROJECT = 1; /** Action id to create a new folder. */ private static final int NEWFOLDER = 2; /** Action id to load all the available datasets. */ private static final int OTHER = 3; /** The text field hosting the name of the file. */ private JTextField nameField; /** Closes the window. */ private JButton closeButton; /** Project the image. */ private JButton projectButton; /** Button to create a new dataset. */ private JButton newFolderButton; /** The component hosting the datasets containing the image. */ private JPanel selectionPane; /** Component to select the time interval. */ private TextualTwoKnobsSlider timeSelection; /** Component to select the z-interval. */ private TextualTwoKnobsSlider zrangeSelection; /** The possible pixels Type. */ private JComboBox pixelsType; /** Check box to apply the rendering settings of the original image. */ private JCheckBox rndSettingsBox; /** The maximum number of timepoints. */ private int maxT; /** The selected projection algorithm. */ private int algorithm; /** Used to sort the containers. */ private ViewerSorter sorter; /** Component used to select the dataset. */ private JComboBox datasetsBox; /** Component used to select the project. */ private JComboBox parentsBox; /** The listener linked to the parents box. */ private ItemListener parentsBoxListener; /** The listener linked to the datasets box. */ private ItemListener datasetsBoxListener; /** The selected container where to import the data. */ private DataObject selectedContainer; /** The selected container where to import the data. */ private DataObject selectedGrandParentContainer; /** The selected dataset.*/ private DatasetData selectedDataset; /** Sets the properties of the dialog. */ private void setProperties() { setTitle(TITLE); setModal(true); } /** * Populates the datasets box depending on the selected project. * * @param newDataset The newly dataset to add to the list. */ private void populateDatasetsBox(DataNode newDataset) { DataNode n = (DataNode) parentsBox.getSelectedItem(); List<DataNode> list = n.getUIDatasetNodes(); List<DataNode> l = new ArrayList<DataNode>(); if (newDataset != null) l.add(newDataset); l.addAll(sorter.sort(list)); datasetsBox.setVisible(l.size() > 0); datasetsBox.removeItemListener(datasetsBoxListener); datasetsBox.removeAllItems(); populateAndAddTooltipsToComboBox(l, datasetsBox); if (selectedContainer != null && selectedContainer instanceof DatasetData) { DatasetData d = (DatasetData) selectedContainer; Iterator<DataNode> i = l.iterator(); boolean set = false; while (i.hasNext()) { n = i.next(); if (n.getDataObject().getId() == d.getId() && n.getDataObject().canLink()) { datasetsBox.setSelectedItem(n); selectedDataset = (DatasetData) n.getDataObject(); set = true; break; } if (!set) { datasetsBox.setSelectedItem(0); n = (DataNode) datasetsBox.getSelectedItem(); selectedDataset = (DatasetData) n.getDataObject(); } } } else { // no node selected datasetsBox.setSelectedItem(0); } datasetsBox.addItemListener(datasetsBoxListener); } /** * Initializes the components. * * @param imageName The name of the image. * @param type The type of projection. * @param maxZ The maximum number of z-sections. * @param startZ The lower bound of the z-section interval. * @param endZ The upper bound of the z-section interval. */ private void initComponents(String imageName, String type, int maxZ, int startZ, int endZ) { parentsBox = new JComboBox(); parentsBoxListener = new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { DataNode node = (DataNode) parentsBox.getSelectedItem(); if (!node.isDefaultNode() && !node.getDataObject().canLink()) { selectedContainer = null; parentsBox.setSelectedIndex(0); } populateDatasetsBox(null); } } }; parentsBox.removeItemListener(parentsBoxListener); datasetsBox = new JComboBox(); datasetsBoxListener = new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { DataNode node = (DataNode) datasetsBox.getSelectedItem(); if (node != null) { if (!node.isDefaultNode() && !node.getDataObject().canLink()) { //Determine the first dataset that be picked. datasetsBox.setSelectedIndex(0); node = (DataNode) datasetsBox.getSelectedItem(); selectedDataset = (DatasetData) node.getDataObject(); } else selectedDataset = (DatasetData) node.getDataObject(); } } } }; datasetsBox.removeItemListener(datasetsBoxListener); rndSettingsBox = new JCheckBox("Apply same rendering settings"); rndSettingsBox.setToolTipText( UIUtilities.formatToolTipText( "Apply the rendering settings to " + "the projected image.")); rndSettingsBox.setSelected(true); zrangeSelection = new TextualTwoKnobsSlider(1, maxZ, startZ, endZ); zrangeSelection.layoutComponents(TextualTwoKnobsSlider.LAYOUT_FIELDS); timeSelection = new TextualTwoKnobsSlider(1, maxT, 1, maxT); timeSelection.layoutComponents(TextualTwoKnobsSlider.LAYOUT_FIELDS); timeSelection.setEnabled(maxT > 1); Map<String, String> map = EditorUtil.PIXELS_TYPE_DESCRIPTION; String[] data = new String[map.size()]; Set set = map.entrySet(); Entry entry; Iterator i = set.iterator(); int index = 0; //String originalType = type; String key; int selectedIndex = 0; while (i.hasNext()) { entry = (Entry) i.next(); key = (String) entry.getKey(); data[index] = (String) entry.getValue(); if (key.equals(type)) selectedIndex = index; index++; } pixelsType = new JComboBox(data); pixelsType.setSelectedIndex(selectedIndex); pixelsType.setEnabled(algorithm == ProjectionParam.SUM_INTENSITY); selectionPane = new JPanel(); selectionPane.setLayout(new BoxLayout(selectionPane, BoxLayout.Y_AXIS)); closeButton = new JButton("Cancel"); closeButton.setToolTipText(UIUtilities.formatToolTipText( "Close the window.")); closeButton.setActionCommand(""+CLOSE); closeButton.addActionListener(this); projectButton = new JButton("Save"); projectButton.setToolTipText(UIUtilities.formatToolTipText( "Project the image.")); projectButton.setActionCommand(""+PROJECT); projectButton.addActionListener(this); newFolderButton = new JButton("New Dataset..."); newFolderButton.setToolTipText(UIUtilities.formatToolTipText( "Create a new Dataset.")); newFolderButton.setActionCommand(""+NEWFOLDER); newFolderButton.addActionListener(this); nameField = new JTextField(); StringBuffer buffer = new StringBuffer(); buffer.append(UIUtilities.removeFileExtension(imageName)); buffer.append(DEFAULT_EXTENSION); nameField.setText(buffer.toString()); nameField.getDocument().addDocumentListener(this); //Display datasets getRootPane().setDefaultButton(projectButton); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { close(); } }); } /** * Builds the various panels used to set the parameters of the * projection. * * @return See above. */ private JPanel buildParametersPanel() { JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); p.add(buildRangePanel(zrangeSelection, "Z Range: ")); p.add(buildRangePanel(timeSelection, "Timepoint: ")); if (pixelsType != null) { p.add(new JSeparator()); p.add(buildPixelsTypePanel()); } JPanel r = UIUtilities.buildComponentPanel(p); r.setBorder(new TitledBorder("")); return r; } /** * Builds and lays out the passed component. * * @param comp The component to lay out. * @param text The text displayed in front of the component. * @return See above. */ private JPanel buildRangePanel(JComponent comp, String text) { JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); p.add(new JLabel(text)); p.add(UIUtilities.buildComponentPanel(comp)); return p; } /** * Builds and lays out the pixels type options. * * @return See above. */ private JPanel buildPixelsTypePanel() { JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); p.add(new JLabel("Data Type: ")); p.add(UIUtilities.buildComponentPanel(pixelsType)); return p; } /** * Builds the controls. * * @return See above. */ private JPanel buildControls() { JPanel p = new JPanel(); p.setBorder(new TitledBorder("")); p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); p.add(selectionPane); p.add(Box.createHorizontalStrut(5)); p.add(newFolderButton); return p; } /** * Creates a row. * * @return See above. */ private JPanel createRow() { JPanel row = new JPanel(); row.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0)); row.setBorder(null); return row; } /** * Returns the file queue and indicates where the files will be imported. * * @return See above. */ private void buildLocationPane() { selectionPane.removeAll(); JPanel row = createRow(); selectionPane.add(row); selectionPane.add(Box.createVerticalStrut(2)); row = createRow(); row.add(UIUtilities.setTextFont(PROJECT_TXT)); row.add(parentsBox); selectionPane.add(row); selectionPane.add(Box.createVerticalStrut(8)); row = createRow(); row.add(UIUtilities.setTextFont(DATASET_TXT)); row.add(datasetsBox); selectionPane.add(row); } /** * Builds the main component. * * @return See above. */ private JPanel buildBody() { JPanel content = new JPanel(); int height = 80; double[][] tl = {{TableLayout.PREFERRED, TableLayout.FILL}, //columns {TableLayout.PREFERRED, TableLayout.PREFERRED, 5, TableLayout.PREFERRED, height, 5, TableLayout.PREFERRED, TableLayout.FILL} }; //rows content.setLayout(new TableLayout(tl)); content.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); content.add(UIUtilities.setTextFont("Name "), "0, 1, LEFT, CENTER"); content.add(nameField, "1, 1, FULL, CENTER"); content.add(new JLabel(), "0, 2, 1, 2"); content.add(UIUtilities.setTextFont("Save in "), "0, 3, LEFT, CENTER"); content.add(buildControls(), "1, 3, 1, 4"); content.add(new JLabel(), "0, 5, 1, 5"); content.add(UIUtilities.setTextFont("Parameters "), "0, 6, LEFT, CENTER"); content.add(buildParametersPanel(), "1, 6, 1, 7"); return content; } /** * Builds and lays out the control. * * @return See above. */ private JPanel buildToolBar() { JPanel bar = new JPanel(); bar.add(closeButton); bar.add(Box.createHorizontalStrut(5)); bar.add(projectButton); bar.add(Box.createHorizontalStrut(20)); return UIUtilities.buildComponentPanelRight(bar); } /** Builds and lays out the UI. */ private void buildGUI() { IconManager icons = IconManager.getInstance(); TitlePanel tp = new TitlePanel(TITLE, "Set the projection's " + "parameters.", icons.getIcon(IconManager.PROJECTION_48)); Container c = getContentPane(); c.setLayout(new BorderLayout(5, 5)); c.add(tp, BorderLayout.NORTH); c.add(buildBody(), BorderLayout.CENTER); c.add(buildToolBar(), BorderLayout.SOUTH); } /** * Adds a new entry to the display, and indicates to create a new dataset. * * @param name The name of the dataset to create. */ private void createDataset(String name) { DatasetData d = new DatasetData(); d.setName(name); selectedDataset = d; populateDatasetsBox(new DataNode(d)); } /** Closes and disposes. */ private void close() { setVisible(false); dispose(); } /** Sets the enabled flag of the {@link #projectButton}. */ private void enableSave() { String name = nameField.getText(); if (name == null) projectButton.setEnabled(false); else { name = name.trim(); int l = name.length(); projectButton.setEnabled(l > 0 && l < 256); } } /** Projects the image. */ private void project() { int startT = 0, endT = 0; if (maxT > 0) { startT = (int) timeSelection.getStartValue()-1; endT = (int) timeSelection.getEndValue()-1; } ProjectionRef ref = new ProjectionRef(); if (selectedDataset != null) { ref.setDatasets(Arrays.asList(selectedDataset)); if (selectedDataset.getId() <= 0) { DataNode node = (DataNode) parentsBox.getSelectedItem(); if (node != null && !node.isDefaultNode()) ref.setProject((ProjectData) node.getDataObject()); } } ref.setImageName(nameField.getText()); ref.setTInterval(startT, endT); double s = zrangeSelection.getStartValue(); double e = zrangeSelection.getEndValue(); ref.setZInterval((int) s-1, (int) e-1); ref.setApplySettings(rndSettingsBox.isSelected()); firePropertyChange(PROJECTION_PROPERTY, null, ref); close(); } /** * Creates a new instance. * * @param owner The owner of the frame. * @param selectedContainer The default container. * @param selectedGrandParentContainer The container hosting the dataset. */ public ProjSavingDialog(JFrame owner, DataObject selectedContainer, DataObject selectedGrandParentContainer) { super(owner); this.selectedContainer = selectedContainer; this.selectedGrandParentContainer = selectedGrandParentContainer; setProperties(); sorter = new ViewerSorter(); } /** * Initializes the dialog. * * @param algorithm The selected projection algorithm. * @param maxT The maximum number of time-points. * @param pixelsType The type of pixels of the original image. * @param imageName The name of the original image. * @param containers The containers containing the image. * @param maxZ The maximum number of z-sections. * @param startZ The lower bound of the z-section interval. * @param endZ The upper bound of the z-section interval. */ public void initialize(int algorithm, int maxT, String pixelsType, String imageName, Collection containers, int maxZ, int startZ, int endZ) { this.maxT = maxT; this.algorithm = algorithm; initComponents(imageName, pixelsType, maxZ, startZ, endZ); buildGUI(); setContainers(containers); } /** * Sets the projection interval value. * * @param startZ The lower bound of the z-section interval. * @param endZ The upper bound of the z-section interval. */ public void setProjectionInterval(int startZ, int endZ) { zrangeSelection.setInterval(startZ, endZ); } /** * Takes the dataNdoes and populates the combo box with the values as well * as adding a tooltip for each item * * @param dataNodes the nodes used to be displayed in the combo box * @param comboBox the JComboBox that hosts the options */ private void populateAndAddTooltipsToComboBox(List<DataNode> dataNodes, JComboBox comboBox) { List<String> tooltips = new ArrayList<String>(dataNodes.size()); List<String> lines; ExperimenterData exp; for (DataNode n : dataNodes) { exp = getExperimenter(n.getOwner()); comboBox.addItem(n); lines = new ArrayList<String>(); if (exp != null) { lines.add("<b>Owner: </b>"+EditorUtil.formatExperimenter(exp)); } lines.addAll(UIUtilities.wrapStyleWord(n.getFullName())); tooltips.add(UIUtilities.formatToolTipText(lines)); } //To be modified. exp = ImViewerAgent.getUserDetails(); ComboBoxToolTipRenderer renderer = new ComboBoxToolTipRenderer( exp.getId()); comboBox.setRenderer(renderer); renderer.setTooltips(tooltips); } /** * Returns the loaded experimenter corresponding to the specified user. * if the user is not loaded. Returns <code>null</code> if no user * can be found. * * @param owner The experimenter to handle. * @return see above. */ private ExperimenterData getExperimenter(ExperimenterData owner) { if (owner == null) return null; if (owner.isLoaded()) return owner; List l = (List) ImViewerAgent.getRegistry().lookup( LookupNames.USERS_DETAILS); if (l == null) return null; Iterator i = l.iterator(); ExperimenterData exp; long id = owner.getId(); while (i.hasNext()) { exp = (ExperimenterData) i.next(); if (exp.getId() == id) return exp; } return null; } /** * Sets the available containers. * * @param containers The value to set. */ public void setContainers(Collection containers) { if (CollectionUtils.isEmpty(containers)) return; parentsBox.removeItemListener(parentsBoxListener); parentsBox.removeAllItems(); datasetsBox.removeAllItems(); List<DataNode> topList = new ArrayList<DataNode>(); List<DataNode> datasetsList = new ArrayList<DataNode>(); DataObject ho; DataNode n; if (CollectionUtils.isNotEmpty(containers)) { Iterator<DataObject> i = containers.iterator(); while (i.hasNext()) { ho = i.next(); if (ho instanceof ProjectData) { n = new DataNode((DataObject) ho); topList.add(n); } else if (ho instanceof DatasetData) { n = new DataNode((DataObject) ho); datasetsList.add(n); } } } List<DataNode> sortedList = new ArrayList<DataNode>(); if (topList.size() > 0) { sortedList = sorter.sort(topList); } //check if new top nodes List<DataNode> finalList = new ArrayList<DataNode>(); if (datasetsList.size() > 0) finalList.add(new DataNode(datasetsList)); finalList.addAll(sortedList); populateAndAddTooltipsToComboBox(finalList, parentsBox); if (selectedGrandParentContainer != null && selectedGrandParentContainer instanceof ProjectData) { for (int i = 0; i < parentsBox.getItemCount(); i++) { n = (DataNode) parentsBox.getItemAt(i); if (n.getDataObject().getId() == selectedGrandParentContainer.getId() && n.getDataObject().canLink()) { parentsBox.setSelectedIndex(i); break; } } } parentsBox.addItemListener(parentsBoxListener); populateDatasetsBox(null); buildLocationPane(); } /** * Closes or projects the image. * @see ActionListener#actionPerformed(ActionEvent) */ public void actionPerformed(ActionEvent e) { int index = Integer.parseInt(e.getActionCommand()); switch (index) { case PROJECT: project(); break; case CLOSE: close(); break; case OTHER: firePropertyChange(LOAD_ALL_PROPERTY, Boolean.valueOf(false), Boolean.valueOf(true)); break; case NEWFOLDER: CreateFolderDialog d = new CreateFolderDialog(this, "New Dataset"); d.pack(); d.setDefaultName("untitled dataset"); d.addPropertyChangeListener( CreateFolderDialog.CREATE_FOLDER_PROPERTY, this); UIUtilities.centerAndShow(this, d); } } /** * Creates a new dataset. * @see PropertyChangeListener#propertyChange(PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (CreateFolderDialog.CREATE_FOLDER_PROPERTY.equals(name)) { String folderName = (String) evt.getNewValue(); if (folderName != null && folderName.trim().length() > 0) createDataset(folderName); } } /** * Enables the save button depending on the value entered for the name. * @see DocumentListener#insertUpdate(DocumentEvent) */ public void insertUpdate(DocumentEvent e) { enableSave(); } /** * Enables the save button depending on the value entered for the name. * @see DocumentListener#removeUpdate(DocumentEvent) */ public void removeUpdate(DocumentEvent e) { enableSave(); } /** * Required by the {@link DocumentListener} I/F but no-op implementation * in our case. * @see DocumentListener#changedUpdate(DocumentEvent) */ public void changedUpdate(DocumentEvent e) {} }