001package jmri.util.swing; 002 003import javax.annotation.Nonnull; 004import javax.swing.event.DocumentEvent; 005import javax.swing.event.DocumentListener; 006import javax.swing.JTextArea; 007import javax.swing.text.BadLocationException; 008import javax.swing.text.Element; 009import jmri.util.ThreadingUtil; 010 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014/** 015 * Create a new TextAreaFIFO, an extended JTextArea 016 * Keeps message log windows to a reasonable length 017 * Scrolls down to last line of textarea by default 018 * Originally based on https://community.oracle.com/thread/1373400 019 * Modified for JMRI by Steve Young (c) 2018 020 * 021 */ 022public class TextAreaFIFO extends JTextArea implements DocumentListener { 023 private int _maxLines; 024 private Boolean _autoScroll; 025 026 /** 027 * Add text to the console 028 * 029 * @param lines number of lines 030 */ 031 public TextAreaFIFO(int lines) { 032 _maxLines = lines; 033 _autoScroll = true; 034 getDocument().addDocumentListener( this ); 035 } 036 037 @Override 038 public void insertUpdate(DocumentEvent e) { 039 ThreadingUtil.runOnGUIEventually( ()->{ 040 removeLines(); 041 }); 042 } 043 @Override 044 public void removeUpdate(DocumentEvent e ) { 045 ThreadingUtil.runOnGUIEventually ( ()->{ 046 removeLines(); 047 }); 048 } 049 @Override 050 public void changedUpdate(DocumentEvent e) { 051 ThreadingUtil.runOnGUIEventually( ()->{ 052 removeLines(); 053 }); 054 } 055 056 /** 057 * Set whether the JTextArea should scroll to bottom on update 058 * 059 * @param newval autoscrolls if true 060 */ 061 public void setAutoScroll(@Nonnull Boolean newval) { 062 _autoScroll = newval; 063 if (_autoScroll) { 064 ThreadingUtil.runOnGUIEventually( ()->{ 065 removeLines(); 066 }); 067 } 068 } 069 070 /** 071 * Edit maximum lines in JTextArea before trimming from top 072 * 073 * @param newval Number of lines 074 */ 075 public void setMaxLines(int newval){ 076 _maxLines = newval; 077 } 078 079 080 /** 081 * Main internal method to trim from top, then if needed, move scroll position 082 * 083 */ 084 private void removeLines() { 085 Element root = getDocument().getDefaultRootElement(); 086 while (root.getElementCount() > ( _maxLines + 1 ) ) { 087 Element firstLine = root.getElement(0); 088 try { 089 getDocument().remove(0, firstLine.getEndOffset()); 090 } catch(BadLocationException ble) { 091 log.error("bad location",ble); 092 } 093 } 094 if ( _autoScroll ) { 095 setCaretPosition( getDocument().getLength() ); 096 } 097 } 098 099 /** 100 * Removes document listener 101 * 102 */ 103 public void dispose() { 104 getDocument().removeDocumentListener( this ); 105 } 106 private final static Logger log = LoggerFactory.getLogger(TextAreaFIFO.class); 107}