/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2016 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.metadata.editor;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToggleButton;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.openmicroscopy.shoola.util.CommonsLangUtils;
import org.jdesktop.swingx.JXTaskPane;
import org.openmicroscopy.shoola.agents.events.iviewer.ViewImage;
import org.openmicroscopy.shoola.agents.events.iviewer.ViewImageObject;
import org.openmicroscopy.shoola.agents.events.treeviewer.DataObjectSelectionEvent;
import org.openmicroscopy.shoola.agents.metadata.IconManager;
import org.openmicroscopy.shoola.agents.metadata.MetadataViewerAgent;
import org.openmicroscopy.shoola.agents.metadata.ROICountLoader;
import org.openmicroscopy.shoola.agents.util.EditorUtil;
import omero.gateway.SecurityContext;
import org.openmicroscopy.shoola.env.event.EventBus;
import org.openmicroscopy.shoola.util.file.modulo.ModuloInfo;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
import org.openmicroscopy.shoola.util.ui.omeeditpane.OMEWikiComponent;
import org.openmicroscopy.shoola.util.ui.omeeditpane.WikiDataObject;
import ome.model.units.BigResult;
import omero.model.Length;
import omero.model.LengthI;
import omero.model.enums.UnitsLength;
import omero.gateway.model.AnnotationData;
import omero.gateway.model.ChannelData;
import omero.gateway.model.DataObject;
import omero.gateway.model.DatasetData;
import omero.gateway.model.FileData;
import omero.gateway.model.ImageData;
import omero.gateway.model.PixelsData;
import omero.gateway.model.PlateAcquisitionData;
import omero.gateway.model.PlateData;
import omero.gateway.model.ProjectData;
import omero.gateway.model.ScreenData;
import omero.gateway.model.TagAnnotationData;
import omero.gateway.model.WellData;
import omero.gateway.model.WellSampleData;
/**
* Displays the properties of the selected object.
*
* @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>
* @author Scott Littlewood
* <a href="mailto:sylittlewood@dundee.ac.uk">sylittlewood@dundee.ac.uk</a>
* @version 3.0
* @since OME3.0
*/
public class PropertiesUI
extends AnnotationUI
implements DocumentListener, FocusListener,
PropertyChangeListener
{
/** The title associated to this component. */
static final String TITLE = "Properties";
/** The default description. */
private static final String DEFAULT_DESCRIPTION_TEXT = "Add Description";
/** The text for the creation date. */
private static final String CREATIONDATE_TEXT = "Creation Date: ";
/** Text indicating to edit the description.*/
private static final String EDIT_DESC_TEXT = "Edit the description.";
/**Text indicating to edit the channels.*/
private static final String EDIT_CHANNEL_TEXT = "Edit the channels.";
/** The default height of the description.*/
private static final int HEIGHT = 120;
/** The default width of the description.*/
private static final int WIDTH = 100;
/** Maximum number of characters shown per line in the
* channel names component
*/
private static final int MAX_CHANNELNAMES_LENGTH_IN_CHARS = 40;
/** Button to edit the description. */
private JToggleButton descriptionButtonEdit;
/** The description before possible modification.*/
private String originalDescription;
/** The component hosting the name of the <code>DataObject</code>. */
private JTextArea typePane;
/** The component hosting the description of the <code>DataObject</code>. */
private OMEWikiComponent descriptionWiki;
/** The component hosting the {@link #descriptionWiki}. */
private JPanel descriptionPanel;
/** The label displaying the parent of the node. */
private JLabel parentLabel;
/** The label displaying the parent of the node. */
private JLabel gpLabel;
/** The label displaying the parent of the node. */
private JLabel wellLabel;
/** The area displaying the channels information. */
private JLabel channelsArea;
/** Reference to the control. */
private EditorControl controller;
/** The text associated to the data object. */
private String text;
/** ScrollPane hosting the {@link #descriptionWiki} component.*/
private JScrollPane descriptionScrollPane;
/** Button to edit the channels. */
private JButton editChannel;
/** Component displayed when editing the channels.*/
private ChannelEditUI channelEditPane;
/** Components hosting the channels' details.*/
private JComponent channelsPane;
/** The label showing the ROI count */
private JLabel roiCountLabel;
/** Builds and lays out the components displaying the channel information.*/
private void buildChannelsPane()
{
editChannel.setVisible(model.canEdit());
channelsPane = channelsArea;
}
/**
* Returns the <code>JXTaskPane</code> hosting the component.
*
* @param parent The value to check.
* @return See above.
*/
private Container getComponent(Container parent)
{
if (parent == null) return null;
if (parent instanceof JXTaskPane) return parent;
return getComponent(parent.getParent());
}
/** Resets the size of the components hosting this component.*/
private void resetComponentSize()
{
Container pane = getComponent(getParent());
pane.setSize(getPreferredSize());
pane.validate();
pane.repaint();
}
/** Modifies the UI so the user can edit the channels.*/
private void editChannels()
{
Map data = model.getChannelData();
if (data == null || data.size() == 0) return;
if (channelEditPane == null) {
Object ho = model.getParentRootObject();
if (model.getParentRootObject() instanceof WellData)
ho = model.getGrandParentRootObject();
channelEditPane = new ChannelEditUI(model.getChannelData(), ho);
channelEditPane.addPropertyChangeListener(this);
}
channelsPane = channelEditPane;
editChannel.setVisible(false);
removeAll();
buildGUI();
resetComponentSize();
}
/** Modifies the UI to display the initial channels details.*/
private void cancelChannelsEdit()
{
buildChannelsPane();
removeAll();
buildGUI();
resetComponentSize();
}
/** Initializes the components composing this display. */
private void initComponents()
{
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBackground(UIUtilities.BACKGROUND_COLOR);
Font f;
parentLabel = new JLabel();
f = parentLabel.getFont();
Font newFont = f.deriveFont(f.getStyle(), f.getSize()-2);
parentLabel.setOpaque(false);
parentLabel.setFont(newFont);
parentLabel.setBackground(UIUtilities.BACKGROUND_COLOR);
gpLabel = new JLabel();
gpLabel.setOpaque(false);
gpLabel.setFont(newFont);
gpLabel.setBackground(UIUtilities.BACKGROUND_COLOR);
wellLabel = new JLabel();
wellLabel.setOpaque(false);
wellLabel.setFont(newFont);
wellLabel.setBackground(UIUtilities.BACKGROUND_COLOR);
typePane = createTextPane();
typePane.setEditable(false);
newFont = f.deriveFont(f.getStyle(), f.getSize()-2);
descriptionWiki = new OMEWikiComponent(false);
descriptionWiki.setDefaultText(DEFAULT_DESCRIPTION_TEXT);
descriptionWiki.installObjectFormatters();
descriptionWiki.setFont(newFont);
descriptionWiki.setEnabled(false);
descriptionWiki.setAllowOneClick(true);
descriptionWiki.addFocusListener(this);
descriptionWiki.addPropertyChangeListener(this);
typePane.setFont(f.deriveFont(Font.BOLD));
typePane.setFont(f.deriveFont(f.getStyle(), f.getSize()-2));
f = parentLabel.getFont();
parentLabel.setFont(f.deriveFont(Font.BOLD));
parentLabel.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
f = gpLabel.getFont();
gpLabel.setFont(f.deriveFont(Font.BOLD));
gpLabel.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
f = wellLabel.getFont();
wellLabel.setFont(f.deriveFont(Font.BOLD));
wellLabel.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
channelsArea = UIUtilities.createComponent(null);
channelsPane = channelsArea;
IconManager icons = IconManager.getInstance();
descriptionButtonEdit = new JToggleButton(icons.getIcon(IconManager.EDIT_12));
formatButton(descriptionButtonEdit, EDIT_DESC_TEXT);
editChannel = new JButton(icons.getIcon(IconManager.EDIT_12));
formatButton(editChannel, EDIT_CHANNEL_TEXT);
descriptionWiki.setEnabled(false);
ItemListener l = new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (e.getSource() instanceof JToggleButton) {
JToggleButton b = (JToggleButton) e.getSource();
if (b.isSelected()) {
expandDescriptionField(true);
editField(descriptionWiki);
} else
save();
}
}
};
descriptionButtonEdit.addItemListener(l);
editChannel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editChannels();
}
});
}
/**
* Formats the specified button.
*
* @param button The button to handle.
* @param text The tool tip text.
* @param actionID The action command id.
*/
private void formatButton(JButton button, String text)
{
button.setOpaque(false);
UIUtilities.unifiedButtonLookAndFeel(button);
button.setBackground(UIUtilities.BACKGROUND_COLOR);
button.setToolTipText(text);
}
/**
* Formats the specified button.
*
* @param button The button to handle.
* @param text The tool tip text.
* @param actionID The action command id.
*/
private void formatButton(JToggleButton button, String text)
{
button.setOpaque(false);
button.setBackground(UIUtilities.BACKGROUND_COLOR);
button.setBorder(new EmptyBorder(2, 2, 2, 2));
button.setToolTipText(text);
}
/**
* Lays out the plate fields.
*
* @param plate The plate to handle.
* @return See above.
*/
private JPanel layoutPlateContent(PlateData plate)
{
JPanel content = new JPanel();
content.setBackground(UIUtilities.BACKGROUND_COLOR);
content.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
content.setLayout(new GridBagLayout());
JLabel l = new JLabel();
Font font = l.getFont();
int size = font.getSize()-2;
Map<JLabel, JComponent> components =
new LinkedHashMap<JLabel, JComponent>();
String v = plate.getPlateType();
JLabel value;
if (v != null && v.trim().length() > 0) {
l = UIUtilities.setTextFont(EditorUtil.TYPE, Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setFont(font.deriveFont(font.getStyle(), size));
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
value.setText(v);
components.put(l, value);
}
l = UIUtilities.setTextFont(EditorUtil.EXTERNAL_IDENTIFIER,
Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setFont(font.deriveFont(font.getStyle(), size));
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
v = plate.getExternalIdentifier();
if (v == null || v.length() == 0) v = NO_SET_TEXT;
value.setText(v);
components.put(l, value);
l = UIUtilities.setTextFont(EditorUtil.STATUS, Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setFont(font.deriveFont(font.getStyle(), size));
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
v = plate.getStatus();
if (v == null || v.length() == 0) v = NO_SET_TEXT;
value.setText(v);
components.put(l, value);
layoutComponents(content, components);
return content;
}
/**
* Lays out the well fields.
*
* @param well The well to handle.
* @return See above.
*/
private JPanel layoutWellContent(WellData well)
{
JPanel content = new JPanel();
content.setBackground(UIUtilities.BACKGROUND_COLOR);
content.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
content.setLayout(new GridBagLayout());
JLabel l = new JLabel();
Font font = l.getFont();
int size = font.getSize()-2;
Map<JLabel, JComponent> components =
new LinkedHashMap<JLabel, JComponent>();
String v = well.getWellType();
JLabel value;
if (v != null && v.trim().length() > 0) {
l = UIUtilities.setTextFont(EditorUtil.TYPE, Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setFont(font.deriveFont(font.getStyle(), size));
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
value.setText(v);
components.put(l, value);
}
l = UIUtilities.setTextFont(EditorUtil.EXTERNAL_DESCRIPTION,
Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setFont(font.deriveFont(font.getStyle(), size));
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
v = well.getExternalDescription();
if (v == null || v.length() == 0) v = NO_SET_TEXT;
value.setText(v);
components.put(l, value);
l = UIUtilities.setTextFont(EditorUtil.STATUS, Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setFont(font.deriveFont(font.getStyle(), size));
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
v = well.getStatus();
if (v == null || v.length() == 0) v = NO_SET_TEXT;
value.setText(v);
components.put(l, value);
layoutComponents(content, components);
return content;
}
/**
* Lays out the screen fields.
*
* @param screen The screen to handle.
* @return See above.
*/
private JPanel layoutScreenContent(ScreenData screen)
{
JPanel content = new JPanel();
content.setBackground(UIUtilities.BACKGROUND_COLOR);
content.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
content.setLayout(new GridBagLayout());
JLabel l = new JLabel();
Font font = l.getFont();
int size = font.getSize()-2;
Map<JLabel, JComponent> components =
new LinkedHashMap<JLabel, JComponent>();
l = UIUtilities.setTextFont("Protocol Identifier:", Font.BOLD, size);
JLabel value = UIUtilities.createComponent(null);
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
String v = screen.getProtocolIdentifier();
if (v == null || v.length() == 0) v = NO_SET_TEXT;
value.setText(v);
components.put(l, value);
l = UIUtilities.setTextFont("Protocol Description:", Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setFont(font.deriveFont(font.getStyle(), size));
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
v = screen.getProtocolDescription();
if (v == null || v.length() == 0) v = NO_SET_TEXT;
value.setText(v);
components.put(l, value);
l = UIUtilities.setTextFont("ReagentSet Identifier:", Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setFont(font.deriveFont(font.getStyle(), size));
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
v = screen.getReagentSetIdentifier();
if (v == null || v.length() == 0) v = NO_SET_TEXT;
value.setText(v);
components.put(l, value);
l = UIUtilities.setTextFont("ReagentSet Description:", Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
value.setFont(font.deriveFont(font.getStyle(), size));
v = screen.getReagentSetDescripion();
if (v == null || v.length() == 0) v = NO_SET_TEXT;
value.setText(v);
components.put(l, value);
layoutComponents(content, components);
return content;
}
/**
* Lays out the passed components.
*
* @param pane The main pane.
* @param components The components to lay out.
*/
private void layoutComponents(JPanel pane,
Map<JLabel, JComponent> components)
{
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.WEST;
c.insets = new Insets(0, 2, 2, 0);
Entry<JLabel, JComponent> entry;
Iterator<Entry<JLabel, JComponent>>
i = components.entrySet().iterator();
c.gridy = 0;
while (i.hasNext()) {
c.gridx = 0;
entry = i.next();
++c.gridy;
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
c.fill = GridBagConstraints.NONE; //reset to default
c.weightx = 0.0;
pane.add(entry.getKey(), c);
c.gridx++;
pane.add(Box.createHorizontalStrut(5), c);
c.gridx++;
c.gridwidth = GridBagConstraints.REMAINDER;//end row
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
pane.add(entry.getValue(), c);
}
}
/**
* Returns the pixels size as a string.
*
* @param details The map to convert.
* @param component The component displaying the information.
* @return See above.
*/
private String formatPixelsSize(Map details, JLabel component)
{
// First make sure that no conversion exception has occurred
Object obj = details.get(EditorUtil.PIXEL_SIZE_X);
if (obj instanceof BigResult) {
MetadataViewerAgent.logBigResultExeption(this, obj, EditorUtil.PIXEL_SIZE_X);
return "N/A";
}
obj = details.get(EditorUtil.PIXEL_SIZE_Y);
if (obj instanceof BigResult) {
MetadataViewerAgent.logBigResultExeption(this, obj, EditorUtil.PIXEL_SIZE_Y);
return "N/A";
}
obj = details.get(EditorUtil.PIXEL_SIZE_Z);
if (obj instanceof BigResult) {
MetadataViewerAgent.logBigResultExeption(this, obj, EditorUtil.PIXEL_SIZE_Z);
return "N/A";
}
Length x = (Length) details.get(EditorUtil.PIXEL_SIZE_X);
Length y = (Length) details.get(EditorUtil.PIXEL_SIZE_Y);
Length z = (Length) details.get(EditorUtil.PIXEL_SIZE_Z);
Double dx = null, dy = null, dz = null;
boolean number = true;
NumberFormat nf = new DecimalFormat("0.00");
UnitsLength unit = null;
try {
x = UIUtilities.transformSize(x);
dx = x.getValue();
unit = x.getUnit();
} catch (Exception e) {
number = false;
}
try {
if (unit == null) {
y = UIUtilities.transformSize(y);
dy = y.getValue();
unit = y.getUnit();
}
else {
y = new LengthI(y, unit);
dy = y.getValue();
}
} catch (Exception e) {
number = false;
}
try {
if (unit == null) {
z = UIUtilities.transformSize(z);
dz = z.getValue();
unit = z.getUnit();
}
else {
z = new LengthI(z, unit);
dz = z.getValue();
}
} catch (Exception e) {
number = false;
}
String label = "Pixels Size (";
String value = "";
String tooltip = "<html><body>";
if (dx != null && dx.doubleValue() > 0) {
value += nf.format(dx);
tooltip += "X: "+x.getValue()+" "+LengthI.lookupSymbol(x.getUnit())+"<br>";
label += "X";
}
if (dy != null && dy.doubleValue() > 0) {
if (value.length() == 0) value += nf.format(dy);
else value +="x"+nf.format(dy);;
tooltip += "Y: "+y.getValue()+" "+LengthI.lookupSymbol(y.getUnit())+"<br>";
label += "Y";
}
if (dz != null && dz.doubleValue() > 0) {
if (value.length() == 0) value += nf.format(dz);
else value +="x"+nf.format(dz);
tooltip += "Z: "+z.getValue()+" "+LengthI.lookupSymbol(z.getUnit())+"<br>";
label += "Z";
}
label += ") ";
component.setToolTipText(tooltip);
if (value.length() == 0) return null;
component.setText(value);
if (unit == null) unit = UnitsLength.MICROMETER;
label += "("+LengthI.lookupSymbol(unit)+")";
return label+":";
}
/**
* Builds the panel hosting the information
*
* @param details The information to display.
* @param image The image of reference.
* @return See above.
*/
private JPanel buildContentPanel(Map details, ImageData image)
{
JPanel content = new JPanel();
content.setBackground(UIUtilities.BACKGROUND_COLOR);
content.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
content.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.NONE;
c.weightx = 1;
c.anchor = GridBagConstraints.NORTHWEST;
c.insets = new Insets(0, 0, 2, 2);
c.gridy = 0;
c.gridx = 0;
JLabel l = new JLabel();
Font font = l.getFont();
int size = font.getSize()-2;
JLabel label;
JLabel value;
String v = model.formatDate(image);
if(!CommonsLangUtils.isEmpty(v)) {
label = UIUtilities.setTextFont(EditorUtil.ACQUISITION_DATE+":",
Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setText(v);
content.add(label, c);
c.gridx++;
content.add(value, c);
c.gridy++;
c.gridx = 0;
}
try { //just to be on the save side
label = UIUtilities.setTextFont(EditorUtil.IMPORTED_DATE+":",
Font.BOLD, size);
value = UIUtilities.createComponent(null);
v = UIUtilities.formatDefaultDate(image.getInserted());
value.setText(v);
content.add(label, c);
c.gridx++;
content.add(value, c);
c.gridy++;
} catch (Exception e) {
}
label = UIUtilities.setTextFont(EditorUtil.XY_DIMENSION+":", Font.BOLD,
size);
value = UIUtilities.createComponent(null);
v = (String) details.get(EditorUtil.SIZE_X);
v += " x ";
v += (String) details.get(EditorUtil.SIZE_Y);
value.setText(v);
c.gridx = 0;
content.add(label, c);
c.gridx++;
content.add(value, c);
c.gridy++;
label = UIUtilities.setTextFont(EditorUtil.PIXEL_TYPE+":", Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setText((String) details.get(EditorUtil.PIXEL_TYPE));
c.gridx = 0;
content.add(label, c);
c.gridx++;
content.add(value, c);
value = UIUtilities.createComponent(null);
String s = formatPixelsSize(details, value);
if (s != null) {
c.gridy++;
label = UIUtilities.setTextFont(s, Font.BOLD, size);
c.gridx = 0;
content.add(label, c);
c.gridx++;
content.add(value, c);
}
//parse modulo T.
Map<Integer, ModuloInfo> modulo = model.getModulo();
ModuloInfo moduloT = modulo.get(ModuloInfo.T);
c.gridy++;
label = UIUtilities.setTextFont(EditorUtil.Z_T_FIELDS+":", Font.BOLD,
size);
value = UIUtilities.createComponent(null);
v = (String) details.get(EditorUtil.SECTIONS);
v += " x ";
if (moduloT != null) {
String time = (String) details.get(EditorUtil.TIMEPOINTS);
int t = Integer.parseInt(time);
v += ""+(t/moduloT.getSize());
} else {
v += (String) details.get(EditorUtil.TIMEPOINTS);
}
value.setText(v);
c.gridx = 0;
content.add(label, c);
c.gridx++;
content.add(value, c);
c.gridy++;
if (moduloT != null) {
label = UIUtilities.setTextFont(EditorUtil.SMALL_T_VARIABLE,
Font.BOLD, size);
value = UIUtilities.createComponent(null);
value.setText(""+moduloT.getSize());
c.gridx = 0;
content.add(label, c);
c.gridx++;
content.add(value, c);
c.gridy++;
}
if (!model.isNumerousChannel() && model.getRefObjectID() > 0) {
label = UIUtilities.setTextFont(EditorUtil.CHANNELS+":",
Font.BOLD, size);
c.gridx = 0;
content.add(label, c);
c.gridx++;
c.fill = GridBagConstraints.HORIZONTAL;
content.add(channelsPane, c);
c.fill = GridBagConstraints.NONE;
c.gridx++;
content.add(editChannel, c);
c.gridy++;
}
label = new JLabel("...");
label = UIUtilities.setTextFont(EditorUtil.ROI_COUNT+":", Font.BOLD, size);
c.gridx = 0;
content.add(label, c);
c.gridx++;
roiCountLabel = UIUtilities.createComponent(null);
roiCountLabel.setText("...");
content.add(roiCountLabel, c);
loadROICount(image);
return content;
}
/**
* Initializes a <code>TextPane</code>.
*
* @return See above.
*/
private JTextArea createTextPane()
{
JTextArea pane = new JTextArea();
pane.setOpaque(false);
pane.setBackground(UIUtilities.BACKGROUND_COLOR);
return pane;
}
/**
* Lays out the components using a <code>FlowLayout</code>.
*
* @param button The component to lay out.
* @param component The component to lay out.
* @param sizeRow The size of the row.
* @return See above.
*/
private JPanel layoutEditablefield(Component button, JComponent component)
{
JPanel p = new JPanel();
p.setBackground(UIUtilities.BACKGROUND_COLOR);
p.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.WEST;
c.insets = new Insets(2, 2, 2, 2);
c.gridy = 0;
c.gridx = 0;
c.weightx = 1;
p.add(component, c);
c.gridx++;
if (button != null) {
c.fill = GridBagConstraints.NONE;
c.weightx = 0;
c.anchor = GridBagConstraints.EAST;
p.add(button, c);
}
return p;
}
/**
* Builds the properties component.
*
* @return See above.
*/
private JPanel buildProperties()
{
Object refObject = model.getRefObject();
JPanel p = new JPanel();
p.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
p.setBackground(UIUtilities.BACKGROUND_COLOR);
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
p.add(Box.createVerticalStrut(2));
int w = 10;
JPanel l = UIUtilities.buildComponentPanel(gpLabel, 0, 0);
l.setBackground(UIUtilities.BACKGROUND_COLOR);
p.add(layoutEditablefield(Box.createHorizontalStrut(w), l));
l = UIUtilities.buildComponentPanel(parentLabel, 0, 0);
l.setBackground(UIUtilities.BACKGROUND_COLOR);
p.add(layoutEditablefield(Box.createHorizontalStrut(w), l));
l = UIUtilities.buildComponentPanel(wellLabel, 0, 0);
l.setBackground(UIUtilities.BACKGROUND_COLOR);
p.add(layoutEditablefield(Box.createHorizontalStrut(w), l));
if (refObject instanceof ImageData ||
refObject instanceof DatasetData ||
refObject instanceof ProjectData ||
refObject instanceof TagAnnotationData ||
refObject instanceof WellSampleData ||
refObject instanceof PlateData ||
refObject instanceof ScreenData ||
refObject instanceof PlateAcquisitionData) {
descriptionScrollPane = new JScrollPane(descriptionWiki);
descriptionScrollPane.setBorder(AnnotationUI.EDIT_BORDER);
descriptionPanel = new JPanel(new GridBagLayout());
descriptionPanel.setBackground(UIUtilities.BACKGROUND_COLOR);
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(2, 2, 2, 2);
c.gridx = 0;
c.gridy = 0;
c.gridheight = 2;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.weighty = 1;
c.anchor = GridBagConstraints.NORTHWEST;
descriptionPanel.add(descriptionScrollPane, c);
c.gridx = 1;
c.gridy = 0;
c.gridheight = 1;
c.fill = GridBagConstraints.NONE;
c.weightx = 0;
c.weighty = 0;
c.anchor = GridBagConstraints.NORTHEAST;
descriptionPanel.add(descriptionButtonEdit, c);
boolean hasDescription = !descriptionWiki.getText().equals(DEFAULT_DESCRIPTION_TEXT);
expandDescriptionField(hasDescription);
p.add(descriptionPanel);
p.add(Box.createVerticalStrut(5));
}
return p;
}
/** Expands/Collapses the description text field */
private void expandDescriptionField(boolean expand) {
if (descriptionScrollPane == null)
return;
if (expand) {
Dimension viewportSize = new Dimension(WIDTH, HEIGHT);
descriptionScrollPane.getViewport().setPreferredSize(viewportSize);
} else {
descriptionScrollPane.getViewport().setPreferredSize(null);
}
revalidate();
}
/**
* Builds the panel hosting the {@link #nameArea} and the
* {@link #descriptionArea}.
*/
private void buildGUI()
{
setBackground(UIUtilities.BACKGROUND);
add(buildProperties());
Object refObject = model.getRefObject();
PixelsData data = null;
ImageData img = null;
if (refObject instanceof ImageData) {
img = (ImageData) refObject;
try {
data = ((ImageData) refObject).getDefaultPixels();
} catch (Exception e) {}
} else if (refObject instanceof WellSampleData) {
img = ((WellSampleData) refObject).getImage();
if (img != null && img.getId() > 0)
data = img.getDefaultPixels();
Object parent = model.getParentRootObject();
if (parent instanceof WellData) {
add(Box.createVerticalStrut(5));
add(layoutWellContent((WellData) parent));
}
} else if (refObject instanceof PlateData) {
add(Box.createVerticalStrut(5));
add(layoutPlateContent((PlateData) refObject));
} else if (refObject instanceof ScreenData) {
add(Box.createVerticalStrut(5));
add(layoutScreenContent((ScreenData) refObject));
}
add(Box.createVerticalStrut(5));
if (data != null) {
add(buildContentPanel(EditorUtil.transformPixelsData(data), img));
} else if (refObject instanceof DatasetData
|| refObject instanceof ProjectData
|| refObject instanceof PlateData
|| refObject instanceof PlateAcquisitionData
|| refObject instanceof ScreenData) {
DataObject dob = (DataObject) refObject;
Timestamp crDate = dob.getCreated();
if (crDate != null) {
JLabel createDateLabel = new JLabel();
Font font = createDateLabel.getFont();
int size = font.getSize() - 2;
createDateLabel.setFont((new JLabel()).getFont().deriveFont(
Font.BOLD, size));
createDateLabel.setText(CREATIONDATE_TEXT);
JLabel createDateValue = new JLabel();
createDateValue.setFont((new JLabel()).getFont().deriveFont(
Font.PLAIN, size));
createDateValue.setText(UIUtilities.formatDefaultDate(crDate));
JPanel p = new JPanel();
p.setLayout(new GridLayout(1, 2));
p.add(createDateLabel);
p.add(createDateValue);
p.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
p.setBackground(UIUtilities.BACKGROUND_COLOR);
add(p);
}
}
}
/**
* Modifies the passed components depending on the value of the
* passed flag.
*
* @param panel The panel to handle.
* @param field The field to handle.
* @param editable Pass <code>true</code> if to <code>edit</code>,
* <code>false</code> otherwise.
*/
private void editField(JComponent field)
{
descriptionWiki.setEnabled(true);
descriptionScrollPane.setBorder(EDIT_BORDER_BLACK);
field.requestFocus();
}
/**
* Returns the label associated to the well.
*
* @param well The object to handle.
* @param columnIndex Indicates how to label the columns.
* @param rowIndex Indicates how to label the rows.
* @return See above.
*/
private String getWellLabel(WellData well, int columnIndex, int rowIndex)
{
int k = well.getRow()+1;
String rowText = "";
if (rowIndex == PlateData.ASCENDING_LETTER)
rowText = UIUtilities.LETTERS.get(k);
else if (rowIndex == PlateData.ASCENDING_NUMBER)
rowText = ""+k;
k = well.getColumn()+1;
String columnText = "";
if (columnIndex == PlateData.ASCENDING_LETTER)
columnText = UIUtilities.LETTERS.get(k);
else if (columnIndex == PlateData.ASCENDING_NUMBER)
columnText = ""+k;
String value = rowText+"-"+columnText;
return value;
}
/** Sets the text of the parent label. */
private void setParentLabel()
{
Object parent = model.getParentRootObject();
if (parent instanceof WellData) {
WellData well = (WellData) parent;
PlateData plate = well.getPlate();
String text = plate.getName();
String s = UIUtilities.formatPartialName(text);
parentLabel.setText("Plate: "+s);
parentLabel.setToolTipText(text);
parentLabel.repaint();
text = "Well "+getWellLabel(well, plate.getColumnSequenceIndex(),
plate.getRowSequenceIndex());
wellLabel.setText(text);
}
parent = model.getGrandParentRootObject();
if (parent instanceof ScreenData) {
ScreenData screen = (ScreenData) parent;
text = "Screen: ";
text += screen.getName();
gpLabel.setText(text);
gpLabel.repaint();
}
}
/**
* Creates a new instance.
*
* @param model Reference to the {@link EditorModel}.
* Mustn't be <code>null</code>.
* @param controller Reference to the {@link EditorControl}.
* Mustn't be <code>null</code>.
*/
PropertiesUI(EditorModel model, EditorControl controller)
{
super(model);
if (controller == null)
throw new IllegalArgumentException("No control.");
this.controller = controller;
title = TITLE;
initComponents();
}
/** Allows to edit or not the name and description.*/
private void editNames()
{
Object refObject = model.getRefObject();
boolean b = model.canEdit();
if (refObject instanceof FileData) {
b = false;
}
descriptionButtonEdit.setEnabled(b);
}
/**
* Overridden to lay out the UI.
* @see AnnotationUI#buildUI()
*/
protected void buildUI()
{
removeAll();
Object refObject = model.getRefObject();
text = model.getObjectTypeAsString(refObject);
if (model.isMultiSelection())
return;
descriptionWiki.removeDocumentListener(this);
boolean b = model.canEdit();
originalDescription = model.getRefObjectDescription();
if (CommonsLangUtils.isEmpty(originalDescription))
originalDescription = DEFAULT_DESCRIPTION_TEXT;
descriptionWiki.setText(originalDescription);
expandDescriptionField(!originalDescription.equals(DEFAULT_DESCRIPTION_TEXT));
descriptionWiki.setCaretPosition(0);
descriptionWiki.setBackground(UIUtilities.BACKGROUND_COLOR);
descriptionWiki.setForeground(UIUtilities.DEFAULT_FONT_COLOR);
editNames();
if (b) {
descriptionWiki.addDocumentListener(this);
}
setParentLabel();
buildChannelsPane();
buildGUI();
}
/**
* Sets the <code>enabled</code> flag to <code>false</code> when the
* channels are loading.
*/
void onChannelDataLoading()
{
editChannel.setEnabled(false);
}
/** Sets the focus on the name area. */
void setFocusOnName() { //namePane.requestFocus();
}
/** Updates the data object. */
void setParentRootObject()
{
Object object = model.getRefObject();
setParentLabel();
if (!(object instanceof WellSampleData)) return;
originalDescription = model.getRefObjectDescription();
if (originalDescription == null || originalDescription.length() == 0)
originalDescription = DEFAULT_DESCRIPTION_TEXT;
descriptionWiki.setText(originalDescription);
expandDescriptionField(!originalDescription.equals(DEFAULT_DESCRIPTION_TEXT));
boolean b = model.canEdit();
descriptionButtonEdit.setEnabled(b);
descriptionButtonEdit.setSelected(false);
if (b) {
descriptionWiki.addDocumentListener(this);
}
}
void save() {
updateDataObject();
model.fireAnnotationSaving(null, Collections.emptyList(), false);
}
/** Updates the data object. */
void updateDataObject()
{
if (!hasDataToSave()) return;
Object object = model.getRefObject();
String desc = descriptionWiki.getText().trim();
if (desc == null) desc = "";
if (object instanceof ProjectData) {
ProjectData p = (ProjectData) object;
p.setDescription(desc);
} else if (object instanceof DatasetData) {
DatasetData p = (DatasetData) object;
p.setDescription(desc);
} else if (object instanceof ImageData) {
ImageData p = (ImageData) object;
p.setDescription(desc);
} else if (object instanceof TagAnnotationData) {
TagAnnotationData p = (TagAnnotationData) object;
p.setTagDescription(desc);
} else if (object instanceof ScreenData) {
ScreenData p = (ScreenData) object;
p.setDescription(desc);
} else if (object instanceof PlateData) {
PlateData p = (PlateData) object;
p.setDescription(desc);
} else if (object instanceof WellSampleData) {
WellSampleData well = (WellSampleData) object;
ImageData img = well.getImage();
img.setDescription(desc);
} else if (object instanceof FileData) {
FileData f = (FileData) object;
if (f.getId() > 0) return;
} else if (object instanceof PlateAcquisitionData) {
PlateAcquisitionData pa = (PlateAcquisitionData) object;
pa.setDescription(desc);
}
}
/**
* Sets the channels when loaded.
*
* @param channels The value to set.
*/
void setChannelData(Map channels)
{
if (channels == null) return;
editChannel.setEnabled(model.canEdit());
int n = channels.size()-1;
Iterator k = channels.keySet().iterator();
int j = 0;
StringBuffer buffer = new StringBuffer();
while (k.hasNext()) {
buffer.append(((ChannelData) k.next()).getChannelLabeling());
if (j != n)
buffer.append(", ");
j++;
}
String text = CommonsLangUtils.wrap(buffer.toString(),
MAX_CHANNELNAMES_LENGTH_IN_CHARS, "<br>", true);
channelsArea.setText("<html>"+text+"</html>");
channelsArea.revalidate();
channelsArea.repaint();
}
/**
* Returns the text.
*
* @return See above.
*/
String getText() { return text; }
/**
* Overridden to set the title of the component.
* @see AnnotationUI#getComponentTitle()
*/
protected String getComponentTitle() { return TITLE; }
/**
* No implementation in this case.
* @see AnnotationUI#getAnnotationToRemove()
*/
protected List<Object> getAnnotationToRemove() { return null; }
/**
* No implementation in this case.
* @see AnnotationUI#getAnnotationToSave()
*/
protected List<AnnotationData> getAnnotationToSave() { return null; }
/**
* Returns <code>true</code> if the data object has been edited,
* <code>false</code> otherwise.
* @see AnnotationUI#hasDataToSave()
*/
protected boolean hasDataToSave()
{
if (model.isMultiSelection())
return false;
String name = originalDescription;
String value = descriptionWiki.getText();
value = value.trim();
if (name == null) return value.length() != 0;
name = OMEWikiComponent.prepare(name.trim(), true);
value = OMEWikiComponent.prepare(value.trim(), true);
if (DEFAULT_DESCRIPTION_TEXT.equals(name) &&
DEFAULT_DESCRIPTION_TEXT.equals(value)) return false;
return !name.equals(value);
}
/**
* Clears the data to save.
* @see AnnotationUI#clearData(Object)
*/
protected void clearData(Object oldObject)
{
originalDescription = model.getRefObjectDescription();
descriptionWiki.removeDocumentListener(this);
if (CommonsLangUtils.isEmpty(originalDescription))
originalDescription = DEFAULT_DESCRIPTION_TEXT;
descriptionWiki.setText(originalDescription);
expandDescriptionField(!originalDescription.equals(DEFAULT_DESCRIPTION_TEXT));
descriptionButtonEdit.setSelected(false);
descriptionWiki.addDocumentListener(this);
channelEditPane = null;
descriptionWiki.setEnabled(false);
editNames();
if (oldObject == null) return;
if (!model.isSameObject(oldObject)) {
channelsArea.setText("");
parentLabel.setText("");
wellLabel.setText("");
gpLabel.setText("");
}
}
/**
* Clears the UI.
* @see AnnotationUI#clearDisplay()
*/
protected void clearDisplay() {}
/**
* Sets the title of the component.
* @see AnnotationUI#setComponentTitle()
*/
protected void setComponentTitle() {}
/**
* Fires property indicating that some text has been entered.
*
* @see DocumentListener#insertUpdate(DocumentEvent)
*/
public void insertUpdate(DocumentEvent e) {
}
/**
* Fires property indicating that some text has been entered.
* @see DocumentListener#removeUpdate(DocumentEvent)
*/
public void removeUpdate(DocumentEvent e)
{
}
/**
* Resets the default text of the text fields if <code>null</code> or
* length <code>0</code>.
* @see FocusListener#focusLost(FocusEvent)
*/
public void focusLost(FocusEvent e)
{
String text = descriptionWiki.getText();
editNames();
if (CommonsLangUtils.isBlank(text)) {
descriptionWiki.removeDocumentListener(this);
descriptionWiki.setText(DEFAULT_DESCRIPTION_TEXT);
descriptionWiki.addDocumentListener(this);
firePropertyChange(EditorControl.SAVE_PROPERTY,
Boolean.valueOf(false), Boolean.valueOf(true));
}
}
/**
* Sets the position of the caret and selects the text depending on the
* source.
* @see FocusListener#focusGained(FocusEvent)
*/
public void focusGained(FocusEvent e) {}
/**
* Listens to property changes fired by the {@link #descriptionWiki}.
* @see PropertyChangeListener#propertyChange(PropertyChangeEvent)
*/
@SuppressWarnings("unchecked")
public void propertyChange(PropertyChangeEvent evt)
{
String name = evt.getPropertyName();
EventBus bus = MetadataViewerAgent.getRegistry().getEventBus();
if (OMEWikiComponent.WIKI_DATA_OBJECT_PROPERTY.equals(name)) {
WikiDataObject object = (WikiDataObject) evt.getNewValue();
long id = object.getId();
switch (object.getIndex()) {
case WikiDataObject.IMAGE:
if (id > 0) {
ViewImage event = new ViewImage(
model.getSecurityContext(),
new ViewImageObject(id), null);
event.setPlugin(MetadataViewerAgent.runAsPlugin());
bus.post(event);
}
}
} else if (OMEWikiComponent.WIKI_DATA_OBJECT_ONE_CLICK_PROPERTY.equals(
name)) {
WikiDataObject object = (WikiDataObject) evt.getNewValue();
long id = object.getId();
switch (object.getIndex()) {
case WikiDataObject.IMAGE:
bus.post(new DataObjectSelectionEvent(ImageData.class, id));
break;
case WikiDataObject.DATASET:
bus.post(new DataObjectSelectionEvent(DatasetData.class,
id));
break;
case WikiDataObject.PROJECT:
bus.post(new DataObjectSelectionEvent(
ProjectData.class, id));
}
} else if (ChannelEditUI.CANCEL_PROPERTY.equals(name)) {
cancelChannelsEdit();
} else if (ChannelEditUI.SAVE_PROPERTY.equals(name)) {
List<ChannelData> channels = (List<ChannelData>) evt.getNewValue();
model.fireChannelSaving(channels, false);
cancelChannelsEdit();
} else if (ChannelEditUI.APPLY_TO_ALL_PROPERTY.equals(name)) {
List<ChannelData> channels = (List<ChannelData>) evt.getNewValue();
model.fireChannelSaving(channels, true);
cancelChannelsEdit();
} else if (OMEWikiComponent.TEXT_UPDATE_PROPERTY.equals(name)) {
firePropertyChange(EditorControl.SAVE_PROPERTY,
Boolean.valueOf(false), Boolean.valueOf(true));
}
}
/**
* Required by the {@link DocumentListener} I/F but no-operation
* implementation in our case.
* @see DocumentListener#changedUpdate(DocumentEvent)
*/
public void changedUpdate(DocumentEvent e) {}
/**
* Starts an asyc. call to load the number of ROIs
*/
void loadROICount(ImageData image) {
ROICountLoader l = new ROICountLoader(new SecurityContext(image.getGroupId()), this, image.getId());
l.load();
}
/**
* Updates label showing the ROI count
* @param n Number of ROIs of the current image
*/
public void updateROICount(int n) {
roiCountLabel.setText(""+n);
}
}