blob: 9faee2ecadcb03a7bf614a124715318c9a7bc893 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.gsf.testrunner.ui;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.accessibility.AccessibleContext;
import javax.swing.JEditorPane;
import javax.swing.JScrollPane;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.netbeans.modules.gsf.testrunner.ui.output.OutputDocument;
import org.netbeans.modules.gsf.testrunner.ui.output.OutputEditorKit;
import org.openide.ErrorManager;
import org.openide.util.NbBundle;
/**
*
* @author Marian Petras
*/
final class ResultPanelOutput extends JScrollPane
implements ActionListener {
private static final boolean LOG = false;
static final Color selectedFg;
static final Color unselectedFg;
static final Color selectedErr;
static final Color unselectedErr;
private static final int UPDATE_DELAY = 300; //milliseconds
static {
/*
* The property names and colour value constants were copied from class
* org.netbeans.core.output2.WrappedTextView.
*/
Color color;
color = UIManager.getColor("nb.output.foreground.selected"); //NOI18N
if (color == null) {
color = UIManager.getColor("textText"); //NOI18N
if (color == null) {
color = Color.BLACK;
}
}
selectedFg = color;
color = UIManager.getColor("nb.output.foreground"); //NOI18N
if (color == null) {
color = selectedFg;
}
unselectedFg = color;
color = UIManager.getColor("nb.output.err.foreground.selected");//NOI18N
if (color == null) {
color = new Color(164, 0, 0);
}
selectedErr = color;
color = UIManager.getColor("nb.output.err.foreground"); //NOI18N
if (color == null) {
color = selectedErr;
}
unselectedErr = color;
}
/** */
private final JEditorPane textPane;
/** */
private final Document doc;
/** */
private final ResultDisplayHandler displayHandler;
private Timer timer = null;
/*
* accessed from multiple threads but accessed only from blocks
* synchronized with the ResultDisplayHandler's output queue lock
*/
private volatile boolean timerRunning = false;
/**
* Creates a new instance of ResultPanelOutput
*/
ResultPanelOutput(ResultDisplayHandler displayHandler) {
super();
if (LOG) {
System.out.println("ResultPanelOutput.<init>");
}
textPane = new JEditorPane();
textPane.setFont(new Font("monospaced", Font.PLAIN, getFont().getSize()));
textPane.setEditorKit(new OutputEditorKit());
textPane.setEditable(false);
textPane.getCaret().setVisible(true);
textPane.getCaret().setBlinkRate(0);
textPane.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
setViewportView(textPane);
/*
* On GTK L&F, background of the text pane is gray, even though it is
* white on a JTextArea. The following is a hack to fix it:
*/
Color background = UIManager.getColor("TextPane.background"); //NOI18N
if (background != null) {
textPane.setBackground(background);
}
doc = textPane.getDocument();
AccessibleContext ac = textPane.getAccessibleContext();
ac.setAccessibleName(NbBundle.getMessage(getClass(),
"ACSN_OutputTextPane"));//NOI18N
ac.setAccessibleDescription(NbBundle.getMessage(getClass(),
"ACSD_OutputTextPane"));//NOI18N
this.displayHandler = displayHandler;
}
/**
*/
@Override
public void addNotify() {
super.addNotify();
final Object[] pendingOutput;
if (LOG) {
System.out.println("ResultPanelOutput.addNotify()");
}
/*
* We must make the following block synchronized using the output queue
* lock to prevent a scenario that some new output would be delivered to
* the display handler and the output listener would not be set yet.
*/
synchronized (displayHandler.getOutputQueueLock()) {
pendingOutput = displayHandler.consumeOutput();
if (pendingOutput.length == 0) {
// displayHandler.setOutputListener(this);
}
}
if (pendingOutput.length != 0) {
displayOutput(pendingOutput);
startTimer();
}
}
/**
*/
void outputAvailable() {
/* Called from the AntLogger's thread */
if (LOG) {
System.out.println("ResultOutputPanel.outputAvailable() - called by the AntLogger");
}
//synchronized (displayHandler.getOutputQueueLock()):
final Object[] pendingOutput = displayHandler.consumeOutput();
assert pendingOutput.length != 0;
new OutputDisplayer(pendingOutput).run();
if (!timerRunning) {
startTimer();
}
}
final class OutputDisplayer implements Runnable {
private final Object[] output;
OutputDisplayer(Object[] output) {
this.output = output;
}
@Override
public void run() {
if (!EventQueue.isDispatchThread()) {
EventQueue.invokeLater(this);
return;
}
displayOutput(output);
}
}
/**
* This method is called by a Swing timer (in the dispatch thread).
*/
@Override
public void actionPerformed(ActionEvent e) {
/* Called by the Swing timer (in the EventDispatch thread) */
assert EventQueue.isDispatchThread();
if (LOG) {
System.out.println("ResultOutputPanel.actionPerformed(...) - called by the timer");
}
final Object[] pendingOutput = displayHandler.consumeOutput();
if (pendingOutput.length != 0) {
displayOutput(pendingOutput);
} else {
synchronized (displayHandler.getOutputQueueLock()) {
stopTimer();
}
}
}
/**
*/
private void startTimer() {
if (LOG) {
System.out.println("ResultPanelOutput.startTimer()");
}
if (timer == null) {
timer = new Timer(UPDATE_DELAY, this);
}
timerRunning = true;
timer.start();
}
/**
*/
private void stopTimer() {
if (LOG) {
System.out.println("ResultPanelOutput.stopTimer()");
}
if (timer != null) {
timer.stop();
timerRunning = false;
}
}
/**
*/
void displayOutput(final Object[] output) {
assert EventQueue.isDispatchThread();
if (LOG) {
System.out.println("ResultPanelOutput.displayOutput(...):");
for (int i = 0; output[i] != null; i++) {
System.out.println(" " + output[i]);
}
}
Object o;
int index = 0;
while ((o = output[index++]) != null) {
boolean errOutput = false;
if (o == Boolean.TRUE) {
o = output[index++];
errOutput = true;
}
displayOutputLine(o.toString(), errOutput);
}
}
/**
*/
private void displayOutputLine(final String text, final boolean error) {
// split to lines, otherwise everything will
// be printed on one line even if the text
// contains line breaks
String[] lines = text.split("\n"); //NO18N
for (int i = 0; i < lines.length; i++) {
if (i == lines.length - 1) {
// add a trailing new line to the last line
lines[i] = lines[i] + "\n"; //NOI18N
}
try {
doc.insertString(doc.getLength(),
lines[i],
error ? OutputDocument.attrs : null);
} catch (BadLocationException ex) {
ErrorManager.getDefault().notify(ErrorManager.ERROR, ex);
}
}
}
//<editor-fold defaultstate="collapsed" desc="displayReport(Report)">
/* *
* /
private void displayReport(final Report report) {
if (report == null) {
clear();
return;
}
try {
doc.insertString(
0,
NbBundle.getMessage(getClass(), "MSG_StdOutput"), //NOI18N
headingStyle);
doc.insertString(
doc.getLength(),
"\n", //NOI18N
headingStyle);
if ((report.outputStd != null) && (report.outputStd.length != 0)) {
displayText(report.outputStd);
}
doc.insertString(
doc.getLength(),
"\n\n", //NOI18N
outputStyle);
doc.insertString(
doc.getLength(),
NbBundle.getMessage(getClass(), "MSG_ErrOutput"), //NOI18N
headingStyle);
if ((report.outputErr != null) && (report.outputErr.length != 0)) {
doc.insertString(
doc.getLength(),
"\n", //NOI18N
headingStyle);
displayText(report.outputErr);
}
} catch (BadLocationException ex) {
ErrorManager.getDefault().notify(ErrorManager.ERROR, ex);
}
}
*/
//</editor-fold>
/**
*/
private void clear() {
assert EventQueue.isDispatchThread();
try {
doc.remove(0, doc.getLength());
} catch (BadLocationException ex) {
ErrorManager.getDefault().notify(ErrorManager.ERROR, ex);
}
}
//<editor-fold defaultstate="collapsed" desc="displayText(String[])">
/* *
* /
private void displayText(final String[] lines) throws BadLocationException {
final int limit = lines.length - 1;
for (int i = 0; i < limit; i++) {
doc.insertString(doc.getLength(),
lines[i] + '\n',
outputStyle);
}
doc.insertString(doc.getLength(),
lines[limit],
outputStyle);
}
*/
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="display(DisplayContents)">
/* *
* /
private void display(ResultDisplayHandler.DisplayContents display) {
assert EventQueue.isDispatchThread();
Report report = display.getReport();
String msg = display.getMessage();
if (report != null) {
displayReport(report);
} else {
clear();
}
}
//</editor-fold>
/* *
* /
//<editor-fold defaultstate="collapsed" desc="updateDisplay()">
private void updateDisplay() {
ResultDisplayHandler.DisplayContents display
= displayHandler.getDisplay();
if (display != null) {
display(display);
}
}
*/
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="stateChanged(ChangeEvent)">
/* *
* /
public void stateChanged(ChangeEvent e) {
updateDisplay();
}
*/
//</editor-fold>
/**
*/
@Override
public boolean requestFocusInWindow() {
return textPane.requestFocusInWindow();
}
}