001package jmri.jmrix; 002 003import java.awt.Component; 004import java.awt.Dimension; 005import java.awt.event.ActionEvent; 006import java.io.File; 007import java.io.FileOutputStream; 008import java.io.PrintStream; 009import java.text.DateFormat; 010import java.text.SimpleDateFormat; 011import java.util.Date; 012 013import javax.annotation.concurrent.GuardedBy; 014import javax.swing.BoxLayout; 015import javax.swing.JButton; 016import javax.swing.JCheckBox; 017import javax.swing.JFileChooser; 018import javax.swing.JLabel; 019import javax.swing.JPanel; 020import javax.swing.JScrollPane; 021import javax.swing.JTextArea; 022import javax.swing.JTextField; 023import javax.swing.JToggleButton; 024import javax.swing.SwingUtilities; 025import javax.swing.text.AbstractDocument; 026import javax.swing.text.AttributeSet; 027import javax.swing.text.BadLocationException; 028import javax.swing.text.DocumentFilter; 029 030import jmri.InstanceManager; 031import jmri.UserPreferencesManager; 032import jmri.util.FileUtil; 033import jmri.util.JmriJFrame; 034import jmri.util.swing.JmriJOptionPane; 035import jmri.util.swing.JmriPanel; 036import jmri.util.swing.TextAreaFIFO; 037import jmri.util.swing.WrapLayout; 038 039/** 040 * Abstract base class for JPanels displaying communications monitor 041 * information. 042 * 043 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2010 044 */ 045public abstract class AbstractMonPane extends JmriPanel { 046 047 /** 048 * {@inheritDoc} 049 */ 050 @Override 051 public abstract String getTitle(); // provide the title for the frame 052 053 /** 054 * Initialize the data source. 055 * <p> 056 * This is invoked at the end of the GUI initialization phase. Subclass 057 * implementations should connect to their data source here. 058 */ 059 protected abstract void init(); 060 061 /** 062 * {@inheritDoc} 063 */ 064 @Override 065 public void dispose() { 066 UserPreferencesManager pm = InstanceManager.getDefault(UserPreferencesManager.class); 067 pm.setSimplePreferenceState(timeStampCheck, timeCheckBox.isSelected()); 068 pm.setSimplePreferenceState(rawDataCheck, rawCheckBox.isSelected()); 069 pm.setSimplePreferenceState(alwaysOnTopCheck, alwaysOnTopCheckBox.isSelected()); 070 pm.setSimplePreferenceState(autoScrollCheck, !autoScrollCheckBox.isSelected()); 071 pm.setProperty(filterFieldCheck, filterFieldCheck, filterField.getText()); 072 monTextPane.dispose(); 073 super.dispose(); 074 } 075 // you'll also have to add the message(Foo) members to handle info to be logged. 076 // these should call nextLine(String line, String raw) with their updates 077 078 // member declarations 079 protected JButton clearButton = new JButton(); 080 protected JToggleButton freezeButton = new JToggleButton(); 081 protected JScrollPane jScrollPane1 = new JScrollPane(); 082 protected TextAreaFIFO monTextPane = new TextAreaFIFO(MAX_LINES); 083 protected JToggleButton startLogButton = new JToggleButton(); 084 protected JButton stopLogButton = new JButton(); 085 protected JCheckBox rawCheckBox = new JCheckBox(); 086 protected JCheckBox timeCheckBox = new JCheckBox(); 087 protected JCheckBox alwaysOnTopCheckBox = new JCheckBox(); 088 protected JCheckBox autoScrollCheckBox = new JCheckBox(); 089 protected JTextField filterField = new JTextField(); 090 protected JLabel filterLabel = new JLabel(Bundle.getMessage("LabelFilterBytes"), JLabel.LEFT); // NOI18N 091 protected JButton openFileChooserButton = new JButton(); 092 protected JTextField entryField = new JTextField(); 093 protected JButton enterButton = new JButton(); 094 String rawDataCheck = this.getClass().getName() + ".RawData"; // NOI18N 095 String timeStampCheck = this.getClass().getName() + ".TimeStamp"; // NOI18N 096 String alwaysOnTopCheck = this.getClass().getName() + ".AlwaysOnTop"; // NOI18N 097 String autoScrollCheck = this.getClass().getName() + ".AutoScroll"; // NOI18N 098 String filterFieldCheck = this.getClass().getName() + ".FilterField"; // NOI18N 099 100 // to find and remember the log file 101 final javax.swing.JFileChooser logFileChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath()); 102 103 public AbstractMonPane() { 104 super(); 105 } 106 107 /** 108 * By default, create just one place (one data pane) to put trace data. 109 */ 110 protected void createDataPanes() { 111 configureDataPane(monTextPane); 112 } 113 114 /** 115 * Do default configuration of a data pane. 116 * 117 * @param textPane a TextAreaFIFO into which the data pane will be placed 118 */ 119 protected void configureDataPane(TextAreaFIFO textPane) { 120 textPane.setVisible(true); 121 textPane.setToolTipText(Bundle.getMessage("TooltipMonTextPane")); // NOI18N 122 textPane.setEditable(false); 123 } 124 125 /** 126 * Provide initial preferred line length. Used to size the initial GUI. 127 * 128 * @return preferred initial number of columns 129 */ 130 protected int getInitialPreferredLineLength() { 131 return 80; 132 } 133 134 /** 135 * Provide initial number of lines to display Used to size the initial GUI. 136 * 137 * @return preferred initial number of rows 138 */ 139 protected int getInitialPreferredLineCount() { 140 return 10; 141 } 142 143 /** 144 * Put data pane(s) in the GUI. 145 */ 146 protected void addDataPanes() { 147 148 // fix a width for current character set 149 JTextField t = new JTextField(getInitialPreferredLineLength()); 150 int x = jScrollPane1.getPreferredSize().width + t.getPreferredSize().width; 151 int y = jScrollPane1.getPreferredSize().height + getInitialPreferredLineCount() * t.getPreferredSize().height; 152 153 jScrollPane1.getViewport().add(monTextPane); 154 jScrollPane1.setPreferredSize(new Dimension(x, y)); 155 jScrollPane1.setVisible(true); 156 157 // add in a JPanel that stays sized as the window changes size 158 JPanel p = new JPanel(); 159 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 160 p.add(jScrollPane1); 161 add(p); 162 } 163 164 /** 165 * {@inheritDoc} 166 */ 167 @Override 168 public void initComponents() { 169 UserPreferencesManager pm = InstanceManager.getDefault(UserPreferencesManager.class); 170 171 // the following code sets the frame's initial state 172 clearButton.setText(Bundle.getMessage("ButtonClearScreen")); // NOI18N 173 clearButton.setVisible(true); 174 clearButton.setToolTipText(Bundle.getMessage("TooltipClearMonHistory")); // NOI18N 175 176 freezeButton.setText(Bundle.getMessage("ButtonFreezeScreen")); // NOI18N 177 freezeButton.setVisible(true); 178 freezeButton.setToolTipText(Bundle.getMessage("TooltipStopScroll")); // NOI18N 179 180 enterButton.setText(Bundle.getMessage("ButtonAddMessage")); // NOI18N 181 enterButton.setVisible(true); 182 enterButton.setToolTipText(Bundle.getMessage("TooltipAddMessage")); // NOI18N 183 184 createDataPanes(); 185 186 entryField.setToolTipText(Bundle.getMessage("TooltipEntryPane")); // NOI18N 187 // cap vertical size to avoid over-growth 188 Dimension currentPreferredSize = entryField.getPreferredSize(); 189 Dimension currentMaximumSize = entryField.getMaximumSize(); 190 currentMaximumSize.height = currentPreferredSize.height; 191 entryField.setMaximumSize(currentMaximumSize); 192 193 //setup filterField 194 filterField.setToolTipText(Bundle.getMessage("TooltipFilter")); // NOI18N 195 filterField.setMaximumSize(currentMaximumSize); 196 try { 197 filterField.setText(pm.getProperty(filterFieldCheck, filterFieldCheck).toString()); //restore prev values 198 } catch (NullPointerException e1) { 199 // leave blank if previous value not retrieved 200 } 201 //automatically uppercase input in filterField, and only accept spaces and valid hex characters 202 ((AbstractDocument) filterField.getDocument()).setDocumentFilter(new DocumentFilter() { 203 final private static String PATTERN = "[0-9a-fA-F ]*+"; // typing inserts individual characters 204 205 @Override 206 public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, 207 AttributeSet attrs) throws BadLocationException { 208 if (text.matches(PATTERN)) { // NOI18N 209 fb.insertString(offset, text.toUpperCase(), attrs); 210 } else { 211 fb.insertString(offset, "", attrs); 212 } 213 } 214 215 @Override 216 public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, 217 AttributeSet attrs) throws BadLocationException { 218 if (text.matches(PATTERN)) { // NOI18N 219 fb.replace(offset, length, text.toUpperCase(), attrs); 220 } else { 221 fb.replace(offset, length, "", attrs); 222 } 223 } 224 }); 225 226 startLogButton.setText(Bundle.getMessage("ButtonStartLogging")); // NOI18N 227 startLogButton.setVisible(true); 228 startLogButton.setToolTipText(Bundle.getMessage("TooltipStartLogging")); // NOI18N 229 230 stopLogButton.setText(Bundle.getMessage("ButtonStopLogging")); // NOI18N 231 stopLogButton.setVisible(true); 232 stopLogButton.setToolTipText(Bundle.getMessage("TooltipStopLogging")); // NOI18N 233 234 rawCheckBox.setText(Bundle.getMessage("ButtonShowRaw")); // NOI18N 235 rawCheckBox.setVisible(true); 236 rawCheckBox.setToolTipText(Bundle.getMessage("TooltipShowRaw")); // NOI18N 237 rawCheckBox.setSelected(pm.getSimplePreferenceState(rawDataCheck)); 238 239 timeCheckBox.setText(Bundle.getMessage("ButtonShowTimestamps")); // NOI18N 240 timeCheckBox.setVisible(true); 241 timeCheckBox.setToolTipText(Bundle.getMessage("TooltipShowTimestamps")); // NOI18N 242 timeCheckBox.setSelected(pm.getSimplePreferenceState(timeStampCheck)); 243 244 alwaysOnTopCheckBox.setText(Bundle.getMessage("ButtonWindowOnTop")); // NOI18N 245 alwaysOnTopCheckBox.setVisible(true); 246 alwaysOnTopCheckBox.setToolTipText(Bundle.getMessage("TooltipWindowOnTop")); // NOI18N 247 alwaysOnTopCheckBox.setSelected(pm.getSimplePreferenceState(alwaysOnTopCheck)); 248 Component ancestor = getTopLevelAncestor(); 249 if (ancestor instanceof JmriJFrame) { 250 ((JmriJFrame) ancestor).setAlwaysOnTop(alwaysOnTopCheckBox.isSelected()); 251 } else { 252 // this pane isn't yet part of a frame, 253 // which can be normal, but 254 if (alwaysOnTopCheckBox.isSelected()) { 255 // in this case we want to access the enclosing frame to setAlwaysOnTop. So defer for a bit.... 256 log.debug("Cannot set Always On Top from preferences due to no Top Level Ancestor"); 257 timerCount = 0; 258 timer = new javax.swing.Timer(20, (java.awt.event.ActionEvent evt) -> { 259 if ((getTopLevelAncestor() != null) && (timerCount > 3) && (getTopLevelAncestor() instanceof JmriJFrame)) { 260 timer.stop(); 261 ((JmriJFrame) getTopLevelAncestor()).setAlwaysOnTop(alwaysOnTopCheckBox.isSelected()); 262 log.debug("set Always On Top"); 263 } else { 264 log.debug("Have to repeat attempt to set Always on Top"); 265 timerCount++; 266 if (timerCount > 50) { 267 log.warn("Took too long to \"Set Always on Top\", failed"); 268 timer.stop(); 269 } 270 } 271 }); 272 timer.start(); 273 } 274 } 275 276 autoScrollCheckBox.setText(Bundle.getMessage("ButtonAutoScroll")); // NOI18N 277 autoScrollCheckBox.setVisible(true); 278 autoScrollCheckBox.setToolTipText(Bundle.getMessage("TooltipAutoScroll")); // NOI18N 279 autoScrollCheckBox.setSelected(!pm.getSimplePreferenceState(autoScrollCheck)); 280 monTextPane.setAutoScroll(!pm.getSimplePreferenceState(autoScrollCheck)); 281 282 openFileChooserButton.setText(Bundle.getMessage("ButtonChooseLogFile")); // NOI18N 283 openFileChooserButton.setVisible(true); 284 openFileChooserButton.setToolTipText(Bundle.getMessage("TooltipChooseLogFile")); // NOI18N 285 286 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 287 288 // add items to GUI 289 addDataPanes(); 290 291 JPanel paneA = new JPanel(); 292 paneA.setLayout(new BoxLayout(paneA, BoxLayout.Y_AXIS)); 293 294 JPanel pane1 = new JPanel(){ 295 @Override 296 public Dimension getPreferredSize() { 297 Dimension min = super.getMinimumSize(); 298 Dimension max = super.getMaximumSize(); 299 return new Dimension(max.width, min.height); 300 } 301 @Override 302 public Dimension getMaximumSize() { 303 return getPreferredSize(); 304 } 305 }; 306 pane1.setLayout(new WrapLayout()); 307 pane1.add(clearButton); 308 pane1.add(freezeButton); 309 pane1.add(rawCheckBox); 310 pane1.add(timeCheckBox); 311 pane1.add(alwaysOnTopCheckBox); 312 pane1.add(autoScrollCheckBox); 313 paneA.add(pane1); 314 addCustomControlPanes(paneA); 315 316 JPanel pane2 = new JPanel(); 317 pane2.setLayout(new BoxLayout(pane2, BoxLayout.X_AXIS)); 318 pane2.add(filterLabel); 319 filterLabel.setLabelFor(filterField); 320 pane2.add(filterField); 321 pane2.add(openFileChooserButton); 322 pane2.add(startLogButton); 323 pane2.add(stopLogButton); 324 paneA.add(pane2); 325 326 JPanel pane3 = new JPanel(); 327 pane3.setLayout(new BoxLayout(pane3, BoxLayout.X_AXIS)); 328 pane3.add(enterButton); 329 pane3.add(entryField); 330 paneA.add(pane3); 331 332 add(paneA); 333 334 // connect actions to buttons 335 clearButton.addActionListener((java.awt.event.ActionEvent e) -> { 336 clearButtonActionPerformed(e); 337 }); 338 startLogButton.addActionListener((java.awt.event.ActionEvent e) -> { 339 startLogButtonActionPerformed(e); 340 }); 341 stopLogButton.addActionListener((java.awt.event.ActionEvent e) -> { 342 stopLogButtonActionPerformed(e); 343 }); 344 openFileChooserButton.addActionListener((java.awt.event.ActionEvent e) -> { 345 openFileChooserButtonActionPerformed(e); 346 }); 347 348 enterButton.addActionListener((java.awt.event.ActionEvent e) -> { 349 enterButtonActionPerformed(e); 350 }); 351 352 alwaysOnTopCheckBox.addActionListener((java.awt.event.ActionEvent e) -> { 353 if ((getTopLevelAncestor() != null) && (getTopLevelAncestor() instanceof JmriJFrame)) { 354 ((JmriJFrame) getTopLevelAncestor()).setAlwaysOnTop(alwaysOnTopCheckBox.isSelected()); 355 } 356 }); 357 358 autoScrollCheckBox.addActionListener((ActionEvent e) -> { 359 monTextPane.setAutoScroll(autoScrollCheckBox.isSelected()); 360 }); 361 362 // set file chooser to a default 363 logFileChooser.setSelectedFile(new File("monitorLog.txt")); 364 365 // connect to data source 366 init(); 367 368 } 369 370 /** 371 * Expand the display with additional options specific to the hardware. 372 * @param parent a Panel (with vertical BoxLayout); overrides should add a new Panel with horizontal BoxLayout to hold the additional options. 373 */ 374 protected void addCustomControlPanes(JPanel parent) { 375 } 376 377 private int timerCount = 0; 378 private javax.swing.Timer timer; 379 380 /** 381 * Set the display window to fixed width font, so that e.g. columns line up. 382 */ 383 public void setFixedWidthFont() { 384 monTextPane.setFont(new java.awt.Font("Monospaced", java.awt.Font.PLAIN, monTextPane.getFont().getSize())); 385 } 386 387 /** 388 * {@inheritDoc} 389 */ 390 @Override 391 public String getHelpTarget() { 392 return "package.jmri.jmrix.AbstractMonFrame"; // NOI18N 393 } 394 395 /** 396 * Log an Message derived message. 397 * 398 * @param message message object to log. 399 */ 400 public void logMessage(Message message) { 401 logMessage("", "", message); 402 } 403 404 /** 405 * Log an Message derived message. 406 * 407 * @param messagePrefix text to prefix the message with. 408 * @param message message object to log. 409 */ 410 public void logMessage(String messagePrefix, Message message) { 411 logMessage(messagePrefix, "", message); 412 } 413 414 /** 415 * Log an Message derived message with a prefixed label. 416 * 417 * @param messagePrefix text to prefix the message with. 418 * @param rawPrefix label to add to the start of the message. 419 * @param message message object to log. 420 */ 421 public void logMessage(String messagePrefix, String rawPrefix, Message message){ 422 // display the raw data if requested 423 StringBuilder raw = new StringBuilder(rawPrefix); 424 if (rawCheckBox.isSelected()) { 425 raw.append(message.toString()); 426 } 427 428 // display the decoded data 429 String text = message.toMonitorString(); 430 nextLine(messagePrefix + " " + text + "\n", raw.toString()); 431 } 432 433 434 public void nextLine(String line, String raw) { 435 nextLineWithTime(new Date(), line, raw); 436 } 437 438 /** 439 * Handle display of traffic. 440 * 441 * @param timestamp timestamp to be pre-pended to the output line (if 442 * timestamping is enabled) 443 * @param line The traffic in normal parsed form, ending with \n 444 * @param raw The traffic in raw form, ending with \n 445 */ 446 public void nextLineWithTime(Date timestamp, String line, String raw) { 447 448 StringBuilder sb = new StringBuilder(120); 449 450 // display the timestamp if requested 451 if (timeCheckBox.isSelected()) { 452 sb.append(df.format(timestamp)).append(": "); 453 } 454 455 // display the raw data if available and requested 456 if (raw != null && rawCheckBox.isSelected()) { 457 sb.append('[').append(raw).append("] "); // NOI18N 458 } 459 460 // display parsed data 461 sb.append(line); 462 synchronized (this) { 463 linesBuffer.append(sb.toString()); 464 } 465 466 // if requested, log to a file. 467 synchronized (this) { 468 if (logStream != null) { 469 String logLine = sb.toString(); 470 if (!newline.equals("\n")) { // NOI18N 471 // have to massage the line-ends 472 int lim = sb.length(); 473 StringBuilder out = new StringBuilder(sb.length() + 10); // arbitrary guess at space 474 for (int i = 0; i < lim; i++) { 475 if (sb.charAt(i) == '\n') { // NOI18N 476 out.append(newline); 477 } else { 478 out.append(sb.charAt(i)); 479 } 480 } 481 logLine = out.toString(); 482 } 483 logStream.print(logLine); 484 } 485 } 486 487 // if frozen, exit without adding to the Swing thread 488 if (freezeButton.isSelected()) { 489 return; 490 } 491 492 // if this message is filtered out, end 493 if (isFiltered(raw)) { 494 return; 495 } 496 497 SwingUtilities.invokeLater(() -> { 498 synchronized (AbstractMonPane.this) { 499 monTextPane.append(linesBuffer.toString()); 500 linesBuffer.setLength(0); 501 } 502 }); 503 } 504 505 /** 506 * Default filtering implementation, more of an example than anything else, 507 * not clear it really works for any system. Override this in 508 * system-specific subclasses to do something useful. 509 * 510 * @param raw A string containing the raw message hex information, in ASCII 511 * encoding, with some "header" information pre-pended. 512 * @return True if the opcode in the raw message matches one of the "filter" 513 * opcodes. False if the opcode does not match any of the "filter" 514 * opcodes. 515 */ 516 protected boolean isFiltered(String raw) { 517 String checkRaw = getOpCodeForFilter(raw); 518 //don't bother to check filter if no raw value passed 519 if (raw != null) { 520 // if first bytes are in the skip list, exit without adding to the Swing thread 521 String[] filters = filterField.getText().toUpperCase().split(" "); 522 523 for (String s : filters) { 524 if (s.equals(checkRaw)) { 525 synchronized (this) { 526 linesBuffer.setLength(0); 527 } 528 return true; 529 } 530 } 531 } 532 return false; 533 } 534 535 /** 536 * Get hex opcode for filtering. 537 * <p> 538 * Reports the "opcode" byte from the string containing the ASCII string 539 * representation of the message. Assumes that there is a generic header on 540 * string, like "Tx - ", and ignores it. 541 * 542 * @param raw a String containing the generic raw hex information, with 543 * pre-pended header. 544 * 545 * @return a two character String containing only the hex representation of 546 * the opcode from the raw message. 547 */ 548 protected String getOpCodeForFilter(String raw) { 549 // note: Generic raw is formatted like "Tx - BB 01 00 45", so extract the correct bytes from it (BB) for comparison 550 if (raw != null && raw.length() >= 7) { 551 return raw.substring(5, 7); 552 } else { 553 return null; 554 } 555 } 556 557 private static final String newline = System.getProperty("line.separator"); // NOI18N 558 559 public synchronized void clearButtonActionPerformed(java.awt.event.ActionEvent e) { 560 // clear the monitoring history 561 linesBuffer.setLength(0); 562 monTextPane.setText(""); 563 } 564 565 public String getFilePathAndName() { 566 String returnString = ""; 567 java.nio.file.Path p = logFileChooser.getSelectedFile().toPath(); 568 if (p.getParent() == null) { 569 // This case is a file path with a "parent" of "null" 570 // 571 // Should instead use the profile directory, as "null" can default to 572 // the JMRI program directory, which might not be user-writable. 573 java.nio.file.Path fileName = p.getFileName(); 574 if (fileName != null) { // getUserFilesPath() never null 575 returnString = FileUtil.getUserFilesPath() + fileName.toString(); 576 } else { 577 log.error("User Files File Path not valid"); 578 } 579 log.warn("File selection dialog box did not provide a path to the specified file. Log will be saved to {}", 580 returnString); 581 } else { 582 returnString = p.toString(); 583 } 584 return returnString; 585 } 586 587 public synchronized void startLogButtonActionPerformed(java.awt.event.ActionEvent e) { 588 // start logging by creating the stream 589 if (logStream == null) { // successive clicks won't restart the file once running 590 // start logging 591 String filePathAndName = getFilePathAndName(); 592 log.warn("startLogButtonActionPerformed: getSelectedFile() returns {} {}", 593 logFileChooser.getSelectedFile().getPath(), logFileChooser.getSelectedFile().getName()); 594 log.warn("startLogButtonActionPerformed: is attempting to use returned file path and file name {}", 595 filePathAndName); 596 File logFile = new File(filePathAndName); 597 try { 598 logStream = new PrintStream(new FileOutputStream(logFile)); 599 } catch (java.io.FileNotFoundException ex) { 600 stopLogButtonActionPerformed(null); 601 log.error("startLogButtonActionPerformed: FileOutputStream cannot open the file '{}'. Exception: {}", logFileChooser.getSelectedFile().getName(), ex.getMessage()); 602 JmriJOptionPane.showMessageDialog(this, 603 (Bundle.getMessage("ErrorCannotOpenFileForWriting", 604 logFileChooser.getSelectedFile().getName(), 605 Bundle.getMessage("ErrorPossibleCauseCannotOpenForWrite"))), 606 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 607 } 608 } else { 609 startLogButton.setSelected(true); // keep toggle on 610 } 611 } 612 613 public synchronized void stopLogButtonActionPerformed(java.awt.event.ActionEvent e) { 614 // stop logging by removing the stream 615 if (logStream != null) { 616 logStream.flush(); 617 logStream.close(); 618 logStream = null; 619 } 620 startLogButton.setSelected(false); 621 } 622 623 public void openFileChooserButtonActionPerformed(java.awt.event.ActionEvent e) { 624 // start at current file, show dialog 625 int retVal = logFileChooser.showSaveDialog(this); 626 627 // handle selection or cancel 628 if (retVal == JFileChooser.APPROVE_OPTION) { 629 synchronized (this) { 630 boolean loggingNow = (logStream != null); 631 stopLogButtonActionPerformed(e); // stop before changing file 632 //File file = logFileChooser.getSelectedFile(); 633 // if we were currently logging, start the new file 634 if (loggingNow) { 635 startLogButtonActionPerformed(e); 636 } 637 } 638 } 639 } 640 641 public void enterButtonActionPerformed(java.awt.event.ActionEvent e) { 642 nextLine(entryField.getText() + "\n", null); // NOI18N 643 } 644 645 public synchronized String getFrameText() { 646 return monTextPane.getText(); 647 } 648 649 /** 650 * Get access to the main text area. This is intended for use in e.g. 651 * scripting to extend the behavior of the window. 652 * 653 * @return the main text area 654 */ 655 public final synchronized JTextArea getTextArea() { 656 return monTextPane; 657 } 658 659 public synchronized String getFilterText() { 660 return filterField.getText(); 661 } 662 663 public synchronized void setFilterText(String text) { 664 filterField.setText(text); 665 } 666 667 @GuardedBy("this") 668 private volatile PrintStream logStream = null; 669 670 // to get a time string 671 private final DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS"); 672 673 @GuardedBy("this") 674 protected StringBuffer linesBuffer = new StringBuffer(); 675 private static final int MAX_LINES = 500; 676 677 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractMonPane.class); 678 679}