/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package alma.acs.logging.table; import java.awt.Component; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import javax.swing.DefaultListSelectionModel; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JFileChooser; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; import alma.acs.logging.tools.CSVConverter; import alma.acs.logging.tools.LogConverter; import alma.acs.logging.tools.TextConverter; import alma.acs.logging.tools.TwikiTableConverter; import alma.acs.logging.tools.XMLConverter; import com.cosylab.logging.LoggingClient; import com.cosylab.logging.engine.log.ILogEntry; import com.cosylab.logging.engine.log.LogTypeHelper; import com.cosylab.logging.engine.log.LogField; import com.cosylab.logging.settings.UserInfoDlg; /** * The JPopupMenu displayed when the user presses the right mouse button * over a row of the table * * @author acaproni * */ public class TablePopupMenu extends JPopupMenu implements ActionListener { /** * The file chooser to see the icon of the given type */ private static final class CustomFileChooser extends JFileChooser { /** * The icon to show for the file type */ private final ImageIcon icon; /** * The filter for the type of the file */ private final FileFilter filter; /** * Constructor * * @param icon */ public CustomFileChooser(ImageIcon icon, FileFilter filter) { if (icon==null || filter==null) { throw new IllegalArgumentException("Invalid null param"); } this.icon=icon; this.filter=filter; } /* (non-Javadoc) * @see javax.swing.JFileChooser#getIcon(java.io.File) */ @Override public Icon getIcon(File f) { if (f.isDirectory()) { return super.getIcon(f); } else if (filter!=null && filter.accept(f)) { return icon; } else { return super.getIcon(f); } } } /** * The class to set the clipboard content * * @author acaproni * */ private static final class TextTransfer implements ClipboardOwner { /** * Empty implementation of the ClipboardOwner interface. */ public void lostOwnership( Clipboard aClipboard, Transferable aContents) { //do nothing } /** * Place a String on the clipboard, and make this class the * owner of the Clipboard's contents. */ public void setClipboardContents( String str){ StringSelection stringSelection = new StringSelection(str); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); try { clipboard.setContents( stringSelection, stringSelection ); } catch (IllegalStateException e) { // This exception may be returned in some cases // It is a temporary situation: we do nothing here // and the user will retry again or most likely // submit an SPR ;-) e.printStackTrace(); } /* Check if the clipboards contains the right string String readStr=null; try { readStr = (String)(clipboard.getContents(this).getTransferData(DataFlavor.stringFlavor)); } catch (Exception e) { e.printStackTrace(); } System.out.println("Write ["+str+"]"); System.out.println("Write ["+readStr+"]"); System.out.println("Comparison="+str.compareTo(readStr));*/ } /** * Get the String residing on the clipboard. * * @return any text found on the Clipboard; if none found, return an * empty String. */ private String getClipboardContents() { String result = ""; Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); //odd: the Object param of getContents is not currently used Transferable contents = clipboard.getContents(null); boolean hasTransferableText = (contents != null) && contents.isDataFlavorSupported(DataFlavor.stringFlavor); if (hasTransferableText) { try { result = (String)contents.getTransferData(DataFlavor.stringFlavor); } catch (UnsupportedFlavorException ex){ //highly unlikely since we are using a standard DataFlavor System.out.println(ex); } catch (IOException ex) { System.out.println(ex); } } return result; } } /** * The menu item to show the error stack. * <P> * It is enabled only if the stack ID is not null */ private final JMenuItem showErrorStack = new JMenuItem("Show error stack"); /** * The menu item to allow the user to add his information */ private final JMenuItem addUserInfo = new JMenuItem("add Info"); /** * The icon of the menu item to save selected logs */ private final ImageIcon saveIcon =new ImageIcon(LogTypeHelper.class.getResource("/disk.png")); /** * The sub menu item to save the selected logs */ private final JMenu saveSelected = new JMenu("Save selected logs..."); /** * The icon of the menu item to save selected logs as twiki table */ private final ImageIcon saveXmlIcon =new ImageIcon(LogTypeHelper.class.getResource("/xml-file.png")); /** * The menu item to save selected logs as XML */ private final JMenuItem saveSelectedAsXML=new JMenuItem("Save as XML",saveXmlIcon); /** * The icon of the menu item to save selected logs as twiki table */ private final ImageIcon saveTextIcon =new ImageIcon(LogTypeHelper.class.getResource("/text-files.gif")); /** * The menu item to save selected logs as plain ASCII */ private final JMenuItem saveSelectedAsText=new JMenuItem("Save as text",saveTextIcon); /** * The icon of the menu item to save selected logs as twiki table */ private final ImageIcon saveTwikiTableIcon =new ImageIcon(LogTypeHelper.class.getResource("/T-twiki.gif")); /** * The menu item to save selected logs as plain ASCII (twiki table) */ private final JMenuItem saveSelectedAsTwiki=new JMenuItem("Save as twiki table",saveTwikiTableIcon); /** * The icon of the menu item to save selected logs as twiki table */ private final ImageIcon saveCsvIcon =new ImageIcon(LogTypeHelper.class.getResource("/csv-files.png")); /** * The menu item to save selected logs as plain ASCII (CSV) */ private final JMenuItem saveSelectedAsCSV=new JMenuItem("Save as CSV",saveCsvIcon); /** * The icon of the menu item to save selected logs */ private final ImageIcon zoomIcon =new ImageIcon(LogTypeHelper.class.getResource("/zoom.png")); /** * The menu item to save the selected logs */ private final JMenuItem zoomOverSelected = new JMenuItem("Drill down",zoomIcon); private LogTableRowSorter sorter; /** * The icon of the clipboard menu item */ private final ImageIcon clipboardIcon =new ImageIcon(LogTypeHelper.class.getResource("/clipboard.png")); /** * The menu to copy the text to the clipboard */ private final JMenu copyClipboard = new JMenu("to clipboard"); /** * Copy the clipboard as text */ private JMenuItem copyClipTxt= new JMenuItem("Copy as text",saveTextIcon); /** * Copy the clipboard as XML */ private JMenuItem copyClipXml= new JMenuItem("Copy as XML",saveXmlIcon); /** * Copy the clipboard as twiki table */ private JMenuItem copyClipTwikiTable= new JMenuItem("Copy as TwikiTable",saveTwikiTableIcon); /** * Copy the clipboard as CSV */ private JMenuItem copyClipCSV= new JMenuItem("Copy as CSV",saveCsvIcon); /** * The text to copy */ private String textToCopy; /** * The row and column under the mouse pointer */ private int row; /** * This property is used to select the logs for the error browser. * Its value is initialized in the constructor by reading the <code>STACKID</code> of the selected log. */ private String stackId; /** * The <code>LoggingClient</code> */ private final LoggingClient loggingClient; /** * The table */ private final LogEntryTable table; /** * The model */ private final LogTableDataModel model; /** * The selection model */ private final DefaultListSelectionModel selectionModel; /** * The helper for the clipboard */ private final TextTransfer textTransfer=new TextTransfer(); /** * Constructor * * @param row The row below the mouse pointer * @param col The col below he pointer * @param text The text below the pointer * @param logCli The <code>LoggingClient</code> */ public TablePopupMenu(LoggingClient logCli,LogEntryTable table) { this.loggingClient=logCli; this.table=table; this.model=table.getLCModel(); this.selectionModel=(DefaultListSelectionModel)table.getSelectionModel(); this.sorter=(LogTableRowSorter)table.getRowSorter(); // Add the Label setLabel("Paste"); // Add the menu items add(zoomOverSelected); zoomOverSelected.addActionListener(this); addSeparator(); add(showErrorStack); addSeparator(); saveSelected.setIcon(saveIcon); saveSelected.add(saveSelectedAsXML); saveSelected.add(saveSelectedAsText); saveSelected.add(saveSelectedAsTwiki); saveSelected.add(saveSelectedAsCSV); add(saveSelected); addSeparator(); copyClipboard.setIcon(clipboardIcon); copyClipboard.add(copyClipXml); copyClipboard.add(copyClipTxt); copyClipboard.add(copyClipTwikiTable); copyClipboard.add(copyClipCSV); add(copyClipboard); add(addUserInfo); // Add the listeners showErrorStack.addActionListener(this); copyClipXml.addActionListener(this); copyClipTxt.addActionListener(this); copyClipTwikiTable.addActionListener(this); copyClipCSV.addActionListener(this); addUserInfo.addActionListener(this); saveSelectedAsCSV.addActionListener(this); saveSelectedAsText.addActionListener(this); saveSelectedAsTwiki.addActionListener(this); saveSelectedAsXML.addActionListener(this); } /** * Handle the events from the menu * * @param e The event */ public void actionPerformed(ActionEvent e) { if (textToCopy==null) { return; } if (e.getSource()==copyClipXml) { copyToClipboard(new XMLConverter()); } else if (e.getSource()==copyClipTxt) { copyToClipboard(new TextConverter(null)); } else if (e.getSource()==copyClipTwikiTable) { copyToClipboard(new TwikiTableConverter(null)); } else if (e.getSource()==copyClipCSV) { copyToClipboard(new CSVConverter()); } else if (e.getSource()==addUserInfo) { // Show the dialog UserInfoDlg dlg = new UserInfoDlg(); if (dlg.okPressed()) { String name = dlg.getInfoName(); String value = dlg.getInfo(); // Replace some chars potentially dangerous for xml value = value.replaceAll("&","&"); value = value.replaceAll("'","'"); value = value.replaceAll("`","'"); value = value.replaceAll("\"","""); value = value.replaceAll("<","<"); value = value.replaceAll(">",">"); name = name.replaceAll("&","&"); name = name.replaceAll("'","'"); name = name.replaceAll("`","'"); name = name.replaceAll("\"","""); name = name.replaceAll("<","<"); name = name.replaceAll(">",">"); ILogEntry logEntry = model.getVisibleLogEntry(table.convertRowIndexToModel(row)); logEntry.addData(name,value); model.replaceLog(table.convertRowIndexToModel(row),logEntry); LogTableDataModel tableModel = (LogTableDataModel) table.getModel(); tableModel.fireTableRowsUpdated(table.convertRowIndexToModel(row),table.convertRowIndexToModel(row)); table.setRowSelectionInterval(row,row); loggingClient.setLogDetailContent(logEntry); } } else if (e.getSource()==saveSelectedAsCSV) { saveSelectedLogs(new CSVConverter(),saveCsvIcon,"txt"); } else if (e.getSource()==saveSelectedAsText) { saveSelectedLogs(new TextConverter(null),saveTextIcon,"txt"); } else if (e.getSource()==saveSelectedAsTwiki) { saveSelectedLogs(new TwikiTableConverter(null),saveTwikiTableIcon,"txt"); } else if (e.getSource()==saveSelectedAsXML) { saveSelectedLogs(new XMLConverter(),saveXmlIcon,"xml"); }else if (e.getSource()==showErrorStack) { if (stackId!=null && !stackId.isEmpty()) { loggingClient.addErrorTab(stackId); } } else if (e.getSource()==zoomOverSelected) { Thread t = new Thread(new Runnable() { public void run() { table.zoom(); } }); t.setDaemon(true); t.setName("TablePopupMenu.actionPerformed.Zoom"); t.start(); } else { System.err.println("Unhandled event "+e); } } /** * Save the selected logs into a file in the passed format. * * @param converter The converter to desired format * @param fileIcon The icon of the file type */ private void saveSelectedLogs(LogConverter converter, ImageIcon fileIcon, String extension) { if (converter==null) { throw new IllegalArgumentException("The converter can't be null"); } if (extension==null || extension.isEmpty()) { throw new IllegalArgumentException("Invalid file extension"); } converter.setCols(buildFields()); // Build the text to save in the file StringBuilder strBuffer = new StringBuilder(); if (!selectionModel.isSelectionEmpty()) { for (int i=selectionModel.getMinSelectionIndex(); i<=selectionModel.getMaxSelectionIndex(); i++) { if (!selectionModel.isSelectedIndex(i)) { continue; } else { ILogEntry log = model.getVisibleLogEntry(sorter.convertRowIndexToModel(i)); strBuffer.append(converter.convert(log)); } } if (strBuffer.length()==0) { // Nothing to save return; } } FileFilter fileFilter = new FileNameExtensionFilter("File "+extension,extension); CustomFileChooser fc = new CustomFileChooser(fileIcon,fileFilter); fc.addChoosableFileFilter(fileFilter); if (fc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { File file = fc.getSelectedFile(); // If not present, add the xml extension if (!file.getAbsolutePath().toLowerCase().endsWith("."+extension)) { file = new File(file.getAbsolutePath()+"."+extension); } FileOutputStream outFStream=null; try { outFStream = new FileOutputStream(file); } catch (FileNotFoundException fnfe) { JOptionPane.showMessageDialog(null,"Error creating "+file.getAbsolutePath()+":\n"+fnfe.getMessage(),"Error saving logs",JOptionPane.ERROR_MESSAGE); return; } try { outFStream.write(strBuffer.toString().getBytes()); outFStream.flush(); outFStream.close(); } catch (IOException ioe) { JOptionPane.showMessageDialog(null,"Error saving "+file.getAbsolutePath()+":\n"+ioe.getMessage(),"Error saving logs",JOptionPane.ERROR_MESSAGE); } } } /** * Show the popup menu * * @param invoker the component in whose space the popup menu is to appear * @param x the x coordinate in invoker's coordinate space at which the popup menu is to be displayed * @param y the y coordinate in invoker's coordinate space at which the popup menu is to be displayed * @param row The row below the pointer * @param col The col below the pointer * @param txt The text below the pointer */ public void show(Component invoker, int x, int y, int row, String txt) { this.row=row; this.textToCopy=txt; // Hide the unneeded buttons boolean singleLineSelected = selectionModel.getMinSelectionIndex() == selectionModel.getMaxSelectionIndex(); // The error stack menu item is enabled only if the stack ID is not empty and not null if (singleLineSelected) { ILogEntry selectedLog = model.getVisibleLogEntry(table.convertRowIndexToModel(row)); stackId = (String)selectedLog.getField(LogField.STACKID); showErrorStack.setEnabled(stackId!=null && !stackId.isEmpty()); } else { stackId=null; showErrorStack.setEnabled(false); } addUserInfo.setEnabled(singleLineSelected); copyClipboard.setEnabled(true); saveSelected.setEnabled(true); // Disable the menu items if the test is null or empty if (textToCopy==null || textToCopy.length()==0) { copyClipboard.setEnabled(false); return; } // Check if the system clipboard is available // and eventually disable the menu item SecurityManager sm = System.getSecurityManager(); if (sm != null) { try { sm.checkSystemClipboardAccess(); } catch (Exception e) { copyClipboard.setEnabled(false); } } // Disable the zoom if only one line is selected zoomOverSelected.setEnabled(!singleLineSelected && loggingClient.getZoomManager().isAvailable()); show(invoker, x, y); } /** * Build the string of log fields to write in the output * * @return the string of log fields to write in the output */ private String buildFields() { int colCount=table.getColumnCount(); StringBuilder ret=new StringBuilder(); for (int t=0; t<colCount; t++) { int modelCount = table.convertColumnIndexToModel(t); if (modelCount==0) { continue; } ret.append(LogField.values()[modelCount-1].id); } return ret.toString(); } private void copyToClipboard(LogConverter converter) { if (converter==null) { throw new IllegalArgumentException("Invalid null converter"); } converter.setCols(buildFields()); // Build the text to copy in the clipboard if (!selectionModel.isSelectionEmpty()) { StringBuffer strBuffer = new StringBuffer(); for (int i=selectionModel.getMinSelectionIndex(); i<=selectionModel.getMaxSelectionIndex(); i++) { if (!selectionModel.isSelectedIndex(i)) { continue; } else { ILogEntry log = model.getVisibleLogEntry(table.convertRowIndexToModel(i)); strBuffer.append(converter.convert(log)); } } // Copy the text to the clipboard textTransfer.setClipboardContents(strBuffer.toString()); } } }