// // TemplateEditor.java // /* OME Metadata Notes application for exploration and editing of OME-XML and OME-TIFF metadata. Copyright (C) 2006-@year@ 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.editor; import java.awt.Component; import java.awt.Dimension; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Hashtable; import javax.swing.ButtonGroup; 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.JPopupMenu; import javax.swing.JRadioButton; import javax.swing.JSeparator; import javax.swing.JSpinner; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.JToolBar; import javax.swing.filechooser.FileFilter; import loci.formats.FormatException; import loci.formats.gui.BufferedImageReader; import loci.ome.notes.Template; import loci.ome.notes.TemplateField; import loci.ome.notes.TemplateTab; import loci.ome.notes.TemplateTools; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.ColumnSpec; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.forms.layout.RowSpec; /** * Main class for template editor. * * <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/editor/TemplateEditor.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/legacy/ome-notes/src/loci/ome/notes/editor/TemplateEditor.java;hb=HEAD">Gitweb</a></dd></dl> */ public class TemplateEditor extends JFrame implements ActionListener { // -- Constants -- /** List of component names. */ protected static final String[] COMPONENT_NAMES = new String[] { "Text field", "Number field", "Choice box", "Check box", "Image thumbnail" }; /** List of component classes. */ protected static final Class[] COMPONENTS = new Class[] { JTextField.class, JSpinner.class, JComboBox.class, JCheckBox.class, JLabel.class }; /** List of component types. */ protected static final String[] COMPONENT_TYPES = new String[] { "var", "int", "enum", "bool", "thumbnail" }; public static final int TAB = 0, GROUP = 1, FIELD = 2; // -- Fields -- /** Main editing panel. */ private JTabbedPane tabPane; /** Panel containing drag-and-drop components. */ private JPanel componentPane; /** Current file name. */ private String currentFile; private JTextField newTabName; private PictureTransferHandler pictureHandler; private int lastMenuX = 0; private int lastMenuY = 0; private JComponent lastMenuComponent = null; private Hashtable[] icons; private Hashtable[] fields; // -- Constructor -- public TemplateEditor() { super("OME Notes Template Editor"); pictureHandler = new PictureTransferHandler(); icons = new Hashtable[0]; fields = new Hashtable[0]; // set up the menu bar JMenuBar 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); JMenuItem saveFile = new JMenuItem("Save"); saveFile.setActionCommand("save"); saveFile.addActionListener(this); file.add(saveFile); JMenuItem quit = new JMenuItem("Quit"); quit.setActionCommand("quit"); quit.addActionListener(this); file.add(quit); menubar.add(file); setJMenuBar(menubar); // set up the toolbar JToolBar toolbar = new JToolBar(); toolbar.setFloatable(false); JButton tab = new JButton("New Tab"); tab.addActionListener(this); tab.setActionCommand("prompt tab"); JButton row = new JButton("Add Row"); row.addActionListener(this); row.setActionCommand("add row"); JButton col = new JButton("Add Column"); col.addActionListener(this); col.setActionCommand("add col"); JButton remove = new JButton("Remove this tab"); remove.addActionListener(this); remove.setActionCommand("removeTab"); toolbar.add(tab); toolbar.add(row); toolbar.add(col); toolbar.add(remove); // set up the rest of the window CellConstraints cc = new CellConstraints(); FormLayout layout = new FormLayout("pref:grow,pref,pref:grow,pref:grow,pref:grow", "pref,5dlu:grow,pref:grow,pref:grow,pref:grow"); JPanel contentPane = new JPanel(layout); contentPane.add(toolbar, cc.xywh(1, 1, 5, 1)); tabPane = new JTabbedPane(); contentPane.add(tabPane, cc.xywh(3, 2, 3, 4)); FormLayout componentLayout = new FormLayout("pref:grow,pref:grow", "pref:grow,pref:grow,pref:grow,pref:grow,pref:grow,pref:grow"); componentPane = new JPanel(componentLayout); componentPane.add(new JLabel("Field Choices"), cc.xywh(1, 1, 2, 1)); try { for (int i=0; i<COMPONENTS.length; i++) { JLabel label = new JLabel(COMPONENT_NAMES[i]); componentPane.add(label, cc.xy(1, i + 2)); JPanel panel = new JPanel(); JComponent component = (JComponent) COMPONENTS[i].newInstance(); component.setPreferredSize(new Dimension(64, 25)); component.setEnabled(false); panel.add(component); DraggableIcon img = new DraggableIcon(panel, -1, -1); img.setTransferHandler(pictureHandler); img.setEditable(false); componentPane.add(img, cc.xy(2, i + 2)); } } catch (Exception e) { error("Failed to create field list", e); } contentPane.add(componentPane, cc.xywh(1, 2, 1, 4)); setContentPane(contentPane); setSize(new Dimension(768, 768)); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setVisible(true); } // -- TemplateEditor API methods -- // -- ActionListener API method -- public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if (cmd.equals("new")) { icons = new Hashtable[0]; fields = new Hashtable[0]; tabPane.removeAll(); repaint(); } else if (cmd.equals("open")) { 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) { String file = chooser.getSelectedFile().getAbsolutePath(); try { Template t = new Template(file); TemplateTab[] tabs = t.getTabs(); for (int i=0; i<tabs.length; i++) { int rows = tabs[i].getRows(); int cols = tabs[i].getColumns(); if (cols == 0) cols = 1; if (rows == 0) rows = 1; addTab(tabs[i].getName(), rows, cols); tabPane.setSelectedIndex(i); for (int j=0; j<tabs[i].getNumFields(); j++) { TemplateField f = tabs[i].getField(j); int x = f.getRow(); int y = f.getColumn(); if (x == -1) x = 1; if (y == -1) y = j + 1; Point p = new Point(x, y); DraggableIcon icon = (DraggableIcon) icons[i].get(p); icon.label = new JLabel(f.getName()); JPanel panel = new JPanel(); panel.add(f.getComponent()); icon.setPanel(panel); } } } catch (Exception exc) { error("Failed to parse template", exc); } tabPane.setSelectedIndex(0); } } else if (cmd.equals("save")) { // build up the template from the components TemplateTab[] tabs = new TemplateTab[tabPane.getTabCount()]; for (int i=0; i<tabs.length; i++) { tabs[i] = new TemplateTab(); tabs[i].setName(tabPane.getTitleAt(i)); JComponent c = (JComponent) tabPane.getComponentAt(i); FormLayout layout = (FormLayout) c.getLayout(); tabs[i].setRows(layout.getRowCount()); tabs[i].setColumns(layout.getColumnCount()); Object[] keys = icons[i].keySet().toArray(); for (int j=0; j<keys.length; j++) { Point p = (Point) keys[j]; DraggableIcon icon = (DraggableIcon) icons[i].get(p); TemplateField f = (TemplateField) fields[i].get(p); if (icon.image != null) { Component[] components = icon.image.getComponents(); JLabel label = icon.label; JComponent component = (JComponent) components[0]; f.setComponent(component); for (int k=0; k<COMPONENTS.length; k++) { if (component.getClass().equals(COMPONENTS[k])) { f.setType(COMPONENT_TYPES[k]); break; } } f.setRow(p.y); f.setColumn(p.x); f.setDefaultValue(TemplateTools.getComponentValue(component)); tabs[i].addField(f); } } } Template t = new Template(tabs, null); // prompt for filename to save to if (currentFile == null) { JFileChooser chooser = new JFileChooser(); FileFilter filter = new FileFilter() { public boolean accept(File f) { return true; } public String getDescription() { return "All files"; } }; chooser.setFileFilter(filter); int status = chooser.showSaveDialog(this); if (status == JFileChooser.APPROVE_OPTION) { currentFile = chooser.getSelectedFile().getAbsolutePath(); if (currentFile == null) return; } } try { t.save(currentFile); } catch (IOException io) { error("Failed to save template", io); } } else if (cmd.equals("quit")) dispose(); else if (cmd.equals("add row")) addRow(); else if (cmd.equals("add col")) addColumn(); else if (cmd.equals("prompt tab")) { // prompt for tab name JPopupMenu menu = new JPopupMenu(); newTabName = new JTextField(); newTabName.setPreferredSize(new Dimension(200, 25)); menu.add(newTabName); JButton b = new JButton("OK"); b.addActionListener(this); b.setActionCommand("new tab"); menu.add(b); JComponent s = (JComponent) e.getSource(); menu.show(s, s.getX(), s.getY()); newTabName.grabFocus(); } else if (cmd.equals("new tab")) { newTabName.getParent().setVisible(false); addTab(newTabName.getText(), 2, 2); } else if (cmd.equals("setName")) { JPopupMenu menu = (JPopupMenu) ((JComponent) e.getSource()).getParent(); DraggableIcon icon = (DraggableIcon) menu.getInvoker(); menu.setVisible(false); String text = ((JTextField) menu.getComponents()[0]).getText(); Point p = new Point(icon.gridx, icon.gridy); int ndx = tabPane.getSelectedIndex(); TemplateField f = (TemplateField) fields[ndx].get(p); f.setName(text); f.setNameMap(null); // set the name if (icon.label != null) icon.remove(icon.label); icon.remove(icon.image); icon.label = new JLabel(text); icon.add(icon.label); icon.add(icon.image); icon.getParent().repaint(); } else if (cmd.equals("changeName")) { // prompt for new field name JPopupMenu menu = new JPopupMenu(); JTextField field = new JTextField(); field.setPreferredSize(new Dimension(200, 25)); menu.add(field); JButton b = new JButton("OK"); b.addActionListener(this); b.setActionCommand("setName"); menu.add(b); menu.show(lastMenuComponent, lastMenuX, lastMenuY); field.grabFocus(); } else if (cmd.equals("nameMap")) { JPopupMenu menu = (JPopupMenu) ((JComponent) e.getSource()).getParent(); DraggableIcon icon = (DraggableIcon) menu.getInvoker(); menu.setVisible(false); MappingWindow w = new MappingWindow(this, true); w.show(lastMenuComponent, lastMenuX, lastMenuY); } else if (cmd.equals("map")) { JPopupMenu menu = (JPopupMenu) ((JComponent) e.getSource()).getParent(); DraggableIcon icon = (DraggableIcon) menu.getInvoker(); menu.setVisible(false); MappingWindow w = new MappingWindow(this, false); w.show(lastMenuComponent, lastMenuX, lastMenuY); } else if (cmd.equals("repeat")) { JMenuItem item = (JMenuItem) e.getSource(); DraggableIcon icon = (DraggableIcon) ((JPopupMenu) item.getParent()).getInvoker(); TemplateField f = getField(icon); if (item.getText().equals("Repeat this field")) { item.setText("Don't repeat this field"); f.setRepeated(true); } else { item.setText("Repeat this field"); f.setRepeated(false); } } else if (cmd.equals("removeField")) { JPopupMenu menu = (JPopupMenu) ((JComponent) e.getSource()).getParent(); DraggableIcon icon = (DraggableIcon) menu.getInvoker(); menu.setVisible(false); int idx = tabPane.getSelectedIndex(); Object[] keys = icons[idx].keySet().toArray(); for (int i=0; i<keys.length; i++) { if (icons[idx].get(keys[i]).equals(icon)) { icons[idx].remove(keys[i]); fields[idx].remove(keys[i]); break; } } icon.remove(icon.label); icon.remove(icon.image); tabPane.repaint(); } else if (cmd.startsWith("removeRow")) { int row = Integer.parseInt(cmd.substring(cmd.indexOf(":") + 1)); JPopupMenu menu = (JPopupMenu) ((JComponent) e.getSource()).getParent(); menu.setVisible(false); JPanel pane = (JPanel) tabPane.getSelectedComponent(); FormLayout layout = (FormLayout) pane.getLayout(); int rows = layout.getRowCount(); int cols = layout.getColumnCount(); int idx = tabPane.getSelectedIndex(); for (int i=0; i<cols; i++) { pane.remove((JComponent) icons[idx].get(new Point(i + 1, row + 1))); } rekey(row, -1); layout.removeRow(row + 1); tabPane.repaint(); } else if (cmd.startsWith("removeColumn")) { int col = Integer.parseInt(cmd.substring(cmd.indexOf(":") + 1)); JPopupMenu menu = (JPopupMenu) ((JComponent) e.getSource()).getParent(); menu.setVisible(false); JPanel pane = (JPanel) tabPane.getSelectedComponent(); FormLayout layout = (FormLayout) pane.getLayout(); int rows = layout.getRowCount(); int cols = layout.getColumnCount(); int idx = tabPane.getSelectedIndex(); for (int i=0; i<rows; i++) { pane.remove((JComponent) icons[idx].get(new Point(col + 1, i + 1))); } rekey(-1, col); layout.removeColumn(col + 1); tabPane.repaint(); } else if (cmd.equals("removeTab")) { int ndx = tabPane.getSelectedIndex(); tabPane.remove(ndx); Hashtable[] h = new Hashtable[icons.length - 1]; Hashtable[] f = new Hashtable[fields.length - 1]; System.arraycopy(icons, 0, h, 0, ndx); System.arraycopy(icons, ndx + 1, h, ndx, h.length - ndx); System.arraycopy(fields, 0, f, 0, ndx); System.arraycopy(fields, ndx + 1, f, ndx, f.length - ndx); icons = h; fields = f; } else if (cmd.equals("specifyChoices")) { JMenuItem item = (JMenuItem) e.getSource(); DraggableIcon icon = (DraggableIcon) ((JPopupMenu) item.getParent()).getInvoker(); TemplateField f = getField(icon); EnumWindow w = new EnumWindow(this, f.getEnums()); w.show(lastMenuComponent, lastMenuX, lastMenuY); } else if (cmd.equals("setThumbSource")) { JPopupMenu menu = new JPopupMenu(); ButtonGroup g = new ButtonGroup(); JRadioButton dataset = new JRadioButton("Use thumbnail from dataset"); dataset.setSelected(true); g.add(dataset); JRadioButton file = new JRadioButton("Use thumbnail from file:"); g.add(file); menu.add(dataset); menu.add(file); JTextField field = new JTextField(); field.setPreferredSize(new Dimension(200, 25)); menu.add(field); JButton b = new JButton("OK"); b.addActionListener(this); b.setActionCommand("applyThumbSource"); menu.add(b); menu.show(lastMenuComponent, lastMenuX, lastMenuY); } else if (cmd.equals("applyThumbSource")) { JPopupMenu menu = (JPopupMenu) ((JComponent) e.getSource()).getParent(); DraggableIcon icon = (DraggableIcon) menu.getInvoker(); Component[] c = menu.getComponents(); JRadioButton dataset = (JRadioButton) c[0]; String text = null; if (!dataset.isSelected()) { JTextField t = (JTextField) c[2]; text = t.getText(); getField(icon).setValueMap(text); } menu.setVisible(false); if (text != null) { try { BufferedImageReader reader = new BufferedImageReader(); reader.setId(text); BufferedImage thumb = reader.openThumbImage(0); JLabel label = (JLabel) icon.image.getComponents()[0]; label.setIcon(new ImageIcon(thumb)); reader.close(); } catch (FormatException exc) { error("Failed to open thumbnail (" + text + ")", exc); } catch (IOException exc) { error("Failed to open thumbnail (" + text + ")", exc); } } } else if (cmd.equals("ok")) { // this event came from an instance of EnumWindow JPanel parent = (JPanel) ((JButton) e.getSource()).getParent(); EnumWindow menu = (EnumWindow) parent.getParent(); DraggableIcon icon = (DraggableIcon) menu.getInvoker(); TemplateField f = getField(icon); menu.setVisible(false); String[] options = menu.getOptions(); f.setEnums(options); JComboBox box = (JComboBox) icon.image.getComponents()[0]; for (int i=0; i<options.length; i++) box.addItem(options[i]); repaint(); } else if (cmd.equals("chooseMapping")) { // this event came from an instance of MappingWindow JTabbedPane parent = (JTabbedPane) ((JButton) e.getSource()).getParent(); MappingWindow menu = (MappingWindow) parent.getParent(); DraggableIcon icon = (DraggableIcon) menu.getInvoker(); TemplateField f = getField(icon); String omexmlMap = null; if (menu.nameMap) f.setNameMap(omexmlMap); else f.setValueMap(omexmlMap); menu.setVisible(false); } } // -- Helper methods -- /** Get the TemplateField corresponding to the given DraggableIcon. */ private TemplateField getField(DraggableIcon icon) { int idx = tabPane.getSelectedIndex(); Object[] keys = icons[idx].keySet().toArray(); for (int i=0; i<keys.length; i++) { if (icons[idx].get(keys[i]).equals(icon)) { return (TemplateField) fields[idx].get(keys[i]); } } return null; } /** Update the icon and template hashtables. */ private void rekey(int row, int col) { int idx = tabPane.getSelectedIndex(); Object[] keys = icons[idx].keySet().toArray(); Hashtable h = new Hashtable(); Hashtable f = new Hashtable(); for (int i=0; i<keys.length; i++) { Point p = (Point) keys[i]; if ((p.x == col + 1 && col != -1) || (p.y == row + 1 && row != -1)) { icons[idx].remove(keys[i]); fields[idx].remove(keys[i]); } else if ((p.x > col + 1 && col != -1) || (p.y > row + 1 && row != -1)) { int x = col != -1 ? p.x - 1 : p.x; int y = col != -1 ? p.y - 1 : p.y; Point k = new Point(x, y); h.put(k, icons[idx].get(keys[i])); f.put(k, fields[idx].get(keys[i])); } } icons[idx] = h; fields[idx] = f; } /** Add a new tab with the given name. */ private void addTab(String name, int rows, int cols) { String rowString = ""; for (int i=0; i<rows; i++) { rowString += "pref:grow"; if (i < rows - 1) rowString += ","; } String colString = ""; for (int i=0; i<cols; i++) { colString += "pref:grow"; if (i < cols - 1) colString += ","; } FormLayout layout = new FormLayout(colString, rowString); Hashtable[] tmp = icons; icons = new Hashtable[tmp.length + 1]; System.arraycopy(tmp, 0, icons, 0, tmp.length); icons[icons.length - 1] = new Hashtable(); tmp = fields; fields = new Hashtable[tmp.length + 1]; System.arraycopy(tmp, 0, fields, 0, tmp.length); fields[fields.length - 1] = new Hashtable(); JPanel panel = new JPanel(layout); for (int x=0; x<rows; x++) { for (int y=0; y<cols; y++) { addIcon(panel, x, y, tabPane.getTabCount()); } } tabPane.addTab(name, panel); } /** Add a new column to the current tab. */ private void addRow() { JPanel selected = (JPanel) tabPane.getSelectedComponent(); FormLayout layout = (FormLayout) selected.getLayout(); layout.appendRow(new RowSpec("pref:grow")); int ndx = tabPane.getSelectedIndex(); for (int i=0; i<layout.getColumnCount(); i++) { addIcon(selected, layout.getRowCount() - 1, i, ndx); } tabPane.repaint(); } /** Add a new row to the current tab. */ private void addColumn() { JPanel selected = (JPanel) tabPane.getSelectedComponent(); FormLayout layout = (FormLayout) selected.getLayout(); layout.appendColumn(new ColumnSpec("pref:grow")); int ndx = tabPane.getSelectedIndex(); for (int i=0; i<layout.getRowCount(); i++) { addIcon(selected, i, layout.getColumnCount() - 1, ndx); } tabPane.repaint(); } /** Add a field icon at the specified grid coordinates. */ private void addIcon(JPanel p, int x, int y, int tab) { CellConstraints cc = new CellConstraints(); DraggableIcon dummy = new DraggableIcon(new JPanel(), y + 1, x + 1); dummy.setTransferHandler(pictureHandler); dummy.setPreferredSize(new Dimension(128, 128)); JPopupMenu menu = new JPopupMenu(); JMenuItem item = new JMenuItem("Change field name"); item.addActionListener(this); item.setActionCommand("changeName"); menu.add(item); item = new JMenuItem("Change name mapping"); item.addActionListener(this); item.setActionCommand("nameMap"); menu.add(item); item = new JMenuItem("Change OME-XML mapping"); item.addActionListener(this); item.setActionCommand("map"); menu.add(item); item = new JMenuItem("Repeat this field"); item.addActionListener(this); item.setActionCommand("repeat"); menu.add(item); menu.add(new JSeparator()); item = new JMenuItem("Remove this field"); item.addActionListener(this); item.setActionCommand("removeField"); menu.add(item); item = new JMenuItem("Remove this row"); item.addActionListener(this); item.setActionCommand("removeRow:" + x); menu.add(item); item = new JMenuItem("Remove this column"); item.addActionListener(this); item.setActionCommand("removeColumn:" + y); menu.add(item); dummy.menu = menu; dummy.listener = this; MouseListener popupListener = new PopupListener(menu); dummy.addMouseListener(popupListener); p.add(dummy, cc.xy(y + 1, x + 1)); icons[tab].put(new Point(y + 1, x + 1), dummy); fields[tab].put(new Point(y + 1, x + 1), new TemplateField()); } /** Display an error message. */ protected void error(String msg, Exception exc) { if (exc != null) { StackTraceElement[] n = exc.getStackTrace(); for (int i=0; i<n.length; i++) { msg += "\n" + n[i]; } } JOptionPane.showMessageDialog(this, msg, "Error!", JOptionPane.ERROR_MESSAGE); } // -- Helper classes -- class PopupListener extends MouseAdapter { JPopupMenu popup; PopupListener(JPopupMenu popupMenu) { popup = popupMenu; } public void mousePressed(MouseEvent e) { maybeShowPopup(e); } public void mouseReleased(MouseEvent e) { maybeShowPopup(e); } private void maybeShowPopup(MouseEvent e) { if (e.isPopupTrigger()) { lastMenuComponent = (JComponent) e.getComponent(); lastMenuX = e.getX(); lastMenuY = e.getY(); popup.show(e.getComponent(), e.getX(), e.getY()); } } } // -- Main method -- public static void main(String[] args) { new TemplateEditor(); } }