// // Notes.java // /* OME Notes library for flexible organization and presentation of OME-XML metadata. Copyright (C) 2006-@year@ Melissa Linkert and Christopher Peterson. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package loci.ome.notes; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Vector; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.border.LineBorder; import javax.swing.filechooser.FileFilter; import loci.common.RandomAccessInputStream; import loci.common.services.DependencyException; import loci.common.services.ServiceException; import loci.common.services.ServiceFactory; import loci.formats.ImageReader; import loci.formats.gui.BufferedImageReader; import loci.formats.gui.GUITools; import loci.formats.meta.AggregateMetadata; import loci.formats.meta.MetadataRetrieve; import loci.formats.meta.MetadataStore; import loci.formats.services.OMEXMLService; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.forms.layout.RowSpec; /** * Main notes window. * * <dl><dt><b>Source code:</b></dt> * <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/legacy/ome-notes/src/loci/ome/notes/Notes.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/legacy/ome-notes/src/loci/ome/notes/Notes.java;hb=HEAD">Gitweb</a></dd></dl> */ public class Notes extends JFrame implements ActionListener { // -- Constants -- /** Template that is loaded automatically. */ private static final String DEFAULT_TEMPLATE = "templates/viewer.template"; private static final CellConstraints CC = new CellConstraints(); // -- Fields -- /** Progress/status bar. */ private JProgressBar progress; /** Menu bar. */ private JMenuBar menubar; /** Menu item for saving data. */ private JMenuItem saveFile; /** Main viewing area. */ private JTabbedPane tabPane; /** Current GUI template. */ private Template currentTemplate; /** Current template file name. */ private String templateName; /** Foreground (font) color. */ private Color foreground; /** Background color. */ private Color background; /** Current font. */ private Font font; /** Name of the current image file. */ private String currentFile; /** Thumbnails for the current file. */ private Vector thumb; private AggregateMetadata metadata; private OMEXMLService service; // -- Constructor -- /** Constructs a new main window with the default template. */ public Notes() { this(null, (String) null); } /** * Constructs a new main window with the given metadata, * and default template. */ public Notes(AggregateMetadata store) { this(null, store); } /** Constructs a new main window with the given metadata and template. */ public Notes(String template, AggregateMetadata store) { super("OME Notes"); try { ServiceFactory factory = new ServiceFactory(); service = (OMEXMLService) factory.getInstance(OMEXMLService.class); } catch (DependencyException e) { e.printStackTrace(); } setupWindow(); // load the appropriate template if (template != null) { loadTemplate(template); } else { templateName = DEFAULT_TEMPLATE; loadTemplate(Notes.class.getResourceAsStream(DEFAULT_TEMPLATE)); } metadata = store; setDefaultCloseOperation(DISPOSE_ON_CLOSE); setVisible(true); } /** Constructs a new main window with the given template. */ public Notes(String template, String newfile) { super("OME Notes"); setupWindow(); // load the appropriate template if (template != null) { loadTemplate(template); } else { templateName = DEFAULT_TEMPLATE; loadTemplate(Notes.class.getResourceAsStream(DEFAULT_TEMPLATE)); } try { if (newfile != null) openFile(newfile); } catch (Exception e) { e.printStackTrace(); } setDefaultCloseOperation(DISPOSE_ON_CLOSE); setVisible(true); } // -- Notes API methods -- /** Load and apply a template from the specified file. */ public void loadTemplate(String filename) { // clear out previous template progress.setString("Loading template " + filename); templateName = filename; // parse the template file try { loadTemplate(new Template(filename)); } catch (IOException e) { e.printStackTrace(); } } /** Load and apply a template from the specified InputStream. */ public void loadTemplate(InputStream stream) { progress.setString("Loading template..."); try { loadTemplate(new Template(stream)); } catch (IOException e) { e.printStackTrace(); } } /** Load and apply the given template. */ public void loadTemplate(Template t) { currentTemplate = t; tabPane.removeAll(); // retrieve defined GUI parameters setSize(new Dimension(currentTemplate.getDefaultWidth(), currentTemplate.getDefaultHeight())); font = new Font(currentTemplate.getFontStyle(), Font.PLAIN, currentTemplate.getFontSize()); int[] fore = currentTemplate.getFontColor(); int[] back = currentTemplate.getBackgroundColor(); foreground = new Color(fore[0], fore[1], fore[2]); background = new Color(back[0], back[1], back[2]); // set up all of the defined tabs and fields TemplateTab[] tabs = currentTemplate.getTabs(); for (int i=0; i<tabs.length; i++) { Vector groups = tabs[i].getAllGroups(); Vector fields = tabs[i].getAllFields(); JScrollPane scroll = new JScrollPane(); JPanel panel = new JPanel(); String rowString = "pref,"; String colString = "pref,pref:grow,pref:grow,pref:grow,"; int numRows = tabs[i].getRows(); int numColumns = tabs[i].getColumns(); for (int j=0; j<numRows; j++) { rowString += "pref:grow,pref,"; } for (int j=0; j<numColumns; j++) { colString += "pref:grow,pref,pref:grow,pref,"; } FormLayout l = new FormLayout(colString, rowString); panel.setLayout(l); scroll.getViewport().add(panel); int[] rowNumber = new int[l.getColumnCount()]; Arrays.fill(rowNumber, 2); for (int j=0; j<groups.size(); j++) { TemplateGroup group = (TemplateGroup) groups.get(j); for (int r=0; r<group.getRepetitions(); r++) { FormLayout layout = (FormLayout) panel.getLayout(); int col = 2; if (currentTemplate.editTemplateFields()) { JButton add = new JButton("+"); add.setActionCommand("cloneGroup " + i + "-" + j); add.addActionListener(this); panel.add(add, CC.xy(col, rowNumber[col - 1])); rowNumber[col - 1]++; col++; JButton remove = new JButton("-"); remove.setActionCommand("removeGroup" + i + "-" + j); remove.addActionListener(this); panel.add(remove, CC.xy(col, rowNumber[col - 1])); rowNumber[col - 1]++; col++; } panel.add(new JLabel(group.getName() + " #" + (r + 1)), CC.xy(col, rowNumber[col - 1])); rowNumber[col - 1] += 3; col++; for (int k=0; k<group.getNumFields(); k++) { TemplateField field = group.getField(r, k); setupField(field, col, r, rowNumber, panel); } rowNumber[col - 1]++; } } for (int j=0; j<fields.size(); j++) { TemplateField f = tabs[i].getField(j); setupField(f, 2, 0, rowNumber, panel); } tabPane.add(scroll, tabs[i].getName()); } setFontAndColors(this); changeEditable(currentTemplate.isEditable(), this); progress.setString(""); pack(); } /** Recursively set the font and colors for the root and all children. */ public void setFontAndColors(Container root) { Component[] components = root instanceof JMenu ? ((JMenu) root).getMenuComponents() : root.getComponents(); for (int i=0; i<components.length; i++) { components[i].setFont(font); components[i].setForeground(foreground); components[i].setBackground(background); if (components[i] instanceof JTextArea || components[i] instanceof JComboBox || components[i] instanceof JCheckBox) { LineBorder b = new LineBorder(foreground); ((JComponent) components[i]).setBorder(b); } if (components[i] instanceof Container) { setFontAndColors((Container) components[i]); } } } /** Recursively enable or disable all chilren (non-Container objects). */ public void changeEditable(boolean enable, Container root) { Component[] c = root.getComponents(); for (int i=0; i<c.length; i++) { if (!(c[i] instanceof JMenuBar) && !(c[i] instanceof JMenu) && !(c[i] instanceof JMenuItem) && !(c[i] instanceof JLabel) && !(c[i] instanceof JScrollBar) && !(c[i] instanceof JTabbedPane)) { c[i].setEnabled(enable); } if (c[i] instanceof Container) { changeEditable(enable, (Container) c[i]); } } saveFile.setEnabled(enable); } // -- ActionListener API methods -- public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if (cmd.equals("new")) { // check if the user wants to save the current metadata first int s = JOptionPane.showConfirmDialog(this, "Save current metadata?", "", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE); if (s == JOptionPane.YES_OPTION) { actionPerformed(new ActionEvent(this, -1, "save")); } thumb.clear(); if (templateName.equals(DEFAULT_TEMPLATE)) { loadTemplate(Notes.class.getResourceAsStream(DEFAULT_TEMPLATE)); } else loadTemplate(templateName); } else if (cmd.equals("open")) { progress.setString("Opening file..."); try { ImageReader reader = new ImageReader(); JFileChooser chooser = GUITools.buildFileChooser(reader); int status = chooser.showOpenDialog(this); if (status == JFileChooser.APPROVE_OPTION) { currentFile = chooser.getSelectedFile().getAbsolutePath(); } if (currentFile == null) return; thumb.clear(); if (templateName.equals(DEFAULT_TEMPLATE)) { loadTemplate(Notes.class.getResourceAsStream(DEFAULT_TEMPLATE)); } else loadTemplate(templateName); openFile(currentFile); } catch (Exception exc) { exc.printStackTrace(); } } else if (cmd.equals("save")) { progress.setString("Saving metadata to companion file..."); currentTemplate.saveFields(metadata); // always save to the current filename + ".ome" metadata = new AggregateMetadata(new ArrayList()); try { String name = currentFile; if (name == null) { JFileChooser chooser = new JFileChooser(); FileFilter filter = new FileFilter() { public boolean accept(File f) { return true; } public String getDescription() { return "OME-XML files"; } }; chooser.setFileFilter(filter); int status = chooser.showSaveDialog(this); if (status == JFileChooser.APPROVE_OPTION) { name = chooser.getSelectedFile().getAbsolutePath(); if (name == null) return; } } if (name == null) return; if (!name.endsWith(".ome")) name += ".ome"; String xml = null; try { xml = service.getOMEXML((MetadataRetrieve) metadata); } catch (ServiceException exc) { } File f = new File(name); FileWriter writer = new FileWriter(f); writer.write(xml); progress.setString("Finished writing companion file (" + name + ")"); } catch (Exception io) { io.printStackTrace(); } } else if (cmd.equals("close")) { dispose(); } else if (cmd.equals("load")) { // prompt for the new template file JFileChooser chooser = new JFileChooser(); FileFilter filter = new FileFilter() { public boolean accept(File f) { return f.getAbsolutePath().endsWith(".template") || f.isDirectory(); } public String getDescription() { return "OME Notes Templates"; } }; chooser.setFileFilter(filter); int status = chooser.showOpenDialog(this); if (status == JFileChooser.APPROVE_OPTION) { loadTemplate(chooser.getSelectedFile().getAbsolutePath()); } try { if (currentFile != null) openFile(currentFile); } catch (Exception exc) { exc.printStackTrace(); } } else if (cmd.startsWith("cloneGroup")) { cmd = cmd.substring(10); int tabIndex = Integer.parseInt(cmd.substring(0, cmd.indexOf("-")).trim()); int groupIndex = Integer.parseInt(cmd.substring(cmd.indexOf("-") + 1).trim()); TemplateTab tab = currentTemplate.getTabs()[tabIndex]; TemplateGroup group = tab.getGroup(groupIndex); group.setRepetitions(group.getRepetitions() + 1); int tabIdx = tabPane.getSelectedIndex(); loadTemplate(currentTemplate); try { if (currentFile != null) openFile(currentFile); } catch (Exception exc) { exc.printStackTrace(); } tabPane.setSelectedIndex(tabIdx); } else if (cmd.startsWith("removeGroup")) { cmd = cmd.substring(11); int tabIndex = Integer.parseInt(cmd.substring(0, cmd.indexOf("-")).trim()); int groupIndex = Integer.parseInt(cmd.substring(cmd.indexOf("-") + 1).trim()); TemplateTab tab = currentTemplate.getTabs()[tabIndex]; TemplateGroup group = tab.getGroup(groupIndex); group.setRepetitions(group.getRepetitions() - 1); int tabIdx = tabPane.getSelectedIndex(); loadTemplate(currentTemplate); try { if (currentFile != null) openFile(currentFile); } catch (Exception exc) { exc.printStackTrace(); } tabPane.setSelectedIndex(tabIdx); } } // -- Helper methods -- private void setupWindow() { thumb = new Vector(); // set up the main panel JPanel contentPane = new JPanel(); contentPane.setLayout(new BorderLayout()); setContentPane(contentPane); // set up the menu bar menubar = new JMenuBar(); JMenu file = new JMenu("File"); JMenuItem newFile = new JMenuItem("New..."); newFile.setActionCommand("new"); newFile.addActionListener(this); file.add(newFile); JMenuItem openFile = new JMenuItem("Open"); openFile.setActionCommand("open"); openFile.addActionListener(this); file.add(openFile); saveFile = new JMenuItem("Save"); saveFile.setActionCommand("save"); saveFile.addActionListener(this); file.add(saveFile); JMenuItem close = new JMenuItem("Close"); close.setActionCommand("close"); close.addActionListener(this); file.add(close); JMenu view = new JMenu("View"); JMenuItem loadTemplate = new JMenuItem("Switch Templates"); loadTemplate.setActionCommand("load"); loadTemplate.addActionListener(this); view.add(loadTemplate); view.add(new JSeparator()); menubar.add(file); menubar.add(view); setJMenuBar(menubar); // add the status bar progress = new JProgressBar(0, 1); progress.setStringPainted(true); contentPane.add(progress, BorderLayout.SOUTH); // provide a place to show metadata tabPane = new JTabbedPane(); contentPane.add(tabPane, BorderLayout.CENTER); } private void openFile(String file) throws Exception { currentFile = file; BufferedImageReader reader = new BufferedImageReader(); reader.setNormalized(true); reader.setOriginalMetadataPopulated(true); progress.setString("Reading " + currentFile); ArrayList delegates = new ArrayList(); if (currentFile.endsWith(".ome")) { RandomAccessInputStream s = new RandomAccessInputStream(currentFile); String xml = s.readString((int) s.length()); s.close(); try { delegates.add(service.createOMEXMLMetadata(xml)); } catch (ServiceException e) { } } else { // first look for a companion file File companion = new File(currentFile + ".ome"); MetadataStore companionStore = null, readerStore = null; if (companion.exists()) { progress.setString("Reading companion file (" + companion + ")"); RandomAccessInputStream s = new RandomAccessInputStream(currentFile); String xml = s.readString((int) s.length()); s.close(); try { companionStore = service.createOMEXMLMetadata(xml); } catch (ServiceException e) { } } try { reader.setMetadataStore(service.createOMEXMLMetadata()); } catch (ServiceException e) { } reader.setId(currentFile); readerStore = reader.getMetadataStore(); if (companion.exists()) { // merge the two OMENode objects // this depends upon the precedence setting in the template progress.setString("Merging companion and original file..."); if (currentTemplate.preferCompanion()) { delegates.add(companionStore); delegates.add(readerStore); } else { delegates.add(readerStore); delegates.add(companionStore); } } else delegates.add(readerStore); // grab thumbnails for (int i=0; i<reader.getSeriesCount(); i++) { reader.setSeries(i); thumb.add(reader.openThumbImage(0)); } reader.close(); } metadata = new AggregateMetadata(delegates); progress.setString("Populating fields..."); currentTemplate.initializeFields(metadata); currentTemplate.populateFields(metadata); loadTemplate(currentTemplate); progress.setString(""); } /** Set up a new field in the current tab. */ private void setupField(TemplateField field, int col, int r, int[] rowNumber, JPanel panel) { JComponent c = field.getComponent(); if (field.getType().equals("thumbnail")) { BufferedImage thumbnail = null; if (field.getValueMap() == null && thumb.size() > 0) { thumbnail = (BufferedImage) thumb.get(r); } else if (field.getValueMap() != null) { try { BufferedImageReader tempReader = new BufferedImageReader(); tempReader.setId(field.getValueMap()); if (tempReader.getImageCount() > 0) { thumbnail = tempReader.openThumbImage(0); } tempReader.close(); } catch (Exception e) { e.printStackTrace(); } } if (thumbnail != null) { ((JLabel) c).setIcon(new ImageIcon(thumbnail)); } } int cndx = (field.getColumn() - 1) * 4 + col; int rowNdx = field.getRow() == -1 ? rowNumber[cndx - 1] : 2*field.getRow(); FormLayout layout = (FormLayout) panel.getLayout(); if (rowNdx > layout.getRowCount() - 1) { layout.appendRow(new RowSpec("pref:grow")); layout.appendRow(new RowSpec("pref")); } panel.add(new JLabel(field.getName()), CC.xy(cndx, rowNdx)); panel.add(c, CC.xy(cndx + 1, rowNdx)); if (field.getRow() == -1) { rowNumber[cndx - 1]++; rowNumber[cndx]++; } if (rowNdx > rowNumber[1]) { for (int i=0; i<col - 1; i++) { rowNumber[i] = rowNdx + 2; } } } // -- Main method -- public static void main(String[] args) { String template = null, data = null; for (int i=0; i<args.length; i++) { if (args[i].equals("-template")) { if (args.length > i + 1) { template = args[++i]; } else System.err.println("Please specify a template file"); } else data = args[i]; } new Notes(template, data); } }