Index: core/output2/src/org/netbeans/core/output2/Controller.java =================================================================== RCS file: /cvs/core/output2/src/org/netbeans/core/output2/Controller.java,v retrieving revision 1.47 diff -u -r1.47 Controller.java --- core/output2/src/org/netbeans/core/output2/Controller.java 1 Jul 2006 08:51:57 -0000 1.47 +++ core/output2/src/org/netbeans/core/output2/Controller.java 14 Aug 2006 06:55:12 -0000 @@ -82,6 +82,16 @@ }); } + public static void ensureViewIn (final OutputWindow window, final NbIO io, + final boolean reuse) { + Mutex.EVENT.readAccess(new Runnable() { + public void run() { + IOEvent evt = new IOEvent (io, window, IOEvent.CMD_CREATE, reuse); + NbIO.post(evt); + } + }); + } + private static final int ACTION_COPY = 0; private static final int ACTION_WRAP = 1; private static final int ACTION_SAVEAS = 2; Index: core/output2/src/org/netbeans/core/output2/IOComponentFactory.java =================================================================== RCS file: core/output2/src/org/netbeans/core/output2/IOComponentFactory.java diff -N core/output2/src/org/netbeans/core/output2/IOComponentFactory.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ core/output2/src/org/netbeans/core/output2/IOComponentFactory.java 14 Aug 2006 06:55:12 -0000 @@ -0,0 +1,60 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ +package org.netbeans.core.output2; + +import org.openide.util.Lookup; + +/** + * Factory for output window components with associated instances of + * IOProvider, for clients which want to embed the output window component + * inside their own component. + * + * @author Tim Boudreau + */ +public abstract class IOComponentFactory { + + //XXX this should be in an API package, but to avoid creating that package + //in CVS just to generate a diff, it's here for now. Just move it to + //e.g. org.netbeans.api.output or similar when ready. + + protected IOComponentFactory() { + } + + /** + * Get an object which can provide its own output component + * and InputOutput instances that talk to it. + */ + public static IOComponentFactory getDefault() { + IOComponentFactory result = (IOComponentFactory) Lookup.getDefault().lookup ( + IOComponentFactory .class); + if (result == null) { + result = new DefaultImpl(); + } + return result; + } + + /** + * Create a new IOComponentManager - call this method when a new output + * component is needed with its associated IOProvider for getting input + * and output streams that read from/write to the output component. + */ + public abstract IOComponentManager newComponentManager(); + + private static final class DefaultImpl extends IOComponentFactory { + public IOComponentManager newComponentManager() { + return new NbIOProvider (new OutputWindow()).getController(); + } + } +} Index: core/output2/src/org/netbeans/core/output2/IOComponentManager.java =================================================================== RCS file: core/output2/src/org/netbeans/core/output2/IOComponentManager.java diff -N core/output2/src/org/netbeans/core/output2/IOComponentManager.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ core/output2/src/org/netbeans/core/output2/IOComponentManager.java 14 Aug 2006 06:55:12 -0000 @@ -0,0 +1,100 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ +package org.netbeans.core.output2; + +import java.awt.Component; +import javax.swing.Action; +import org.openide.windows.IOProvider; +import org.openide.windows.InputOutput; + +/** + * Object which is associates a single output component (which can contain + * multiple tabs with different output - really it just provides another + * instance of the component in the output window) with an IOProvider + * instance - which is a factory for InputOutput objects whose standard out + * and standard error streams will write to text components inside the + * output component. Typical usage is for cases such as the JUnit results + * window, where it is desirable to have output viewable alongside test results, + * and so an output component is needed which does not live in the regular + * output window. + *
+ * Typical usage: + *
+ * class SomeContainer extends javax.swing.JPanel; //or whatever + * IOComponentManager mgr; + * public SomeContainer() { + * mgr = IOComponentFactory.getDefault().newComponentManager(); + * Component outputComp = mgr.getOutputComponent(); + * add (outputComp, <some layout constraint> ); + * } + * + * public void writeSomeOutput (String tabName, String s) { + * mgr.getInputOutput (tabName, true).getOut().println (s); + * } + *+ * There are two major reasons to use this interface as opposed to simply + * using a JEditorPane or similar: + *
getOutputComponent()
.
+ */
+ public abstract IOProvider getIOProvider();
+
+ /**
+ * Convenience method which delegates to getIOProvider().getIO()
.
+ */
+ public final InputOutput getInputOutput (String name, boolean reuse) {
+ return getIOProvider().getIO (name, reuse);
+ }
+
+ /**
+ * Convenience method which delegates to getIOProvider().getIO()
.
+ */
+ public final InputOutput getIO (String name, Action[] toolbarActions) {
+ return getIOProvider().getIO (name, toolbarActions);
+ }
+}
Index: core/output2/src/org/netbeans/core/output2/IOEvent.java
===================================================================
RCS file: /cvs/core/output2/src/org/netbeans/core/output2/IOEvent.java,v
retrieving revision 1.6
diff -u -r1.6 IOEvent.java
--- core/output2/src/org/netbeans/core/output2/IOEvent.java 1 Jul 2006 08:51:58 -0000 1.6
+++ core/output2/src/org/netbeans/core/output2/IOEvent.java 14 Aug 2006 06:55:13 -0000
@@ -151,12 +151,25 @@
* Data associated with this event (used by set toolbar actions)
*/
private Object data = null;
+
+ private OutputWindow dest;
/**
* Used by unit tests to ensure all pending events have been processed before
* continuing.
*/
static int pendingCount = 0;
+ IOEvent(NbIO source, OutputWindow dest, int command, boolean value) {
+ this (source, command, value);
+ this.dest = dest;
+ }
+
+ IOEvent(NbIO source, OutputWindow dest, int command, Object data) {
+ this (source, command, data);
+ this.dest = dest;
+ }
+
+
/**
* Create an IOEvent with the specified source, command and boolean state for the command.
*
@@ -250,15 +263,12 @@
}
public void dispatch() {
- //The only thing needed to make this package fully reentrant (capable of
- //supporting multiple output windows, FWIW) is to replace this line with
- //iterating a registry of OutputContainers, and calling eventDispatched on each.
-
+ OutputWindow win = dest == null ? OutputWindow.DEFAULT : dest;
//Null check below so that if the module has been disabled (the only way
//this can be null), the dead module doesn't reopen its output window
- if (OutputWindow.DEFAULT != null) {
+ if (win != null) {
//Can be null after CMD_DETACH
- OutputWindow.DEFAULT.eventDispatched(this);
+ win.eventDispatched(this);
}
pendingCount--;
}
Index: core/output2/src/org/netbeans/core/output2/NbIO.java
===================================================================
RCS file: /cvs/core/output2/src/org/netbeans/core/output2/NbIO.java,v
retrieving revision 1.13
diff -u -r1.13 NbIO.java
--- core/output2/src/org/netbeans/core/output2/NbIO.java 1 Jul 2006 08:51:58 -0000 1.13
+++ core/output2/src/org/netbeans/core/output2/NbIO.java 14 Aug 2006 06:55:13 -0000
@@ -31,6 +31,8 @@
import java.awt.*;
import java.io.IOException;
import java.io.Reader;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
import org.openide.util.Exceptions;
/** Implementation of InputOutput. Implements calls as a set of
@@ -205,13 +207,27 @@
post (this, IOEvent.CMD_RESET, true);
}
+ private Reference outputWindowRef;
+ void setOutputWindow (OutputWindow window) {
+ OutputWindow win = getOutputWindow();
+ if (win != null && win != window) {
+ throw new IllegalStateException ("Output window already assigned");
+ }
+ this.outputWindowRef = new WeakReference (window);
+ }
+
+ OutputWindow getOutputWindow() {
+ return outputWindowRef != null ? (OutputWindow)
+ outputWindowRef.get() : null;
+ }
+
private static void post (NbIO io, int command, boolean val) {
- IOEvent evt = new IOEvent (io, command, val);
+ IOEvent evt = new IOEvent (io, io.getOutputWindow(), command, val);
post (evt);
}
private static void post (NbIO io, int command, Object data) {
- IOEvent evt = new IOEvent (io, command, data);
+ IOEvent evt = new IOEvent (io, io.getOutputWindow(), command, data);
post (evt);
}
Index: core/output2/src/org/netbeans/core/output2/NbIOProvider.java
===================================================================
RCS file: /cvs/core/output2/src/org/netbeans/core/output2/NbIOProvider.java,v
retrieving revision 1.9
diff -u -r1.9 NbIOProvider.java
--- core/output2/src/org/netbeans/core/output2/NbIOProvider.java 1 Jul 2006 08:51:58 -0000 1.9
+++ core/output2/src/org/netbeans/core/output2/NbIOProvider.java 14 Aug 2006 06:55:13 -0000
@@ -19,6 +19,7 @@
package org.netbeans.core.output2;
+import java.awt.Component;
import java.io.IOException;
import javax.swing.Action;
import org.openide.util.Exceptions;
@@ -37,6 +38,14 @@
private static final String STDOUT = NbBundle.getMessage(NbIOProvider.class,
"LBL_STDOUT"); //NOI18N
+ private OutputWindow outputWindow;
+
+ public NbIOProvider() {}
+
+ public NbIOProvider (OutputWindow window) {
+ this.outputWindow = window;
+ }
+
public OutputWriter getStdOut() {
if (Controller.log) {
Controller.log("NbIOProvider.getStdOut");
@@ -44,7 +53,11 @@
InputOutput stdout = getIO (STDOUT, false, null);
NbWriter out = ((NbIO)stdout).writer();
- Controller.ensureViewInDefault ((NbIO) stdout, true);
+ if (outputWindow == null) {
+ Controller.ensureViewInDefault ((NbIO) stdout, true);
+ } else {
+ Controller.ensureViewIn (outputWindow, (NbIO) stdout, true);
+ }
//ensure it is not closed
if (out != null && out.isClosed()) {
try {
@@ -78,8 +91,13 @@
if (result == null || newIO) {
result = new NbIO(name, toolbarActions);
+ result.setOutputWindow (outputWindow);
namesToIos.add (name, result);
- Controller.ensureViewInDefault (result, newIO);
+ if (outputWindow != null) {
+ Controller.ensureViewIn(outputWindow, result, newIO);
+ } else {
+ Controller.ensureViewInDefault (result, newIO);
+ }
} else {
// mkleint ignore actions if reuse of tabs.
// result.setToolbarActions(toolbarActions);
@@ -99,6 +117,22 @@
*/
static void setWeak(boolean value) {
namesToIos.setWeak(value);
+ }
+
+ IOComponentManager getController() {
+ return new IOComponentControllerImpl();
+ }
+
+ private final class IOComponentControllerImpl extends IOComponentManager {
+ public Component getOutputComponent() {
+ assert outputWindow != null : "Cannot create component controller " + //NOI18N
+ "over default provider"; //NOI18N
+ return outputWindow;
+ }
+
+ public IOProvider getIOProvider() {
+ return NbIOProvider.this;
+ }
}
}
Index: core/output2/test/unit/src/org/netbeans/core/output2/IOComponentFactoryTest.java
===================================================================
RCS file: core/output2/test/unit/src/org/netbeans/core/output2/IOComponentFactoryTest.java
diff -N core/output2/test/unit/src/org/netbeans/core/output2/IOComponentFactoryTest.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/output2/test/unit/src/org/netbeans/core/output2/IOComponentFactoryTest.java 14 Aug 2006 06:55:14 -0000
@@ -0,0 +1,125 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License (the License). You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
+ * or http://www.netbeans.org/cddl.txt.
+ *
+ * When distributing Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://www.netbeans.org/cddl.txt.
+ * If applicable, add the following below the CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ */
+package org.netbeans.core.output2;
+
+import java.awt.BorderLayout;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import javax.swing.JFrame;
+import javax.swing.JSplitPane;
+import javax.swing.SwingUtilities;
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author Tim Boudreau
+ */
+public class IOComponentFactoryTest extends TestCase {
+
+ public IOComponentFactoryTest(String testName) {
+ super(testName);
+ }
+
+ protected void setUp() throws Exception {
+ Controller.log = true;
+ }
+
+ protected void tearDown() throws Exception {
+ }
+
+ public void testGetDefault() {
+ System.out.println("testGetDefault");
+ IOComponentFactory fac = IOComponentFactory.getDefault();
+ assertNotNull (fac);
+ }
+
+ public void testNewComponentManager() throws Exception {
+ System.out.println("testNewComponentManager");
+ IOComponentFactory fac = IOComponentFactory.getDefault();
+ IOComponentManager one = fac.newComponentManager();
+ assertNotNull (one);
+ IOComponentManager two = fac.newComponentManager();
+ assertNotNull (two);
+
+ OutputWindow winA = (OutputWindow) one.getOutputComponent();
+ OutputWindow winB = (OutputWindow) two.getOutputComponent();
+
+ assertNotSame(winA, winB);
+
+ NbIO ioA = (NbIO) one.getInputOutput("one", true);
+ NbIO ioB = (NbIO) two.getInputOutput("two", true);
+ assertNotSame (ioA, ioB);
+
+ JFrame jf = new JFrame();
+ jf.getContentPane().setLayout (new BorderLayout());
+ JSplitPane jsp = new JSplitPane();
+ WaitWindow ww = new WaitWindow (jf);
+ jf.getContentPane().add (jsp, BorderLayout.CENTER);
+ jf.setBounds (20, 20, 500, 500);
+ jf.setVisible(true);
+ ww.waitWindow();
+
+ jsp.setLeftComponent(winA);
+ jsp.setRightComponent(winB);
+
+ NbWriter owA = (NbWriter) ioA.getOut();
+ OutWriter realA = owA.out();
+ for (int i=0; i < 5; i++) {
+ owA.println("Hello from A");
+ }
+ owA.flush();
+ sleep();
+ assertEquals (5, realA.getLines().getLineCount());
+
+ NbWriter owB = (NbWriter) ioB.getOut();
+ OutWriter realB = owB.out();
+ assertEquals (0, realB.getLines().getLineCount());
+ for (int i=0; i < 15; i++) {
+ owB.println("Hello from B");
+ }
+ assertEquals (15, realB.getLines().getLineCount());
+
+ assertSame (jsp, SwingUtilities.getAncestorOfClass(JSplitPane.class, winA));
+ assertSame (jsp, SwingUtilities.getAncestorOfClass(JSplitPane.class, winB));
+
+ Thread.currentThread().sleep (9000);
+
+ }
+
+ private void sleep() throws Exception {
+ Thread.currentThread().sleep(300);
+ }
+
+ private static class WaitWindow extends WindowAdapter {
+ private JFrame frm;
+ WaitWindow (JFrame frm) {
+ this.frm = frm;
+ frm.addWindowListener (this);
+ }
+ public void windowOpened(WindowEvent e) {
+ synchronized (frm) {
+ frm.notifyAll();
+ }
+ }
+
+ public void waitWindow() throws Exception {
+ synchronized (frm) {
+ frm.wait (20000);
+ }
+ }
+
+ }
+
+}