001package jmri.jmrix.can.cbus.swing.console;
002
003import java.awt.Color;
004import java.awt.Desktop;
005import java.io.File;
006import java.io.FileNotFoundException;
007import java.io.FileOutputStream;
008import java.io.IOException;
009import java.io.PrintStream;
010import javax.swing.*;
011import jmri.util.FileUtil;
012import jmri.util.swing.JmriJOptionPane;
013
014/**
015 * Pane for CBUS Console Logging to File Options
016 *
017 * @author Andrew Crosland Copyright (C) 2008
018 * @author Steve Young Copyright (C) 2018
019 */
020public class CbusConsoleLoggingPane extends javax.swing.JPanel {
021
022    private final CbusConsolePane _mainPane;
023    private final JFileChooser logFileChooser;
024    private final JToggleButton startStopLogButton;
025    private final JButton openLogFileButton;
026    private final JButton openFileChooserButton;
027    private final JTextField entryField;
028    private final JButton logenterButton;
029    private static final String IS_LOGGING_TO_FILE = ".isLoggingToFile.";
030
031    // @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "separately interlocked")
032    private transient PrintStream logStream;
033
034    public CbusConsoleLoggingPane(CbusConsolePane mainPane){
035        super();
036        _mainPane = mainPane;
037        // set file chooser to a default
038        logFileChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath());
039        logFileChooser.setSelectedFile(new File(FileUtil.getUserFilesPath()
040            + _mainPane.getMemo().getUserName() + "_monitorLog.txt"));
041
042        startStopLogButton = new JToggleButton();
043        openLogFileButton = new JButton();
044        openFileChooserButton = new JButton();
045        entryField = new JTextField();
046        logenterButton = new JButton();
047        setupPane();
048
049    }
050
051    private void setupPane() {
052
053        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
054        setBorder(BorderFactory.createTitledBorder(
055            BorderFactory.createEtchedBorder(), Bundle.getMessage("Logging")));
056
057        setButtonToolTips();
058
059        add(startStopLogButton);
060        add(openFileChooserButton);
061        add(openLogFileButton);
062        add(logenterButton);
063        add(entryField);
064
065        addListeners();
066
067        var p = jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class);
068        if (p.getSimplePreferenceState(logToFilePreferenceString())) {
069            startStopLogButton.setSelected(true);
070            updateStartStopButtonText();
071            startLogButtonActionPerformed(null);
072        }
073
074    }
075
076    private void setSelectFileToolTip() {
077
078        openFileChooserButton.setToolTipText(logFileChooser.getSelectedFile().getPath());
079
080
081    }
082
083    private void addListeners() {
084
085        startStopLogButton.addActionListener((java.awt.event.ActionEvent e) -> {
086
087            openFileChooserButton.setEnabled(!startStopLogButton.isSelected());
088
089            if (startStopLogButton.isSelected()){
090                startLogButtonActionPerformed(e);
091            }
092            else {
093                stopLogButtonActionPerformed(e);
094            }
095
096            updateStartStopButtonText();
097
098        });
099
100        openFileChooserButton.addActionListener(this::openFileChooserButtonActionPerformed);
101
102        openLogFileButton.addActionListener((java.awt.event.ActionEvent e) -> {
103            try {
104                openLogFileActionPerformed(e);
105            } catch (IOException ex) {
106                log.error("log file open exception", ex);
107            }
108        });
109
110        logenterButton.addActionListener(this::textToLogButtonActionPerformed);
111        entryField.addActionListener(this::textToLogButtonActionPerformed);
112
113        updateStartStopButtonText();
114
115    }
116
117    private void setButtonToolTips() {
118
119        logenterButton.setText(Bundle.getMessage("ButtonAddMessage"));
120        logenterButton.setToolTipText(Bundle.getMessage("TooltipAddMessage"));
121
122        openFileChooserButton.setText(Bundle.getMessage("ButtonChooseLogFile"));
123        setSelectFileToolTip();
124
125        openLogFileButton.setText(Bundle.getMessage("OpenLogFile"));
126        openLogFileButton.setToolTipText(Bundle.getMessage("OpenLogFileTip"));
127
128        entryField.setToolTipText(Bundle.getMessage("EntryAddtoLogTip"));
129
130    }
131
132    private void updateStartStopButtonText(){
133
134        if (startStopLogButton.isSelected()){
135
136            startStopLogButton.setText(Bundle.getMessage("ButtonStopLogging"));
137            startStopLogButton.setToolTipText(Bundle.getMessage("TooltipStopLogging"));
138            startStopLogButton.setForeground(Color.red);
139
140        }
141        else {
142
143            startStopLogButton.setText(Bundle.getMessage("ButtonStartLogging"));
144            startStopLogButton.setToolTipText(Bundle.getMessage("TooltipStartLogging") + " " +
145                Bundle.getMessage("ButtonStartLogTipExtra"));
146            startStopLogButton.setForeground(new JToggleButton().getForeground());
147
148        }
149
150    }
151
152    private void startLogButtonActionPerformed(java.awt.event.ActionEvent e) {
153        // start logging by creating the stream
154        if (logStream == null) {  // successive clicks don't restart the file
155            // start logging
156            try {
157                logStream = new PrintStream(new FileOutputStream(logFileChooser.getSelectedFile()));
158            } catch (FileNotFoundException ex) {
159                log.error("Unable to open log file {}", logFileChooser.getSelectedFile(), ex);
160                // not logging, so do not show the button as logging
161                logStream = null;
162                startStopLogButton.setSelected(false);
163                updateStartStopButtonText();
164            }
165        }
166    }
167
168    private void stopLogButtonActionPerformed(java.awt.event.ActionEvent e) {
169        // stop logging by removing the stream
170        if (logStream != null) {
171            logStream.flush();
172            logStream.close();
173        }
174        logStream = null;
175    }
176
177    private void openFileChooserButtonActionPerformed(java.awt.event.ActionEvent e) {
178        // start at current file, show dialog
179        int retVal = logFileChooser.showSaveDialog(this);
180
181        // handle selection or cancel
182        if (retVal == JFileChooser.APPROVE_OPTION) {
183            boolean loggingNow = (logStream != null);
184            stopLogButtonActionPerformed(e);  // stop before changing file
185            //File file = logFileChooser.getSelectedFile();
186            // if we were currently logging, start the new file
187            if (loggingNow) {
188                startLogButtonActionPerformed(e);
189            }
190        }
191        setSelectFileToolTip();
192    }
193
194    private void openLogFileActionPerformed(java.awt.event.ActionEvent e) throws IOException {
195        // start at current file, show dialog
196        Desktop desktop = Desktop.getDesktop();
197        File dirToOpen;
198
199        try {
200            dirToOpen = logFileChooser.getSelectedFile();
201            desktop.open(dirToOpen);
202        } catch (IllegalArgumentException iae) {
203            JmriJOptionPane.showMessageDialog(_mainPane,
204                (Bundle.getMessage("NoOpenLogFile")), Bundle.getMessage("WarningTitle"),
205                JmriJOptionPane.ERROR_MESSAGE);
206        }
207
208    }
209
210    private void textToLogButtonActionPerformed(java.awt.event.ActionEvent e) {
211        _mainPane.nextLine(entryField.getText() + "\n", entryField.getText() + "\n", -1);
212    }
213
214    private final String newline = System.getProperty("line.separator");
215
216    protected void sendLogToFile( String sbCbus ){
217        if (logStream != null) {
218            String logLine = sbCbus;
219            if (!newline.equals("\n")) {
220                // have to massage the line-ends
221                StringBuilder out = new StringBuilder(sbCbus.length() + 10);  // arbitrary guess at space
222                for (int j = 0; j < sbCbus.length(); j++) {
223                    if (sbCbus.charAt(j) == '\n') {
224                        out.append(newline);
225                    } else {
226                        out.append(sbCbus.charAt(j));
227                    }
228                }
229                logLine = new String(out);
230            }
231            logStream.print(logLine);
232        }
233    }
234
235    private String logToFilePreferenceString(){
236        return getClass().getName() + IS_LOGGING_TO_FILE + _mainPane.getMemo().getSystemPrefix();
237    }
238
239    public void dispose(){
240        var p = jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class);
241        p.setSimplePreferenceState(logToFilePreferenceString(), startStopLogButton.isSelected());
242        // close the log file, the logging state is restored from the preference
243        // when the console is next opened
244        stopLogButtonActionPerformed(null);
245    }
246
247    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusConsoleLoggingPane.class);
248
249}