001package jmri.jmrit.jython;
002
003import java.awt.BorderLayout;
004import java.awt.Font;
005import java.awt.event.ActionEvent;
006import java.awt.event.ItemEvent;
007import java.awt.event.ItemListener;
008import javax.swing.AbstractAction;
009import javax.swing.JButton;
010import javax.swing.JCheckBox;
011import javax.swing.JFrame;
012import javax.swing.JPanel;
013import javax.swing.JScrollPane;
014import javax.swing.JTextArea;
015import javax.swing.ScrollPaneConstants;
016import javax.swing.event.DocumentEvent;
017import javax.swing.event.DocumentListener;
018import jmri.InstanceManager;
019import jmri.UserPreferencesManager;
020import jmri.script.ScriptOutput;
021import jmri.util.JmriJFrame;
022
023/**
024 * This Action creates a JmriJFrame displaying the thread output log from the
025 * {@link RunJythonScript} class.
026 *
027 * @author Bob Jacobsen Copyright (C) 2004
028 * @author Matthew Harris Copyright (C) 2010
029 */
030public class JythonWindow extends AbstractAction {
031
032    private JTextArea area;
033    private JFrame f;
034    private JCheckBox autoScroll;
035    private UserPreferencesManager pref;
036    private JButton clearButton;
037
038    public static final String alwaysOnTopCheck = JythonWindow.class.getName() + ".alwaysOnTop";
039    public static final String alwaysScrollCheck = JythonWindow.class.getName() + ".alwaysScroll";
040    protected JCheckBox alwaysOnTopCheckBox = new JCheckBox();
041
042    /**
043     * Constructor just initializes parent class.
044     *
045     * @param name Action name
046     */
047    public JythonWindow(String name) {
048        super(name);
049    }
050
051    public JythonWindow() {
052        super("Script Output Window");
053    }
054
055    /**
056     * Invoking this action via an event triggers display of a file dialog. If a
057     * file is selected, it's then invoked as a script.
058     */
059    @Override
060    public void actionPerformed(ActionEvent e) {
061        pref = InstanceManager.getDefault(UserPreferencesManager.class);
062
063        f = new JmriJFrame(Bundle.getMessage("TitleOutputFrame"));
064        f.getContentPane().add(
065                new JScrollPane(
066                        area = new javax.swing.JTextArea(ScriptOutput.getDefault().getOutputArea().getDocument(), null, 12, 50),
067                        ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
068                        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
069                ), BorderLayout.CENTER);
070
071        // Add checkbox to enable/disable auto-scrolling
072        JPanel p = new JPanel();
073        p.add(clearButton = new JButton(Bundle.getMessage("ButtonClear")));
074        p.add(autoScroll = new JCheckBox(Bundle.getMessage("CheckBoxAutoScroll"), true));
075        autoScroll.setSelected(pref.getSimplePreferenceState(alwaysScrollCheck));
076        alwaysOnTopCheckBox.setText("Window always on Top");
077        alwaysOnTopCheckBox.setVisible(true);
078        alwaysOnTopCheckBox.setToolTipText("If checked, this window be always be displayed in front of any other window");
079        alwaysOnTopCheckBox.setSelected(pref.getSimplePreferenceState(alwaysOnTopCheck));
080        p.add(alwaysOnTopCheckBox);
081        f.setAlwaysOnTop(alwaysOnTopCheckBox.isSelected());
082
083        autoScroll.addItemListener(new ItemListener() {
084
085            // Reference to the JTextArea of this instantiation
086            JTextArea ta = area;
087
088            @Override
089            public void itemStateChanged(ItemEvent e) {
090                if (e.getStateChange() == ItemEvent.SELECTED) {
091                    doAutoScroll(ta, true);
092                    pref.setSimplePreferenceState(alwaysScrollCheck, autoScroll.isSelected());
093                }
094            }
095        });
096
097        alwaysOnTopCheckBox.addActionListener((ActionEvent ae) -> {
098            f.setAlwaysOnTop(alwaysOnTopCheckBox.isSelected());
099            pref.setSimplePreferenceState(alwaysOnTopCheck, alwaysOnTopCheckBox.isSelected());
100        });
101
102        clearButton.addActionListener((ActionEvent ae) -> {
103            area.setText("");
104        });
105        f.getContentPane().add(p, BorderLayout.PAGE_END);
106
107        // set a monospaced font
108        int size = area.getFont().getSize();
109        area.setFont(new Font("Monospaced", Font.PLAIN, size));
110
111        // Add document listener to scroll to end when modified
112        area.getDocument().addDocumentListener(new DocumentListener() {
113
114            // References to the JTextArea and JCheckBox
115            // of this instantiation
116            JTextArea ta = area;
117            JCheckBox chk = autoScroll;
118
119            @Override
120            public void insertUpdate(DocumentEvent e) {
121                doAutoScroll(ta, chk.isSelected());
122            }
123
124            @Override
125            public void removeUpdate(DocumentEvent e) {
126                doAutoScroll(ta, chk.isSelected());
127            }
128
129            @Override
130            public void changedUpdate(DocumentEvent e) {
131                doAutoScroll(ta, chk.isSelected());
132            }
133        });
134
135        // Scroll to end of document
136        doAutoScroll(area, true);
137
138        f.pack();
139        f.setVisible(true);
140    }
141
142    /**
143     * Method to position caret at end of JTextArea ta when scroll true.
144     *
145     * @param ta     Reference to JTextArea
146     * @param scroll True to move to end
147     */
148    private void doAutoScroll(JTextArea ta, boolean scroll) {
149        if (scroll) {
150            ta.setCaretPosition(ta.getText().length());
151        }
152    }
153
154    public JFrame getFrame() {
155        return f;
156    }
157
158}