/* * org.openmicroscopy.shoola.agents.editor.browser.FieldPanelsComponent * *------------------------------------------------------------------------------ * Copyright (C) 2006-2008 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.util.editorpreview; import java.awt.BorderLayout; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JToolBar; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import org.openmicroscopy.shoola.agents.util.DataComponent; import org.openmicroscopy.shoola.util.ui.IconManager; import org.openmicroscopy.shoola.util.ui.OMETextArea; import org.openmicroscopy.shoola.util.ui.UIUtilities; /** * Displays an entire Editor file. * This is simply the display panel, and does not include E.g the JXTaskPane * Other classes may display this panel in a JXTaskPane, and use the * {@link #getTitle()} method to set the JXTaskPane title. * * Steps are displayed as panels with labeled border, with their * parameters displayed as name-value pairs. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author William Moore      * <a href="mailto:will@lifesci.dundee.ac.uk">will@lifesci.dundee.ac.uk</a> * @version 3.0 * @since 3.0-Beta4 */ public class PreviewPanel extends JPanel implements PropertyChangeListener { /** Bound property indicating to open the file. */ public static final String OPEN_FILE_PROPERTY = "openFile"; /** Bound property indicating modifications of fields. */ public static final String PREVIEW_EDITED_PROPERTY = "previewEdited"; /** max number of characters to display in field value */ public static final int MAX_CHARS = 50; /** The element that holds the value of a parameter */ public static final String VALUE = "v"; /** The element that defines a parameter within a step */ public static final String PARAMETER = "p"; /** The attribute holding the 'level' of a single step in the hierarchy */ public static final String LEVEL = "l"; /** The element defining a single step */ public static final String STEP = "s"; /** The element holding the list of steps for the protocol */ public static final String STEPS = "ss"; /** The element holding the description/abstract of the protocol */ public static final String DESCRIPTION = "d"; /** The element/attribute holding the name of a parameter or protocol */ public static final String NAME = "n"; /** Default text if no data entered. */ private static final String DEFAULT_TEXT = "None"; /** * The Model passes the XML description. Then the steps and the name and * description/abstract of the protocol can be retrieved. */ private PreviewModel model; /** The id of the file. */ private long fileID; /** Collection of fields to update. */ private List<DataComponent> fields; /** * Lays out the title and the a button to open the file. * * @return See above. */ private JPanel layoutTiTle() { IconManager icons = IconManager.getInstance(); JButton open = new JButton(icons.getIcon(IconManager.FILE_EDITOR)); open.setOpaque(false); UIUtilities.unifiedButtonLookAndFeel(open); open.setBackground(UIUtilities.BACKGROUND_COLOR); open.setToolTipText("Open the file."); open.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (fileID >= 0) firePropertyChange(OPEN_FILE_PROPERTY, -1, fileID); } }); JToolBar bar = new JToolBar(); bar.setBorder(null); bar.setFloatable(false); bar.setBackground(UIUtilities.BACKGROUND_COLOR); bar.add(open); JPanel p = new JPanel(); p.setBackground(UIUtilities.BACKGROUND_COLOR); p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); if (fileID > 0) p.add(bar); p.add(UIUtilities.setTextFont(getTitle())); JPanel content = UIUtilities.buildComponentPanel(p, 0, 0); content.setBackground(UIUtilities.BACKGROUND_COLOR); return content; } /** Initializes the components. */ private void initComponents() { setBackground(UIUtilities.BACKGROUND_COLOR); JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); String description = ""; List<String> values = getFormattedDesciption(); if (values.size() == 0) description = "Description and Summary Not available"; else { //TODO: externalize that Iterator<String> i = values.iterator(); StringBuffer buffer = new StringBuffer(); while (i.hasNext()) { buffer.append(i.next()); buffer.append("<br>"); } description = "<html>" + "<span style='font-family:sans-serif;font-size:11pt'>" + buffer.toString() + "</span></html>"; // display description in EditorPane, because text wraps nicely! JEditorPane ep = new JEditorPane("text/html", description); ep.setEditable(false); ep.setBorder(new EmptyBorder(3, 5, 5, 3)); p.add(ep); } List<StepObject> protocolSteps = model.getSteps(); String stepName; JPanel nodePanel; Border border; int indent; fields = new ArrayList<DataComponent>(); List<DataComponent> paramComponents; for (StepObject stepObject : protocolSteps) { // each child node has a list of parameters paramComponents = transformFieldParams(stepObject); // don't add a field unless it has some parameters to display if (paramComponents.isEmpty()) continue; fields.addAll(paramComponents); stepName = stepObject.getName(); indent = (stepObject.getLevel())*10; nodePanel = new JPanel(); border = new EmptyBorder(0, indent, 0, 0); border = BorderFactory.createCompoundBorder(border, BorderFactory.createTitledBorder(stepName)); nodePanel.setBorder(border); nodePanel.setBackground(UIUtilities.BACKGROUND_COLOR); nodePanel.setLayout(new GridBagLayout()); layoutFields(nodePanel, null, paramComponents); p.add(nodePanel); } setLayout(new BorderLayout()); add(layoutTiTle(), BorderLayout.NORTH); add(p, BorderLayout.CENTER); } /** Invokes when the model changes. */ private void rebuildUI() { removeAll(); initComponents(); revalidate(); repaint(); } /** * Lays out the passed component. * * @param pane The main component. * @param button The button to show or hide the unset fields. * @param fields The fields to lay out. */ private void layoutFields(JPanel pane, JButton button, List<DataComponent> fields) { pane.removeAll(); GridBagConstraints c = new GridBagConstraints(); //c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.WEST; c.insets = new Insets(0, 2, 2, 0); for (DataComponent comp : fields) { c.gridx = 0; if (comp.isSetField()) { ++c.gridy; c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last c.fill = GridBagConstraints.NONE; //reset to default c.weightx = 0.0; pane.add(comp.getLabel(), 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(comp.getArea(), c); } } ++c.gridy; c.gridx = 0; c.weightx = 0.0; if (button != null) pane.add(button, c); } /** * Transforms the Step into the corresponding UI objects. * * @param step The step to transform. * @return See above. */ private List<DataComponent> transformFieldParams(StepObject step) { List<DataComponent> cmps = new ArrayList<DataComponent>(); DataComponent comp; JLabel label; JComponent area; String key; String value; label = new JLabel(); Font font = label.getFont(); int sizeLabel = font.getSize()-2; List<StepObject.Param> params = step.getParams(); for (StepObject.Param param : params) { key = param.getName(); value = param.getValue(); if ((value != null) && (value.length() > MAX_CHARS)) { value = value.substring(0, MAX_CHARS-1) + "..."; } area = UIUtilities.createComponent(OMETextArea.class, null); if (value == null || value.equals("")) value = DEFAULT_TEXT; ((OMETextArea) area).setText(value); ((OMETextArea) area).setEditedColor( UIUtilities.EDITED_COLOR); label = UIUtilities.setTextFont(key, Font.BOLD, sizeLabel); label.setBackground(UIUtilities.BACKGROUND_COLOR); comp = new DataComponent(label, area); comp.attachListener(this); comp.setSetField(value != null); comp.setEnabled(false); cmps.add(comp); } return cmps; } /** Invokes when a field is modified. */ private void onFieldModified() { boolean dirty = hasDataToSave(); firePropertyChange(PREVIEW_EDITED_PROPERTY, Boolean.valueOf(!dirty), Boolean.valueOf(dirty)); } /** * Creates a new instance and sets the content with the XML summary. * * @param xmlDescription A preview summary in XML. * @param fileID The id of the file. */ public PreviewPanel(String xmlDescription, long fileID) { this.fileID = fileID; setDescriptionXml(xmlDescription); } /** * Creates an instance without setting the content. * Use {@link #setDescriptionXml(String)} to set the content with XML */ public PreviewPanel() { fileID = -1; } /** * Sets the XML description (XML that summarizes an OMERO.editor file), * as retrieved from the description of a File-Annotation for an Editor file. * * @param xmlDescription */ public void setDescriptionXml(String xmlDescription) { model = new PreviewModel(xmlDescription); rebuildUI(); } /** * Allows classes that want to display this panel to get access to the * protocol title, which is not displayed in this panel. * * @return See above */ public String getTitle() { if (model == null) return "OMERO.editor"; return model.getTitle(); } /** * Allows classes that want to display this panel to get access to the * protocol description, which is not displayed in this panel. * * @return See above. */ public String getDescription() { if (model == null) return ""; return model.getDescription(); } /** * Returns the formatted description. Each element of the array is a * string with maximum <code>10</code> words. * * @return See above. */ public List<String> getFormattedDesciption() { List<String> list = new ArrayList<String>(); String description = getDescription(); if (description == null || description.length() == 0) return list; String[] values = description.split(" "); StringBuffer buffer = null; int n = 0; for (int i = 0; i < values.length; i++) { if (n == 0) buffer = new StringBuffer(); buffer.append(values[i]+" "); n++; if (n == 10) { n = 0; list.add(buffer.toString()); } } return list; } /** * Returns <code>true</code> if the preview has been modified, * <code>false</code> otherwise. * * @return See above. */ public boolean hasDataToSave() { boolean dirty = false; Iterator<DataComponent> i = fields.iterator(); DataComponent comp; while (i.hasNext()) { comp = i.next(); if (comp.isDirty()) { dirty = true; break; } } return dirty; } /** * Updates the description and returns it. * * @return See above. */ public String prepareDataToSave() { return null; } /** * Listens to property fired by the {@link DataComponent}s. * @see PropertyChangeListener#propertyChange(PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (DataComponent.DATA_MODIFIED_PROPERTY.equals(name)) { onFieldModified(); } } }