/* ALMA - Atacama Large Millimiter Array * Copyright (c) European Southern Observatory, 2013 * * 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.gui.widgets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; import java.io.FileWriter; import javax.swing.JFileChooser; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JTextArea; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.text.Element; import alma.acs.gui.util.threadsupport.EDTExecutor; /** * <CODE>ExtendedTextArea</CODE> is a {@link JTextArea} written to replace * <CODE>com.cosylab.gui.components.r2.SmartTextArea</CODE>. * It is a light component with a minimum set of functionalities compared to that of * the abeans <CODE>SmartTextArea</CODE> because most of such functionalities have * never been used in ALMA. * <P> * This widget allows to easily add messages controlling the number of messages * displayed to avoid out of memory while running for long time. * It allows to save the content of the widget in a file by means of a popup menu. * <P> * The widget displays at most {@link #maxNumOfMessages} (default is {@link #defaultNumOfMessages}) * at a given time: when a new message arrives, the oldest one is removed if it is the case. * * @author acaproni * @since ACS 12.1 */ public class ExtendedTextArea extends JTextArea implements MouseListener { /** * The popup menu shown when the user presses the right mouse button * over the <CODE>ExtendedTextArea</CODE> component. * * @author acaproni * @since ACS 12.1 */ class PopupMenu extends JPopupMenu { /** * The menu item to save the content of the widget */ public final JMenuItem saveMI; /** * Constructor. */ public PopupMenu() { saveMI=new JMenuItem("Save"); add(saveMI); saveMI.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final JFileChooser chooser = new JFileChooser(); FileNameExtensionFilter filter = new FileNameExtensionFilter("Text files", "txt"); chooser.setFileFilter(filter); int returnVal = chooser.showSaveDialog(ExtendedTextArea.this); if(returnVal == JFileChooser.APPROVE_OPTION) { Thread saveThread = new Thread(new Runnable() { @Override public void run() { ExtendedTextArea.this.saveContent(chooser.getSelectedFile(),ExtendedTextArea.this.getText()); } },"ExtendedTextArea.saveThread"); saveThread.setDaemon(true); saveThread.start(); } } }); } } /** * The default of the max number of messages displayed by the widget. */ public static final int defaultNumOfMessages=500; /** * The max number of messages displayed by the widget. */ private int maxNumOfMessages=ExtendedTextArea.defaultNumOfMessages; /** * The popup menu * @see PopupMenu */ private PopupMenu menu; /** * Constructor * * @param maxNumOfMessages The max number of messages displayed by the widget */ public ExtendedTextArea(int maxNumOfMessages) { this.maxNumOfMessages=maxNumOfMessages; EDTExecutor.instance().execute(new Runnable() { @Override public void run() { initGUI(); } }); } /** * Constructor with the default max number of messages ({@link #defaultNumOfMessages} */ public ExtendedTextArea() { this(defaultNumOfMessages); } /** * Initialize the widget */ private void initGUI() { setEditable(false); addMouseListener(this); menu = new PopupMenu(); } /** * Append the passed message to the status area. */ public void append(final String msg) { if (msg==null || msg.isEmpty()) { // Nothing to do return; } EDTExecutor.instance().execute(new Runnable() { @Override public void run() { String txtToAppend=(msg.endsWith("\n"))?msg.substring(0,msg.length()-1):msg; if (getDocument().getDefaultRootElement().getElementCount()>0) { txtToAppend="\n"+txtToAppend; } ExtendedTextArea.super.append(txtToAppend); while (getDocument().getDefaultRootElement().getElementCount()>maxNumOfMessages) { Element root = getDocument().getDefaultRootElement(); Element first = root.getElement(0); try { getDocument().remove(first.getStartOffset(), first.getEndOffset()); } catch (Throwable t) { t.printStackTrace(); } } } }); } /** * @see MouseListener */ @Override public void mouseClicked(MouseEvent e) {} /** * @see MouseListener */ @Override public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()){ menu.show(e.getComponent(), e.getX(), e.getY()); } } /** * @see MouseListener */ @Override public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()){ menu.show(e.getComponent(), e.getX(), e.getY()); } } /** * @see MouseListener */ @Override public void mouseEntered(MouseEvent e) {} /** * @see MouseListener */ @Override public void mouseExited(MouseEvent e) {} /** * Save the content of the text area in a file with the passed name. * <P> * This method must not run into the EDT. * * @param outFile The file to save the content of the text area into * @param content The content of the text area */ private void saveContent(File outFile, String content) { if (outFile==null) { throw new IllegalArgumentException("The file can't be null!"); } if (content==null) { throw new IllegalArgumentException("The string to be saved can't be null!"); } if (content.isEmpty()) { return; } FileWriter writer=null; String errorMsg=null; // Set in case of error try { writer = new FileWriter(outFile); writer.write(content); } catch (Throwable t) { errorMsg="Error writing into "+outFile.getPath()+": "+t.getMessage(); System.err.println(errorMsg); t.printStackTrace(System.err); } finally { if (writer!=null) { try { writer.close(); } catch (Throwable t) { String msg="Error closing "+outFile.getPath()+": "+t.getMessage(); errorMsg=(errorMsg==null)?msg:"\n"+msg; System.err.println(msg); t.printStackTrace(System.err); } } } // Report the error, if any if (errorMsg!=null) { JOptionPane.showMessageDialog(this, errorMsg, "Error saving data", JOptionPane.ERROR_MESSAGE); } } }