diff --git a/core.io.ui/arch.xml b/core.io.ui/arch.xml new file mode 100644 --- /dev/null +++ b/core.io.ui/arch.xml @@ -0,0 +1,1121 @@ + + + +]> + + + + &api-questions; + + + + +

+ Small module containing "Output Window" (main IO embedder). It provides + IOEmbedderProvider via service registration. This provider is used + by I/O APIs module to create default instance of IOEmbedder. This default + IOEmbedder is used by IOProvider implementators to access "Output Window" + for operation like adding/removing tabs etc. +

+
+ + + + + +

+ OW output is tested by functional tests. +

+
+ + + + + +

+ XXX no answer for arch-time +

+
+ + + + + +

+ The API is represented by service registration of IOEmbedderProvider. +

+
+ + + + + +

+ Provide "Output Window" for IOProvider implementators. +

+
+ + + + + + + + + + + + +

+ In no manner. +

+
+ + + + + +

+ Yes. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ Yes. +

+
+ + + + + +

+ 1.4 +

+
+ + + + + +

+ JRE is enough. +

+
+ + + + + + + + + + + + +

+ None. +

+
+ + + + + +

+ Any. +

+
+ + + + + +

+ I/O APIs. OpenIDE-Module-Needs: org.openide.windows.IOEmbedder.IOEmbedderProvider +

+
+ + + + + +

+ JAR. +

+
+ + + + + +

+ Yes. +

+
+ + + + + +

+ No package is public. +

+
+ + + + + +

+ Anywhere +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No public API. +

+
+ + + + + +

+ Nothing. +

+
+ + + + + +

+ Nothing. +

+
+ + + + + +

+ Nothing. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ Yes, IOEmbedderProvider via @ServiceProvider +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ XXX no answer for perf-mem +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ Nothing. +

+
+ + + + + +

+ No enforcement. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ Yes. Action/Menu/Shortcut to open "Output Window". +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ +
diff --git a/core.io.ui/build.xml b/core.io.ui/build.xml new file mode 100644 --- /dev/null +++ b/core.io.ui/build.xml @@ -0,0 +1,46 @@ + + + + + + diff --git a/core.io.ui/manifest.mf b/core.io.ui/manifest.mf new file mode 100644 --- /dev/null +++ b/core.io.ui/manifest.mf @@ -0,0 +1,7 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.netbeans.core.io.ui/1 +OpenIDE-Module-Implementation-Version: 1 +OpenIDE-Module-Provides: org.openide.windows.IOEmbedder.IOEmbedderProvider +OpenIDE-Module-Layer: org/netbeans/core/io/ui/layer.xml +OpenIDE-Module-Localizing-Bundle: org/netbeans/core/io/ui/Bundle.properties + diff --git a/core.io.ui/nbproject/project.properties b/core.io.ui/nbproject/project.properties new file mode 100644 --- /dev/null +++ b/core.io.ui/nbproject/project.properties @@ -0,0 +1,44 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, 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-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# Contributor(s): +# +# The Original Software is NetBeans. The Initial Developer of the Original +# Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun +# Microsystems, Inc. All Rights Reserved. +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. + +is.autoload=true +javac.source=1.5 +javac.compilerargs=-Xlint -Xlint:-serial +javadoc.arch=${basedir}/arch.xml +spec.version.base=1.0 diff --git a/core.io.ui/nbproject/project.xml b/core.io.ui/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/core.io.ui/nbproject/project.xml @@ -0,0 +1,46 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.core.io.ui + + + org.openide.awt + + + + 7.4 + + + + org.openide.io + + + + 1.14 + + + + org.openide.util + + + + 7.20 + + + + org.openide.windows + + + + 6.25 + + + + + org.netbeans.core.io.ui.api + + + + diff --git a/core.io.ui/src/org/netbeans/core/io/ui/Bundle.properties b/core.io.ui/src/org/netbeans/core/io/ui/Bundle.properties new file mode 100644 --- /dev/null +++ b/core.io.ui/src/org/netbeans/core/io/ui/Bundle.properties @@ -0,0 +1,15 @@ +OpenIDE-Module-Name=I/O UI + +#IOWindowAction +IOWindow=&Output + +#Title for I/O window +LBL_IO_WINDOW=Output + +#Format for the I/O window title if it includes the name of the current tab +FMT_IO_WINDOW={0} - {1} + +#IO window tab popup +LBL_Close=Close Tab +LBL_CloseAll=Close All Tabs +LBL_CloseOthers=Close Other Tabs diff --git a/core.io.ui/src/org/netbeans/core/io/ui/IOWindow.java b/core.io.ui/src/org/netbeans/core/io/ui/IOWindow.java new file mode 100644 --- /dev/null +++ b/core.io.ui/src/org/netbeans/core/io/ui/IOWindow.java @@ -0,0 +1,636 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.core.io.ui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.FocusEvent; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPopupMenu; +import javax.swing.JTabbedPane; +import javax.swing.JToolBar; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.openide.util.Utilities; +import org.openide.awt.MouseUtils; +import org.openide.awt.TabbedPaneFactory; +import org.openide.util.ImageUtilities; +import org.openide.util.NbBundle; +import org.openide.windows.IOEmbedder; +import org.openide.windows.IOEmbedder.CallBacks; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; + + +/** + * + * @author Tomas Holy + */ +@org.openide.util.lookup.ServiceProvider(service=org.openide.windows.IOEmbedder.IOEmbedderProvider.class, position=100) +public final class IOWindow implements IOEmbedder.IOEmbedderProvider { + private static IOWindowImpl impl; + + IOWindowImpl impl() { + if (impl == null) { + impl = IOWindowImpl.findDefault(); + } + return impl; + } + + public void add(JComponent comp, CallBacks cb, Action[] toolbarActions) { + impl().addTab(comp, cb, toolbarActions); + } + + public JComponent getSelected() { + return impl().getSelectedTab(); + } + + public boolean isActivated() { + return impl().isActivated(); + } + + public void open() { + impl().open(); + } + + public void remove(JComponent comp) { + impl().removeTab(comp); + } + + public void requestActive() { + impl().requestActive(); + } + + public void requestVisible() { + impl().requestVisible(); + } + + public void select(JComponent comp) { + impl().selectTab(comp); + } + + public void setIcon(JComponent comp, Icon icon) { + impl().setIcon(comp, icon); + } + + public void setTitle(JComponent comp, String name) { + impl().setTitle(comp, name); + } + + public void setToolTipText(JComponent comp, String text) { + impl().setToolTipText(comp, text); + } + + static final class IOWindowImpl extends TopComponent implements ChangeListener, PropertyChangeListener { + + static IOWindowImpl DEFAULT; + + static synchronized IOWindowImpl findDefault() { + if (DEFAULT == null) { + TopComponent tc = WindowManager.getDefault().findTopComponent("output"); // NOI18N + if (tc != null) { + if (tc instanceof IOWindowImpl) { + DEFAULT = (IOWindowImpl) tc; + } else { + //This should not happen. Possible only if some other module + //defines different settings file with the same name but different class. + //Incorrect settings file? + IllegalStateException exc = new IllegalStateException("Incorrect settings file. Unexpected class returned." // NOI18N + + " Expected: " + IOWindowImpl.class.getName() // NOI18N + + " Returned: " + tc.getClass().getName()); // NOI18N + Logger.getLogger(IOWindowImpl.class.getName()).log(Level.WARNING, null, exc); + //Fallback to accessor reserved for window system. + IOWindowImpl.getDefault(); + } + } else { + IOWindowImpl.getDefault(); + } + } + return DEFAULT; + } + + /* Singleton accessor reserved for window system ONLY. Used by window system to create + * IOWindowImpl instance from settings file when method is given. Use findDefault + * to get correctly deserialized instance of IOWindowImpl. */ + public static synchronized IOWindowImpl getDefault() { + if (DEFAULT == null) { + DEFAULT = new IOWindowImpl(); + } + return DEFAULT; + } + + public Object readResolve() throws java.io.ObjectStreamException { + return getDefault(); + } + + private static final String ICON_PROP = "tabIcon"; //NOI18N + private static final String TOOLBAR_ACTIONS_PROP = "toolbarActions"; //NOI18N + private static final String TOOLBAR_BUTTONS_PROP = "toolbarButtons"; //NOI18N + private static final String ICON_RESOURCE = "org/netbeans/core/resources/frames/output.png"; // NOI18N + private JTabbedPane pane; + private JComponent singleTab; + private JToolBar toolbar; + private JPopupMenu popupMenu; + private Map tabToCb = new HashMap(); + + public IOWindowImpl() { + pane = TabbedPaneFactory.createCloseButtonTabbedPane(); + pane.addChangeListener(this); + pane.addPropertyChangeListener(TabbedPaneFactory.PROP_CLOSE, this); + setFocusable(true); + + toolbar = new JToolBar(); + toolbar.setOrientation(JToolBar.VERTICAL); + toolbar.setLayout(new BoxLayout(toolbar, BoxLayout.Y_AXIS)); + toolbar.setFloatable(false); + Insets ins = toolbar.getMargin(); + JButton sample = new JButton(); + sample.setBorderPainted(false); + sample.setOpaque(false); + sample.setText(null); + sample.setIcon(new Icon() { + + public int getIconHeight() { + return 16; + } + + public int getIconWidth() { + return 16; + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + } + }); + toolbar.add(sample); + Dimension buttonPref = sample.getPreferredSize(); + Dimension minDim = new Dimension(buttonPref.width + ins.left + ins.right, buttonPref.height + ins.top + ins.bottom); + toolbar.setMinimumSize(minDim); + toolbar.setPreferredSize(minDim); + toolbar.remove(sample); + setLayout(new BorderLayout()); + add(toolbar, BorderLayout.WEST); + toolbar.setBorder(new VariableRightBorder(pane)); + toolbar.setBorderPainted(true); + + popupMenu = new JPopupMenu(); + popupMenu.add(new Close()); + popupMenu.add(new CloseAll()); + popupMenu.add(new CloseOthers()); + pane.addMouseListener(new MouseUtils.PopupMouseAdapter() { + + @Override + protected void showPopup(MouseEvent evt) { + popupMenu.show(IOWindowImpl.this, evt.getX(), evt.getY()); + } + }); + + String name = NbBundle.getMessage(IOWindow.class, "LBL_IO_WINDOW"); + setDisplayName(name); //NOI18N + // setting name to satisfy the accesible name requirement for window. + setName(name); //NOI18N + + setIcon(ImageUtilities.loadImage(ICON_RESOURCE)); // NOI18N + // special title for sliding mode + // XXX - please rewrite to regular API when available - see issue #55955 + putClientProperty("SlidingName", getDisplayName()); //NOI18N + } + + @Override + public void open() { + super.open(); + } + + @Override + public void requestActive() { + super.requestActive(); + } + + @Override + public void requestVisible() { + super.requestVisible(); + } + + boolean activated; + public boolean isActivated() { + return activated; + } + + + public void addTab(JComponent comp, CallBacks cb, Action[] toolbarActions) { + if (toolbarActions != null && toolbarActions.length > 0) { + if (toolbarActions.length > 5) { + throw new IllegalArgumentException("No more than 5 actions allowed in the output window toolbar"); //NOI18N + } + comp.putClientProperty(TOOLBAR_ACTIONS_PROP, toolbarActions); + } + if (cb != null) { + tabToCb.put(comp, cb); + } + setFocusable(false); + if (singleTab != null) { + // only single tab, remove it from TopComp. and add it to tabbed pane + assert pane.getParent() == null; + assert pane.getTabCount() == 0; + remove(singleTab); + pane.add(singleTab); + singleTab = null; + pane.add(comp); + add(pane); + updateWindowName(null); + updateWindowToolTip(null); + } else if (pane.getTabCount() > 0) { + // already several tabs + assert pane.getParent() != null; + assert singleTab == null; + pane.add(comp); + } else { + // nothing yet + assert pane.getParent() == null; + assert singleTab == null; + singleTab = comp; + add(comp); + updateWindowName(singleTab.getName()); + updateWindowToolTip(singleTab.getToolTipText()); + checkTabSelChange(); + } + revalidate(); + } + + public void removeTab(JComponent comp) { + if (singleTab != null) { + assert singleTab == comp; + remove(singleTab); + singleTab = null; + updateWindowName(null); + updateWindowToolTip(null); + checkTabSelChange(); + setFocusable(true); + revalidate(); + } else if (pane.getParent() == this) { + assert pane.getTabCount() > 1; + pane.remove(comp); + if (pane.getTabCount() == 1) { + singleTab = (JComponent) pane.getComponentAt(0); + pane.remove(singleTab); + remove(pane); + add(singleTab); + updateWindowName(singleTab.getName()); + updateWindowToolTip(singleTab.getToolTipText()); + } + revalidate(); + } + CallBacks cb = tabToCb.get(comp); + if (cb != null) { + cb.closed(); + } + } + + public void selectTab(JComponent comp) { + if (!isOpened()) { + open(); + } + if (!isShowing()) { + requestVisible(); + } + if (singleTab == null) { + pane.setSelectedComponent(comp); + } + } + + public JComponent getSelectedTab() { + return singleTab != null ? singleTab : (JComponent) pane.getSelectedComponent(); + } + + public void setTitle(JComponent comp, String name) { + comp.setName(name); + if (singleTab != null) { + assert singleTab == comp; + updateWindowName(name); + } else { + assert pane.getParent() == this; + int idx = pane.indexOfComponent(comp); + assert idx >= 0; + pane.setTitleAt(idx, name); + } + } + + public void setToolTipText(JComponent comp, String text) { + comp.setToolTipText(text); + if (singleTab != null) { + assert singleTab == comp; + updateWindowToolTip(text); + } else { + assert pane.getParent() == this; + int idx = pane.indexOfComponent(comp); + assert idx >= 0; + pane.setToolTipTextAt(idx, text); + } + } + + public void setIcon(JComponent comp, Icon icon) { + if (comp == singleTab) { + comp.putClientProperty(ICON_PROP, icon); + return; + } + int idx = pane.indexOfComponent(comp); + if (idx < 0) { + return; + } + comp.putClientProperty(ICON_PROP, icon); + pane.setIconAt(idx, icon); + pane.setDisabledIconAt(idx, icon); + } + + @Override + public int getPersistenceType() { + return PERSISTENCE_ALWAYS; + } + + @Override + public String preferredID() { + return "output"; //NOI18N + } + + @Override + public String getToolTipText() { + return getDisplayName(); + } + + @Override + public void processFocusEvent(FocusEvent fe) { + super.processFocusEvent(fe); + if (Boolean.TRUE.equals(getClientProperty("isSliding"))) { //NOI18N + repaint(200); + } + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + if (hasFocus()) { + Insets ins = getInsets(); + Color col = UIManager.getColor("controlShadow"); //NOI18N + //Draw *some* focus indication + if (col == null) { + col = java.awt.Color.GRAY; + } + g.setColor(col); + g.drawRect( + ins.left + 2, + ins.top + 2, + getWidth() - (ins.left + ins.right + 4), + getHeight() - (ins.top + ins.bottom + 4)); + } + } + + void updateWindowName(String name) { + String winName = NbBundle.getMessage(IOWindowImpl.class, "LBL_IO_WINDOW"); //NOI18N + if (name != null) { + String newName = NbBundle.getMessage(IOWindowImpl.class, "FMT_IO_WINDOW", new Object[]{winName, name}); //NOI18N + if (newName.indexOf("") != -1) { + newName = Utilities.replaceString(newName, "", ""); //NOI18N + setHtmlDisplayName("" + newName); //NOI18N + } else { + setDisplayName(newName); + setHtmlDisplayName(null); + } + } else { + setDisplayName(winName); + setHtmlDisplayName(null); + } + + } + + void updateWindowToolTip(String toolTipText) { + setToolTipText(toolTipText == null ? NbBundle.getMessage(IOWindowImpl.class, "LBL_IO_WINDOW") : toolTipText); + } + + private void updateToolbar(JComponent comp) { + toolbar.removeAll(); + if (comp != null) { + JButton[] buttons = getTabButtons(comp); + for (int i = 0; i < buttons.length; i++) { + toolbar.add(buttons[i]); + } + } + toolbar.revalidate(); + toolbar.repaint(); + } + + JButton[] getTabButtons(JComponent comp) { + JButton[] buttons = (JButton[]) comp.getClientProperty(TOOLBAR_BUTTONS_PROP); + if (buttons != null) { + return buttons; + } + Action[] actions = (Action[]) comp.getClientProperty(TOOLBAR_ACTIONS_PROP); + if (actions == null) { + return new JButton[0]; + } + + buttons = new JButton[actions.length]; + for (int i=0; i < buttons.length; i++) { + buttons[i] = new JButton(actions[i]); + buttons[i].setBorderPainted(false); + buttons[i].setOpaque(false); + buttons[i].setText(null); + buttons[i].putClientProperty("hideActionText", Boolean.TRUE); //NOI18N + if (actions[i].getValue (Action.SMALL_ICON) == null) { + throw new IllegalStateException ("No icon provided for " + actions[i]); //NOI18N + } + } + return buttons; + } + + + + @Override + protected void componentActivated() { + super.componentActivated(); + activated = true; + JComponent comp = getSelectedTab(); + CallBacks cb = tabToCb.get(comp); + if (cb != null) { + cb.activated(); + } + } + + @Override + protected void componentDeactivated() { + super.componentDeactivated(); + activated = false; + JComponent comp = getSelectedTab(); + CallBacks cb = tabToCb.get(comp); + if (cb != null) { + cb.deactivated(); + } + } + + public void stateChanged(ChangeEvent e) { + checkTabSelChange(); + } + + public void propertyChange(PropertyChangeEvent evt) { + if (TabbedPaneFactory.PROP_CLOSE.equals(evt.getPropertyName())) { + JComponent comp = (JComponent) evt.getNewValue(); + removeTab(comp); + } + } + + JComponent lastSelTab; + void checkTabSelChange() { + JComponent sel = getSelectedTab(); + if (sel != lastSelTab) { + lastSelTab = sel; + updateToolbar(sel); + } + } + + private void closeOtherTabs() { + assert pane.getParent() == this; + JComponent sel = getSelectedTab(); + for (int i = 0; i < pane.getTabCount(); i++) { + JComponent comp = (JComponent) pane.getComponentAt(0); + if (comp != sel) { + removeTab(comp); + } + } + } + + private void closeAllTabs() { + for (int i = 0; i < pane.getTabCount(); i++) { + JComponent comp = (JComponent) pane.getComponentAt(0); + removeTab(comp); + } + } + + private class Close extends AbstractAction { + + public Close() { + super(NbBundle.getMessage(IOWindowImpl.class, "LBL_Close")); + } + + public void actionPerformed(ActionEvent e) { + removeTab(getSelectedTab()); + } + } + + private class CloseAll extends AbstractAction { + + public CloseAll() { + super(NbBundle.getMessage(IOWindowImpl.class, "LBL_CloseAll")); + } + + public void actionPerformed(ActionEvent e) { + closeAllTabs(); + } + } + + private class CloseOthers extends AbstractAction { + + public CloseOthers() { + super(NbBundle.getMessage(IOWindowImpl.class, "LBL_CloseOthers")); + } + + public void actionPerformed(ActionEvent e) { + closeOtherTabs(); + } + } + + private class VariableRightBorder implements Border { + + private JTabbedPane pane; + + public VariableRightBorder(JTabbedPane pane) { + this.pane = pane; + } + + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (pane.getParent() != IOWindowImpl.this) { + Color old = g.getColor(); + g.setColor(getColor()); + g.drawLine(x + width - 1, y, x + width - 1, y + height); + g.setColor(old); + } + } + + public Color getColor() { + if (Utilities.isMac()) { + Color c1 = UIManager.getColor("controlShadow"); + Color c2 = UIManager.getColor("control"); + return new Color((c1.getRed() + c2.getRed()) / 2, + (c1.getGreen() + c2.getGreen()) / 2, + (c1.getBlue() + c2.getBlue()) / 2); + } else { + return UIManager.getColor("controlShadow"); + } + } + + public Insets getBorderInsets(Component c) { + if (pane.getParent() == IOWindowImpl.this) { + return new Insets(0, 0, 0, 0); + } + return new Insets(0, 0, 0, 2); + } + + public boolean isBorderOpaque() { + return true; + } + } + } +} diff --git a/core.io.ui/src/org/netbeans/core/io/ui/IOWindowAction.java b/core.io.ui/src/org/netbeans/core/io/ui/IOWindowAction.java new file mode 100644 --- /dev/null +++ b/core.io.ui/src/org/netbeans/core/io/ui/IOWindowAction.java @@ -0,0 +1,76 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.core.io.ui; + +import org.openide.util.HelpCtx; +import org.openide.util.NbBundle; +import org.openide.util.actions.CallableSystemAction; +import org.openide.windows.IOEmbedder; + +/** The action which shows standard IO component. +* +* @author Tomas Holy +*/ +public final class IOWindowAction extends CallableSystemAction { + + public void performAction() { + IOEmbedder embedder = IOEmbedder.getDefault(); + embedder.open(); + embedder.requestActive(); + } + + @Override + protected boolean asynchronous() { + return false; + } + + public String getName() { + return NbBundle.getBundle(IOWindowAction.class).getString("IOWindow"); + } + + public HelpCtx getHelpCtx() { + return new HelpCtx(IOWindowAction.class); + } + + @Override + protected String iconResource() { + return "org/netbeans/core/resources/frames/output.png"; // NOI18N + } +} diff --git a/core.io.ui/src/org/netbeans/core/io/ui/layer.xml b/core.io.ui/src/org/netbeans/core/io/ui/layer.xml new file mode 100644 --- /dev/null +++ b/core.io.ui/src/org/netbeans/core/io/ui/layer.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core.io.ui/src/org/netbeans/core/io/ui/resources/output.settings b/core.io.ui/src/org/netbeans/core/io/ui/resources/output.settings new file mode 100644 --- /dev/null +++ b/core.io.ui/src/org/netbeans/core/io/ui/resources/output.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/core.io.ui/src/org/netbeans/core/io/ui/resources/output.wsmode b/core.io.ui/src/org/netbeans/core/io/ui/resources/output.wsmode new file mode 100644 --- /dev/null +++ b/core.io.ui/src/org/netbeans/core/io/ui/resources/output.wsmode @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/core.io.ui/src/org/netbeans/core/io/ui/resources/output.wstcgrp b/core.io.ui/src/org/netbeans/core/io/ui/resources/output.wstcgrp new file mode 100644 --- /dev/null +++ b/core.io.ui/src/org/netbeans/core/io/ui/resources/output.wstcgrp @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/core.io.ui/src/org/netbeans/core/io/ui/resources/output.wstcref b/core.io.ui/src/org/netbeans/core/io/ui/resources/output.wstcref new file mode 100644 --- /dev/null +++ b/core.io.ui/src/org/netbeans/core/io/ui/resources/output.wstcref @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/core.output2/manifest.mf b/core.output2/manifest.mf --- a/core.output2/manifest.mf +++ b/core.output2/manifest.mf @@ -3,6 +3,5 @@ OpenIDE-Module-Localizing-Bundle: org/netbeans/core/output2/Bundle.properties OpenIDE-Module-Implementation-Version: 1 OpenIDE-Module-Provides: org.openide.windows.IOProvider -OpenIDE-Module-Layer: org/netbeans/core/output2/layer.xml AutoUpdate-Essential-Module: true diff --git a/core.output2/nbproject/project.xml b/core.output2/nbproject/project.xml --- a/core.output2/nbproject/project.xml +++ b/core.output2/nbproject/project.xml @@ -99,6 +99,10 @@ unit + org.netbeans.core.io.ui + + + org.netbeans.core.output2 diff --git a/core.output2/src/org/netbeans/core/output2/Controller.java b/core.output2/src/org/netbeans/core/output2/Controller.java --- a/core.output2/src/org/netbeans/core/output2/Controller.java +++ b/core.output2/src/org/netbeans/core/output2/Controller.java @@ -41,50 +41,19 @@ package org.netbeans.core.output2; -import java.awt.Component; -import java.awt.Container; -import java.awt.FileDialog; -import java.awt.Frame; -import java.awt.KeyboardFocusManager; -import java.awt.Point; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.beans.PropertyChangeListener; import java.io.CharConversionException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import javax.swing.AbstractAction; +import java.util.HashMap; +import java.util.Map; import javax.swing.Action; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JComponent; -import javax.swing.JFileChooser; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; -import javax.swing.KeyStroke; import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; -import javax.swing.text.BadLocationException; -import org.netbeans.core.output2.ui.AbstractOutputTab; -import org.openide.DialogDisplayer; -import org.openide.NotifyDescriptor; -import org.openide.actions.FindAction; import org.openide.util.Exceptions; import org.openide.util.Mutex; -import org.openide.util.NbBundle; -import org.openide.util.Utilities; +import org.openide.windows.IOEmbedder; import org.openide.windows.OutputEvent; -import org.openide.windows.OutputListener; -import org.openide.windows.WindowManager; import org.openide.xml.XMLUtil; /** @@ -95,176 +64,71 @@ * controller. The controller is fully stateless, and stores information of interest in * the components as appropriate. */ -public class Controller { //XXX public only for debug access to logging code +public class Controller { public static void ensureViewInDefault (final NbIO io, final boolean reuse) { Mutex.EVENT.readAccess(new Runnable() { public void run() { - OutputWindow.findDefault(); IOEvent evt = new IOEvent (io, 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; - private static final int ACTION_CLOSE = 3; - private static final int ACTION_NEXTERROR = 4; - private static final int ACTION_PREVERROR = 5; - private static final int ACTION_SELECTALL = 6; - private static final int ACTION_FIND = 7; - private static final int ACTION_FINDNEXT = 8; - private static final int ACTION_NAVTOLINE = 9; - private static final int ACTION_POSTMENU = 10; - private static final int ACTION_FINDPREVIOUS = 11; - private static final int ACTION_CLEAR = 12; - private static final int ACTION_NEXTTAB = 13; - private static final int ACTION_PREVTAB = 14; -// issue 59447 -// private static final int ACTION_TO_EDITOR = 15; - + static Controller controller; - //Package private for unit tests - Action copyAction = new ControllerAction (ACTION_COPY, - "ACTION_COPY"); //NOI18N - Action wrapAction = new ControllerAction (ACTION_WRAP, - "ACTION_WRAP"); //NOI18N - Action saveAsAction = new ControllerAction (ACTION_SAVEAS, - "ACTION_SAVEAS"); //NOI18N - Action closeAction = new ControllerAction (ACTION_CLOSE, - "ACTION_CLOSE"); //NOI18N - Action nextErrorAction = new ControllerAction (ACTION_NEXTERROR, - "ACTION_NEXT_ERROR" ); //NOI18N - Action prevErrorAction = new ControllerAction (ACTION_PREVERROR, - "ACTION_PREV_ERROR" ); //NOI18N - Action selectAllAction = new ControllerAction (ACTION_SELECTALL, - "ACTION_SELECT_ALL"); //NOI18N - Action findAction = new ControllerAction (ACTION_FIND, - "ACTION_FIND"); //NOI18N - Action findNextAction = new ControllerAction (ACTION_FINDNEXT, - "ACTION_FIND_NEXT"); //NOI18N - Action findPreviousAction = new ControllerAction (ACTION_FINDPREVIOUS, - "ACTION_FIND_PREVIOUS"); //NOI18N - Action navToLineAction = new ControllerAction (ACTION_NAVTOLINE, "navToLine", //NOI18N - KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); - Action postMenuAction = new ControllerAction (ACTION_POSTMENU, "postMenu", //NOI18N - KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.SHIFT_DOWN_MASK)); - Action clearAction = new ControllerAction (ACTION_CLEAR, "ACTION_CLEAR"); - - Action nextTabAction = new ControllerAction (ACTION_NEXTTAB, "NextViewAction", //NOI18N - (KeyStroke)null); - Action prevTabAction = new ControllerAction (ACTION_PREVTAB, "PreviousViewAction", //NOI18N - (KeyStroke)null); -// issue 59447 -// Action toEditorAction = new ControllerAction (ACTION_TO_EDITOR, "ToEditorAction", -// // if you ever change or remove the shortcut, check the popup hiding hack. in postPopuMenu() -// KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); + static Controller getDefault() { + if (controller == null) { + controller = new Controller(); + } + return controller; + } - private Object[] popupItems = new Object[] { - copyAction, new JSeparator(), findAction, findNextAction, - new JSeparator(), - wrapAction, new JSeparator(), saveAsAction, clearAction, closeAction, - }; - - private Action[] kbdActions = new Action[] { - copyAction, selectAllAction, findAction, findNextAction, - findPreviousAction, wrapAction, saveAsAction, closeAction, - navToLineAction, postMenuAction, clearAction, //toEditorAction, - }; + private Map ioToTab = new HashMap(); Controller() {} - private OutputTab createOutputTab (OutputWindow win, NbIO io, boolean activateContainer, boolean reuse) { - AbstractOutputTab[] ov = win.getTabs(); - OutputTab result = null; - if (LOG) log ("Find or create component for nbio " + io); - - for (int i=0; i < ov.length; i++) { - OutputTab oc = (OutputTab) ov[i]; - if (oc.getIO() == io) { - if (LOG) log ("Found an existing tab"); - result = oc; - break; - } - } - if (result == null) { - if (LOG) log ("Didn't find an existing open tab, checking hidden tabs"); - OutputTab[] hidden = win.getHiddenTabs(); - for (int i=0; i < hidden.length; i++) { - OutputTab oc = hidden[i]; - if (hidden[i].getIO() == io) { - if (LOG) log ("Found a hidden tab with the same IO. Unhiding it for reuse"); - result = oc; - unhideHiddenView (win, result); - break; - } - } - } - - if (LOG) log ("FindOrCreate: " + io.getName() + " found=" + (result != - null) + " for io " + io); - - if (result == null) { - if (LOG) log ("Find or create creating " + io.getName()); - result = createAndInstallView (win, io); - } - if (result != null) { - // install handlers to prev/next actions - result.getActionMap ().put ("jumpPrev", this.prevErrorAction); // NOI18N - result.getActionMap ().put ("jumpNext", this.nextErrorAction); // NOI18N - result.getActionMap ().put (FindAction.class.getName (), this.findAction); - result.getActionMap ().put (javax.swing.text.DefaultEditorKit.copyAction, this.copyAction); - } - - return result; + void eventDispatched(IOEvent ioe) { + if (Controller.LOG) Controller.log ("Event received: " + ioe); + NbIO io = ioe.getIO(); + int command = ioe.getCommand(); + boolean value = ioe.getValue(); + Object data = ioe.getData(); + OutputTab comp = ioToTab.get(io); + if (Controller.LOG) Controller.log ("Passing command to controller " + ioe); + performCommand(io.getIOEmbedder(), comp, io, command, value, data); + ioe.consume(); } - /** - * Creates and installs an output view - * - * @param win The owning container - * @param io The IO whose output is to be displayed - * @return A new OutputTab attached to the passed IO - */ - private OutputTab createAndInstallView (OutputWindow win, NbIO io) { - if (LOG) log ("Create and install a new tab for : " + io.getName()); + private OutputTab createOutputTab(IOEmbedder ioEmbedder, NbIO io) { + if (LOG) log ("Create component for nbio " + io); OutputTab result = new OutputTab (io); result.setName (io.getName() + " "); Action[] a = io.getToolbarActions(); if (a != null) { result.setToolbarActions(a); } - for (int i=0; i < kbdActions.length; i++) { - result.installKeyboardAction(kbdActions[i]); - } - - if (LOG) log ("Adding and selecting new tab " + result); - win.add (result); + + if (LOG) log ("Adding new tab " + result); + ioToTab.put(io, result); + ioEmbedder.add(result, result, a); //Make sure names are boldfaced for all open streams - if the tabbed - //pane was just added in, it will just have used the name of the + //pane was just added in, it will just have used the name of the //component, which won't contain html - AbstractOutputTab[] aot = win.getTabs(); - for (int i=0; i < aot.length; i++) { - updateName(win, (OutputTab) aot[i]); + for (OutputTab tab : ioToTab.values()) { + updateName(ioEmbedder, tab); } return result; } - /** - * Output views can be hidden by the user invoking close before the output stream for - * the output has been closed. In this case, they are stored in the OutputWindow, - * and can be reopened if new output arrives. This method will remove a component - * from the set of hidden components and re-add it to the component hierarchy. - * - * @param win The owning container - * @param hidden The output component which is hidden but was not closed when it was hidden - */ - private void unhideHiddenView (OutputWindow win, OutputTab hidden) { - if (LOG) log ("Unhiding hidden tab for " + hidden.getIO()); - win.add (hidden); - win.removeHiddenView(hidden); + void removeTab(NbIO io) { + ioToTab.remove(io); + } + + void removeFromUpdater(OutputTab tab) { + if (nameUpdater != null) { + nameUpdater.remove(tab); + } } /** @@ -274,15 +138,16 @@ * technique and the CoalescedNameUpdater to coalesce all name changes - otherwise * the name change may be delayed. * + * @param ioEmbedder IOEmbedder managing tab * @param tab The component whose name may need adjusting */ - private void updateName (OutputWindow win, OutputTab tab) { + void updateName(IOEmbedder ioEmbedder, OutputTab tab) { if (nameUpdater == null) { if (LOG) log ("Update name for " + tab.getIO() + " dispatching a name updater"); - nameUpdater = new CoalescedNameUpdater(win); + nameUpdater = new CoalescedNameUpdater(); SwingUtilities.invokeLater(nameUpdater); } - nameUpdater.add (tab); + nameUpdater.add(ioEmbedder, tab); } private CoalescedNameUpdater nameUpdater = null; @@ -300,18 +165,12 @@ * queue. */ private class CoalescedNameUpdater implements Runnable { - private Set components = new HashSet(); - private OutputWindow win; - CoalescedNameUpdater (OutputWindow win) { - this.win = win; + private Map components = new HashMap(); + CoalescedNameUpdater() { } - /** - * Add a tab whose name should be changed. - * @param tab The tab - */ - public void add (OutputTab tab) { - components.add (tab); + public void add(IOEmbedder ioEmbedder, OutputTab tab) { + components.put(tab, ioEmbedder); } public void remove(OutputTab tab) { @@ -319,748 +178,43 @@ } public void run() { - for (OutputTab t: components) { + for (OutputTab t: components.keySet()) { NbIO io = t.getIO(); + if (!ioToTab.containsKey(io)) { + continue; + } if (LOG) { log ("Update name for " + io.getName() + " stream " + "closed is " + io.isStreamClosed()); } - if (win.isAncestorOf(t)) { - String escaped; - try { - escaped = XMLUtil.toAttributeValue(io.getName()); - } catch (CharConversionException e) { - escaped = io.getName(); - } - boolean wasReset = io.checkReset(); - // XXX useHtml name appears to be backwards? -jglick - boolean useHtml = io.isStreamClosed() && !wasReset; - - String name = useHtml ? io.getName() + " " : - "" + escaped - + "  "; //NOI18N - - if (LOG) log (" set name to " + name); - //#88204 apostophes are escaped in xm but not html - win.setTabTitle (t, name.replace("'", "'")); + String escaped; + try { + escaped = XMLUtil.toAttributeValue(io.getName()); + } catch (CharConversionException e) { + escaped = io.getName(); } + boolean wasReset = io.checkReset(); + // XXX useHtml name appears to be backwards? -jglick + boolean useHtml = io.isStreamClosed() && !wasReset; + + String name = useHtml ? io.getName() + " " : "" + escaped + "  "; //NOI18N + + if (LOG) { + log(" set name to " + name); + } + //#88204 apostophes are escaped in xm but not html + io.getIOEmbedder().setTitle(t, name.replace("'", "'")); } nameUpdater = null; } } - - private void forceName(OutputWindow win, OutputTab tab) { - if (LOG) log ("ForceName ensuring non-html tab name"); - if (nameUpdater != null) { - if (LOG) log (" an update was queued, aborting it"); - nameUpdater.remove(tab); - } - if (win.isAncestorOf(tab)) { - String escaped; - try { - escaped = XMLUtil.toAttributeValue(tab.getIO().getName() + " "); - } catch (CharConversionException e) { - escaped = tab.getIO().getName() + " "; - } - if (LOG) log (" setting non-html name " + escaped); - //#88204 apostophes are escaped in xm but not html - win.setTabTitle (tab, escaped.replace("'", "'")); - } - } - - /** - * Called when a ControllerAction is invoked, either by the keyboard or - * from the popup menu. - * - * @param win The output window where it was invoked - * @param tab The tab it was invoked on - * @param id The ID of the action - */ - public void actionPerformed(OutputWindow win, OutputTab tab, int id) { - switch (id) { - case ACTION_COPY: - tab.getOutputPane().copy(); - break; - case ACTION_WRAP: - boolean wrapped = tab.getOutputPane().isWrapped(); - tab.getOutputPane().setWrapped(!wrapped); - break; - case ACTION_SAVEAS: - saveAs (tab); - break; - case ACTION_CLOSE: - close (win, tab, false); - break; - case ACTION_NEXTERROR: - sendCaretToError(win, tab, false); - break; - case ACTION_PREVERROR: - sendCaretToError(win, tab, true); - break; - case ACTION_SELECTALL: - tab.getOutputPane().selectAll(); - break; - case ACTION_FIND: - int start = tab.getOutputPane().getSelectionStart(); - int end = tab.getOutputPane().getSelectionEnd(); - String str = null; - if (start > 0 && end > start) { - try { - str = tab.getOutputPane().getDocument().getText(start, end - start); - } catch (BadLocationException ex) { - ex.printStackTrace(); - } - } - FindDialogPanel.showFindDialog(tab.getFindActionListener(findNextAction, findPreviousAction, copyAction), str); - break; - case ACTION_FINDNEXT: - findNext (tab); - break; - case ACTION_FINDPREVIOUS : - findPrevious (tab); - break; - case ACTION_NAVTOLINE : - if (LOG) log ("Action NAVTOLINE received"); - openLineIfError (tab); - break; - case ACTION_POSTMENU : - if (LOG) log ("Action POSTMENU received"); - postPopupMenu(win, tab, new Point(0,0), tab); - break; - case ACTION_CLEAR : - if (LOG) log ("Action CLEAR receieved"); - NbIO io = tab.getIO(); - - if (io != null) { - NbWriter writer = io.writer(); - if (writer != null) { - try { - if (LOG) log ("Resetting the writer for Clear"); - writer.reset(); - forceName(win, tab); - } catch (IOException ioe) { - Exceptions.printStackTrace(ioe); - } - } else if (LOG) { - log ("IO's NbWriter is null"); - } - } else if (LOG) { - log ("Clear on a tab with no IO"); - } - break; - case ACTION_NEXTTAB : - if (LOG) log ("Action NEXTTAB received"); - win.selectNextTab(tab); - break; - case ACTION_PREVTAB : - if (LOG) log ("Action PREVTAB received"); - win.selectPreviousTab(tab); - break; -// #issue 59447 -// case ACTION_TO_EDITOR : -// if (log) log ("Action TO_EDITOR received"); //NOI18N -// Mode m = WindowManager.getDefault().findMode ("editor"); //NOI18N -// if (m != null) { -// TopComponent tc = m.getSelectedTopComponent(); -// if (tc != null) { -// tc.requestActive(); -// } -// } -// break; - - default : - assert false; - } - } - - /** - * Called when a line is clicked - if an output listener is listening on that - * line, it will be sent outputLineAction. - * @param tab - */ - private void openLineIfError(OutputTab tab) { - OutWriter out = tab.getIO().out(); - if (out != null) { - int line = tab.getOutputPane().getCaretLine(); - OutputListener lis = out.getLines().getListenerForLine(line); - if (lis != null) { - if (LOG) log (" Sending action for getLine " + line); - ignoreCaretChanges = true; - tab.getOutputPane().sendCaretToLine(line, true); - ignoreCaretChanges = false; - ControllerOutputEvent coe = new ControllerOutputEvent (tab.getIO(), line); - lis.outputLineAction(coe); - } - } - } - - - - /** - * Find the next match for the previous search contents, starting at - * the current caret position. - * - * @param tab The tab - */ - private void findNext (OutputTab tab) { - OutWriter out = tab.getIO().out(); - if (out != null) { - String lastPattern = FindDialogPanel.getPanel().getPattern(); - if (lastPattern != null) { - out.getLines().find(lastPattern); - } - Matcher matcher = out.getLines().getForwardMatcher(); - int pos = tab.getOutputPane().getCaretPos(); - if (pos >= tab.getOutputPane().getLength() || pos < 0) { - pos = 0; - } - - if (matcher != null && matcher.find (pos)) { - tab.getOutputPane().setSelection(matcher.start(), matcher.end()); - copyAction.setEnabled(true); - } else { - Toolkit.getDefaultToolkit().beep(); - } - } - } - - /** - * Find the match before the current caret position, using the previously - * searched for value. - * - * @param tab The tab - */ - private void findPrevious (OutputTab tab) { - OutWriter out = tab.getIO().out(); - if (out != null) { - String lastPattern = FindDialogPanel.getPanel().getPattern(); - if (lastPattern != null) { - out.getLines().find(lastPattern); - } - Matcher matcher = out.getLines().getReverseMatcher(); - - int length = tab.getOutputPane().getLength(); - int pos = length - tab.getOutputPane().getSelectionStart(); - - if (pos >= tab.getOutputPane().getLength()-1 || pos < 0) { - pos = 0; - } - if (LOG) log ("Reverse search from " + pos); - if (matcher != null && matcher.find (pos)) { - int start = length - matcher.end(); - int end = length - matcher.start(); - tab.getOutputPane().setSelection(start, end); - copyAction.setEnabled(true); - } else { - Toolkit.getDefaultToolkit().beep(); - } - } - } - - /** - * Update the enabled state of the actions based on the state of the passed - * tab. If the tab is not currently selected, does nothing. - * - * @param win The output window - * @param tab The tab, presumably the selected one - */ - private void updateActions (OutputWindow win, OutputTab tab) { - if (tab == win.getSelectedTab()) { - OutputPane pane = (OutputPane) tab.getOutputPane(); - int len = pane.getLength(); - boolean enable = len > 0; - findAction.setEnabled (enable); - OutWriter out = tab.getIO().out(); -// findNextAction.setEnabled (out != null && out.getLines().getForwardMatcher() != null); -// findPreviousAction.setEnabled (out != null && out.getLines().getForwardMatcher() != null); - saveAsAction.setEnabled (enable); - selectAllAction.setEnabled(enable); - copyAction.setEnabled(pane.hasSelection()); - boolean hasErrors = out == null ? false : out.getLines().firstListenerLine() != -1; - nextErrorAction.setEnabled(hasErrors); - prevErrorAction.setEnabled(hasErrors); - } - } - - /** - * Close the tab. If programmatic is false and it is the last - * tab, the output window will be closed as well. - * - * @param win The owning output window - * @param tab The tab - * @param programmatic False if the user requested the tab to be closed - */ - public void close(OutputWindow win, OutputTab tab, boolean programmatic) { - //NotifyRemoved callback will take care of putting it into the hidden view list if - //its output is still open. - Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); - boolean hadFocus = focusOwner != null && (focusOwner == win || win.isAncestorOf(focusOwner)); - - win.remove(tab); //Triggers a call to notifyRemoved() - boolean winClosed = false; - if (!programmatic && win.getTabs().length == 0) { - if (LOG) log ("Last tab closed by user, closing output window."); - win.close(); - winClosed = true; - } - if (hadFocus) { - if (!winClosed && win.getSelectedTab() != null) { - if (LOG) log ("Trying to send focus to the newly selected tab"); - win.getSelectedTab().requestFocus(); - } - } - if (LOG) log ("Close received, removing " + tab + " from component"); - } - - /** - * Holds the last written to directory for the save as file chooser. - */ - private static String lastDir = null; - - /** - * Invokes a file dialog and if a file is chosen, saves the output to - * that file. - * - * @param tab The tab - */ - private void saveAs(OutputTab tab) { - OutWriter out = tab.getIO().out(); - if (out == null) { - return; - } - File f = showFileChooser (tab); - if (f != null) { - try { - synchronized (out) { - out.getLines().saveAs(f.getPath()); - } - } catch (IOException ioe) { - NotifyDescriptor notifyDesc = new NotifyDescriptor( - NbBundle.getMessage(Controller.class, "MSG_SaveAsFailed", f.getPath()), - NbBundle.getMessage(Controller.class, "LBL_SaveAsFailedTitle"), - NotifyDescriptor.DEFAULT_OPTION, - NotifyDescriptor.ERROR_MESSAGE, - new Object[] {NotifyDescriptor.OK_OPTION}, - NotifyDescriptor.OK_OPTION); - - DialogDisplayer.getDefault().notify(notifyDesc); - } - } - } - - /** - * Shows a file dialog and an overwrite dialog if the file exists, returning - * null if the user chooses not to overwrite. Will use an AWT FileDialog for - * Aqua, per Apple UI guidelines. - * - * @param owner A parent component for the dialog - the top level ancestor will - * actually be used so positioning is correct - * @return A file to write to - */ - private static File showFileChooser (JComponent owner) { - File f = null; - String dlgTtl = NbBundle.getMessage (Controller.class, "TITLE_SAVE_DLG"); //NOI18N - - boolean isAqua = "Aqua".equals(UIManager.getLookAndFeel().getID()); //NOI18N - - if (isAqua) { - //Apple UI guidelines recommend against ever using JFileChooser - FileDialog fd = new FileDialog((Frame) owner.getTopLevelAncestor(), dlgTtl, FileDialog.SAVE); - if (lastDir != null && new File (lastDir).exists()) { - fd.setDirectory(lastDir); - } - fd.setModal(true); - fd.setVisible(true); - String s = fd.getDirectory() + fd.getFile(); - f = new File(s); - if (f.exists() && f.isDirectory()) { - f = null; - } - } else { - JFileChooser jfc = new JFileChooser(); - if (lastDir != null && new File(lastDir).exists()) { - File dir = new File (lastDir); - if (dir.exists()) { - jfc.setCurrentDirectory(dir); - } - } - jfc.setName(dlgTtl); - jfc.setDialogTitle(dlgTtl); - - if (jfc.showSaveDialog(owner.getTopLevelAncestor()) == JFileChooser.APPROVE_OPTION) { - f = jfc.getSelectedFile(); - } - } - - if (f != null && f.exists() && !isAqua) { //Aqua's file dialog takes care of this - String msg = NbBundle.getMessage(Controller.class, - "FMT_FILE_EXISTS", new Object[] { f.getName() }); //NOI18N - String title = NbBundle.getMessage(Controller.class, - "TITLE_FILE_EXISTS"); //NOI18N - if (JOptionPane.showConfirmDialog(owner.getTopLevelAncestor(), msg, title, - JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { - f = null; - } - } - if (f != null) { - lastDir = f.getParent(); - } - return f; - } - - /** - * Called when the selected component is changed in an output container. - * - * @param win The owning container - * @param former The previously selected output view, or null - * @param current The newly selected output view, or null - */ - public void selectionChanged(OutputWindow win, OutputTab former, - OutputTab current) { - if (former != null) { - former.updateTimestamp(); - } - if (current != null) { - current.updateTimestamp(); - updateActions (win, current); - } - } - - /** - * Messaged when the container becomes activated in the netbeans window system - * @param win The container - */ - public void notifyActivated(OutputWindow win) { - OutputTab tab = (OutputTab) win.getSelectedTab(); - if (tab != null) { - updateActions (win, tab); - } - } - - /** - * Sends the caret in a tab to the nearest error line to its current position, selecting - * that line. - * - * @param win The output window - * @param tab the tab - * @param backward If the search should be done in reverse - */ - private void sendCaretToError(OutputWindow win, OutputTab tab, boolean backward) { - if (tab == null) { - //We're being invoked from SystemAction via main menu - no associated component - tab = (OutputTab) win.getSelectedTab(); - if (tab == null) { - return; - } - } - OutWriter out = tab.getIO().out(); - if (out != null) { - int line = tab.getOutputPane().getCaretLine(); - if (!tab.getOutputPane().isLineSelected(line)) { - line += backward ? 1 : -1; - } - - if (line >= tab.getOutputPane().getLineCount()-1) { - line = 0; - } - //FirstF12: #48485 - caret is already on the first listener line, - //so F12 jumps to the second error. So search from 0 the first time after a reset - int newline = out.getLines().nearestListenerLine(line, backward); - if (LOG) { - log ("sendCaretToError - caret line: " + line + - " nearest listener line " + newline); - } - if (newline == line) { - if (!backward && line != tab.getOutputPane().getLineCount()) { - newline = out.getLines().nearestListenerLine(line+1, backward); - } else if (backward && line > 0) { - newline = out.getLines().nearestListenerLine(line-1, backward); - } else { - return; - } - } - if (newline != -1) { - if (LOG) - log("Sending caret to error line " + newline); - tab.getOutputPane().sendCaretToLine(newline, true); - if (!win.isActivated()) { - OutputListener l = out.getLines().getListenerForLine(newline); - - ControllerOutputEvent ce = new ControllerOutputEvent (tab.getIO(), newline); - l.outputLineAction(ce); - } - } - } - } - - /** - * Called when an output tab has been removed from the component hierarchy. - * If its io is not closed, holds a reference to it in a list of closed - * tabs, re-showing it on request, or finally disposing its IO and - * releasing it if it has not been shown again. - */ - public void notifyRemoved(OutputTab tab) { - assert SwingUtilities.isEventDispatchThread(); - if (LOG) log ("Tab " + tab + " has been CLOSED. Disposing its IO."); - NbIO io = tab.getIO(); - if (io != null) { - io.setClosed(true); - } - NbWriter w = io.writer(); - if (w != null && w.isClosed()) { - //Will dispose the document - tab.setDocument(null); - } else if (w != null) { - //Something is still writing to the stream, but we're getting rid of the tab. Don't dispose - //the writer, just kill the tab's document - tab.getDocument().disposeQuietly(); - } - } - - /** - * Called when input has been sent by the user via the input component - * - * @param win The output window - * @param tab The tab component - * @param txt The input entered - */ - public void notifyInput(OutputWindow win, OutputTab tab, String txt) { - if (Controller.LOG) Controller.log ("Notify input on " + tab + " - " + txt); - NbIO io = tab.getIO(); - if (io != null) { - NbIO.IOReader in = io.in(); - if (in != null) { - if (Controller.LOG) Controller.log ("Sending input to " + in); - - in.pushText (txt + "\n"); - //#56070 - copy input to output, TODO - make it more different color.. - io.getOut().println(txt); - } - } - } - - /** - * Fetch the output listener for a given line in a given tab - * - * @param tab The output tab - * @param line The line to find a listener on - * @return An output listener or null - */ - private OutputListener listenerForLine (OutputTab tab, int line) { - OutWriter out = tab.getIO().out(); - if (out != null) { - return out.getLines().getListenerForLine(line); - } - return null; - } - - /** - * Called when the user has clicked a line in the text view - * @param win The output window - * @param tab The tab - * @param line The line which was clicked - */ - public void lineClicked(OutputWindow win, OutputTab tab, int line) { - OutputListener l = listenerForLine (tab, line); - if (l != null) { - ControllerOutputEvent oe = new ControllerOutputEvent (tab.getIO(), line); - l.outputLineAction(oe); - //Select the text on click - tab.getOutputPane().sendCaretToLine(line, true); - } - } - - /** - * Post the output window's popup menu - * - * @param win The output window - * @param tab The tab - * @param p The point clicked - * @param src The source of the click event - */ - public void postPopupMenu(OutputWindow win, OutputTab tab, Point p, Component src) { - if (LOG) { - log ("post popup menu for " + tab.getName()); - } - JPopupMenu popup = new JPopupMenu(); - popup.putClientProperty ("container", win); //NOI18N - popup.putClientProperty ("component", tab); //NOI18N - Action[] a = tab.getToolbarActions(); - if (a.length > 0) { - boolean added = false; - for (int i=0; i < a.length; i++) { - if (a[i].getValue(Action.NAME) != null) { - // add the proxy that doesn't show icons #67451 - popup.add (new ProxyAction(a[i])); - added = true; - } - } - if (added) { - popup.add (new JSeparator()); - } - } - for (int i=0; i < popupItems.length; i++) { - if (popupItems[i] instanceof JSeparator) { - popup.add ((JSeparator) popupItems[i]); - } else { - if (popupItems[i] != wrapAction) { - JMenuItem item = popup.add((Action) popupItems[i]); - if (popupItems[i] == findAction) { - item.setMnemonic(KeyEvent.VK_F); - } - } else { - JCheckBoxMenuItem item = - new JCheckBoxMenuItem((Action) popupItems[i]); - - item.setSelected(tab.getOutputPane().isWrapped()); - popup.add (item); - } - } - } - // hack to remove the esc keybinding when doing popup.. - KeyStroke esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - JComponent c = tab.getOutputPane().getTextView(); - Object escHandle = c.getInputMap().get(esc); - c.getInputMap().remove(esc); - tab.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).remove(esc); - - popup.addPopupMenuListener(new PMListener(popupItems, escHandle)); - popup.show(src, p.x, p.y); - - } - - private static class ProxyAction implements Action { - private Action orig; - ProxyAction(Action original) { - orig = original; - } - - public Object getValue(String key) { - if (Action.SMALL_ICON.equals(key)) { - return null; - } - return orig.getValue(key); - } - - public void putValue(String key, Object value) { - orig.putValue(key, value); - } - - public void setEnabled(boolean b) { - orig.setEnabled(b); - } - - public boolean isEnabled() { - return orig.isEnabled(); - } - - public void addPropertyChangeListener(PropertyChangeListener listener) { - orig.addPropertyChangeListener(listener); - } - - public void removePropertyChangeListener(PropertyChangeListener listener) { - orig.removePropertyChangeListener(listener); - } - - public void actionPerformed(ActionEvent e) { - orig.actionPerformed(e); - } - } - - /** - * #47166 - a disposed tab which has had its popup menu shown remains - * referenced through PopupItems->JSeparator->PopupMenu->Invoker->OutputPane->OutputTab - */ - private static class PMListener implements PopupMenuListener { - private Object[] popupItems; - private Object handle; - PMListener (Object[] popupItems, Object escHandle) { - this.popupItems = popupItems; - handle = escHandle; - } - - public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { - JPopupMenu popup = (JPopupMenu) e.getSource(); - popup.removeAll(); - popup.setInvoker(null); - // hack - AbstractOutputTab tab = (AbstractOutputTab)popup.getClientProperty("component"); - KeyStroke esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - JComponent c = tab.getOutputPane().getTextView(); - c.getInputMap().put(esc, handle); - tab.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(esc, handle); - - //hack end - popup.putClientProperty ("container", null); //NOI18N - popup.putClientProperty ("component", null); //NOI18N - popup.removePopupMenuListener(this); - for (int i=0; i < popupItems.length; i++) { - if (popupItems[i] instanceof ControllerAction) { - ((ControllerAction) popupItems[i]).clearListeners(); - } - } - } - - public void popupMenuCanceled(PopupMenuEvent e) { - popupMenuWillBecomeInvisible(e); - } - - public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - //do nothing - } - } - - /** - * Called when the text caret has changed lines - will call OutputListener.outputLineSelected if - * there is a listener for that line. - * - * @param tab The output tab - * @param line The line the caret is in - */ - public void caretEnteredLine(OutputTab tab, int line) { - if (!ignoreCaretChanges) { - OutputListener l = listenerForLine (tab, line); - if (LOG) { - log ("Caret entered line " + line + " notifying listener " + l); - } - if (l != null) { - ControllerOutputEvent oe = new ControllerOutputEvent (tab.getIO(), line); - l.outputLineSelected(oe); - } - } else { - if (LOG) { - log ("Caret entered line " + line + " which has no listener"); - } - } - } - - /** - * Called when an event has been received from the document (indicating that new - * output has been appended. Handles InputOutput.isFocusTaken(), and updates the - * tab title in the case the stream has been closed, and updates the actions if - * it is the selected tab. - * - * @param win - * @param tab - */ - public void documentChanged(OutputWindow win, OutputTab tab) { - if (tab.getIO().isFocusTaken()) { - //Not at all sure that isFocusTaken() is a terribly bright idea to begin with - win.setSelectedTab(tab); - win.requestVisible(); - } - updateName(win, tab); - if (tab == win.getSelectedTab() && win.isActivated()) { - updateActions(win, tab); - } - } /** * Handles IOEvents posted into the AWT Event Queue by NbIO instances whose methods have * been called, as received by an OutputTab which has identified the event as being * intended for it. * - * @param win The output container owning the IO, or in the case of CMD_CREATE, the + * @param ioEmbedder The output container owning the IO, or in the case of CMD_CREATE, the * one that received the event * @param tab The output component associated with this IO, if any * @param io The IO which originated the event @@ -1068,61 +222,48 @@ * @param value The boolean value of the command, if pertinent * @param data The data associated with the command, if pertinent */ - public void performCommand(OutputWindow win, OutputTab tab, NbIO io, int command, + public void performCommand(IOEmbedder ioEmbedder, OutputTab tab, NbIO io, int command, boolean value, Object data) { if (LOG) { log ("PERFORMING: " + IOEvent.cmdToString(command) + " value=" + value + " on " + io + " tob " + tab); } - OutWriter out = io.out(); - switch (command) { case IOEvent.CMD_CREATE : - createOutputTab(win, io, io.isFocusTaken(), value); + createOutputTab(ioEmbedder, io); break; case IOEvent.CMD_INPUT_VISIBLE : if (value && tab == null) { - tab = createOutputTab(win, io, io.isFocusTaken(), value); + tab = createOutputTab(ioEmbedder, io); } if (tab != null) { tab.setInputVisible(value); - win.setSelectedTab(tab); + ioEmbedder.select(tab); } break; case IOEvent.CMD_SELECT : if (tab == null) { - tab = createOutputTab(win, io, io.isFocusTaken(), value); + tab = createOutputTab(ioEmbedder, io); } - if (!win.isOpened()) { - win.open(); + ioEmbedder.open(); + if (io.isFocusTaken()) { + ioEmbedder.requestActive(); + } else { + ioEmbedder.requestVisible(); } - if (io.isFocusTaken()) { - win.requestActive(); - } else { - win.requestVisible(); - } - if (win.getSelectedTab() != tab) { - if (tab.getParent() == null) { - //It was hidden - win.add(tab); - } - win.setSelectedTab(tab); - updateName(win,tab); - } + ioEmbedder.select(tab); break; case IOEvent.CMD_SET_TOOLBAR_ACTIONS : if (tab == null && data != null) { - tab = createOutputTab(win, io, io.isFocusTaken(), value); + tab = createOutputTab(ioEmbedder, io); } Action[] a = (Action[]) data; tab.setToolbarActions(a); break; case IOEvent.CMD_CLOSE : if (tab != null) { - close(win, tab, true); - win.revalidate(); - win.repaint(); + ioEmbedder.remove(tab); } else { io.dispose(); } @@ -1135,193 +276,32 @@ io.out().dispose(); } } else { - if (tab.getParent() != null) { - updateName(win, tab); - if (tab.getIO().out() != null && tab.getIO().out().getLines().firstListenerLine() == -1) { - tab.getOutputPane().ensureCaretPosition(); - } - if (tab == win.getSelectedTab()) { - updateActions (win, tab); - } - } else { - //The tab had been kept around to be re-shown, but now the stream is closed, dispose it - win.removeHiddenView(tab); - if (io.out() != null) { - io.out().dispose(); - } + updateName(ioEmbedder, tab); + if (tab.getIO().out() != null && tab.getIO().out().getLines().firstListenerLine() == -1) { + tab.getOutputPane().ensureCaretPosition(); + } + if (tab == ioEmbedder.getSelected()) { + tab.updateActions(); } } } else { if (tab != null && tab.getParent() != null) { - updateName(win, tab); + updateName(ioEmbedder, tab); } } break; case IOEvent.CMD_RESET : if (tab == null) { if (LOG) log ("Got a reset on an io with no tab. Creating a tab."); - performCommand (win, null, io, IOEvent.CMD_CREATE, value, data); - win.requestVisible(); + performCommand (ioEmbedder, null, io, IOEvent.CMD_CREATE, value, data); + ioEmbedder.requestVisible(); return; } if (LOG) log ("Setting io " + io + " on tab " + tab); -// tab.setDocument (new OutputDocument((OutWriter)io.getOut())); - tab.setIO(io); - win.setSelectedTab(tab); - updateName(win, tab); - // Undesirable in practice: win.requestVisibleForNewTab(); + tab.reset(); + updateName(ioEmbedder, tab); if (LOG) log ("Reset on " + tab + " tab displayable " + tab.isDisplayable() + " io " + io + " io.out " + io.out()); break; - case IOEvent.CMD_ICON : - win.setTabIcon(tab, io.getIcon()); - break; - } - } - - /** - * Called when the output stream has been closed, to navigate to the - * first line which shows an error (if any). - * - * @param comp The output component whose IO's stream has been closed. - */ - private void navigateToFirstErrorLine (OutputTab comp) { - OutWriter out = comp.getIO().out(); - if (out != null) { - int line = comp.getFirstNavigableListenerLine(); - if (Controller.LOG) Controller.log ("NAV TO FIRST LISTENER LINE: " + line); - if (line > 0) { - comp.getOutputPane().sendCaretToLine (line, false); - if (isSDI(comp)) { - comp.requestActive(); - } - } - } - } - - - private static boolean isSDI (OutputTab comp) { - Container c = comp.getTopLevelAncestor(); - return (c != WindowManager.getDefault().getMainWindow()); - } - - /** - * Flag used to block navigating the editor to the first error line when - * selecting the error line in the output window after a build (or maybe - * it should navigate the editor there? Could be somewhat rude...) - */ - boolean ignoreCaretChanges = false; - - void hasSelectionChanged(OutputWindow outputWindow, OutputTab tab, boolean val) { - if (tab == outputWindow.getSelectedTab()) { - copyAction.setEnabled(val); - selectAllAction.setEnabled(!tab.getOutputPane().isAllSelected()); - } - } - - void hasOutputListenersChanged(OutputWindow win, OutputTab tab, boolean hasOutputListeners) { - if (hasOutputListeners && tab.getOutputPane().isScrollLocked()) { - navigateToFirstErrorLine(tab); - } - } - - /** - * A stateless action which will find the owning OutputTab's controller and call - * actionPerformed with its ID as an argument. - */ - private static class ControllerAction extends AbstractAction { - private int id; - /** - * Create a ControllerAction with the specified action ID (constants defined in Controller), - * using the specified bundle key. Expects the following contents in the bundle: - *
    - *
  • A name for the action matching the passed key
  • - *
  • An accelerator for the action matching [key].accel
  • - *
- * @param id An action ID - * @param bundleKey A key for the bundle associated with the Controller class - * @see org.openide.util.Utilities#stringToKey - */ - ControllerAction (int id, String bundleKey) { - if (bundleKey != null) { - String name = NbBundle.getMessage(Controller.class, bundleKey); - KeyStroke accelerator = getAcceleratorFor(bundleKey); - this.id = id; - putValue (NAME, name); - putValue (ACCELERATOR_KEY, accelerator); - } - } - - /** - * Create a ControllerAction with the specified ID, name and keystroke. Actions created - * using this constructor will not be added to the popup menu of the component. - * - * @param id The ID - * @param name A programmatic name for the item - * @param stroke An accelerator keystroke - */ - ControllerAction (int id, String name, KeyStroke stroke) { - this.id = id; - putValue (NAME, name); - putValue (ACCELERATOR_KEY, stroke); - } - - void clearListeners() { - PropertyChangeListener[] l = changeSupport.getPropertyChangeListeners(); - for (int i=0; i < l.length; i++) { - removePropertyChangeListener (l[i]); - } - } - - /** - * Get a keyboard accelerator from the resource bundle, with special handling - * for the mac keyboard layout. - * - * @param name The bundle key prefix - * @return A keystroke - */ - private static KeyStroke getAcceleratorFor (String name) { - String key = name + ".accel"; //NOI18N - if (Utilities.isMac()) { - key += ".mac"; //NOI18N - } - return Utilities.stringToKey(NbBundle.getMessage(Controller.class, key)); - } - - public int getID() { - return id; - } - - public void actionPerformed(ActionEvent e) { - if (LOG) log ("ACTION PERFORMED: " + getValue(NAME)); - Component c = (Component) e.getSource(); - - OutputTab outComp = c instanceof OutputTab ? (OutputTab) c : - c instanceof OutputWindow ? null : - (OutputTab) SwingUtilities.getAncestorOfClass(OutputTab.class, c); - - OutputWindow win= c instanceof OutputWindow ? (OutputWindow) c : - (OutputWindow) SwingUtilities.getAncestorOfClass(OutputWindow.class, outComp); - - if (win == null) { - win = OutputWindow.findDefault(); - } - if (outComp == null && win != null) { - outComp = (OutputTab) win.getSelectedTab(); - } - - if (win == null && outComp == null) { - //For popup menus, we store the component they were invoked over in - //client properties - JPopupMenu jpm = (JPopupMenu) SwingUtilities.getAncestorOfClass (JPopupMenu.class, c); - if (jpm != null) { - win = (OutputWindow) jpm.getClientProperty ("win"); //NOI18N - outComp = (OutputTab) jpm.getClientProperty ("component"); //NOI18N - } - } - Controller cont = win.getController(); - if (cont != null) { - cont.actionPerformed (win, outComp, getID()); - } } } @@ -1426,14 +406,5 @@ } return logStream; } - - void inputEof(OutputTab tab) { - if (Controller.LOG) Controller.log ("Input EOF"); - NbIO io = tab.getIO(); - NbIO.IOReader in = io.in(); - if (in != null) { - in.eof(); - } - } } diff --git a/core.output2/src/org/netbeans/core/output2/IOEvent.java b/core.output2/src/org/netbeans/core/output2/IOEvent.java --- a/core.output2/src/org/netbeans/core/output2/IOEvent.java +++ b/core.output2/src/org/netbeans/core/output2/IOEvent.java @@ -41,7 +41,6 @@ package org.netbeans.core.output2; import java.awt.*; -import java.util.Arrays; /** * An event type which carries data about an operation performed on an @@ -124,38 +123,11 @@ * Set the toolbar actions that should be displayed. */ static final int CMD_SET_TOOLBAR_ACTIONS = 10; - /** - * XXX may not be supported - dispose of the default output window instance - */ - static final int CMD_DETACH = 11; - /** - * change the icon on the tab.. - */ - static final int CMD_ICON = 12; - - - /** - * Array of IDs for checking legal values and generating a string representing the event. - */ - private static final int[] IDS = new int[] { - CMD_CREATE, - CMD_OUTPUT_VISIBLE, - CMD_INPUT_VISIBLE, - CMD_ERR_VISIBLE, - CMD_ERR_SEPARATED, - CMD_FOCUS_TAKEN, - CMD_SELECT, - CMD_CLOSE, - CMD_STREAM_CLOSED, - CMD_RESET, - CMD_SET_TOOLBAR_ACTIONS, - CMD_DETACH, - CMD_ICON - }; + private static final int CMD_LAST = 11; /** - * Strings matching the values in the IDS array for generating a string representing the event. + * Strings representing the event. */ private static final String[] CMDS = new String[] { "CREATE", //NOI18N @@ -169,7 +141,6 @@ "STREAM_CLOSED", //NOI18N "RESET", //NOI18N "SET_TOOLBAR_ACTIONS", //NOI18N - "DETACH" //NOI18N }; /** @@ -198,7 +169,7 @@ IOEvent(NbIO source, int command, boolean value) { //Null source only for destroying the default instance super(source == null ? new Object() : source, command + IO_EVENT_MASK); - assert Arrays.binarySearch (IDS, command) >= 0 : "Unknown command: " + command; //NOI18N + assert command >= 0 && command < CMD_LAST : "Unknown command: " + command; //NOI18N consumed = false; this.value = value; pendingCount++; @@ -260,6 +231,7 @@ * * @return If the event is consumed */ + @Override public boolean isConsumed() { return consumed; } @@ -268,10 +240,12 @@ * Overridden to avoid a bit of work AWTEvent does that's not * necessary for us. */ + @Override public void consume() { consumed = true; } + @Override public String toString() { return "IOEvent@" + System.identityHashCode(this) + "-" + cmdToString(getCommand()) + " on " + getIO() + @@ -279,20 +253,11 @@ } 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. - - //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) { - //Can be null after CMD_DETACH - OutputWindow.DEFAULT.eventDispatched(this); - } + Controller.getDefault().eventDispatched(this); pendingCount--; } public static String cmdToString (int cmd) { - return CMDS[Arrays.binarySearch(IDS, cmd)]; + return CMDS[cmd]; } } diff --git a/core.output2/src/org/netbeans/core/output2/NbIO.java b/core.output2/src/org/netbeans/core/output2/NbIO.java --- a/core.output2/src/org/netbeans/core/output2/NbIO.java +++ b/core.output2/src/org/netbeans/core/output2/NbIO.java @@ -49,6 +49,7 @@ import java.io.IOException; import java.io.Reader; import org.openide.util.Exceptions; +import org.openide.windows.IOEmbedder; /** Implementation of InputOutput. Implements calls as a set of * "commands" which are passed up to Dispatcher to be run on the event @@ -59,7 +60,7 @@ class NbIO implements InputOutput { private Boolean focusTaken = null; - private Boolean closed = null; + private boolean closed = false; private final String name; private Action[] actions; @@ -68,13 +69,17 @@ private Icon icon; + private IOEmbedder ioEmbedder; + /** Creates a new instance of NbIO * @param name The name of the IO * @param toolbarActions an optional set of toolbar actions + * @param iowin parent container accessor (null for default) */ - NbIO(String name, Action[] toolbarActions) { + NbIO(String name, Action[] toolbarActions, IOEmbedder ioEmbedder) { this(name); this.actions = toolbarActions; + this.ioEmbedder = ioEmbedder != null ? ioEmbedder : IOEmbedder.getDefault(); } /** Package private constructor for unit tests */ @@ -94,7 +99,11 @@ String getName() { return name; } - + + IOEmbedder getIOEmbedder() { + return ioEmbedder; + } + public OutputWriter getErr() { return ((NbWriter) getOut()).getErr(); } @@ -134,11 +143,11 @@ } void setClosed (boolean val) { - closed = val ? Boolean.TRUE : Boolean.FALSE; + closed = val; } public boolean isClosed() { - return Boolean.TRUE.equals(closed); + return closed; } public boolean isErrSeparated() { @@ -214,7 +223,7 @@ private boolean wasReset = false; public void reset() { if (Controller.LOG) Controller.log (this + ": reset"); - closed = null; + closed = false; streamClosedSet = false; streamClosed = false; @@ -265,12 +274,6 @@ @SuppressWarnings("deprecation") public Reader flushReader() { return getIn(); - } - - public void setIcon(Icon icn) { - icon = icn; - post (this, IOEvent.CMD_ICON, icn); - } public Icon getIcon() { diff --git a/core.output2/src/org/netbeans/core/output2/NbIOProvider.java b/core.output2/src/org/netbeans/core/output2/NbIOProvider.java --- a/core.output2/src/org/netbeans/core/output2/NbIOProvider.java +++ b/core.output2/src/org/netbeans/core/output2/NbIOProvider.java @@ -46,6 +46,7 @@ import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.windows.IOProvider; +import org.openide.windows.IOEmbedder; import org.openide.windows.InputOutput; import org.openide.windows.OutputWriter; @@ -59,12 +60,14 @@ private static final String STDOUT = NbBundle.getMessage(NbIOProvider.class, "LBL_STDOUT"); //NOI18N + + private static final String NAME = "output2"; // NOI18N public OutputWriter getStdOut() { if (Controller.LOG) { Controller.log("NbIOProvider.getStdOut"); } - InputOutput stdout = getIO (STDOUT, false, null); + InputOutput stdout = getIO (STDOUT, false); NbWriter out = ((NbIO)stdout).writer(); Controller.ensureViewInDefault ((NbIO) stdout, true); @@ -75,7 +78,7 @@ out = (NbWriter) stdout.getOut(); } catch (IOException e) { Exceptions.printStackTrace(e); - stdout = getIO (STDOUT, true, null); + stdout = getIO (STDOUT, true); out = (NbWriter) stdout.getOut(); } } else { @@ -86,22 +89,32 @@ public InputOutput getIO(String name, boolean newIO) { - return getIO (name, newIO, new Action[0]); + return getIO (name, newIO, new Action[0], null); } @Override public InputOutput getIO(String name, Action[] toolbarActions) { - return getIO (name, true, toolbarActions); + return getIO (name, true, toolbarActions, null); } - - private InputOutput getIO(String name, boolean newIO, Action[] toolbarActions) { + + @Override + public InputOutput getIO(String name, Action[] additionalActions, IOEmbedder ioEmbedder) { + return getIO(name, true, additionalActions, ioEmbedder); + } + + @Override + public String getName() { + return NAME; + } + + private InputOutput getIO(String name, boolean newIO, Action[] toolbarActions, IOEmbedder ioEmbedder) { if (Controller.LOG) { Controller.log("GETIO: " + name + " new:" + newIO); } NbIO result = namesToIos.get(name); if (result == null || newIO) { - result = new NbIO(name, toolbarActions); + result = new NbIO(name, toolbarActions, ioEmbedder); namesToIos.add (name, result); Controller.ensureViewInDefault (result, newIO); } else { diff --git a/core.output2/src/org/netbeans/core/output2/OutputPane.java b/core.output2/src/org/netbeans/core/output2/OutputPane.java --- a/core.output2/src/org/netbeans/core/output2/OutputPane.java +++ b/core.output2/src/org/netbeans/core/output2/OutputPane.java @@ -87,6 +87,7 @@ } } + @Override public void setMouseLine (int line, Point p) { Document doc = getDocument(); if (doc instanceof OutputDocument) { @@ -157,6 +158,7 @@ * numbers of calls to viewToModel if the cursor is never going to be * changed anyway. */ + @Override public void mouseMoved (MouseEvent evt) { Document doc = getDocument(); if (doc instanceof OutputDocument) { @@ -166,6 +168,7 @@ } } + @Override public void mousePressed(MouseEvent e) { super.mousePressed(e); if (e.getSource() == textView && SwingUtilities.isLeftMouseButton(e)) { @@ -189,6 +192,7 @@ return (OutputTab) SwingUtilities.getAncestorOfClass (OutputTab.class, this); } + @Override protected void setDocument (Document doc) { if (doc == null) { Document d = getDocument(); @@ -266,7 +270,7 @@ InputMap map = result.getInputMap(); MyInputMap myMap = new MyInputMap(); myMap.setParent(map); - result.setInputMap(result.WHEN_FOCUSED, myMap); + result.setInputMap(JEditorPane.WHEN_FOCUSED, myMap); Action act = new AbstractAction() { public void actionPerformed(ActionEvent arg0) { @@ -296,6 +300,7 @@ super(); } + @Override public Object get(KeyStroke keyStroke) { KeyStroke stroke = KeyStroke.getKeyStroke("control shift O"); if (keyStroke.equals(stroke)) { @@ -325,6 +330,7 @@ } private static final class GEP extends JEditorPane { + @Override public java.awt.Color getBackground() { return UIManager.getColor("text"); } diff --git a/core.output2/src/org/netbeans/core/output2/OutputTab.java b/core.output2/src/org/netbeans/core/output2/OutputTab.java --- a/core.output2/src/org/netbeans/core/output2/OutputTab.java +++ b/core.output2/src/org/netbeans/core/output2/OutputTab.java @@ -42,41 +42,79 @@ package org.netbeans.core.output2; import java.awt.Component; +import java.awt.Container; +import java.awt.FileDialog; +import java.awt.Frame; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.beans.PropertyChangeListener; +import java.io.CharConversionException; +import java.io.File; +import java.io.IOException; import java.util.regex.Matcher; +import javax.swing.AbstractAction; import javax.swing.Action; +import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.JSeparator; +import javax.swing.KeyStroke; import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import javax.swing.text.BadLocationException; import javax.swing.text.Document; +import org.netbeans.core.output2.Controller.ControllerOutputEvent; import org.netbeans.core.output2.ui.AbstractOutputPane; import org.netbeans.core.output2.ui.AbstractOutputTab; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.actions.FindAction; +import org.openide.util.Exceptions; +import org.openide.util.NbBundle; +import org.openide.util.Utilities; +import org.openide.windows.IOEmbedder; +import org.openide.windows.OutputListener; +import org.openide.windows.WindowManager; +import org.openide.xml.XMLUtil; /** * A component representing one tab in the output window. */ -final class OutputTab extends AbstractOutputTab { - private NbIO io; +final class OutputTab extends AbstractOutputTab implements IOEmbedder.CallBacks { + private final NbIO io; - OutputTab (NbIO io) { + OutputTab(NbIO io) { this.io = io; if (Controller.LOG) Controller.log ("Created an output component for " + io); OutputDocument doc = new OutputDocument (((NbWriter) io.getOut()).out()); - setDocument (doc); - } + setDocument(doc); - @Override - public void addNotify() { - super.addNotify(); - if (io != null) io.setClosed(false); - } + installKeyboardAction(copyAction); + installKeyboardAction(selectAllAction); + installKeyboardAction(findAction); + installKeyboardAction(findNextAction); + installKeyboardAction(findPreviousAction); + installKeyboardAction(wrapAction); + installKeyboardAction(saveAsAction); + installKeyboardAction(closeAction); + installKeyboardAction(copyAction); + installKeyboardAction(navToLineAction); + installKeyboardAction(postMenuAction); + installKeyboardAction(clearAction); - @Override - public void removeNotify() { - if (io != null) io.setClosed(true); - super.removeNotify(); + getActionMap().put("jumpPrev", this.prevErrorAction); // NOI18N + getActionMap().put("jumpNext", this.nextErrorAction); // NOI18N + getActionMap().put(FindAction.class.getName(), this.findAction); + getActionMap().put(javax.swing.text.DefaultEditorKit.copyAction, this.copyAction); + } @Override @@ -92,16 +130,9 @@ } } - public void setIO (NbIO io) { - if (Controller.LOG) Controller.log ("Replacing io on " + this + " with " + io + " out is " + (io != null ? io.getOut() : null)); - if (io != null) { - setDocument (new OutputDocument(((NbWriter) io.getOut()).out())); - io.setClosed(false); - } else { - if (this.io != null) this.io.setClosed(true); - this.io = null; - setDocument(null); - } + public void reset() { + setDocument(new OutputDocument(((NbWriter) io.getOut()).out())); + io.setClosed(false); } public OutputDocument getDocument() { @@ -119,70 +150,60 @@ protected void inputSent(String txt) { if (Controller.LOG) Controller.log("Input sent on OutputTab: " + txt); getOutputPane().lockScroll(); - findOutputWindow().inputSent(this, txt); + NbIO.IOReader in = io.in(); + if (in != null) { + if (Controller.LOG) Controller.log("Sending input to " + in); + in.pushText(txt + "\n"); + io.getOut().println(txt); + } } protected void inputEof() { - if (Controller.LOG) Controller.log ("Input EOF on OutputTab: "); - findOutputWindow().inputEof(this); + if (Controller.LOG) Controller.log ("Input EOF"); + NbIO.IOReader in = io.in(); + if (in != null) { + in.eof(); + } } public void hasSelectionChanged(boolean val) { - OutputWindow win = findOutputWindow(); - if (win != null) { - win.hasSelectionChanged(this, val); + if (isShowing()) { + copyAction.setEnabled(val); + selectAllAction.setEnabled(!getOutputPane().isAllSelected()); } } public NbIO getIO() { return io; } - - private long timestamp = 0; - void updateTimestamp() { - timestamp = System.currentTimeMillis(); - } - - long getTimestamp() { - return timestamp; - } - - private OutputWindow findOutputWindow() { - if (getParent() != null) { - return (OutputWindow) SwingUtilities.getAncestorOfClass(OutputWindow.class, this); - } else { - return (OutputWindow) getClientProperty ("outputWindow"); //NOI18N - } - } void requestActive() { - findOutputWindow().requestActive(); + io.getIOEmbedder().requestActive(); } public void lineClicked(int line) { - findOutputWindow().lineClicked (this, line); + OutWriter out = io.out(); + if (out == null) { + return; + } + OutputListener l = out.getLines().getListenerForLine(line); + if (l != null) { + ControllerOutputEvent oe = new ControllerOutputEvent (io, line); + l.outputLineAction(oe); + //Select the text on click + getOutputPane().sendCaretToLine(line, true); + } } - - boolean linePressed (int line, Point p) { - OutWriter out = getIO().out(); + + boolean linePressed(int line, Point p) { + OutWriter out = io.out(); if (out != null) { return out.getLines().getListenerForLine(line) != null; } else { return false; } - } - - public void postPopupMenu(Point p, Component src) { - findOutputWindow().postPopupMenu(this, p, src); } - public void caretEnteredLine(int line) { - OutputWindow ow = findOutputWindow(); - if (ow != null) { - ow.caretEnteredLine(this, line); - } - } - private int firstNavigableListenerLine = -1; /** * Do not unlock scrollbar unless there is a bona-fide error to @@ -209,21 +230,84 @@ } private boolean hasOutputListeners = false; + public void documentChanged() { - OutputWindow win = findOutputWindow(); - if (win != null) { - boolean hadOutputListeners = hasOutputListeners; - if (getFirstNavigableListenerLine() == -1) { - return; + boolean hadOutputListeners = hasOutputListeners; + if (getFirstNavigableListenerLine() == -1) { + return; + } + hasOutputListeners = io.out() != null && io.out().getLines().firstListenerLine() >= 0; + if (hasOutputListeners != hadOutputListeners) { + hasOutputListenersChanged(hasOutputListeners); + } + + IOEmbedder ioEmbedder = io.getIOEmbedder(); + if (io.isFocusTaken()) { + ioEmbedder.select(this); + ioEmbedder.requestVisible(); + } + Controller.getDefault().updateName(ioEmbedder, this); + if (this == ioEmbedder.getSelected() && ioEmbedder.isActivated()) { + updateActions(); + } + } + + /** + * Called when the output stream has been closed, to navigate to the + * first line which shows an error (if any). + */ + private void navigateToFirstErrorLine() { + OutWriter out = io.out(); + if (out != null) { + int line = getFirstNavigableListenerLine(); + if (Controller.LOG) { + Controller.log("NAV TO FIRST LISTENER LINE: " + line); } - hasOutputListeners = getIO().out() != null && getIO().out().getLines().firstListenerLine() >= 0; - if (hasOutputListeners != hadOutputListeners) { - win.hasOutputListenersChanged(this, hasOutputListeners); + if (line > 0) { + getOutputPane().sendCaretToLine(line, false); + if (isSDI()) { + requestActive(); + } } - win.documentChanged(this); } } + private boolean isSDI() { + Container c = getTopLevelAncestor(); + return (c != WindowManager.getDefault().getMainWindow()); + } + + void hasOutputListenersChanged(boolean hasOutputListeners) { + if (hasOutputListeners && getOutputPane().isScrollLocked()) { + navigateToFirstErrorLine(); + } + } + + public void activated() { + updateActions(); + } + + public void closed() { + io.setClosed(true); + Controller.getDefault().removeTab(io); + io.setClosed(true); + NbWriter w = io.writer(); + if (w != null && w.isClosed()) { + //Will dispose the document + setDocument(null); + } else if (w != null) { + //Something is still writing to the stream, but we're getting rid of the tab. Don't dispose + //the writer, just kill the tab's document + getDocument().disposeQuietly(); + } + } + + public void deactivated() { + } + + public void selected() { + } + /** * Determine if the new caret position is close enough that the scrollbar should be re-locked * to the end of the document. @@ -232,12 +316,10 @@ * @return if it should be locked */ public boolean shouldRelock(int dot) { - if (io != null) { - OutWriter w = io.out(); - if (w != null && !w.isClosed()) { - int dist = Math.abs(w.getLines().getCharCount() - dot); - return dist < 100; - } + OutWriter w = io.out(); + if (w != null && !w.isClosed()) { + int dist = Math.abs(w.getLines().getCharCount() - dot); + return dist < 100; } return false; } @@ -298,5 +380,586 @@ } } } - } + } + + /** + * Fetch the output listener for a given line + * + * @param line The line to find a listener on + * @return An output listener or null + */ + private OutputListener listenerForLine(int line) { + OutWriter out = io.out(); + if (out != null) { + return out.getLines().getListenerForLine(line); + } + return null; + } + /** + * Flag used to block navigating the editor to the first error line when + * selecting the error line in the output window after a build (or maybe + * it should navigate the editor there? Could be somewhat rude...) + */ + boolean ignoreCaretChanges = false; + + /** + * Called when the text caret has changed lines - will call OutputListener.outputLineSelected if + * there is a listener for that line. + * + * @param line The line the caret is in + */ + void caretEnteredLine(int line) { + if (!ignoreCaretChanges) { + OutputListener l = listenerForLine(line); + if (l != null) { + ControllerOutputEvent oe = new ControllerOutputEvent(io, line); + l.outputLineSelected(oe); + } + } + } + + /** + * Sends the caret in a tab to the nearest error line to its current position, selecting + * that line. + * + * @param backward If the search should be done in reverse + */ + private void sendCaretToError(boolean backward) { + OutWriter out = io.out(); + if (out != null) { + int line = getOutputPane().getCaretLine(); + if (!getOutputPane().isLineSelected(line)) { + line += backward ? 1 : -1; + } + + if (line >= getOutputPane().getLineCount() - 1) { + line = 0; + } + //FirstF12: #48485 - caret is already on the first listener line, + //so F12 jumps to the second error. So search from 0 the first time after a reset + int newline = out.getLines().nearestListenerLine(line, backward); + if (newline == line) { + if (!backward && line != getOutputPane().getLineCount()) { + newline = out.getLines().nearestListenerLine(line + 1, backward); + } else if (backward && line > 0) { + newline = out.getLines().nearestListenerLine(line - 1, backward); + } else { + return; + } + } + if (newline != -1) { + getOutputPane().sendCaretToLine(newline, true); + if (!io.getIOEmbedder().isActivated()) { + OutputListener l = out.getLines().getListenerForLine(newline); + ControllerOutputEvent ce = new ControllerOutputEvent(io, newline); + l.outputLineAction(ce); + } + } + } + } + + /** + * Find the next match for the previous search contents, starting at + * the current caret position. + * + */ + private void findNext() { + OutWriter out = io.out(); + if (out != null) { + String lastPattern = FindDialogPanel.getPanel().getPattern(); + if (lastPattern != null) { + out.getLines().find(lastPattern); + } + Matcher matcher = out.getLines().getForwardMatcher(); + int pos = getOutputPane().getCaretPos(); + if (pos >= getOutputPane().getLength() || pos < 0) { + pos = 0; + } + + if (matcher != null && matcher.find(pos)) { + getOutputPane().setSelection(matcher.start(), matcher.end()); + copyAction.setEnabled(true); + } else { + Toolkit.getDefaultToolkit().beep(); + } + } + } + + /** + * Find the match before the current caret position, using the previously + * searched for value. + * + * @param tab The tab + */ + private void findPrevious() { + OutWriter out = io.out(); + if (out != null) { + String lastPattern = FindDialogPanel.getPanel().getPattern(); + if (lastPattern != null) { + out.getLines().find(lastPattern); + } + Matcher matcher = out.getLines().getReverseMatcher(); + + int length = getOutputPane().getLength(); + int pos = length - getOutputPane().getSelectionStart(); + + if (pos >= getOutputPane().getLength() - 1 || pos < 0) { + pos = 0; + } + if (matcher != null && matcher.find(pos)) { + int start = length - matcher.end(); + int end = length - matcher.start(); + getOutputPane().setSelection(start, end); + copyAction.setEnabled(true); + } else { + Toolkit.getDefaultToolkit().beep(); + } + } + } + + /** + * Holds the last written to directory for the save as file chooser. + */ + private static String lastDir = null; + + /** + * Invokes a file dialog and if a file is chosen, saves the output to that file. + */ + void saveAs() { + OutWriter out = io.out(); + if (out == null) { + return; + } + File f = showFileChooser(this); + if (f != null) { + try { + synchronized (out) { + out.getLines().saveAs(f.getPath()); + } + } catch (IOException ioe) { + NotifyDescriptor notifyDesc = new NotifyDescriptor( + NbBundle.getMessage(OutputTab.class, "MSG_SaveAsFailed", f.getPath()), + NbBundle.getMessage(OutputTab.class, "LBL_SaveAsFailedTitle"), + NotifyDescriptor.DEFAULT_OPTION, + NotifyDescriptor.ERROR_MESSAGE, + new Object[]{NotifyDescriptor.OK_OPTION}, + NotifyDescriptor.OK_OPTION); + + DialogDisplayer.getDefault().notify(notifyDesc); + } + } + } + + /** + * Shows a file dialog and an overwrite dialog if the file exists, returning + * null if the user chooses not to overwrite. Will use an AWT FileDialog for + * Aqua, per Apple UI guidelines. + * + * @param owner A parent component for the dialog - the top level ancestor will + * actually be used so positioning is correct + * @return A file to write to + */ + private static File showFileChooser(JComponent owner) { + File f = null; + String dlgTtl = NbBundle.getMessage(Controller.class, "TITLE_SAVE_DLG"); //NOI18N + + boolean isAqua = "Aqua".equals(UIManager.getLookAndFeel().getID()); //NOI18N + + if (isAqua) { + //Apple UI guidelines recommend against ever using JFileChooser + FileDialog fd = new FileDialog((Frame) owner.getTopLevelAncestor(), dlgTtl, FileDialog.SAVE); + if (lastDir != null && new File(lastDir).exists()) { + fd.setDirectory(lastDir); + } + fd.setModal(true); + fd.setVisible(true); + String s = fd.getDirectory() + fd.getFile(); + f = new File(s); + if (f.exists() && f.isDirectory()) { + f = null; + } + } else { + JFileChooser jfc = new JFileChooser(); + if (lastDir != null && new File(lastDir).exists()) { + File dir = new File(lastDir); + if (dir.exists()) { + jfc.setCurrentDirectory(dir); + } + } + jfc.setName(dlgTtl); + jfc.setDialogTitle(dlgTtl); + + if (jfc.showSaveDialog(owner.getTopLevelAncestor()) == JFileChooser.APPROVE_OPTION) { + f = jfc.getSelectedFile(); + } + } + + if (f != null && f.exists() && !isAqua) { //Aqua's file dialog takes care of this + String msg = NbBundle.getMessage(Controller.class, + "FMT_FILE_EXISTS", new Object[]{f.getName()}); //NOI18N + String title = NbBundle.getMessage(Controller.class, + "TITLE_FILE_EXISTS"); //NOI18N + if (JOptionPane.showConfirmDialog(owner.getTopLevelAncestor(), msg, title, + JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { + f = null; + } + } + if (f != null) { + lastDir = f.getParent(); + } + return f; + } + + /** + * Called when a line is clicked - if an output listener is listening on that + * line, it will be sent outputLineAction. + */ + private void openLineIfError() { + OutWriter out = io.out(); + if (out != null) { + int line = getOutputPane().getCaretLine(); + OutputListener lis = out.getLines().getListenerForLine(line); + if (lis != null) { + ignoreCaretChanges = true; + getOutputPane().sendCaretToLine(line, true); + ignoreCaretChanges = false; + ControllerOutputEvent coe = new ControllerOutputEvent(io, line); + lis.outputLineAction(coe); + } + } + } + + /** + * Post the output window's popup menu + * + * @param p The point clicked + * @param src The source of the click event + */ + void postPopupMenu(Point p, Component src) { + JPopupMenu popup = new JPopupMenu(); + Action[] a = getToolbarActions(); + if (a.length > 0) { + boolean added = false; + for (int i = 0; i < a.length; i++) { + if (a[i].getValue(Action.NAME) != null) { + // add the proxy that doesn't show icons #67451 + popup.add(new ProxyAction(a[i])); + added = true; + } + } + if (added) { + popup.add(new JSeparator()); + } + } + + for (int i = 0; i < popupItems.length; i++) { + if (popupItems[i] instanceof JSeparator) { + popup.add((JSeparator) popupItems[i]); + } else { + if (popupItems[i] != wrapAction) { + JMenuItem item = popup.add((Action) popupItems[i]); + if (popupItems[i] == findAction) { + item.setMnemonic(KeyEvent.VK_F); + } + } else { + JCheckBoxMenuItem item = + new JCheckBoxMenuItem((Action) popupItems[i]); + + item.setSelected(getOutputPane().isWrapped()); + popup.add(item); + } + } + } + // hack to remove the esc keybinding when doing popup.. + KeyStroke esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + JComponent c = getOutputPane().getTextView(); + Object escHandle = c.getInputMap().get(esc); + c.getInputMap().remove(esc); + getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).remove(esc); + + popup.addPopupMenuListener(new PMListener(popupItems, escHandle)); + popup.show(src, p.x, p.y); + + } + + void updateActions() { + OutputPane pane = (OutputPane) getOutputPane(); + int len = pane.getLength(); + boolean enable = len > 0; + findAction.setEnabled(enable); + OutWriter out = io.out(); + saveAsAction.setEnabled(enable); + selectAllAction.setEnabled(enable); + copyAction.setEnabled(pane.hasSelection()); + boolean hasErrors = out == null ? false : out.getLines().firstListenerLine() != -1; + nextErrorAction.setEnabled(hasErrors); + prevErrorAction.setEnabled(hasErrors); + } + + private void disableHtmlName() { + Controller.getDefault().removeFromUpdater(this); + String escaped; + try { + escaped = XMLUtil.toAttributeValue(io.getName() + " "); + } catch (CharConversionException e) { + escaped = io.getName() + " "; + } + //#88204 apostophes are escaped in xm but not html + io.getIOEmbedder().setTitle(this, escaped.replace("'", "'")); + } + private static final int ACTION_COPY = 0; + private static final int ACTION_WRAP = 1; + private static final int ACTION_SAVEAS = 2; + private static final int ACTION_CLOSE = 3; + private static final int ACTION_NEXTERROR = 4; + private static final int ACTION_PREVERROR = 5; + private static final int ACTION_SELECTALL = 6; + private static final int ACTION_FIND = 7; + private static final int ACTION_FINDNEXT = 8; + private static final int ACTION_NAVTOLINE = 9; + private static final int ACTION_POSTMENU = 10; + private static final int ACTION_FINDPREVIOUS = 11; + private static final int ACTION_CLEAR = 12; + private static final int ACTION_NEXTTAB = 13; + private static final int ACTION_PREVTAB = 14; + + Action copyAction = new TabAction(ACTION_COPY, "ACTION_COPY"); //NOI18N + Action wrapAction = new TabAction(ACTION_WRAP, "ACTION_WRAP"); //NOI18N + Action saveAsAction = new TabAction(ACTION_SAVEAS, "ACTION_SAVEAS"); //NOI18N + Action closeAction = new TabAction(ACTION_CLOSE, "ACTION_CLOSE"); //NOI18N + Action nextErrorAction = new TabAction(ACTION_NEXTERROR, "ACTION_NEXT_ERROR"); //NOI18N + Action prevErrorAction = new TabAction(ACTION_PREVERROR, "ACTION_PREV_ERROR"); //NOI18N + Action selectAllAction = new TabAction(ACTION_SELECTALL, "ACTION_SELECT_ALL"); //NOI18N + Action findAction = new TabAction(ACTION_FIND, "ACTION_FIND"); //NOI18N + Action findNextAction = new TabAction(ACTION_FINDNEXT, "ACTION_FIND_NEXT"); //NOI18N + Action findPreviousAction = new TabAction(ACTION_FINDPREVIOUS, "ACTION_FIND_PREVIOUS"); //NOI18N + Action navToLineAction = new TabAction(ACTION_NAVTOLINE, "navToLine", //NOI18N + KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); + Action postMenuAction = new TabAction(ACTION_POSTMENU, "postMenu", //NOI18N + KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.SHIFT_DOWN_MASK)); + Action clearAction = new TabAction(ACTION_CLEAR, "ACTION_CLEAR"); + Action nextTabAction = new TabAction(ACTION_NEXTTAB, "NextViewAction", //NOI18N + (KeyStroke) null); + Action prevTabAction = new TabAction(ACTION_PREVTAB, "PreviousViewAction", //NOI18N + (KeyStroke) null); + private Object[] popupItems = new Object[]{ + copyAction, new JSeparator(), findAction, findNextAction, new JSeparator(), + wrapAction, new JSeparator(), saveAsAction, clearAction, closeAction,}; + + /** + * A stateless action which will find the owning OutputTab's controller and call + * actionPerformed with its ID as an argument. + */ + class TabAction extends AbstractAction { + + private int id; + + /** + * Create a ControllerAction with the specified action ID (constants defined in Controller), + * using the specified bundle key. Expects the following contents in the bundle: + *
    + *
  • A name for the action matching the passed key
  • + *
  • An accelerator for the action matching [key].accel
  • + *
+ * @param id An action ID + * @param bundleKey A key for the bundle associated with the Controller class + * @see org.openide.util.Utilities#stringToKey + */ + TabAction(int id, String bundleKey) { + if (bundleKey != null) { + String name = NbBundle.getMessage(OutputTab.class, bundleKey); + KeyStroke accelerator = getAcceleratorFor(bundleKey); + this.id = id; + putValue(NAME, name); + putValue(ACCELERATOR_KEY, accelerator); + } + } + + /** + * Create a ControllerAction with the specified ID, name and keystroke. Actions created + * using this constructor will not be added to the popup menu of the component. + * + * @param id The ID + * @param name A programmatic name for the item + * @param stroke An accelerator keystroke + */ + TabAction(int id, String name, KeyStroke stroke) { + this.id = id; + putValue(NAME, name); + putValue(ACCELERATOR_KEY, stroke); + } + + void clearListeners() { + PropertyChangeListener[] l = changeSupport.getPropertyChangeListeners(); + for (int i = 0; i < l.length; i++) { + removePropertyChangeListener(l[i]); + } + } + + /** + * Get a keyboard accelerator from the resource bundle, with special handling + * for the mac keyboard layout. + * + * @param name The bundle key prefix + * @return A keystroke + */ + private KeyStroke getAcceleratorFor(String name) { + String key = name + ".accel"; //NOI18N + if (Utilities.isMac()) { + key += ".mac"; //NOI18N + } + return Utilities.stringToKey(NbBundle.getMessage(OutputTab.class, key)); + } + + public int getID() { + return id; + } + + public void actionPerformed(ActionEvent e) { + switch (id) { + case ACTION_COPY: + getOutputPane().copy(); + break; + case ACTION_WRAP: + boolean wrapped = getOutputPane().isWrapped(); + getOutputPane().setWrapped(!wrapped); + break; + case ACTION_SAVEAS: + saveAs(); + break; + case ACTION_CLOSE: + io.getIOEmbedder().remove(OutputTab.this); + break; + case ACTION_NEXTERROR: + sendCaretToError(false); + break; + case ACTION_PREVERROR: + sendCaretToError(true); + break; + case ACTION_SELECTALL: + getOutputPane().selectAll(); + break; + case ACTION_FIND: + int start = getOutputPane().getSelectionStart(); + int end = getOutputPane().getSelectionEnd(); + String str = null; + if (start > 0 && end > start) { + try { + str = getOutputPane().getDocument().getText(start, end - start); + } catch (BadLocationException ex) { + ex.printStackTrace(); + } + } + FindDialogPanel.showFindDialog(getFindActionListener(findNextAction, findPreviousAction, copyAction), str); + break; + case ACTION_FINDNEXT: + findNext(); + break; + case ACTION_FINDPREVIOUS: + findPrevious(); + break; + case ACTION_NAVTOLINE: + openLineIfError(); + break; + case ACTION_POSTMENU: + postPopupMenu(new Point(0, 0), OutputTab.this); + break; + case ACTION_CLEAR: + NbWriter writer = io.writer(); + if (writer != null) { + try { + writer.reset(); + disableHtmlName(); + } catch (IOException ioe) { + Exceptions.printStackTrace(ioe); + } + } + break; + default: + assert false; + } + } + } + + private static class ProxyAction implements Action { + + private Action orig; + + ProxyAction(Action original) { + orig = original; + } + + public Object getValue(String key) { + if (Action.SMALL_ICON.equals(key)) { + return null; + } + return orig.getValue(key); + } + + public void putValue(String key, Object value) { + orig.putValue(key, value); + } + + public void setEnabled(boolean b) { + orig.setEnabled(b); + } + + public boolean isEnabled() { + return orig.isEnabled(); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + orig.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + orig.removePropertyChangeListener(listener); + } + + public void actionPerformed(ActionEvent e) { + orig.actionPerformed(e); + } + } + + /** + * #47166 - a disposed tab which has had its popup menu shown remains + * referenced through PopupItems->JSeparator->PopupMenu->Invoker->OutputPane->OutputTab + */ + private class PMListener implements PopupMenuListener { + + private Object[] popupItems; + private Object handle; + + PMListener(Object[] popupItems, Object escHandle) { + this.popupItems = popupItems; + handle = escHandle; + } + + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + JPopupMenu popup = (JPopupMenu) e.getSource(); + popup.removeAll(); + popup.setInvoker(null); + // hack + KeyStroke esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + JComponent c = getOutputPane().getTextView(); + c.getInputMap().put(esc, handle); + getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(esc, handle); + + //hack end + popup.removePopupMenuListener(this); + for (int i = 0; i < popupItems.length; i++) { + if (popupItems[i] instanceof OutputTab.TabAction) { + ((OutputTab.TabAction) popupItems[i]).clearListeners(); + } + } + } + + public void popupMenuCanceled(PopupMenuEvent e) { + popupMenuWillBecomeInvisible(e); + } + + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + //do nothing + } + } } diff --git a/core.output2/src/org/netbeans/core/output2/OutputWindow.java b/core.output2/src/org/netbeans/core/output2/OutputWindow.java deleted file mode 100644 --- a/core.output2/src/org/netbeans/core/output2/OutputWindow.java +++ /dev/null @@ -1,385 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, 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-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.core.output2; - -import java.awt.AWTEvent; -import java.awt.Color; -import java.awt.Component; -import java.awt.Graphics; -import java.awt.Insets; -import java.awt.Point; -import java.awt.event.FocusEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.HashSet; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.UIManager; -import org.netbeans.core.output2.ui.AbstractOutputTab; -import org.netbeans.core.output2.ui.AbstractOutputWindow; -import org.openide.util.ImageUtilities; -import org.openide.util.NbBundle; -import org.openide.util.Utilities; -import org.openide.windows.TopComponent; -import org.openide.windows.WindowManager; - -/** - * An output window. Note this class contains no logic of interest - all - * events of interest are passed to the Controller which - * manages this instance (and possibly others). - *

- * The mechanism for displaying/not displaying the tabbed pane is handled in - * the superclass, which overrides addImpl() and remove() to automatically install - * the tabbed pane if more than one view is added, and remove it if only one - * is present - so it is enough to simply call add() and remove() with instances - * of OutputTab and the management of tabs will be taken care of automatically. - */ -public class OutputWindow extends AbstractOutputWindow { - private Controller controller; - static OutputWindow DEFAULT = null; - public static final String ICON_RESOURCE = - "org/netbeans/core/resources/frames/output.png"; // NOI18N - - private MouseListener activateListener = new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - //#83829 - requestActive(); - } - }; - - public OutputWindow() { - this (new Controller()); - enableEvents(AWTEvent.FOCUS_EVENT_MASK); - putClientProperty ("dontActivate", Boolean.TRUE); - getActionMap().put("PreviousViewAction", controller.prevTabAction); - getActionMap().put("NextViewAction", controller.nextTabAction); - } - - @Override - public void addNotify() { - super.addNotify(); - pane.addMouseListener(activateListener); - } - - @Override - public void removeNotify() { - super.removeNotify(); - pane.removeMouseListener(activateListener); - } - - - protected void closeRequest(AbstractOutputTab tab) { - controller.close (this, (OutputTab) tab, false); - } - - OutputWindow (Controller controller) { - if (Controller.LOG) Controller.log("Created an output window"); - this.controller = controller; - setDisplayName (NbBundle.getMessage(OutputWindow.class, "LBL_OUTPUT")); //NOI18N - // setting name to satisfy the accesible name requirement for window. - setName (NbBundle.getMessage(OutputWindow.class, "LBL_OUTPUT")); //NOI18N - - setIcon(ImageUtilities.loadImage(ICON_RESOURCE)); // NOI18N - // special title for sliding mode - // XXX - please rewrite to regular API when available - see issue #55955 - putClientProperty("SlidingName", getDisplayName()); //NOI18N - } - - public static synchronized OutputWindow findDefault() { - if (DEFAULT == null) { - //If settings file is correctly defined call of WindowManager.findTopComponent() will - //call TestComponent00.getDefault() and it will set static field component. - TopComponent tc = WindowManager.getDefault().findTopComponent("output"); // NOI18N - if (tc != null) { - if (!(tc instanceof OutputWindow)) { - //This should not happen. Possible only if some other module - //defines different settings file with the same name but different class. - //Incorrect settings file? - IllegalStateException exc = new IllegalStateException - ("Incorrect settings file. Unexpected class returned." // NOI18N - + " Expected:" + OutputWindow.class.getName() // NOI18N - + " Returned:" + tc.getClass().getName()); // NOI18N - Logger.getLogger(OutputWindow.class.getName()).log(Level.WARNING, null, exc); - //Fallback to accessor reserved for window system. - OutputWindow.getDefault(); - } - } else { - OutputWindow.getDefault(); - } - } - return DEFAULT; - } - /* Singleton accessor reserved for window system ONLY. Used by window system to create - * OutputWindow instance from settings file when method is given. Use findDefault - * to get correctly deserialized instance of OutputWindow. */ - public static synchronized OutputWindow getDefault() { - if (DEFAULT == null) { - DEFAULT = new OutputWindow(); - } - return DEFAULT; - } - - @Override - public int getPersistenceType() { - return PERSISTENCE_ALWAYS; - } - - @Override - public String preferredID() { - return "output"; //NOI18N - } - - public Object readResolve() throws java.io.ObjectStreamException { - return getDefault(); - } - - @Override - public String getToolTipText() { - return getDisplayName(); - } - - Controller getController() { - return controller; - } - - @Override - public void requestVisible () { - if (Controller.LOG) { - Controller.log("Request visible"); - Controller.logStack(); - } - super.requestVisible(); - } - - @Override - public void processFocusEvent (FocusEvent fe) { - super.processFocusEvent (fe); - if (Boolean.TRUE.equals(getClientProperty("isSliding"))) { //NOI18N - repaint(200); - } - } - - @Override - public void paintComponent (Graphics g) { - super.paintComponent (g); - if (hasFocus()) { - Insets ins = getInsets(); - Color col = UIManager.getColor ("controlShadow"); //NOI18N - //Draw *some* focus indication - if (col == null) { - col = java.awt.Color.GRAY; - } - g.setColor(col); - g.drawRect ( - ins.left + 2, - ins.top + 2, - getWidth() - (ins.left + ins.right + 4), - getHeight() - (ins.top + ins.bottom + 4)); - } - } - - @Override - public void requestActive() { - boolean act = isActivated(); - if (Controller.LOG) Controller.log("Request active"); - super.requestActive(); - if (!act) { - requestFocus(); - } - } - - private boolean activated = false; - @Override - protected void componentActivated () { - if (Controller.LOG) Controller.log("ComponentActivated"); - super.componentActivated(); - activated = true; - controller.notifyActivated (this); - requestFocus(); - } - - @Override - protected void componentDeactivated() { - if (Controller.LOG) Controller.log("ComponentDeactivated"); - super.componentDeactivated(); - activated = false; - } - - protected void removed(AbstractOutputTab view) { - if (Controller.LOG) Controller.log("Removed tab " + view); - if (Controller.LOG) Controller.log ("Tab has been removed. Notifying controller."); - controller.notifyRemoved((OutputTab) view); - } - - protected void selectionChanged(AbstractOutputTab former, AbstractOutputTab current) { - controller.selectionChanged (this, (OutputTab) former, (OutputTab) current); - } - - void lineClicked(OutputTab outputComponent, int line) { - controller.lineClicked (this, outputComponent, line); - } - - void postPopupMenu(OutputTab outputComponent, Point p, Component src) { - controller.postPopupMenu (this, outputComponent, p, src); - } - - void caretEnteredLine(OutputTab outputComponent, int line) { - controller.caretEnteredLine(outputComponent, line); - } - - void documentChanged(OutputTab comp) { - controller.documentChanged (this, comp); - } - - private HashSet hiddenTabs = null; - void putHiddenView (OutputTab comp) { - if (hiddenTabs == null) { - hiddenTabs = new HashSet(); - } - comp.putClientProperty("outputWindow", this); //NOI18N - hiddenTabs.add(comp); - if (comp.getParent() != null) { - comp.getParent().remove(comp); - } - } - - void removeHiddenView (OutputTab comp) { - hiddenTabs.remove(comp); - comp.putClientProperty("outputWindow", null); //NOI18N - } - - @Override - public void setSelectedTab (AbstractOutputTab op) { - if (op.getParent() == null && hiddenTabs.contains(op)) { - removeHiddenView ((OutputTab) op); - add(op); - } - super.setSelectedTab (op); - } - - protected void updateSingletonName(String name) { - String winName = NbBundle.getMessage(OutputWindow.class, "LBL_OUTPUT"); //NOI18N - if (name != null) { - String newName = NbBundle.getMessage(OutputWindow.class, - "FMT_OUTPUT", new Object[] {winName, name}); //NOI18N - if (newName.indexOf ("") != -1) { - newName = Utilities.replaceString(newName, "", ""); //NOI18N - setHtmlDisplayName("" + newName); //NOI18N - } else { - setDisplayName(newName); - setHtmlDisplayName(null); - } - } else { - setDisplayName(winName); - setHtmlDisplayName(null); - } - } - - - OutputTab[] getHiddenTabs() { - if (hiddenTabs != null && !hiddenTabs.isEmpty()) { - OutputTab[] result = new OutputTab[hiddenTabs.size()]; - return hiddenTabs.toArray(result); - } - return new OutputTab[0]; - } - - OutputTab getTabForIO (NbIO io) { - AbstractOutputTab[] views = getTabs(); - for (int i=0; i < views.length; i++) { - if (((OutputTab) views[i]).getIO() == io) { - return ((OutputTab) views[i]); - } - } - OutputTab[] hidden = getHiddenTabs(); - for (int i=0; i < hidden.length; i++) { - if (hidden[i].getIO() == io) { - return hidden[i]; - } - } - return null; - } - - void eventDispatched(IOEvent ioe) { - if (Controller.LOG) Controller.log ("Event received: " + ioe); - NbIO io = ioe.getIO(); - int command = ioe.getCommand(); - boolean value = ioe.getValue(); - Object data = ioe.getData(); - OutputTab comp = getTabForIO (io); - if (command == IOEvent.CMD_DETACH) { - if (!ioe.isConsumed()) { - //Can be used by ModuleInstall to dispose of the current output window if desired - ioe.consume(); - DEFAULT = null; - return; - } - } - if (Controller.LOG) Controller.log ("Passing command to controller " + ioe); - controller.performCommand (this, comp, io, command, value, data); - ioe.consume(); - } - - void hasSelectionChanged(OutputTab tab, boolean val) { - controller.hasSelectionChanged(this, tab, val); - } - - public boolean isActivated() { - return activated; - } - - void hasOutputListenersChanged(OutputTab tab, boolean hasOutputListeners) { - controller.hasOutputListenersChanged(this, tab, hasOutputListeners); - } - - void inputEof(OutputTab tab) { - if (Controller.LOG) Controller.log ("Input EOF on " + this); - controller.inputEof(tab); - } - - void inputSent(OutputTab c, String txt) { - if (Controller.LOG) Controller.log ("Notifying controller input sent " + txt); - controller.notifyInput(this, c, txt); - } -} diff --git a/core.output2/src/org/netbeans/core/output2/OutputWindowAction.java b/core.output2/src/org/netbeans/core/output2/OutputWindowAction.java deleted file mode 100644 --- a/core.output2/src/org/netbeans/core/output2/OutputWindowAction.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, 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-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.core.output2; - -import org.openide.util.HelpCtx; -import org.openide.util.NbBundle; -import org.openide.util.actions.CallableSystemAction; -import org.openide.windows.Mode; -import org.openide.windows.TopComponent; -import org.openide.windows.WindowManager; - -/** The action which shows standard IO component. -* -* @author Dafe Simonek -*/ -public final class OutputWindowAction extends CallableSystemAction { - - public void performAction() { - OutputWindow output = OutputWindow.findDefault(); - output.open(); - output.requestActive(); - } - - @Override - protected boolean asynchronous() { - return false; - } - - public String getName() { - return NbBundle.getBundle(OutputWindowAction.class).getString("OutputWindow"); - } - - public HelpCtx getHelpCtx() { - return new HelpCtx (OutputWindowAction.class); - } - - @Override - protected String iconResource () { - return "org/netbeans/core/resources/frames/output.png"; // NOI18N - } -} diff --git a/core.output2/src/org/netbeans/core/output2/layer.xml b/core.output2/src/org/netbeans/core/output2/layer.xml deleted file mode 100644 --- a/core.output2/src/org/netbeans/core/output2/layer.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/core.output2/src/org/netbeans/core/output2/resources/output.settings b/core.output2/src/org/netbeans/core/output2/resources/output.settings deleted file mode 100644 --- a/core.output2/src/org/netbeans/core/output2/resources/output.settings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/core.output2/src/org/netbeans/core/output2/resources/output.wsmode b/core.output2/src/org/netbeans/core/output2/resources/output.wsmode deleted file mode 100644 --- a/core.output2/src/org/netbeans/core/output2/resources/output.wsmode +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/core.output2/src/org/netbeans/core/output2/resources/output.wstcgrp b/core.output2/src/org/netbeans/core/output2/resources/output.wstcgrp deleted file mode 100644 --- a/core.output2/src/org/netbeans/core/output2/resources/output.wstcgrp +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - diff --git a/core.output2/src/org/netbeans/core/output2/resources/output.wstcref b/core.output2/src/org/netbeans/core/output2/resources/output.wstcref deleted file mode 100644 --- a/core.output2/src/org/netbeans/core/output2/resources/output.wstcref +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputTab.java b/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputTab.java --- a/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputTab.java +++ b/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputTab.java @@ -255,5 +255,4 @@ JButton[] getToolbarButtons() { return buttons; } - } diff --git a/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputWindow.java b/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputWindow.java deleted file mode 100644 --- a/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputWindow.java +++ /dev/null @@ -1,551 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, 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-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.core.output2.ui; - -import java.awt.event.MouseEvent; -import javax.swing.border.Border; -import org.netbeans.core.output2.Controller; -import org.openide.util.Utilities; -import org.openide.windows.TopComponent; -import org.openide.awt.TabbedPaneFactory; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeEvent; -import java.lang.reflect.Method; -import javax.swing.plaf.TabbedPaneUI; -import org.openide.awt.MouseUtils; -import org.openide.util.NbBundle; - -/** - * A panel which, if more than one AbstractOutputTab is added to it, instead - * adds additional views to an internal tabbed pane. - * - * @author Tim Boudreau - */ -public abstract class AbstractOutputWindow extends TopComponent implements ChangeListener, PropertyChangeListener { - protected JTabbedPane pane = TabbedPaneFactory.createCloseButtonTabbedPane(); - private static final String ICON_PROP = "tabIcon"; //NOI18N - private JToolBar toolbar = null; - private JPopupMenu popupMenu; - - /** Creates a new instance of AbstractOutputWindow */ - public AbstractOutputWindow() { - pane.addChangeListener(this); - pane.addPropertyChangeListener(TabbedPaneFactory.PROP_CLOSE, this); - setFocusable(true); - toolbar = new JToolBar(); - toolbar.setOrientation(JToolBar.VERTICAL); - toolbar.setLayout(new BoxLayout(toolbar, BoxLayout.Y_AXIS)); - toolbar.setFloatable(false); - Insets ins = toolbar.getMargin(); - JButton sample = new JButton(); - sample.setBorderPainted(false); - sample.setOpaque(false); - sample.setText(null); - sample.setIcon(new Icon() { - public int getIconHeight() { - return 16; - } - public int getIconWidth() { - return 16; - } - public void paintIcon(Component c, Graphics g, int x, int y) { - } - }); - toolbar.add(sample); - Dimension buttonPref = sample.getPreferredSize(); - Dimension minDim = new Dimension(buttonPref.width + ins.left + ins.right, buttonPref.height + ins.top + ins.bottom); - toolbar.setMinimumSize(minDim); - toolbar.setPreferredSize(minDim); - toolbar.remove(sample); - setLayout(new BorderLayout()); - add(toolbar, BorderLayout.WEST); - toolbar.setBorder(new VariableRightBorder(pane)); - toolbar.setBorderPainted(true); - - popupMenu = new JPopupMenu(); - popupMenu.add(new Close()); - popupMenu.add(new CloseAll()); - popupMenu.add(new CloseOthers()); - pane.addMouseListener(new MouseUtils.PopupMouseAdapter() { - - @Override - protected void showPopup(MouseEvent evt) { - popupMenu.show(AbstractOutputWindow.this, evt.getX(), evt.getY()); - } - }); - } - - public void propertyChange(PropertyChangeEvent pce) { - if (TabbedPaneFactory.PROP_CLOSE.equals(pce.getPropertyName())) { - AbstractOutputTab tab = (AbstractOutputTab) pce.getNewValue(); - closeRequest(tab); - } - } - - protected abstract void closeRequest(AbstractOutputTab tab); - - protected abstract void removed(AbstractOutputTab view); - - @Override - protected void addImpl(Component c, Object constraints, int idx) { - setFocusable(false); - Component focusOwner = - KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - boolean hadFocus = hasFocus() || isAncestorOf(focusOwner); - - synchronized (getTreeLock()) { - if (c instanceof AbstractOutputTab) { - AbstractOutputTab aop = getInternalTab(); - if (aop != null) { - if (aop == c) { - return; - } - super.remove(aop); - assert pane.getParent() != this; - pane.add(aop); - setTabIcon(aop, (Icon)aop.getClientProperty(ICON_PROP)); - pane.add(c); - setTabIcon((AbstractOutputTab)c, (Icon)((AbstractOutputTab)c).getClientProperty(ICON_PROP)); - - super.addImpl(pane, constraints, idx); - updateSingletonName(null); - revalidate(); - } else if (pane.getParent() == this) { - pane.add(c); - setTabIcon((AbstractOutputTab) c, (Icon)((AbstractOutputTab)c).getClientProperty(ICON_PROP)); - revalidate(); - } else { - super.addImpl(c, constraints, idx); - setTabIcon((AbstractOutputTab) c, (Icon)((AbstractOutputTab)c).getClientProperty(ICON_PROP)); - setToolbarButtons(((AbstractOutputTab)c).getToolbarButtons()); - //#48819 - a bit obscure usecase, but revalidate() is call in the if branches above as well.. - revalidate(); - } - if (hadFocus) { - //Do not call c.requestFocus() directly, it can be - //discarded when adding tabs and focus will go to null. - //@see AbstractOutputWindow.requestFocus() - requestFocus(); - } - - return; - } - super.addImpl(c, constraints, idx); - } - if (getComponentCount() == 2 && getComponent(1) instanceof AbstractOutputTab) { - updateSingletonName(getComponent(1).getName()); - } - revalidate(); - } - - public final AbstractOutputTab[] getTabs() { - ArrayList al = - new ArrayList(pane.getParent() == this ? pane.getTabCount() : getComponentCount()); - if (pane.getParent() == this) { - int tabs = pane.getTabCount(); - for (int i=0; i < tabs; i++) { - Component c = pane.getComponentAt(i); - if (c instanceof AbstractOutputTab) { - al.add((AbstractOutputTab)c); - } - } - } else { - Component[] c = getComponents(); - for (int i=0; i < c.length; i++) { - if (c[i] instanceof AbstractOutputTab) { - al.add((AbstractOutputTab)c[i]); - } - } - } - AbstractOutputTab[] result = new AbstractOutputTab[al.size()]; - return al.toArray(result); - } - - - @Override - public void remove(Component c) { - AbstractOutputTab removedSelectedView = null; - synchronized (getTreeLock()) { - if (c.getParent() == pane && c instanceof AbstractOutputTab) { - if (c == pane.getSelectedComponent()) { - if (Controller.LOG) Controller.log("Selected view is being removed: " + c.getName()); - removedSelectedView = (AbstractOutputTab) c; - } - checkWinXPLFBug(); - pane.remove(c); - if (pane.getTabCount() == 1) { - Component comp = pane.getComponentAt(0); - pane.remove(comp); - super.remove(pane); - add(comp); - updateSingletonName(c.getName()); - setToolbarButtons(((AbstractOutputTab)comp).getToolbarButtons()); - revalidate(); - } - } else { - if (c == getSelectedTab()) { - removedSelectedView = (AbstractOutputTab) c; - } - super.remove(c); - setToolbarButtons(new JButton[0]); - updateSingletonName(null); - } - if (removedSelectedView != null) { - fire(removedSelectedView); - } - } - if (c instanceof AbstractOutputTab && c.getParent() == null) { - removed((AbstractOutputTab) c); - } - if (getComponentCount() == 2 && getComponent(1) instanceof AbstractOutputTab) { - updateSingletonName(getComponent(1).getName()); - } - revalidate(); - setFocusable(getComponentCount() == 1); - } - - private AbstractOutputTab getInternalTab() { - Component[] c = getComponents(); - for (int i=0; i < c.length; i++) { - if (c[i] instanceof AbstractOutputTab) { - return (AbstractOutputTab) c[i]; - } - } - return null; - } - - public final AbstractOutputTab getSelectedTab() { - if (pane.getParent() == this) { - return (AbstractOutputTab) pane.getSelectedComponent(); - } else { - return getInternalTab(); - } - } - - public void setSelectedTab(AbstractOutputTab op) { - assert (op.getParent() == this || op.getParent() == pane); - if (Controller.LOG) { - Controller.log("SetSelectedTab: " + op + " parent is " + op.getParent()); - } - if (pane.getParent() == this && op != pane.getSelectedComponent()) { - pane.setSelectedComponent(op); - } - - getActionMap().setParent(op.getActionMap()); - } - - public void setTabTitle(AbstractOutputTab tab, String name) { - if (tab.getParent() == pane) { - int index = pane.indexOfComponent(tab); - if (Controller.LOG) { - Controller.log("setTabTitle: #" + index + " '" + pane.getTitleAt(index) + "' -> '" + name + "'"); - } - pane.setTitleAt(index, name); - } else if (tab.getParent() == this) { - updateSingletonName(name); - } - tab.setName(name); - } - - public void setTabIcon(AbstractOutputTab tab, Icon icon) { - if (icon != null) { - tab.putClientProperty(ICON_PROP, icon); - if (pane.indexOfComponent(tab) != -1) { - pane.setIconAt(pane.indexOfComponent(tab), icon); - pane.setDisabledIconAt(pane.indexOfComponent(tab), icon); - } - } - } - - @SuppressWarnings("deprecation") - @Override public void requestFocus() { - if (!isShowing()) { - return; - } - AbstractOutputTab tab = getSelectedTab(); - if (tab != null && pendingFocusRunnable == null) { - //Adding the tab may yet need to be processed, so escape the - //current event loop via invokeLater() - pendingFocusRunnable = new Runnable() { - public void run() { - AbstractOutputTab tab = getSelectedTab(); - if (tab != null) { - tab.requestFocus(); - } - pendingFocusRunnable = null; - } - }; - SwingUtilities.invokeLater(pendingFocusRunnable); - } else { - super.requestFocus(); - } - } - - private Runnable pendingFocusRunnable = null; - - /** - * Updates the component name to include the name of a tab. If passed null - * arguments, should update the name to the default which does not include the - * tab name. - * - * @param name A name for the tab - */ - protected abstract void updateSingletonName(String name); - - - private AbstractOutputTab lastKnownSelection = null; - protected void fire(AbstractOutputTab formerSelection) { - AbstractOutputTab selection = getSelectedTab(); - if (formerSelection != selection) { - selectionChanged(formerSelection, selection); - lastKnownSelection = selection; - if (selection != null) { - setToolbarButtons(selection.getToolbarButtons()); - } else { - setToolbarButtons(new JButton[0]); - } - } - } - - private void setToolbarButtons(JButton[] buttons) { - toolbar.removeAll(); - for (int i = 0; i < buttons.length; i++) { - toolbar.add(buttons[i]); - } - toolbar.revalidate(); - toolbar.repaint(); - - } - - public void stateChanged(ChangeEvent e) { - if (pane.getSelectedComponent() instanceof AbstractOutputPane) { - ((AbstractOutputPane) pane.getSelectedComponent()).ensureCaretPosition(); - } - fire(lastKnownSelection); - } - - protected abstract void selectionChanged(AbstractOutputTab former, AbstractOutputTab current); - - private final boolean isGtk = "GTK".equals(UIManager.getLookAndFeel().getID()) || //NOI18N - UIManager.getLookAndFeel().getClass().getSuperclass().getName().indexOf("Synth") != -1; //NOI18N - /** - * Overridden to fill in the background color, since Synth/GTKLookAndFeel ignores - * setOpaque(true). - * @see http://www.netbeans.org/issues/show_bug.cgi?id=43024 - */ - @Override - public void paint(Graphics g) { - if (isGtk) { - //Presumably we can get this fixed for JDK 1.5.1 - Color c = getBackground(); - if (c == null) { - c = java.awt.Color.WHITE; - } - g.setColor(c); - g.fillRect(0, 0, getWidth(), getHeight()); - } - super.paint(g); - } - - /** - * Set next tab relatively to the given tab. If the give tab is the last one - * the first is selected. - * - * @param tab relative tab - */ - public final void selectNextTab(AbstractOutputTab tab) { - AbstractOutputTab[] tabs = this.getTabs(); - if (tabs.length > 1) { - int nextTabIndex = getSelectedTabIndex(tabs, tab) + 1; - if (nextTabIndex > (tabs.length - 1)) { - nextTabIndex = 0; - } - this.setSelectedTab(tabs[nextTabIndex]); - } - } - - /** - * Set previous tab relatively to the given tab. If the give tab is the - * first one the last is selected. - * - * @param tab relative tab - */ - public final void selectPreviousTab(AbstractOutputTab tab) { - AbstractOutputTab[] tabs = this.getTabs(); - if (tabs.length > 1) { - int prevTabIndex = getSelectedTabIndex(tabs, tab) - 1; - if (prevTabIndex < 0) { - prevTabIndex = tabs.length - 1; - } - this.setSelectedTab(tabs[prevTabIndex]); - } - } - - private int getSelectedTabIndex(AbstractOutputTab[] tabs, AbstractOutputTab tab) { - for (int i = 0; i < tabs.length; i++) { - if (tabs[i] == tab) { - return i; - } - } - return -1; - } - - // JDK 1.5, Win L&F - removing a tab causes a relayout and that uses onl data in UI class, - // causing it to throw ArrayOutofbounds if removing the last one. - // hacking around it by resetting the bad old data before removal. - // #56628 - private void checkWinXPLFBug() { - if ("Windows".equals(UIManager.getLookAndFeel().getID())) { //NOi18N - TabbedPaneUI ui = pane.getUI(); - try { - Method method = ui.getClass().getDeclaredMethod("setRolloverTab", new Class[] {Integer.TYPE}); //NOI18N - if (method != null) { - method.setAccessible(true); - method.invoke(ui, new Object[] { new Integer(-1) }); - method.setAccessible(false); - } - } catch (Exception exc) { - // well let's cross fingers and see.. - } - } - } - - private class VariableRightBorder implements Border { - - private JTabbedPane pane; - - public VariableRightBorder(JTabbedPane pane) { - this.pane = pane; - } - - public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { - if (pane.getParent() != AbstractOutputWindow.this) { - Color old = g.getColor(); - g.setColor(getColor()); - g.drawLine(x + width - 1, y, x + width - 1, y + height); - g.setColor(old); - } - } - - public Color getColor() { - if (Utilities.isMac()) { - Color c1 = UIManager.getColor("controlShadow"); - Color c2 = UIManager.getColor("control"); - return new Color((c1.getRed() + c2.getRed()) / 2, - (c1.getGreen() + c2.getGreen()) / 2, - (c1.getBlue() + c2.getBlue()) / 2); - } else { - return UIManager.getColor("controlShadow"); - } - } - - public Insets getBorderInsets(Component c) { - if (pane.getParent() == AbstractOutputWindow.this) { - return new Insets(0,0,0,0); - } - return new Insets(0, 0, 0, 2); - } - - public boolean isBorderOpaque() { - return true; - } - - } - - private void closeOtherTabs() { - AbstractOutputTab[] tabs = getTabs(); - AbstractOutputTab curTab = getSelectedTab(); - for (int i = 0; i < tabs.length; i++) { - AbstractOutputTab tab = tabs[i]; - if (tab != curTab) { - closeRequest(tab); - } - } - } - - private void closeAllTabs() { - AbstractOutputTab[] tabs = getTabs(); - for (int i = 0; i < tabs.length; i++) { - closeRequest(tabs[i]); - } - } - - private class Close extends AbstractAction { - - public Close() { - super(NbBundle.getMessage(AbstractOutputWindow.class, "LBL_Close")); - } - - public void actionPerformed(ActionEvent e) { - closeRequest(getSelectedTab()); - } - } - - private class CloseAll extends AbstractAction { - - public CloseAll() { - super(NbBundle.getMessage(AbstractOutputWindow.class, "LBL_CloseAll")); - } - - public void actionPerformed(ActionEvent e) { - closeAllTabs(); - } - } - - private class CloseOthers extends AbstractAction { - - public CloseOthers() { - super(NbBundle.getMessage(AbstractOutputWindow.class, "LBL_CloseOthers")); - } - - public void actionPerformed(ActionEvent e) { - closeOtherTabs(); - } - } -} diff --git a/core.output2/test/unit/src/org/netbeans/core/output2/LifecycleTest.java b/core.output2/test/unit/src/org/netbeans/core/output2/LifecycleTest.java --- a/core.output2/test/unit/src/org/netbeans/core/output2/LifecycleTest.java +++ b/core.output2/test/unit/src/org/netbeans/core/output2/LifecycleTest.java @@ -43,9 +43,16 @@ import java.awt.BorderLayout; import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; import junit.framework.TestCase; +import org.openide.util.Exceptions; +import org.openide.windows.IOEmbedder; +import org.openide.windows.IOEmbedder.IOEmbedderProvider; /** * @@ -57,40 +64,38 @@ super(testName); } - private OutputWindow win; + private IOEmbedder embedder; private NbIO io; - private OutWriter out = null; JFrame jf = null; OutputTab tab = null; OutputPane pane = null; + @Override protected void setUp() throws java.lang.Exception { -// Controller.logStdOut = true; -// Controller.log = true; - - jf = new JFrame(); - win = new OutputWindow(); - OutputWindow.DEFAULT = win; - jf.getContentPane().setLayout (new BorderLayout()); - jf.getContentPane().add (win, BorderLayout.CENTER); - jf.setBounds (20, 20, 700, 300); - io = (NbIO) new NbIOProvider().getIO ("Test", false); - SwingUtilities.invokeAndWait (new Shower()); - io.select(); - sleep(); - sleep(); - tab = (OutputTab) win.getSelectedTab(); + SwingUtilities.invokeAndWait(new Runnable() { + + public void run() { + embedder = IOEmbedder.getDefault(); + jf = new JFrame(); + jf.getContentPane().setLayout(new BorderLayout()); + jf.getContentPane().add(getIOWindow(), BorderLayout.CENTER); + jf.setBounds(20, 20, 700, 300); + jf.setVisible(true); + io = (NbIO) new NbIOProvider().getIO("Test", false); + io.select(); + tab = (OutputTab) embedder.getSelected(); + pane = (OutputPane) tab.getOutputPane(); + } + }); if (tab == null) { - fail ("Failed in setup - selected tab was null"); + fail("Failed in setup - selected tab was null"); } - pane = (OutputPane) tab.getOutputPane(); - sleep(); } + @Override protected void tearDown() { tab = null; pane = null; - out = null; if (jf != null) { jf.dispose(); } @@ -98,9 +103,9 @@ if (io != null) { NbIOProvider.dispose(io); } + io.closeInputOutput(); io = null; - win = null; - OutputWindow.DEFAULT = null; + embedder = null; sleep(); } @@ -120,24 +125,18 @@ private final void dosleep() { try { - Thread.currentThread().sleep(200); + Thread.sleep(200); SwingUtilities.invokeAndWait (new Runnable() { public void run() { System.currentTimeMillis(); } }); - Thread.currentThread().sleep(200); + Thread.sleep(200); } catch (Exception e) { fail (e.getMessage()); } } - public class Shower implements Runnable { - public void run() { - jf.setVisible(true); - } - } - public void testGetErr() throws Exception { System.out.println("testGetOut"); ErrWriter err = io.writer().err(); @@ -260,11 +259,9 @@ sleep(); writer.close(); sleep(); - io.closeInputOutput(); sleep(); - - assertNull ("Should be no selected tab after closeInputOutput", win.getSelectedTab()); + assertNull ("Should be no selected tab after closeInputOutput", getSelectedTab()); } public void testFilesCleanedUp() throws Exception { @@ -320,5 +317,51 @@ assertFalse ("Reset on a used writer should replace its underlying output", writer.out() == out); } + + static JComponent getIOWindow() { + IOEmbedder embedder = IOEmbedder.getDefault(); + JComponent comp = null; + try { + try { + Field f = embedder.getClass().getDeclaredField("provider"); + f.setAccessible(true); + IOEmbedderProvider prov = (IOEmbedderProvider) f.get(embedder); + Method m = prov.getClass().getDeclaredMethod("impl", new Class[0]); + m.setAccessible(true); + comp = (JComponent) m.invoke(prov); + } catch (InvocationTargetException ex) { + Exceptions.printStackTrace(ex); + } catch (NoSuchMethodException ex) { + Exceptions.printStackTrace(ex); + } catch (IllegalArgumentException ex) { + Exceptions.printStackTrace(ex); + } catch (IllegalAccessException ex) { + Exceptions.printStackTrace(ex); + } + } catch (NoSuchFieldException ex) { + Exceptions.printStackTrace(ex); + } catch (SecurityException ex) { + Exceptions.printStackTrace(ex); + } + return comp; + } + + JComponent getSelectedTab() { + class R implements Runnable { + JComponent tab; + public void run() { + tab = embedder.getSelected(); + } + } + R r = new R(); + try { + SwingUtilities.invokeAndWait(r); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } catch (InvocationTargetException ex) { + Exceptions.printStackTrace(ex); + } + return r.tab; + } } diff --git a/core.output2/test/unit/src/org/netbeans/core/output2/OutputWindowTest.java b/core.output2/test/unit/src/org/netbeans/core/output2/OutputWindowTest.java deleted file mode 100644 --- a/core.output2/test/unit/src/org/netbeans/core/output2/OutputWindowTest.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, 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-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.core.output2; - -import java.awt.BorderLayout; -import java.awt.Point; -import java.awt.event.ActionEvent; -import javax.swing.JFrame; -import javax.swing.SwingUtilities; -import junit.framework.TestCase; -import org.netbeans.core.output2.ui.AbstractOutputPane; -import org.netbeans.core.output2.ui.AbstractOutputTab; -import org.openide.windows.OutputEvent; -import org.openide.windows.OutputListener; - -/** - * - * @author Tim Boudreau - */ -public class OutputWindowTest extends TestCase { - - public OutputWindowTest(String testName) { - super(testName); - } - - private OutputWindow win; - private NbIO io; - private OutWriter out = null; - JFrame jf = null; - NbIOProvider provider = null; - - protected void setUp() throws Exception { - jf = new JFrame(); - win = new OutputWindow(); - OutputWindow.DEFAULT = win; - jf.getContentPane().setLayout (new BorderLayout()); - jf.getContentPane().add (win, BorderLayout.CENTER); - jf.setBounds (20, 20, 700, 300); - provider = new NbIOProvider(); - io = (NbIO) provider.getIO ("Test", false); - SwingUtilities.invokeAndWait (new Shower()); - } - - private final void sleep() { - try { - Thread.currentThread().sleep(200); - SwingUtilities.invokeAndWait (new Runnable() { - public void run() { - System.currentTimeMillis(); - } - }); - Thread.currentThread().sleep(200); - } catch (Exception e) { - fail (e.getMessage()); - } - } - - public class Shower implements Runnable { - public void run() { - jf.setVisible(true); - } - } - - public void testTabsShownAndHidden() { - System.out.println ("testTabsShownAndHidden"); - int n = win.getTabs().length; - assertTrue ("Number of tabs should be 1, not " + n, n == 1); - NbIO io2 = (NbIO) provider.getIO("Test2", true); - sleep(); - n = win.getTabs().length; - assertTrue ("Number of tabs should be 2, not " + n, n == 2); - - io2.closeInputOutput(); - sleep(); - - n = win.getTabs().length; - assertTrue ("After closeInputOutput on second tab, number of tabs should be 1, not " + n, n == 1); - } - - public void testTabNamesUpdatedCorrectly() throws Exception { - System.out.println ("testTabNamesUpdatedCorrectly"); - io.select(); - for (int i=0; i < 100; i++) { - io.getOut().println ("Here is some text we can delete"); - } - sleep(); - sleep(); - sleep(); - sleep(); - io.getOut().flush(); - - assertTrue ("Tab name should be html but is " + win.getDisplayName(), - win.getHtmlDisplayName() != null && win.getHtmlDisplayName().indexOf("<") >= 0); - - io.getOut().close(); - sleep(); - sleep(); - assertFalse ("Tab name should not be html", win.getHtmlDisplayName() != null && win.getHtmlDisplayName().indexOf("<") >= 0); - - final OutputTab tab = (OutputTab) win.getSelectedTab(); - - //Ensure the actions are paying attention - SwingUtilities.invokeLater (new Runnable() { - public void run() { - win.getController().postPopupMenu(win, tab, new Point(0,0), tab); - win.getController().clearAction.actionPerformed(new ActionEvent (tab, ActionEvent.ACTION_PERFORMED, "clear")); - } - }); - sleep(); - sleep(); - assertFalse ("Tab name should not be html", win.getHtmlDisplayName() != null && win.getHtmlDisplayName().indexOf("<") >= 0); - - io.getOut().reset(); - sleep(); - io.getOut().println("And here is some more text"); - sleep(); - - assertTrue ("Tab name should be html", win.getHtmlDisplayName() != null && win.getHtmlDisplayName().indexOf("<") >= 0); - - io.getOut().close(); - sleep(); - sleep(); - assertFalse ("Tab name should not be html", win.getHtmlDisplayName() != null && win.getHtmlDisplayName().indexOf("<") >= 0); - - } - -// mkleint: this test is non deterministic, teh sleep() calls are not a guarantee for reproducibility. - -// public void testScrollToEnd() throws Exception { -// System.out.println("testScrollToEnd"); -// NbIO io2 = (NbIO) provider.getIO("ScrollDown", true); -// io2.select(); -// sleep(); -// AbstractOutputTab tab = win.getSelectedTab(); -// assertNotNull("Selected tab should not be null", tab); -// -// AbstractOutputPane pane = tab.getOutputPane(); -// OutputDocument doc = (OutputDocument) pane.getDocument(); -// -// for (int i = 0; i < 20; i++) { -// io2.getOut().println("Hellooo... "); -// } -// sleep(); -// int end = doc.getEndPosition().getOffset(); -// int carretPos = pane.getCaretPos(); -// assertEquals("Caret position should be at the end (end: " + end -// + ", carretPos: " + carretPos + ")", -// end, carretPos); -// io2.closeInputOutput(); -// sleep(); -// } - - public void testAbleToRetrieveSameInputOutputInstance() { - System.out.println ("testAbleToRetrieveSameInputOutputInstance"); - NbIO io2 = (NbIO) provider.getIO("Test2", true); - sleep(); - - NbIO io3 = (NbIO) provider.getIO("Test", false); - assertSame ("Requesting a tab with a name already in use should return the original InputOutput", io3, io); - - NbIO io4 = (NbIO) provider.getIO("Test2", false); - assertSame ("Requesting a second tab with a name already in use should return the same InputOutput", io4, io2); - - NbIO io5 = (NbIO) provider.getIO("Test", true); - assertNotSame ("Requesting a new InputOutput with a name matching another tab should not return the other tab", io5, io); - } - - public void testIOclosed() { - System.out.println ("testIOclosed"); - assertFalse ("If a tab is showing, its InputOutput should not say it is closed", io.isClosed()); - NbIO io2 = (NbIO) provider.getIO("Test2", true); - sleep(); - assertFalse ("If a tab is showing, its InputOutput should not say it is closed", io2.isClosed()); - - assertFalse ("Adding another tab should not make the first tab think it has been closed, its InputOutput should not say it is closed", io.isClosed()); - - io2.closeInputOutput(); - sleep(); - - assertTrue ("After programmatically closing a tab, its InputOutput should return true from isClosed()", io2.isClosed()); - - io2 = (NbIO) provider.getIO("Test3", true); - sleep(); - - NbWriter writer = (NbWriter) io.getOut(); - writer.println ("Hello world"); - sleep(); - OutWriter outwriter = writer.out(); - - assertFalse ("After removing and adding another tab, the last tab added should not say it has been closed", io2.isClosed()); - io.select(); - sleep(); - - final OutputTab tab = (OutputTab) win.getSelectedTab(); - assertSame ("After calling InputOutput.select(), a tab owing the IO it was requested on should be the selected tab", tab.getIO(), io); - - //Ensure the actions are paying attention - SwingUtilities.invokeLater (new Runnable() { - public void run() { - win.getController().postPopupMenu(win, tab, new Point(0,0), tab); - win.getController().clearAction.actionPerformed(new ActionEvent (tab, ActionEvent.ACTION_PERFORMED, "clear")); - } - }); - sleep(); - sleep(); - - io.getOut().println ("Goodbye world"); - - assertSame ("OutputWriter should not be replaced by calling clearing/reset() on it", writer, io.getOut()); - assertNotSame ("Underlying OutWriter should be replaced by calling reset() on a used NbWriter", outwriter, writer.out()); - } - - - public void testListenersCleared() throws Exception { -// Controller.log = true; -// Controller.logStdOut = true; - - System.out.println ("testListenersCleared"); - io.select(); - io.getOut().println ("Helloooooo...."); - sleep(); - L[] ls = new L[20]; - for (int i=0; i < ls.length; i++) { - L l = new L(); - io.getOut().println ("Hyperlink " + i, l); - ls[i] = l; - } - sleep(); - - for (int i=0; i < ls.length; i++) { - ls[i].assertNotCleared("Newly written listeners should be cleared"); - } - - sleep(); - - final OutputTab tab = (OutputTab) win.getSelectedTab(); - assertNotNull ("Selected tab should not be null", tab); - assertSame ("After calling InputOutput.select(), a tab owing the IO it was requested on should be the selected tab", tab.getIO(), io); - - //Ensure the actions are paying attention - SwingUtilities.invokeLater (new Runnable() { - public void run() { - win.getController().postPopupMenu(win, tab, new Point(0,0), tab); - win.getController().clearAction.actionPerformed(new ActionEvent (tab, ActionEvent.ACTION_PERFORMED, "clear")); - } - }); - sleep(); - sleep(); - - for (int i=0; i < ls.length; i++) { - ls[i].assertCleared("After invoking the GUI's clear action, listeners should be cleared"); - } - - L[] ls2 = new L[20]; - for (int i=0; i < ls2.length; i++) { - L l = new L(); - io.getOut().println ("Second round of hyperlinks " + i, l); - ls2[i] = l; - } - sleep(); - - for (int i=0; i < ls2.length; i++) { - ls2[i].assertNotCleared("The second round of listeners were cleared prematurely"); - } - - //Make sure the old ones are untouched - for (int i=0; i < ls.length; i++) { - //These should not have been touched. The last assertCleared() cleared the cleared flag :-o - ls[i].assertNotCleared("Already cleared listeners should be unreferenced and should not be cleared a second time"); - } - - //Ensure the actions are paying attention - SwingUtilities.invokeLater (new Runnable() { - public void run() { - win.getController().postPopupMenu(win, tab, new Point(0,0), tab); - win.getController().clearAction.actionPerformed(new ActionEvent (tab, ActionEvent.ACTION_PERFORMED, "clear")); - } - }); - sleep(); - sleep(); - - for (int i=0; i < ls2.length; i++) { - ls2[i].assertCleared("After invoking the Clear Output action a second time, the newly written listeners were not cleared - " + i); - } - - //Make sure the old ones are untouched again - clearing a new set of lines should not - //touch the old listeners - they are already forgotten - for (int i=0; i < ls.length; i++) { - //These should not have been touched. The last assertCleared() cleared the cleared flag :-o - ls[i].assertNotCleared("Already cleared listeners should not be touched by clearing or writing new data"); - } - - L[] ls3 = new L[20]; - for (int i=0; i < ls3.length; i++) { - L l = new L(); - io.getOut().println ("Third round of hyperlinks " + i, l); - ls3[i] = l; - } - sleep(); - - for (int i=0; i < ls3.length; i++) { - ls3[i].assertNotCleared("Third round of writes with listeners had those listeners prematurely cleared"); - } - - //This time we test it using reset(), not programmatically invoking the GUI call to do the same - io.reset(); - sleep(); - sleep(); - - for (int i=0; i < ls3.length; i++) { - ls3[i].assertCleared("InputOutput.reset() should cause all OutputListeners to be cleared"); - } - - io = (NbIO) provider.getIO("Another tab", true); - - L[] ls4 = new L[20]; - for (int i=0; i < ls4.length; i++) { - L l = new L(); - io.getOut().println ("Third round of hyperlinks " + i, l); - ls4[i] = l; - } - sleep(); - - for (int i=0; i < ls4.length; i++) { - ls4[i].assertNotCleared("Premature clear"); - } - - io.getOut().close(); //Close the stream - sleep(); - //And this time with closeInputOutput() - io.closeInputOutput(); - sleep(); - sleep(); - sleep(); - sleep(); - sleep(); - - for (int i=0; i < ls4.length; i++) { - ls4[i].assertCleared("CloseInputOutput should cause all OutputListeners to be cleared"); - } - } - - - public class L implements OutputListener { - private OutputEvent clearedEvent = null; - public void assertCleared(String msg) { - assertNotNull (msg, clearedEvent); - clearedEvent = null; - } - - public void assertNotCleared(String msg) { - assertNull (msg, clearedEvent); - } - - public void outputLineSelected(OutputEvent ev) { - } - - public void outputLineAction(OutputEvent ev) { - } - - public void outputLineCleared(OutputEvent ev) { - clearedEvent = ev; - } - } - -} diff --git a/core.output2/test/unit/src/org/netbeans/core/output2/WrappedTextViewTest.java b/core.output2/test/unit/src/org/netbeans/core/output2/WrappedTextViewTest.java --- a/core.output2/test/unit/src/org/netbeans/core/output2/WrappedTextViewTest.java +++ b/core.output2/test/unit/src/org/netbeans/core/output2/WrappedTextViewTest.java @@ -47,17 +47,21 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.io.File; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import javax.imageio.ImageIO; +import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; +import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; import javax.swing.text.Position; import javax.swing.text.View; import junit.framework.TestCase; import org.netbeans.core.output2.ui.AbstractOutputPane; -import org.netbeans.core.output2.ui.AbstractOutputTab; +import org.openide.util.Exceptions; import org.openide.util.Utilities; +import org.openide.windows.IOEmbedder; import org.openide.windows.OutputEvent; import org.openide.windows.OutputListener; @@ -70,23 +74,30 @@ public WrappedTextViewTest(String testName) { super(testName); } - - private OutputWindow win; + + IOEmbedder iowin; private NbIO io; - private OutWriter out = null; JFrame jf = null; static int testNum; + @Override protected void setUp() throws Exception { - jf = new JFrame(); - win = new OutputWindow(); - OutputWindow.DEFAULT = win; - jf.getContentPane().setLayout (new BorderLayout()); - jf.getContentPane().add (win, BorderLayout.CENTER); - jf.setBounds (20, 20, 700, 300); - io = (NbIO) new NbIOProvider().getIO ("Test" + testNum++, false); - SwingUtilities.invokeAndWait (new Shower()); + SwingUtilities.invokeAndWait(new Runnable() { + + public void run() { + iowin = IOEmbedder.getDefault(); + JComponent wnd = LifecycleTest.getIOWindow(); + jf = new JFrame(); + jf.getContentPane().setLayout(new BorderLayout()); + jf.getContentPane().add(wnd, BorderLayout.CENTER); + jf.setBounds(20, 20, 700, 300); + jf.setVisible(true); + io = (NbIO) new NbIOProvider().getIO("Test" + testNum++, false); + } + }); + sleep(); + io.select(); io.getOut().println ("Test line 1"); sleep(); sleep(); @@ -108,45 +119,56 @@ SwingUtilities.invokeAndWait (new Runnable() { public void run() { - win.getSelectedTab().getOutputPane().setWrapped(true); + ((OutputTab) iowin.getSelected()).getOutputPane().setWrapped(true); } }); } private final void sleep() { try { - Thread.currentThread().sleep(200); + Thread.sleep(200); SwingUtilities.invokeAndWait (new Runnable() { public void run() { System.currentTimeMillis(); } }); - Thread.currentThread().sleep(200); + Thread.sleep(200); } catch (Exception e) { fail (e.getMessage()); } } - public class Shower implements Runnable { - public void run() { - jf.setVisible(true); - } - } - /** * tests if caret position is computed correctly (see issue #122492) */ public void testViewToModel() { - Graphics g = win.getSelectedTab().getOutputPane().getGraphics(); - FontMetrics fm = g.getFontMetrics(win.getSelectedTab().getOutputPane().getTextView().getFont()); - int charWidth = fm.charWidth('m'); - int charHeight = fm.getHeight(); - int fontDescent = fm.getDescent(); - float x = charWidth * 50; - float y = charHeight * 1 + fontDescent; - int charPos = win.getSelectedTab().getOutputPane().getTextView().getUI().getRootView(null).viewToModel(x, y, new Rectangle(), new Position.Bias[]{}); - int expCharPos = (Utilities.getOperatingSystem() & Utilities.OS_WINDOWS_MASK) != 0 ? 45 : 43; - assertTrue("viewToModel returned wrong value (it would result in bad caret position)!", charPos == expCharPos); + final Integer pos1 = new Integer(-2); + final Integer pos2 = new Integer(-1); + class R implements Runnable { + int charPos; + int expCharPos; + public void run() { + AbstractOutputPane pane = ((OutputTab) iowin.getSelected()).getOutputPane(); + Graphics g = pane.getGraphics(); + FontMetrics fm = g.getFontMetrics(pane.getTextView().getFont()); + int charWidth = fm.charWidth('m'); + int charHeight = fm.getHeight(); + int fontDescent = fm.getDescent(); + float x = charWidth * 50; + float y = charHeight * 1 + fontDescent; + charPos = pane.getTextView().getUI().getRootView(null).viewToModel(x, y, new Rectangle(), new Position.Bias[]{}); + expCharPos = (Utilities.getOperatingSystem() & Utilities.OS_WINDOWS_MASK) != 0 ? 45 : 43; + } + } + R r = new R(); + try { + SwingUtilities.invokeAndWait(r); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } catch (InvocationTargetException ex) { + Exceptions.printStackTrace(ex); + } + assertTrue("viewToModel returned wrong value (it would result in bad caret position)!", r.charPos == r.expCharPos); } public void testModelToView() throws Exception { @@ -157,87 +179,88 @@ //FOR PRODUCTION AND USE IT JUST FOR DEBUGGING return; } - - AbstractOutputTab tab = win.getSelectedTab(); - AbstractOutputPane pane = tab.getOutputPane(); - JTextComponent text = pane.getTextView(); - - View view = text.getUI().getRootView(text); - - Rectangle r = new Rectangle(1,1,1,1); - Rectangle alloc = new Rectangle(); - - java.awt.image.ColorModel model = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(). - getDefaultScreenDevice().getDefaultConfiguration().getColorModel(java.awt.Transparency.TRANSLUCENT); - java.awt.image.BufferedImage img = new java.awt.image.BufferedImage(model, - model.createCompatibleWritableRaster(text.getWidth() + 10, text.getHeight() + 10), model.isAlphaPremultiplied(), null); - - text.paint (img.getGraphics()); - boolean errorsFound = false; - ArrayList errors = new ArrayList(); - - System.out.println("...scanning " + (text.getWidth() * text.getHeight() + " pixels to make sure viewToModel() matches modeltoView(). Expect it to take about 10 minutes.")); - - for (int y=0; y < text.getHeight(); y++) { - r.y = y; - for (int x=0; x < text.getWidth(); x++) { - r.x = x; - alloc.setBounds (0, 0, text.getWidth(), text.getHeight()); - - int vtm = view.viewToModel (x, y, alloc, new Position.Bias[1]); - - Rectangle mtv = (Rectangle) view.modelToView (vtm, Position.Bias.Forward, vtm, Position.Bias.Forward, new Rectangle (0, 0, text.getWidth(), text.getHeight())); - - int xvtm = view.viewToModel (mtv.x, mtv.y, alloc, new Position.Bias[1]); + class R implements Runnable { + boolean errorsFound; + java.awt.image.BufferedImage img; + ArrayList errors; - if (vtm != xvtm) { - errorsFound = true; - try { - errors.add ("ViewToModel(" + x + "," + y + ") returns character position " + vtm + "; modelToView on " + vtm + " returns " + mtv + "; that Rectangle's corner, passed back to viewToModel maps to a different character position: " + xvtm + "\n"); - img.setRGB(x, y, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); - img.setRGB(x-1, y-1, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); - img.setRGB(x+1, y-1, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); - img.setRGB(x+1, y+1, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); - img.setRGB(x-1, y+1, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); - } catch (ArrayIndexOutOfBoundsException aioobe) { - System.err.println("OUT OF BOUNDS: " + x + "," + y + " image width " + img.getWidth() + " img height " + img.getHeight()); + public void run() { + AbstractOutputPane pane = ((OutputTab) iowin.getSelected()).getOutputPane(); + JTextComponent text = pane.getTextView(); + + View view = text.getUI().getRootView(text); + + Rectangle r = new Rectangle(1, 1, 1, 1); + Rectangle alloc = new Rectangle(); + + java.awt.image.ColorModel model = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration().getColorModel(java.awt.Transparency.TRANSLUCENT); + img = new java.awt.image.BufferedImage(model, + model.createCompatibleWritableRaster(text.getWidth() + 10, text.getHeight() + 10), model.isAlphaPremultiplied(), null); + + text.paint(img.getGraphics()); + errorsFound = false; + errors = new ArrayList(); + + System.out.println("...scanning " + (text.getWidth() * text.getHeight() + " pixels to make sure viewToModel() matches modeltoView(). Expect it to take about 10 minutes.")); + + for (int y = 0; y < text.getHeight(); y++) { + r.y = y; + for (int x = 0; x < text.getWidth(); x++) { + r.x = x; + alloc.setBounds(0, 0, text.getWidth(), text.getHeight()); + + int vtm = view.viewToModel(x, y, alloc, new Position.Bias[1]); + + Rectangle mtv = null; + try { + mtv = (Rectangle) view.modelToView(vtm, Position.Bias.Forward, vtm, Position.Bias.Forward, new Rectangle(0, 0, text.getWidth(), text.getHeight())); + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + + int xvtm = view.viewToModel(mtv.x, mtv.y, alloc, new Position.Bias[1]); + + if (vtm != xvtm) { + errorsFound = true; + try { + errors.add("ViewToModel(" + x + "," + y + ") returns character position " + vtm + "; modelToView on " + vtm + " returns " + mtv + "; that Rectangle's corner, passed back to viewToModel maps to a different character position: " + xvtm + "\n"); + img.setRGB(x, y, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); + img.setRGB(x - 1, y - 1, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); + img.setRGB(x + 1, y - 1, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); + img.setRGB(x + 1, y + 1, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); + img.setRGB(x - 1, y + 1, vtm > xvtm ? Color.RED.getRGB() : Color.BLUE.getRGB()); + } catch (ArrayIndexOutOfBoundsException aioobe) { + System.err.println("OUT OF BOUNDS: " + x + "," + y + " image width " + img.getWidth() + " img height " + img.getHeight()); + } + + System.err.println(x + "," + y + "=" + vtm + " -> [" + mtv.x + "," + mtv.y + "," + mtv.width + "," + mtv.height + "]->" + xvtm); + } + + r.y = y; //just in case + r.width = 1; + r.height = 1; } - - System.err.println(x + "," + y + "=" + vtm + " -> [" + mtv.x + "," + mtv.y + "," + mtv.width + "," + mtv.height + "]->" + xvtm); } - - r.y = y; //just in case - r.width = 1; - r.height = 1; } } + R r = new R(); + SwingUtilities.invokeAndWait(r); - if (errorsFound) { + if (r.errorsFound) { String dir = System.getProperty ("java.io.tmpdir"); if (!dir.endsWith(File.separator)) { dir += File.separator; } String fname = dir + "outputWindowDiffs.png"; - ImageIO.write (img, "png", new File (fname)); + ImageIO.write (r.img, "png", new File (fname)); fail ("In a wrapped view, some points as mapped by viewToModel do " + "not map back to the same coordinates in viewToModel. \nA bitmap " + "of the problem coordinates is saved in " + fname + " Problem" + - "spots are marked in red and blue.\n" + errors); + "spots are marked in red and blue.\n" + r.errors); } - - - -/* - try { - Thread.currentThread().sleep (40000); - } catch (Exception e) {} - */ - -// WrappedTextView wtv = win.getSelectedTab().getOutputPane().getTextView().getUI().getView(); } - - public class L implements OutputListener { public void outputLineSelected(OutputEvent ev) { @@ -248,7 +271,5 @@ public void outputLineCleared(OutputEvent ev) { } - } - } diff --git a/junit/nbproject/project.xml b/junit/nbproject/project.xml --- a/junit/nbproject/project.xml +++ b/junit/nbproject/project.xml @@ -209,6 +209,14 @@ + org.openide.io + + + + 1.15 + + + org.openide.loaders diff --git a/junit/src/org/netbeans/modules/junit/output/Manager.java b/junit/src/org/netbeans/modules/junit/output/Manager.java --- a/junit/src/org/netbeans/modules/junit/output/Manager.java +++ b/junit/src/org/netbeans/modules/junit/output/Manager.java @@ -306,12 +306,12 @@ /* Called from the AntLogger's thread */ final ResultDisplayHandler displayHandler = getDisplayHandler(session); + displayInWindow(session, sessionType, displayHandler, sessionEnd); if (!sessionEnd) { displayHandler.displayMessage(message); } else { displayHandler.displayMessageSessionFinished(message); } - displayInWindow(session, sessionType, displayHandler, sessionEnd); // /* @@ -413,6 +413,8 @@ final ResultWindow window = ResultWindow.getInstance(); if (displayHandler != null) { window.addDisplayComponent(displayHandler.getDisplayComponent()); + window.setOutputComp(displayHandler.getOutputComponent()); + displayHandler.createIO(window.getIOEmbedder()); } if (promote) { window.promote(); diff --git a/junit/src/org/netbeans/modules/junit/output/OutputDocument.java b/junit/src/org/netbeans/modules/junit/output/OutputDocument.java deleted file mode 100644 --- a/junit/src/org/netbeans/modules/junit/output/OutputDocument.java +++ /dev/null @@ -1,711 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, 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-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - * - * Contributor(s): - * - * Portions Copyrighted 2007 Sun Microsystems, Inc. - */ - -package org.netbeans.modules.junit.output; - -import java.awt.EventQueue; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.UndoableEditListener; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.Element; -import javax.swing.text.Position; -import javax.swing.text.Segment; - -/** - * Simple document for displaying textual output produced by JUnit tests. - * - * @author Marian Petras - */ -final class OutputDocument implements Document { - - static final AttributeSet attrs = new TrivialAttributeSet(); - - final RootElement rootElement; - - private DocElement[] docElements = new DocElement[100]; - private int docElementsCount; - private final DocElement lastDocElement - = new DocElement(0, "", false); //NOI18N - private int length = 0; - - private final Position startPosition = new SimplePosition(0); - private final Position endPosition = new EndPosition(); - private final Element[] rootElements; - - private Map properties; - private DocumentListener[] docListeners; - - OutputDocument() { - rootElement = new RootElement(); - rootElements = new Element[] { rootElement }; - - docElements[0] = lastDocElement; - docElementsCount = 1; - } - - public int getLength() { - return length; - } - - public void addDocumentListener(DocumentListener listener) { - if (listener == null) { - return; - } - - if (docListeners == null) { - docListeners = new DocumentListener[1]; - docListeners[0] = listener; - } else { - DocumentListener[] oldArr = docListeners; - docListeners = new DocumentListener[oldArr.length + 1]; - System.arraycopy(oldArr, 0, - docListeners, 0, - oldArr.length); - docListeners[oldArr.length] = listener; - } - } - - public void removeDocumentListener(DocumentListener listener) { - if (listener == null) { - return; - } - - if (docListeners == null) { - return; - } - - int index = -1; - for (int i = 0; i < docListeners.length; i++) { - if (docListeners[i] == listener) { - index = i; - break; - } - } - if (index != -1) { - if (docListeners.length == 1) { - docListeners = null; - } else { - DocumentListener[] oldArr = docListeners; - docListeners = new DocumentListener[oldArr.length - 1]; - if (index != 0) { - System.arraycopy(oldArr, 0, - docListeners, 0, - index); - } - if (index != oldArr.length - 1) { - System.arraycopy(oldArr, index + 1, - docListeners, index, - oldArr.length - (index + 1)); - } - } - } - } - - public void addUndoableEditListener(UndoableEditListener listener) { - //no-op - } - - public void removeUndoableEditListener(UndoableEditListener listener) { - //no-op - } - - public Object getProperty(Object key) { - return (properties != null) ? properties.get(key) : null; - } - - public void putProperty(Object key, Object value) { - if (properties == null) { - properties = new HashMap(7); - } - properties.put(key, value); - } - - public void remove(int offset, int len) throws BadLocationException { - checkLocation(offset); - assert false : "modification is not supported"; //NOI18N - } - - public void insertString(int offset, String str, AttributeSet a) throws BadLocationException { - checkLocation(offset); - if (offset != getLength()) { - assert false : "modification is not supported"; //NOI18N - return; - } - - final int strLen = str.length(); - if (strLen == 0) { - return; - } - - if (docElementsCount == docElements.length) { - DocElement[] oldElems = docElements; - int oldCapacity = oldElems.length; - int newCapacity = (oldCapacity < 100000) ? oldCapacity * 2 - : oldCapacity * 14 / 10; - docElements = new DocElement[newCapacity]; - System.arraycopy(oldElems, 0, - docElements, 0, - docElementsCount - 1); //minus 'lastElement' - } - - DocElement newElem = new DocElement(offset, str, a != null); - int index = docElementsCount - 1; - docElements[index] = newElem; - docElements[docElementsCount++] = lastDocElement; - - lastDocElement.offset = offset + strLen; - - length += strLen; - - fireTextAppended(newElem, index); - } - - final class DocElement implements Element { - - int offset; - final int length; - String text; - char[] chars; - final boolean isError; - - DocElement(int offset, String text, boolean isError) { - this.offset = offset; - this.text = text; - this.chars = null; - this.length = text.length(); - this.isError = isError; - } - - char[] getChars() { - if (chars == null) { - chars = text.toCharArray(); - text = null; - } - return chars; - } - - String getString() { - if (text == null) { - text = new String(chars); - chars = null; - } - return text; - } - - void appendToBuf(StringBuilder buf) { - if (text != null) { - buf.append(text); - } else { - assert (chars != null); - buf.append(chars); - } - } - - void appendToBuf(StringBuilder buf, int offset) { - int innerOffset = offset - this.offset; - if (text != null) { - buf.append(text, innerOffset, length); - } else { - assert (chars != null); - buf.append(chars, innerOffset, length - innerOffset); - } - } - - void appendToBuf(StringBuilder buf, int startOffset, int endOffset) { - if (text != null) { - buf.append(text, startOffset - offset, endOffset - offset); - } else { - assert (chars != null); - buf.append(chars, startOffset - offset, endOffset - startOffset); - } - } - - public Document getDocument() { - return OutputDocument.this; - } - - public Element getParentElement() { - return rootElement; - } - - public String getName() { - return "DocElement"; - } - - public AttributeSet getAttributes() { - return attrs; - } - - public int getStartOffset() { - return offset; - } - - public int getEndOffset() { - return offset + length; - } - - public int getElementIndex(int offset) { - throw new UnsupportedOperationException("Not supported."); - } - - public int getElementCount() { - return 0; - } - - public Element getElement(int index) { - throw new UnsupportedOperationException("Not supported."); - } - - public boolean isLeaf() { - return true; - } - - } - - /* element index information cache: */ - int cachedOffsetStart = -1; - int cachedOffsetEnd = -1; - int cachedIndex = -1; - - int getElementIndex(int offset) { - if (cachedIndex != -1) { - - /* First, try the cache: */ - if ((offset == cachedOffsetStart) - || (offset > cachedOffsetStart) && (offset < cachedOffsetEnd)) { - return cachedIndex; - } - - /* - * OK, the cached did not hit the offset. - * Before going to the CPU-intensive cache, try whether element - * following the cached one is not the one we are looking for: - */ - if (offset == cachedOffsetEnd) { - DocElement docElement = docElements[cachedIndex + 1]; - assert docElement.offset == offset; - cachedIndex++; - cachedOffsetStart = cachedOffsetEnd; - cachedOffsetEnd = cachedOffsetStart + docElement.length; - return cachedIndex; - } - } - - if (offset == 0) { - return 0; - } - if (offset >= length) { - return docElementsCount - 1; - } - - /* We did not get the index from the cache. So do some actual work: */ - int startIndex; - int endIndex; - if (cachedIndex == -1) { - startIndex = 0; - endIndex = docElementsCount - 1; - } else if (offset > cachedOffsetEnd) { - startIndex = cachedIndex + 1; - endIndex = docElementsCount - 1; - } else { - startIndex = 0; - endIndex = cachedIndex; - } - int cycles = 0; - while ((endIndex - startIndex) > 3) { - cycles++; - int middle = (startIndex + endIndex + 1) / 2; - if (docElements[middle].offset >= offset) { - endIndex = middle; - } else { - startIndex = middle; - } - } - int index = startIndex; - while (docElements[index].offset < offset) { - cycles++; - index++; - } - if (docElements[index].offset != offset) { - index--; - } - - DocElement docElem = docElements[index]; - - if ((cachedIndex == -1) || (index >= cachedIndex)) { - cachedIndex = index; - cachedOffsetStart = docElem.offset; - cachedOffsetEnd = docElem.offset + docElem.length; - } - return index; - } - - /** - * Notifies document listeners that a text has been appended. - * - * @param length length of the appended text - */ - private void fireTextAppended(DocElement newElem, int index) { - if (docListeners != null) { - DocumentEvent e = new DocInsertEvent(newElem, index); - for (int i = 0; i < docListeners.length; i++) { - docListeners[i].insertUpdate(e); - } - } - } - - public String getText(int offset, int length) throws BadLocationException { - checkLocation(offset); - if (length < 0) { - throw new BadLocationException("negative length", offset); //NOI18N - } - if (offset + length > getLength()) { - throw new BadLocationException( - "(offset[" + offset + "] + length[" + length //NOI18N - + "]) go beyond total length[" + getLength() + ']', //NOI18N - getLength()); - } - - if (length == 0) { - return ""; //NOI18N - } - - int elemIndex = getElementIndex(offset); - DocElement docElem = docElements[elemIndex]; - if ((offset == docElem.offset) && (length == docElem.length)) { - return docElem.getString(); - } else if (offset + length <= docElem.offset + docElem.length) { - return docElem.getString().substring(offset - docElem.offset, - offset - docElem.offset + length); - } else { - int finalOffset = offset + length; - StringBuilder buf = new StringBuilder(length); - - /* append the first line: */ - if (docElem.offset == offset) { - docElem.appendToBuf(buf); - } else { - docElem.appendToBuf(buf, offset); - } - - /* append whole lines: */ - while ((docElem = docElements[++elemIndex]).offset < finalOffset) { - docElem.appendToBuf(buf); - } - - /* append the last line: */ - if (finalOffset > docElem.offset) { - docElem.appendToBuf(buf, docElem.offset, finalOffset); - } - - return buf.toString(); - } - } - - public void getText(int offset, int length, Segment txt) throws BadLocationException { - checkLocation(offset); - if (length < 0) { - throw new BadLocationException("negative length", offset); //NOI18N - } - if (length == 0) { - txt.array = new char[0]; - txt.offset = 0; - txt.count = 0; - return; - } - if (offset + length > getLength()) { - throw new BadLocationException("too long text requested", //NOI18N - getLength()); - } - - int elemIndex = getElementIndex(offset); - DocElement docElem = docElements[elemIndex]; - if ((offset == docElem.offset) && (length == docElem.length)) { - txt.array = docElem.getChars(); - txt.offset = 0; - txt.count = length; - } else if (offset + length <= docElem.offset + docElem.length) { - txt.array = docElem.getChars(); - txt.offset = offset - docElem.offset; - txt.count = length; - } else if (txt.isPartialReturn()) { - txt.array = docElem.getChars(); - txt.offset = offset - docElem.offset; - txt.count = docElem.offset + docElem.length - offset; - } else { - int finalOffset = offset + length; - int charsStoredCount; - char[] result = new char[length]; - - /* append the first line: */ - System.arraycopy(docElem.getChars(), offset - docElem.offset, - result, 0, - charsStoredCount = docElem.offset + docElem.length - offset); - - /* append whole lines: */ - while ((docElem = docElements[++elemIndex]).offset < finalOffset) { - System.arraycopy(docElem.getChars(), 0, - result, charsStoredCount, - docElem.length); - charsStoredCount += docElem.length; - } - - /* append the last line: */ - if (docElem.offset < finalOffset) { - System.arraycopy(docElem.getChars(), 0, - result, charsStoredCount, - finalOffset - docElem.offset); - } - - txt.array = result; - txt.offset = 0; - txt.count = length; - } - } - - public Position getStartPosition() { - return startPosition; - } - - public Position getEndPosition() { - return endPosition; - } - - public Position createPosition(int offset) throws BadLocationException { - checkLocation(offset); - return (offset != length) ? new SimplePosition(offset) - : new EndPosition(); - } - - private void checkLocation(int offset) throws BadLocationException { - if (offset < 0) { - throw new BadLocationException( - "negative offset", //NOI18N - offset); - } - if (offset > length) { - throw new BadLocationException( - "offset > length (" + length + ')', //NOI18N - offset); - } - } - - public Element[] getRootElements() { - return rootElements; - } - - public Element getDefaultRootElement() { - return rootElement; - } - - public void render(Runnable r) { - assert EventQueue.isDispatchThread(); - r.run(); - } - - private static final class SimplePosition implements Position { - - private final int offset; - - SimplePosition(int offset) { - this.offset = offset; - } - - public int getOffset() { - assert EventQueue.isDispatchThread(); - return offset; - } - - } - - private final class EndPosition implements Position { - - public int getOffset() { - assert EventQueue.isDispatchThread(); - return getLength(); - } - - } - - private final class DocInsertEvent implements DocumentEvent, DocumentEvent.ElementChange { - private final int index; - private final DocElement docElem; - private Element[] childrenAdded; - private DocInsertEvent(DocElement docElem, int index) { - this.docElem = docElem; - this.index = index; - } - public int getOffset() { return docElem.offset; } - public int getLength() { return docElem.length; } - public Document getDocument() { return OutputDocument.this; } - public EventType getType() { return EventType.INSERT; } - public ElementChange getChange(Element elem) { - return (elem == OutputDocument.this.rootElement) ? this : null; - } - - public Element getElement() { - return OutputDocument.this.rootElement; - } - - public int getIndex() { - return index; - } - - public Element[] getChildrenRemoved() { - return null; - } - - public Element[] getChildrenAdded() { - if (childrenAdded == null) { - childrenAdded = new Element[] { docElem }; - } - return childrenAdded; - } - } - - final class RootElement implements Element { - - private static final String ROOT_NAME = "root element"; - - RootElement() { - } - - public Document getDocument() { - return OutputDocument.this; - } - - public Element getParentElement() { - return null; - } - - public String getName() { - return ROOT_NAME; - } - - public AttributeSet getAttributes() { - return attrs; - } - - public int getStartOffset() { - return 0; - } - - public int getEndOffset() { - return OutputDocument.this.getLength(); - } - - public int getElementIndex(int offset) { - return OutputDocument.this.getElementIndex(offset); - } - - public int getElementCount() { - return OutputDocument.this.docElementsCount; - } - - public Element getElement(int index) { - return OutputDocument.this.docElements[index]; - } - - DocElement getDocElement(int index) { - return OutputDocument.this.docElements[index]; - } - - public boolean isLeaf() { - return false; - } - - } - - private static final class TrivialAttributeSet implements AttributeSet { - - private static final String NAME = "Trivial Attribute Set"; //NOI18N - - public int getAttributeCount() { - return 1; - } - - public boolean isDefined(Object attrName) { - return attrName.equals(NameAttribute); - } - - public boolean isEqual(AttributeSet attr) { - return (attr.getAttributeCount() == 1) - && NAME.equals(attr.getAttribute(NameAttribute)); - } - - public AttributeSet copyAttributes() { - return this; - } - - public Object getAttribute(Object key) { - return NameAttribute.equals(key) ? NAME : null; - } - - public Enumeration getAttributeNames() { - return Collections.enumeration(Collections.singleton(NameAttribute)); - } - - public boolean containsAttribute(Object name, Object value) { - if (name == null) { - throw new IllegalArgumentException(); - } - return NameAttribute.equals(name) && NAME.equals(value); - } - - public boolean containsAttributes(AttributeSet attributes) { - int attrCount = attributes.getAttributeCount(); - return (attrCount == 0) - || (attrCount == 1) - && (NAME.equals(attributes.getAttribute(NameAttribute))); - } - - public AttributeSet getResolveParent() { - return null; - } - - } - -} \ No newline at end of file diff --git a/junit/src/org/netbeans/modules/junit/output/OutputEditorKit.java b/junit/src/org/netbeans/modules/junit/output/OutputEditorKit.java deleted file mode 100644 --- a/junit/src/org/netbeans/modules/junit/output/OutputEditorKit.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, 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-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - * - * Contributor(s): - * - * Portions Copyrighted 2007 Sun Microsystems, Inc. - */ - -package org.netbeans.modules.junit.output; - -import javax.swing.text.DefaultEditorKit; -import javax.swing.text.Document; -import javax.swing.text.Element; -import javax.swing.text.PlainView; -import javax.swing.text.View; -import javax.swing.text.ViewFactory; - -/** - * - * @author Marian Petras - */ -final class OutputEditorKit extends DefaultEditorKit - implements ViewFactory { - - OutputEditorKit() { - super(); - } - - @Override - public ViewFactory getViewFactory() { - return this; - } - - public View create(Element element) { - return (element instanceof OutputDocument.RootElement) - ? new OutputView(element) - : new PlainView(element); - } - - @Override - public Document createDefaultDocument() { - return new OutputDocument(); - } - -} \ No newline at end of file diff --git a/junit/src/org/netbeans/modules/junit/output/OutputView.java b/junit/src/org/netbeans/modules/junit/output/OutputView.java deleted file mode 100644 --- a/junit/src/org/netbeans/modules/junit/output/OutputView.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, 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-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - * - * Contributor(s): - * - * Portions Copyrighted 2007 Sun Microsystems, Inc. - */ - -package org.netbeans.modules.junit.output; - -import java.awt.Color; -import java.awt.EventQueue; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.Shape; -import java.awt.Toolkit; -import java.util.Collections; -import java.util.Map; -import javax.swing.UIManager; -import javax.swing.text.BadLocationException; -import javax.swing.text.Element; -import javax.swing.text.JTextComponent; -import javax.swing.text.PlainView; -import javax.swing.text.Segment; -import javax.swing.text.Utilities; -import org.netbeans.modules.junit.output.OutputDocument.DocElement; - -/** - * - * @author Marian Petras - * @author Tim Boudreau - */ -final class OutputView extends PlainView { - - private final Segment SEGMENT = new Segment(); - private final OutputDocument.RootElement rootElement; - - private int selStart, selEnd; - private static Color selectedErr; - private static Color unselectedErr; - private static Map hintsMap = null; - - private Color selectedFg, unselectedFg; - - /* set antialiasing hints when it's requested */ - private static final boolean antialias - = Boolean.getBoolean("swing.aatext") //NOI18N - || "Aqua".equals(UIManager.getLookAndFeel().getID()); //NOI18N - - static { - selectedErr = UIManager.getColor("nb.output.err.foreground.selected"); //NOI18N - if (selectedErr == null) { - selectedErr = new Color(164, 0, 0); - } - unselectedErr = UIManager.getColor("nb.output.err.foreground"); //NOI18N - if (unselectedErr == null) { - unselectedErr = selectedErr; - } - } - - @SuppressWarnings("unchecked") - static final Map getHints() { - if (hintsMap == null) { - //Thanks to Phil Race for making this possible - hintsMap = (Map) Toolkit.getDefaultToolkit().getDesktopProperty( - "awt.font.desktophints"); //NOI18N - if (hintsMap == null) { - hintsMap = antialias - ? Collections.singletonMap( - RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_ON) - : Collections.emptyMap(); - } - } - return hintsMap; - } - - OutputView(Element element) { - super(element); - rootElement = (OutputDocument.RootElement) element; - } - - @Override - public void paint(Graphics g, Shape a) { - ((Graphics2D) g).addRenderingHints(getHints()); - - final JTextComponent textComp = (JTextComponent) getContainer(); - selStart = textComp.getSelectionStart(); - selEnd = textComp.getSelectionEnd(); - unselectedFg = textComp.isEnabled() - ? textComp.getForeground() - : textComp.getDisabledTextColor(); - selectedFg = textComp.getCaret().isSelectionVisible() - ? textComp.getSelectedTextColor() - : unselectedFg; - super.paint(g, a); - } - - @Override - protected void drawLine(int lineIndex, Graphics g, int x, int y) { - DocElement docElem = rootElement.getDocElement(lineIndex); - try { - drawLine(docElem, g, x, y); - } catch (BadLocationException e) { - throw new IllegalStateException("cannot draw line " + lineIndex); //NOI18N - } - } - - private void drawLine(DocElement elem, Graphics g, int x, int y) throws BadLocationException { - final int p0 = elem.getStartOffset(); - final int p1 = elem.getEndOffset(); - final boolean isError = elem.isError; - - if ((selStart == selEnd) || (selectedFg == unselectedFg)) { - /* no selection or invisible selection */ - x = drawText(g, x, y, p0, p1, isError, false, elem); - } else if ((p0 >= selStart && p0 <= selEnd) - && (p1 >= selStart && p1 <= selEnd)) { - /* whole line selected */ - x = drawText(g, x, y, p0, p1, isError, true, elem); - } else if (selStart >= p0 && selStart <= p1) { - if (selEnd >= p0 && selEnd <= p1) { - x = drawText(g, x, y, p0, selStart, isError, false, elem); - x = drawText(g, x, y, selStart, selEnd, isError, true, elem); - x = drawText(g, x, y, selEnd, p1, isError, false, elem); - } else { - x = drawText(g, x, y, p0, selStart, isError, false, elem); - x = drawText(g, x, y, selStart, p1, isError, true, elem); - } - } else if (selEnd >= p0 && selEnd <= p1) { - x = drawText(g, x, y, p0, selEnd, isError, true, elem); - x = drawText(g, x, y, selEnd, p1, isError, false, elem); - } else { - x = drawText(g, x, y, p0, p1, isError, false, elem); - } - } - - private int drawText(Graphics g, - int x, int y, - int startOffset, int endOffset, - boolean error, - boolean selected, - DocElement docElem) throws BadLocationException { - Segment s = EventQueue.isDispatchThread() ? SEGMENT : new Segment(); - s.array = docElem.getChars(); - s.offset = startOffset - docElem.offset; - s.count = endOffset - startOffset; - - g.setColor(getColor(error, selected)); - - return Utilities.drawTabbedText(s, x, y, g, this, startOffset); - } - - private Color getColor(boolean error, boolean selected) { - return error ? (selected ? selectedErr - : unselectedErr) - : (selected ? selectedFg - : unselectedFg); - } - -} \ No newline at end of file diff --git a/junit/src/org/netbeans/modules/junit/output/ResultDisplayHandler.java b/junit/src/org/netbeans/modules/junit/output/ResultDisplayHandler.java --- a/junit/src/org/netbeans/modules/junit/output/ResultDisplayHandler.java +++ b/junit/src/org/netbeans/modules/junit/output/ResultDisplayHandler.java @@ -41,6 +41,7 @@ package org.netbeans.modules.junit.output; +import java.awt.BorderLayout; import java.awt.Component; import java.awt.EventQueue; import java.lang.reflect.InvocationTargetException; @@ -49,10 +50,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.swing.JComponent; +import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.SwingUtilities; import org.openide.ErrorManager; import org.netbeans.modules.junit.JUnitSettings; +import org.openide.windows.IOEmbedder; +import org.openide.windows.IOProvider; +import org.openide.windows.InputOutput; +import org.openide.windows.OutputWriter; /** * @@ -67,10 +74,11 @@ /** */ private ResultPanelTree treePanel; /** */ - private ResultPanelOutput outputListener; - /** */ private Component displayComp; - + + private JComponent outputComp; + + private InputOutput inOut; /** Creates a new instance of ResultDisplayHandler */ ResultDisplayHandler() { @@ -84,14 +92,24 @@ } return displayComp; } - + + JComponent getOutputComponent() { + if (outputComp == null) { + outputComp = new JPanel(new BorderLayout()); + } + return outputComp; + } + + void createIO(IOEmbedder ioEmbedder) { + inOut = IOProvider.getDefault().getIO("JUnit", null, ioEmbedder); + } + /** */ private Component createDisplayComp() { Component left = new StatisticsPanel(this); - Component right = new ResultPanelOutput(this); JSplitPane splitPane - = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right) { + = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, getOutputComponent()) { @Override public void addNotify() { @@ -147,46 +165,12 @@ return queueLock; } - /** - */ - void setOutputListener(ResultPanelOutput outputPanel) { - synchronized (queueLock) { - this.outputListener = outputPanel; - } - } - + /** */ void displayOutput(final String text, final boolean error) { - - /* Called from the AntLogger's thread */ - - synchronized (queueLock) { - if (outputQueue == null) { - outputQueue = new Object[40]; - outputQueueAvailSpace = outputQueue.length - 1; - outputQueueSize = 0; - } - final int itemSpace = error ? 2 : 1; - if ((outputQueueAvailSpace -= itemSpace) < 0) { - int newCapacity = (outputQueue.length < 640) - ? outputQueue.length * 2 - : (outputQueue.length * 3) / 2; - Object[] oldQueue = outputQueue; - outputQueue = new Object[newCapacity]; - System.arraycopy(oldQueue, 0, outputQueue, 0, outputQueueSize); - - outputQueueAvailSpace += outputQueue.length - oldQueue.length; - } - if (error) { - outputQueue[outputQueueSize++] = Boolean.TRUE; - } - outputQueue[outputQueueSize++] = text; - - if (outputListener != null) { - outputListener.outputAvailable(); - } - } + OutputWriter out = error ? inOut.getErr() : inOut.getOut(); + out.println(text); } /** @@ -269,7 +253,7 @@ if (treePanel == null) { message = msg; return; - } + } } displayInDispatchThread("displayMsg", new Object[] {msg}); //NOI18N } diff --git a/junit/src/org/netbeans/modules/junit/output/ResultPanelOutput.java b/junit/src/org/netbeans/modules/junit/output/ResultPanelOutput.java deleted file mode 100644 --- a/junit/src/org/netbeans/modules/junit/output/ResultPanelOutput.java +++ /dev/null @@ -1,427 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, 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-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.modules.junit.output; - -import java.awt.Color; -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.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."); - } - - 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); - 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 accessibleContext = textPane.getAccessibleContext(); - accessibleContext.setAccessibleName( - NbBundle.getMessage(getClass(), "ACSN_OutputTextPane"));//NOI18N - accessibleContext.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(); - displayHandler.setOutputListener(null); - if (!timerRunning) { - startTimer(); - } - } - - final class OutputDisplayer implements Runnable { - private final Object[] output; - OutputDisplayer(Object[] output) { - this.output = output; - } - public void run() { - if (!EventQueue.isDispatchThread()) { - EventQueue.invokeLater(this); - return; - } - displayOutput(output); - } - } - - /** - * This method is called by a Swing timer (in the dispatch thread). - */ - 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(); - displayHandler.setOutputListener(this); - } - } - } - - /** - */ - 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) { - try { - doc.insertString(doc.getLength(), - text + "\n", //NOI18N - error ? OutputDocument.attrs : null); - } catch (BadLocationException ex) { - ErrorManager.getDefault().notify(ErrorManager.ERROR, ex); - } - } - - // - /* * - * / - 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); - } - } - */ - // - - /** - */ - private void clear() { - assert EventQueue.isDispatchThread(); - - try { - doc.remove(0, doc.getLength()); - } catch (BadLocationException ex) { - ErrorManager.getDefault().notify(ErrorManager.ERROR, ex); - } - } - - // - /* * - * / - 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); - } - */ - // - - // - /* * - * / - private void display(ResultDisplayHandler.DisplayContents display) { - assert EventQueue.isDispatchThread(); - - Report report = display.getReport(); - String msg = display.getMessage(); - if (report != null) { - displayReport(report); - } else { - clear(); - } - } - // - - /* * - * / - // - private void updateDisplay() { - ResultDisplayHandler.DisplayContents display - = displayHandler.getDisplay(); - if (display != null) { - display(display); - } - } - */ - // - - // - /* * - * / - public void stateChanged(ChangeEvent e) { - updateDisplay(); - } - */ - // - - /** - */ - @Override - public boolean requestFocusInWindow() { - return textPane.requestFocusInWindow(); - } - -} diff --git a/junit/src/org/netbeans/modules/junit/output/ResultWindow.java b/junit/src/org/netbeans/modules/junit/output/ResultWindow.java --- a/junit/src/org/netbeans/modules/junit/output/ResultWindow.java +++ b/junit/src/org/netbeans/modules/junit/output/ResultWindow.java @@ -45,9 +45,14 @@ import java.awt.Component; import java.awt.EventQueue; import java.lang.ref.WeakReference; +import javax.swing.Action; +import javax.swing.Icon; +import javax.swing.JComponent; import org.openide.util.HelpCtx; import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; +import org.openide.windows.IOEmbedder; +import org.openide.windows.IOEmbedder.CallBacks; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; @@ -184,5 +189,71 @@ */ private Object readResolve() throws java.io.ObjectStreamException { return ResultWindow.getDefault(); - } + } + + private boolean activated; + private JComponent outputComp; + private JComponent outputTab; + private IOEmbedder ioEmbedder; + + IOEmbedder getIOEmbedder() { + if (ioEmbedder == null) { + ioEmbedder = IOEmbedder.create(new IOEmbedderImpl()); + } + return ioEmbedder; + } + + void setOutputComp(JComponent comp) { + outputComp = comp; + } + + @Override + protected void componentActivated() { + activated = true; + } + + @Override + protected void componentDeactivated() { + activated = false; + } + + private class IOEmbedderImpl implements IOEmbedder.IOEmbedderProvider { + + public void add(JComponent comp, CallBacks cb, Action[] toolbarActions) { + outputTab = comp; + outputComp.add(comp); + } + + public void remove(JComponent comp) { + outputTab = null; + outputComp.remove(comp); + } + + public void select(JComponent comp) { + } + + public JComponent getSelected() { + return outputTab; + } + + public boolean isActivated() { + return activated; + } + + public void open() { + } + + + public void requestActive() { + } + + public void requestVisible() { + } + + public void setIcon(JComponent comp, Icon icon) { + } + + public void setTitle(JComponent comp, String name) { + } + } } diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties --- a/nbbuild/cluster.properties +++ b/nbbuild/cluster.properties @@ -199,6 +199,7 @@ autoupdate.services,\ autoupdate.ui,\ core.execution,\ + core.io.ui,\ core.kit,\ core.multiview,\ core.nativeaccess,\ diff --git a/o.n.core/src/org/netbeans/core/actions/LogViewerSupport.java b/o.n.core/src/org/netbeans/core/actions/LogViewerSupport.java --- a/o.n.core/src/org/netbeans/core/actions/LogViewerSupport.java +++ b/o.n.core/src/org/netbeans/core/actions/LogViewerSupport.java @@ -45,17 +45,11 @@ package org.netbeans.core.actions; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.File; import java.util.*; import java.io.*; import java.util.logging.Level; import java.util.logging.Logger; -import org.openide.filesystems.*; import org.openide.util.*; -import org.openide.windows.IOProvider; -import org.openide.windows.InputOutput; import org.openide.windows.*; /** Connects the output stream of a file to the IDE output window. @@ -110,27 +104,8 @@ final int MAX_LINES = 10000; String line; - ////System.out.println("io close or not"+io.isClosed()); - if (io.isClosed()){//tab is closed by the user - shouldStop =true; - } - else{ - // it is possilbe in the case of only - // 1 tab, that the tab is hidden, not - // closed. In this case we need to - // detect that and close our stream - // anyway to unlock the log file - shouldStop =true; //assume the tab is hidden - TopComponent.Registry rr= TopComponent.getRegistry(); - for (TopComponent tc: rr.getOpened()) { - if (tc.toString().startsWith("org.netbeans.core.output2.OutputWindow")){ - // the tab is not hidden so we should not stopped!!! - shouldStop =false; - break; - } - } - } - + shouldStop = io.isClosed(); + if (!shouldStop) { try { if (lines >= MAX_LINES) { diff --git a/openide.io/apichanges.xml b/openide.io/apichanges.xml --- a/openide.io/apichanges.xml +++ b/openide.io/apichanges.xml @@ -104,6 +104,58 @@ + + +

Multiple IOProvider support, possibility to specify "parent container" + for IO tab, access to "additional features" of InputOutput + + + + + +

Added static + IOProvider.get(String name) to get specific implementation of IOProvider. Added + IOProvider.getName() + which should be overriden by subclasses to provide its name (ID). +

+

IOEmbedder + was introduced as an accessor to "parent container" for IO components (tab). + + IOProvider.getIO(String name, Action[] additionalActions, IOEmbedder ioEmbedder) + can be used to specify alternative embedder. +

+

+ Some "feature classes" were introduced to provide API to additional features + provided by different implementations IOProvider: + + IOColorLines, + IOColorPrint, + IOColors, + IODocking, + IOExecution, + IOFont, + IOFontSize, + IOHistoryLength, + IOPosition, + IOTab. + +

+
+ + + + + + + + + + + + + + + No need to require IOProvider token anymore diff --git a/openide.io/manifest.mf b/openide.io/manifest.mf --- a/openide.io/manifest.mf +++ b/openide.io/manifest.mf @@ -1,7 +1,8 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.io -OpenIDE-Module-Specification-Version: 1.14 +OpenIDE-Module-Specification-Version: 1.15 OpenIDE-Module-Localizing-Bundle: org/openide/io/Bundle.properties OpenIDE-Module-Recommends: org.openide.windows.IOProvider +OpenIDE-Module-Needs: org.openide.windows.IOEmbedder.IOEmbedderProvider AutoUpdate-Essential-Module: true diff --git a/openide.io/src/org/openide/windows/IOColorLines.java b/openide.io/src/org/openide/windows/IOColorLines.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOColorLines.java @@ -0,0 +1,112 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import java.awt.Color; +import org.openide.util.Lookup; + +/** + * Line printing with custom color + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IOColorLines { + + private static IOColorLines find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IOColorLines.class); + } + return null; + } + + /** + * Prints line with selected color + * @param io IO to print to + * @param text a string to print to the tab + * Makes the UI respond appropriately, eg. stop the automatic scrolling + * or highlight the hyperlink. + * @param color a color for the line of text + */ + public static void println(InputOutput io, CharSequence text, Color color) { + IOColorLines iocl = find(io); + if (iocl != null) { + iocl.println(text, null, false, color); + } + } + + /** + * Prints line with selected color + * @param io IO to print to + * @param text a string to print to the tab + * @param listener a listener that will receive events about this line + * @param important important mark the line as important. + * Makes the UI respond appropriately, eg. stop the automatic scrolling + * or highlight the hyperlink. + * @param color a color for the line of text + */ + public static void println(InputOutput io, CharSequence text, OutputListener listener, boolean important, Color color) { + IOColorLines iocl = find(io); + if (iocl != null) { + iocl.println(text, listener, important, color); + } + } + + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Prints line with selected color + * @param text a string to print to the tab + * @param listener a listener that will receive events about this line + * @param important important mark the line as important. + * Makes the UI respond appropriately, eg. stop the automatic scrolling + * or highlight the hyperlink. + * @param color a color for the line of text + */ + abstract protected void println(CharSequence text, OutputListener listener, boolean important, Color color); +} diff --git a/openide.io/src/org/openide/windows/IOColorPrint.java b/openide.io/src/org/openide/windows/IOColorPrint.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOColorPrint.java @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import java.awt.Color; +import org.openide.util.Lookup; + +/** + * Color printing + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IOColorPrint { + + private static IOColorPrint find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IOColorPrint.class); + } + return null; + } + + + /** + * Prints text in selected color + * @param io IO to print to + * @param text text to print + * @param color text color + */ + public static void print(InputOutput io, CharSequence text, Color color) { + IOColorPrint iocl = find(io); + if (iocl != null) { + iocl.print(text,color); + } + } + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Prints text in selected color + * @param text text to print + * @param color text color + */ + abstract protected void print(CharSequence text, Color color); +} diff --git a/openide.io/src/org/openide/windows/IOColors.java b/openide.io/src/org/openide/windows/IOColors.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOColors.java @@ -0,0 +1,170 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import java.awt.Color; +import org.openide.util.Lookup; + +/** + * Settings of current colors for out/err/in + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IOColors { + + private static IOColors find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IOColors.class); + } + return null; + } + + /** + * Gets current color for output + * @param io InputOutput to operate on + * @return current color for output or null if not supported + */ + public static Color getOutColor(InputOutput io) { + IOColors ioc = find(io); + return ioc != null ? ioc.getOutColor() : null; + } + + /** + * Sets specified color for output + * @param io InputOutput to operate on + * @param color new color for output + */ + public static void setOutColor(InputOutput io, Color color) { + IOColors ioc = find(io); + if (ioc != null) { + ioc.setOutColor(color); + } + } + + /** + * Gets current color for output in error mode + * @param io InputOutput to operate on + * @return current color for error output or null if not supported + */ + public static Color getErrColor(InputOutput io) { + IOColors ioc = find(io); + return ioc != null ? ioc.getOutColor() : null; + } + + /** + * Sets specified color for output in error mode + * @param io InputOutput to operate on + * @param color new color for output in error mode + */ + public static void setErrColor(InputOutput io, Color color) { + IOColors ioc = find(io); + if (ioc != null) { + ioc.setOutColor(color); + } + } + + /** + * Gets current color for input + * @param io InputOutput to operate on + * @return current color for error input or null if not supported + */ + public static Color getInColor(InputOutput io) { + IOColors ioc = find(io); + return ioc != null ? ioc.getOutColor() : null; + } + + /** + * Sets specified color for input + * @param io InputOutput to operate on + * @param color new color for input + */ + public static void setInColor(InputOutput io, Color color) { + IOColors ioc = find(io); + if (ioc != null) { + ioc.setOutColor(color); + } + } + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Gets current color for output + * @return current color for output + */ + abstract protected Color getOutColor(); + + /** + * Sets specified color for output + * @param color new color for output + */ + abstract protected void setOutColor(Color color); + + /** + * Gets current color for output in error mode + * @return current color for error output + */ + abstract protected Color getErrColor(); + + /** + * Sets specified color for output in error mode + * @param color new color for output in error mode + */ + abstract protected void setErrColor(Color color); + + /** + * Gets current color for input + * @return current color for error input + */ + abstract protected Color getInColor(); + + /** + * Sets specified color for input + * @param color new color for input + */ + abstract protected void setInColor(Color color); +} diff --git a/openide.io/src/org/openide/windows/IODocking.java b/openide.io/src/org/openide/windows/IODocking.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IODocking.java @@ -0,0 +1,99 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import org.openide.util.Lookup; + +/** + * Docking undocking IO component from parent container + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IODocking { + + private static IODocking find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IODocking.class); + } + return null; + } + + /** + * Docks IO component for specified IO + * @param io IO to operate on + */ + public static void dock(InputOutput io) { + IODocking iod = find(io); + if (iod != null) { + iod.dock(); + } + } + + /** + * Undocks IO component for specified IO + * @param io IO to operate on + */ + public static void undock(InputOutput io) { + IODocking iod = find(io); + if (iod != null) { + iod.undock(); + } + } + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Docks IO component for specified IO + */ + abstract protected void dock(); + + /** + * Undocks IO component for specified IO + */ + abstract protected void undock(); +} diff --git a/openide.io/src/org/openide/windows/IOEmbedder.java b/openide.io/src/org/openide/windows/IOEmbedder.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOEmbedder.java @@ -0,0 +1,288 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.Action; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import org.openide.util.Lookup; +import org.openide.util.Parameters; + +/** + * IOEmbedder is accessor class to parent container of IO tabs for IOProvider implementations. + * @since 1.15 + * @author Tomas Holy + */ +public final class IOEmbedder { + + /** + * Factory method for IOEmbedder instances + * @param provider IOEmbedderProvider implemantation + * @return IOEmbedder instance + */ + public static IOEmbedder create(IOEmbedderProvider provider) { + Parameters.notNull("provider", provider); + return new IOEmbedder(provider); + } + + private static IOEmbedder defaultIOEmbedder; + public static IOEmbedder getDefault() { + if (defaultIOEmbedder == null) { + IOEmbedderProvider provider = Lookup.getDefault().lookup(IOEmbedderProvider.class); + defaultIOEmbedder = create(provider); + } + return defaultIOEmbedder; + } + + /** private constructor */ + private IOEmbedder(IOEmbedderProvider provider) { + this.provider = provider; + } + + private IOEmbedderProvider provider; + + /** + * Opens parent container + */ + public void open() { + log(provider, "open()"); + provider.open(); + } + + /** + * Activates parent container + */ + public void requestActive() { + log(provider, "requestActive()"); + provider.requestActive(); + } + + /** + * Selects parent container (if it is opened), but does not activate it + */ + public void requestVisible() { + log(provider, "requestVisible()"); + provider.requestVisible(); + } + + /** + * Checks if parent container is activated + * @return true if parent container is activated + */ + public boolean isActivated() { + log(provider, "isActivated()"); + return provider.isActivated(); + } + + /** + * Adds component to parent container + * @param comp component to be added + * @param cb callbacks for added component or null if not interested in notifications + * @param toolbarActions toolbar actions for component or null if no actions provided + * @see CallBacks + */ + public void add(JComponent comp, CallBacks cb, Action[] toolbarActions) { + log(provider, "requestVisible()", comp, cb, toolbarActions); + provider.add(comp, cb, toolbarActions); + } + + /** + * Removes component from parent container + * @param comp component that should be removed + */ + public void remove(JComponent comp) { + log(provider, "remove()", comp); + provider.remove(comp); + } + + /** + * Selects component in parent container + * @param comp component that should be selected + */ + public void select(JComponent comp) { + log(provider, "select()", comp); + provider.select(comp); + } + + /** + * Gets currently selected component in parent container + * @return selected tab + */ + public JComponent getSelected() { + log(provider, "getSelected()"); + return provider.getSelected(); + } + + /** + * Sets title for provided component + * @param comp component for which title should be set + * @param name component title + */ + public void setTitle(JComponent comp, String name) { + log(provider, "setTitle()", comp, name); + provider.setTitle(comp, name); + } + + /** + * Sets tool tip text for provided component + * @param comp component for which title should be set + * @param text component title + */ + public void setToolTipText(JComponent comp, String text) { + log(provider, "setToolTipText()", comp, text); + provider.setToolTipText(comp, text); + } + + /** + * Sets icon for provided component + * @param comp component for which icon should be set + * @param icon component icon + */ + public void setIcon(JComponent comp, Icon icon) { + log(provider, "setIcon()", comp, icon); + provider.setIcon(comp, icon); + } + + /** + * SPI for providers of parent container for IO components (tabs) + */ + public interface IOEmbedderProvider { + + /** + * Parent container for should be opened + */ + void open(); + + /** + * Parent container for should be activated + */ + void requestActive(); + + /** + * Parent container for should be selected (if opened) + */ + void requestVisible(); + + /** + * Checks whether parent container is activated + * @return true if activated + */ + boolean isActivated(); + + /** + * Provided component should be added to parent container + * @param comp component to add + * @param cb callbacks for component notifications or null if component does not need notifications + * @param toolbarActions toolbar actions or null if no actions for component + */ + void add(JComponent comp, CallBacks cb, Action[] toolbarActions); + + /** + * Provided component should be removed from parent container + * @param comp component to remove + */ + void remove(JComponent comp); + + /** + * Provided component should be selected + * @param comp component to select + */ + void select(JComponent comp); + + /** + * Currently selected io component should be returned + * @return currently selected io component or null + */ + JComponent getSelected(); + + /** + * Should set title for provided component (e.g. tab title) + * @param comp component for which title should be set + * @param name component title + */ + void setTitle(JComponent comp, String name); + + /** + * Should set title for provided component (e.g. tab title) + * @param comp component for which title should be set + * @param text component tool tip text + */ + void setToolTipText(JComponent comp, String text); + + /** + * Should set icon for provided component + * @param comp component for which icon should set + * @param icon component icon + */ + void setIcon(JComponent comp, Icon icon); + + } + + public interface CallBacks { + + /** tab closed */ + void closed(); + + /** tab selected */ + void selected(); + + /** parent container activated and tab is selected */ + void activated(); + + /** parent container deactivated and tab is selected */ + void deactivated(); + } + + private static final Logger LOGGER = Logger.getLogger(IOEmbedder.class.getName()); + private static Level logLevel = Level.FINER; + + private synchronized void log(Object provider, String msg, Object... items) { + if (LOGGER.isLoggable(logLevel)) { + LOGGER.log(logLevel, provider.getClass() + ": " + msg); + for (Object o : items) { + LOGGER.log(logLevel, " " + o); + } + } + assert SwingUtilities.isEventDispatchThread() : "Should be called from AWT thread."; + } +} diff --git a/openide.io/src/org/openide/windows/IOExecution.java b/openide.io/src/org/openide/windows/IOExecution.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOExecution.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import org.openide.util.Lookup; + +/** + * Command execution + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IOExecution { + private static IOExecution find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IOExecution.class); + } + return null; + } + + /** + * Executes command in provided IO + * @param io IO to operate on + * @param command command to execute + */ + public static void executeCommand(InputOutput io, String command) { + IOExecution ioe = find(io); + if (ioe != null) { + ioe.executeCommand(command); + } + } + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Executes command + * @param command command to execute + */ + abstract protected void executeCommand(String command); +} diff --git a/openide.io/src/org/openide/windows/IOFont.java b/openide.io/src/org/openide/windows/IOFont.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOFont.java @@ -0,0 +1,102 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import java.awt.Font; +import org.openide.util.Lookup; + +/** + * Font settings in IO component + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IOFont { + + private static IOFont find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IOFont.class); + } + return null; + } + + /** + * Gets currently selected font + * @param io IO to operate on + * @return currently set font or null if not supported + */ + public static Font getFont(InputOutput io) { + IOFont iof = find(io); + return iof != null ? iof.getFont() : null; + } + + /** + * Sets selected font + * @param io IO to operate on + * @param font font to set + */ + public static void setFont(InputOutput io, Font font) { + IOFont iof = find(io); + if (iof != null) { + iof.setFont(font); + } + } + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Gets currently selected font + * @return currently set font + */ + abstract protected Font getFont(); + + /** + * Sets selected font + * @param font font to set + */ + abstract protected void setFont(Font font); +} diff --git a/openide.io/src/org/openide/windows/IOFontSize.java b/openide.io/src/org/openide/windows/IOFontSize.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOFontSize.java @@ -0,0 +1,101 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import org.openide.util.Lookup; + +/** + * Font size settings + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IOFontSize { + + private static IOFontSize find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IOFontSize.class); + } + return null; + } + + /** + * Get current font size + * @param io IO to operate on + * @return current font size or -1 if not supported + */ + public static int getSize(InputOutput io) { + IOFontSize iofs = find(io); + return iofs != null ? iofs.getSize() : -1; + } + + /** + * Sets font size + * @param io IO to operate on + * @param size new font size + */ + public static void setSize(InputOutput io, int size) { + IOFontSize iofs = find(io); + if (iofs != null) { + iofs.setSize(size); + } + } + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Get current font size + * @return current font size + */ + abstract protected int getSize(); + + /** + * Sets font size + * @param size new font size + */ + abstract protected void setSize(int size); +} diff --git a/openide.io/src/org/openide/windows/IOHistoryLength.java b/openide.io/src/org/openide/windows/IOHistoryLength.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOHistoryLength.java @@ -0,0 +1,101 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import org.openide.util.Lookup; + +/** + * History length settings + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IOHistoryLength { + + private static IOHistoryLength find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IOHistoryLength.class); + } + return null; + } + + /** + * Gets current history lenght + * @param io IO to operate on + * @return history length in lines or -1 if not supported + */ + public static int getLength(InputOutput io) { + IOHistoryLength ioh = find(io); + return ioh != null ? ioh.getLength() : -1; + } + + /** + * Sets history length limit + * @param io IO to operate on + * @param lineCount history lenght limit in number of lines + */ + public static void setLength(InputOutput io, int lineCount) { + IOHistoryLength ioh = find(io); + if (ioh != null) { + ioh.setLength(lineCount); + } + } + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Gets current history lenght + * @return history length in lines + */ + abstract protected int getLength(); + + /** + * Sets history length limit + * @param lineCount history lenght limit in number of lines + */ + abstract protected void setLength(int lineCount); +} diff --git a/openide.io/src/org/openide/windows/IOPosition.java b/openide.io/src/org/openide/windows/IOPosition.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOPosition.java @@ -0,0 +1,135 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import org.openide.util.Lookup; + +/** + * Navigation in IO component + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IOPosition { + + private static IOPosition find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IOPosition.class); + } + return null; + } + + /** + * Gets current position (in number of chars) in IO + * @param io IO to operate on + * @return current position or -1 if not supported + */ + public static long currentPos(InputOutput io) { + IOPosition iop = find(io); + return iop != null ? iop.currentPos() : -1; + } + + /** + * Gets current line number + * @param io IO to operate on + * @return current line number or -1 if not supported + */ + public static long currentLine(InputOutput io) { + IOPosition iop = find(io); + return iop != null ? iop.currentLine() : -1; + } + + /** + * Scrolls to specified position (in chars) + * @param io IO to operate on + * @param pos position to scroll to (in chars) + * @return real position it was scrolled to (e.g. nearest possible position) or -1 if not supported + */ + public static long scrollToPos(InputOutput io, long pos) { + IOPosition iop = find(io); + return iop != null ? iop.scrollToPos(pos) : -1; + } + + /** + * Scrolls to specified line + * @param io IO to operate on + * @param line line to scroll to + * @return real line it was scrolled to (e.g. nearest possible line) or -1 if not supported + */ + public static long scrollToLine(InputOutput io, long line) { + IOPosition iop = find(io); + return iop != null ? iop.scrollToLine(line) : -1; + } + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Gets current position (in number of chars) in IO + * @return current position + */ + abstract protected long currentPos(); + + /** + * Gets current line number + * @return current line number + */ + abstract protected long currentLine(); + + /** + * Scrolls to specified position (in chars) + * @param pos position to scroll to (in chars) + * @return real position it was scrolled to (e.g. nearest possible position) + */ + abstract protected long scrollToPos(long pos); + + /** + * Scrolls to specified line + * @param line line to scroll to + * @return real line it was scrolled to (e.g. nearest possible line) + */ + abstract protected long scrollToLine(long line); +} diff --git a/openide.io/src/org/openide/windows/IOProvider.java b/openide.io/src/org/openide/windows/IOProvider.java --- a/openide.io/src/org/openide/windows/IOProvider.java +++ b/openide.io/src/org/openide/windows/IOProvider.java @@ -47,6 +47,7 @@ import java.io.PrintStream; import java.io.Reader; import java.io.StringWriter; +import java.util.Collection; import javax.swing.Action; import org.openide.util.Lookup; @@ -77,10 +78,26 @@ return iop; } + /** + * Gets IOProvider of selected name or delegates to getDefault() if none was found + * @param name ID of provider + * @return the instance corresponding to provided name or default instance if not found + * @since 1.15 + */ + public static IOProvider get(String name) { + Collection res = Lookup.getDefault().lookupAll(IOProvider.class); + for (IOProvider iop : res) { + if (iop.getName().equals(name)) { + return iop; + } + } + return getDefault(); + } + /** Subclass constructor. */ protected IOProvider() {} - /** + /** * Get a named instance of InputOutput, which represents an output tab in * the output window. Streams for reading/writing can be accessed via * getters on the returned instance. @@ -113,7 +130,34 @@ public InputOutput getIO(String name, Action[] additionalActions) { return getIO(name, true); } - + + /** + * @param name A localized display name for the tab + * @param additionalActions array of actions that are added to the toolbar, Can be empty array, but not null. + * The number of actions should not exceed 5 and each should have the Action.SMALL_ICON property defined. + * @param ioEmbedder parent container accessor + * @return an InputOutput instance for accessing the new tab + * @see InputOutput + * @since 1.15 + *
Note: The method is non-abstract for backward compatibility reasons only. If you are + * extending IOProvider and implementing its abstract classes, you are encouraged to override + * this method as well. The default implementation falls back to the getIO(name, additionalActions) method, ignoring the ioEmbedder passed. + */ + public InputOutput getIO(String name, Action[] additionalActions, IOEmbedder ioEmbedder) { + return getIO(name, additionalActions); + } + + /** + * Gets name (ID) of provider + * @return name of provider + * @since 1.15 + *
Note: The method is non-abstract for backward compatibility reasons only. If you are + * extending IOProvider you should override this method. The default implementation returns "" + */ + public String getName() { + return ""; + } + /** Support writing to the Output Window on the main tab or a similar output device. * @return a writer for the standard NetBeans output area */ diff --git a/openide.io/src/org/openide/windows/IOTab.java b/openide.io/src/org/openide/windows/IOTab.java new file mode 100644 --- /dev/null +++ b/openide.io/src/org/openide/windows/IOTab.java @@ -0,0 +1,136 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, 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-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.windows; + +import javax.swing.Icon; +import org.openide.util.Lookup; + +/** + * Settings of tool tip/icon for IO component (tab) + * @since 1.15 + * @author Tomas Holy + */ +public abstract class IOTab { + private static IOTab find(InputOutput io) { + if (io instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) io; + return p.getLookup().lookup(IOTab.class); + } + return null; + } + + /** + * Gets current tab icon for specified IO + * @param io IO to operate on + * @return current tab icon or null if not supported + */ + public static Icon getIcon(InputOutput io) { + IOTab iot = find(io); + return iot != null ? iot.getIcon() : null; + } + + /** + * Sets icon to tab corresponding to specified IO + * @param io IO to operate on + * @param icon tab icon + */ + public static void setIcon(InputOutput io, Icon icon) { + IOTab iot = find(io); + if (iot != null) { + iot.setIcon(icon); + } + } + + /** + * Gets current tool tip text for specified IO + * @param io IO to operate on + * @return current tool tip text or null if not supported + */ + public static String getToolTipText(InputOutput io) { + IOTab iot = find(io); + return iot != null ? iot.getToolTipText() : null; + } + + /** + * Sets tool tip text to tab corresponding to specified IO + * @param io IO to operate on + * @param text new tool tip text + */ + public static void setToolTipText(InputOutput io, String text) { + IOTab iot = find(io); + if (iot != null) { + iot.setToolTipText(text); + } + } + + /** + * Checks whether this feature is supported for provided IO + * @param io IO to check on + * @return true if supported + */ + public static boolean isSupported(InputOutput io) { + return find(io) != null; + } + + /** + * Gets current tab icon + * @return current tab icon + */ + abstract protected Icon getIcon(); + + + /** + * Sets icon to tab + * @param icon tab icon + */ + abstract protected void setIcon(Icon icon); + + /** + * Gets current tool tip text + * @return current tool tip text + */ + abstract protected String getToolTipText(); + + /** + * Sets tool tip text to tab + * @param text new tool tip text + */ + abstract protected void setToolTipText(String text); +}