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}