Index: apichanges.xml =================================================================== RCS file: /shared/data/ccvs/repository/openide/text/apichanges.xml,v --- apichanges.xml 8 Feb 2007 14:34:01 -0000 1.17 +++ apichanges.xml 2 Mar 2007 16:31:20 -0000 @@ -14,7 +14,7 @@ "Portions Copyrighted [year] [name of copyright owner]" The Original Software is NetBeans. The Initial Developer of the Original -Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun +Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. --> @@ -23,6 +23,26 @@ Text API + + + Adding Line.SHOW_REUSE and Line.SHOW_REUSE_NEW constants for Line.show method + + + + + +

The Line.show() method accepts show mode constant, + that influences the way the Line is displayed on the request. + These additional constants provide new modes for opening the line + in a shared editor window that can be replaced by subsequent calls of + Line.show(SHOW_REUSE) on Lines from different + Document. This is useful for quick source browsing without + cluttering the UI with too many opened editors. +

+
+ + +
CloneableEditorSupportRedirector Index: manifest.mf =================================================================== RCS file: /shared/data/ccvs/repository/openide/text/manifest.mf,v --- manifest.mf 8 Feb 2007 14:34:01 -0000 1.15 +++ manifest.mf 2 Mar 2007 16:31:20 -0000 @@ -1,5 +1,5 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.text -OpenIDE-Module-Specification-Version: 6.13 +OpenIDE-Module-Specification-Version: 6.14 OpenIDE-Module-Localizing-Bundle: org/openide/text/Bundle.properties Index: src/org/openide/text/Line.java =================================================================== RCS file: /shared/data/ccvs/repository/openide/text/src/org/openide/text/Line.java,v --- src/org/openide/text/Line.java 13 Dec 2006 19:26:47 -0000 1.4 +++ src/org/openide/text/Line.java 2 Mar 2007 16:31:21 -0000 @@ -13,7 +13,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.text; @@ -69,6 +69,22 @@ */ public final static int SHOW_TOFRONT = 3; + /** Takes the focus in case the editor is already opened and shows the line. + * Replaces (closes) the last editor opened using SHOW_REUSE in case + * the user haven't interacted with it much (e.g. haven't modified it). + * Opens a new editor in case there is no such reusable editor + * and marks it for editor reusal. + * @see #show(int) show + */ + public final static int SHOW_REUSE = 4; + + /** Focuses or opens given editor, marking it as reusable editor if it + * was not opened before. Similar to {@link #SHOW_REUSE) but ignores + * currently reusable editor. + * @see #show(int) show + */ + public final static int SHOW_REUSE_NEW = 5; + /** Instance of null implementation of Line.Part */ static final private Line.Part nullPart = new Line.NullPart(); @@ -151,7 +167,8 @@ public abstract void show(int kind, int column); /** Shows the line (at the first column). - * @param kind one of {@link #SHOW_TRY_SHOW}, {@link #SHOW_SHOW}, or {@link #SHOW_GOTO} + * @param kind one of {@link #SHOW_TRY_SHOW}, {@link #SHOW_SHOW}, {@link #SHOW_GOTO}, + * {@link #SHOW_REUSE} or {@link #SHOW_REUSE_NEW} * @see #show(int, int) */ public void show(int kind) { Index: src/org/openide/text/EditorSupportLineSet.java =================================================================== RCS file: /shared/data/ccvs/repository/openide/text/src/org/openide/text/EditorSupportLineSet.java,v --- src/org/openide/text/EditorSupportLineSet.java 23 Nov 2006 05:26:45 -0000 1.3 +++ src/org/openide/text/EditorSupportLineSet.java 2 Mar 2007 16:31:21 -0000 @@ -13,7 +13,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.text; @@ -81,14 +81,15 @@ return; } - CloneableEditorSupport.Pane editor = support.openAt(pos, column); - - if (kind == SHOW_GOTO) { - editor.getComponent().requestActive(); - } else if (kind == SHOW_TOFRONT) { - editor.getComponent().toFront(); - editor.getComponent().requestActive(); + CloneableEditorSupport.Pane editor; + + if (kind == SHOW_REUSE || kind == SHOW_REUSE_NEW) { + editor = support.openReuse(pos, column, kind); + } else { + editor = support.openAt(pos, column); + if (kind == SHOW_TOFRONT) editor.getComponent().toFront(); } + editor.getComponent().requestActive(); } /** This method will be used for annotation of part of the text on the line.*/ Index: src/org/openide/text/CloneableEditorSupport.java =================================================================== RCS file: /shared/data/ccvs/repository/openide/text/src/org/openide/text/CloneableEditorSupport.java,v --- src/org/openide/text/CloneableEditorSupport.java 8 Feb 2007 14:34:02 -0000 1.29 +++ src/org/openide/text/CloneableEditorSupport.java 2 Mar 2007 16:31:22 -0000 @@ -13,7 +13,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.text; @@ -1657,6 +1657,8 @@ return false; } + // source modified, remove it from tab-reusing slot + lastReusable.clear(); updateTitles(); return true; @@ -2023,11 +2025,19 @@ } } + private static Reference lastReusable = new WeakReference(null); + + // temporal - should be replaced by better impl in winsys + private static void replaceTc(TopComponent orig, TopComponent open) { + orig.close(); + open.open(); + } + // #18981. There could happen a thing also another class type // of CloneableTopCoponent then CloneableEditor could be in allEditors. /** Opens a CloneableEditor component. */ - private Pane openPane() { + private Pane openPane(boolean reuse) { Pane ce = null; boolean displayMsgOpened = false; @@ -2054,8 +2064,19 @@ } // #36601 - open moved outside getLock() synchronization - ce.getComponent().open(); - + CloneableTopComponent ctc = ce.getComponent(); + if (reuse && displayMsgOpened) { + CloneableTopComponent last = lastReusable.get(); + if (last != null) { + replaceTc(last, ctc); + } else { + ctc.open(); + } + lastReusable = new WeakReference(ctc); + } else { + ctc.open(); + } + if (displayMsgOpened) { String msg = messageOpened(); @@ -2109,19 +2130,36 @@ return null; } } - + + final Pane openReuse(final PositionRef pos, final int column, int mode) { + if (mode == Line.SHOW_REUSE_NEW) lastReusable.clear(); + return openAtImpl(pos, column, true); + } + /** Forcibly create one editor component. Then set the caret * to the given position. * @param pos where to place the caret + * @param column where to place the caret * @return always non-null editor * @since 5.2 */ protected final Pane openAt(final PositionRef pos, final int column) { + return openAtImpl(pos, column, false); + } + /** Forcibly create one editor component. Then set the caret + * to the given position. + * @param pos where to place the caret + * @param column where to place the caret + * @param reuse if true, the infrastructure tries to reuse other, already opened editor + * for the purpose of opening this file/line. + * @return always non-null editor + */ + private final Pane openAtImpl(final PositionRef pos, final int column, boolean reuse) { CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this); if (redirect != null) { - return redirect.openAt(pos, column); + return redirect.openAtImpl(pos, column, reuse); } - final Pane e = openPane(); + final Pane e = openPane(reuse); final Task t = prepareDocument(); e.ensureVisible(); class Selector implements TaskListener, Runnable { Index: test/unit/src/org/openide/text/ReusableEditorTest.java =================================================================== RCS file: test/unit/src/org/openide/text/ReusableEditorTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/text/ReusableEditorTest.java 2 Mar 2007 16:31:22 -0000 @@ -0,0 +1,318 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.text; + + +import java.beans.PropertyChangeListener; +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JEditorPane; +import junit.framework.*; +import org.netbeans.junit.*; +import org.openide.util.Lookup; +import org.openide.util.Mutex; +import org.openide.util.lookup.*; + + +/** Testing the behavior of editor reusal framework. + * The behavior was discussed thoroughly at issue 94607. + * + * @author Petr Nejedly + */ +public class ReusableEditorTest extends NbTestCase { + CES c1, c2, c3; + + /** + * Test ctor + * @param testName + */ + public ReusableEditorTest(java.lang.String testName) { + super(testName); + } + + + /** + * Prepares few editors at the test dispoition. + */ + protected void setUp () { + c1 = createSupport("c1"); + c2 = createSupport("c2"); + c3 = createSupport("c3"); + } + + /** + * Closes any precreated editors left open. + */ + @Override + protected void tearDown() { + forceClose(c1); + forceClose(c2); + forceClose(c3); + } + + /** + * Test that verifies SHOW_REUSE closes original tab (keeps only one) + * Scenario: + * 1. Open first file with SHOW_REUSE + * 2. Open second file with SHOW_REUSE + * 3. Verify first is closed + * 4. Open first file with SHOW_REUSE + * 5. Verify second is closed + */ + public void testReuse() { + openAndCheck(c1, Line.SHOW_REUSE); // 1 + openAndCheck(c2, Line.SHOW_REUSE); // 2 + assertClosed(c1); // 3 + openAndCheck(c1, Line.SHOW_REUSE); // 4 + assertClosed(c2); // 5 + } + + /** Test that verifies SHOW_REUSE doesn't reuse modified, even saved tab + * 1. Open first file with SHOW_REUSE + * 2. Modify it + * 3. Open second file with SHOW_REUSE + * 4. Verify first still open + * 5. Modify second file + * 6. Unmodify second file + * 7. Open third file with SHOW_REUSE + * 8. Verify second still open + */ + public void testKeepTouched() { + openAndCheck(c1, Line.SHOW_REUSE); // 1 + c1.notifyModified(); // 2 + openAndCheck(c2, Line.SHOW_REUSE); // 3 + assertOpened(c1); // 4 + c2.notifyModified(); // 5 + c2.notifyUnmodified(); // 6 + openAndCheck(c3, Line.SHOW_REUSE); // 7 + assertOpened(c2); // 8 + assertOpened(c1); + } + + /** Test that verifies SHOW_REUSE don't consider non-reusable tabs. + * There are three things tested: + * A) Don't replace ordinary tabs + * B) Don't mark ordinary tabs as reusable if switched to + * C) Keep reusable tab mark even through (B) + * + * Scenario: + * 1. Open first file using SHOW_GOTO + * 2. Open second file using SHOW_REUSE + * 3. Verify first still opened (A) + * 4. open first using SHOW_REUSE + * 5. verify second still opened + * 6. open third file using SHOW_REUSE + * 7. verify first still opened (B) + * 8. verify second closed (C) + */ + public void testLeaveNonreusable() { + openAndCheck(c1, Line.SHOW_GOTO); // 1 + openAndCheck(c2, Line.SHOW_REUSE); // 2 + assertOpened(c1); // 3 + + openAndCheck(c1, Line.SHOW_REUSE); // 4 + assertOpened(c2); // 5 + openAndCheck(c3, Line.SHOW_REUSE); // 6 + assertOpened(c1); // 7 + + assertClosed(c2); // 8 + } + + /** Test that verifies SHOW_REUSE_NEW don't close existing reusable tab, + * but can be reused itself + * + * Scenario: + * 1. Open first file using SHOW_REUSE + * 2. Open second file using SHOW_REUSE_NEW + * 3. Verify first still opened + * 4. Open third using SHOW_REUSE + * 5. verify second closed + */ + public void testReuseNewKeepsOld() { + openAndCheck(c1, Line.SHOW_REUSE); // 1 + openAndCheck(c2, Line.SHOW_REUSE_NEW); // 2 + assertOpened(c1); // 3 + openAndCheck(c3, Line.SHOW_REUSE); // 4 + assertClosed(c2); // 5 + } + + /** + * Test that specifies behaviour of SHOW_REUSE_NEW in case currently + * reusable tab is not the selected one. + * + * Scenario: + * 1. Open first file using SHOW_REUSE + * 2. Open second file using SHOW_GOTO + * 3. Open third file using SHOW_REUSE_NEW + * 4. Verify first still open. + */ + public void testReuseNewKeepsOldEvenWhenNotFocused() { + openAndCheck(c1, Line.SHOW_REUSE); // 1 + openAndCheck(c2, Line.SHOW_GOTO); // 2 + openAndCheck(c3, Line.SHOW_REUSE_NEW); // 3 + assertOpened(c1); // 4 + } + + private CES createSupport(String txt) { + Env env = new Env(); + env.content = txt; + CES c = new CES(env, Lookups.singleton(txt)); + env.support = c; + return c; + } + + private void openAndCheck(final CES ces, final int mode) { + Mutex.EVENT.readAccess(new Mutex.Action() { + public Void run() { + ces.getLineSet().getCurrent(0).show(mode); + return null; + } + + }); + assertOpened(ces); + } + + private void forceClose(CES ces) { + if (ces.isModified()) ces.notifyUnmodified(); + ces.close(); + } + + private void assertClosed(CES ces) { + assertEquals(0, getOpenedCount(ces)); + } + + private void assertOpened(CES ces) { + assertEquals(1, getOpenedCount(ces)); + } + + private int getOpenedCount(final CES ces) { + return Mutex.EVENT.readAccess(new Mutex.Action() { + public Integer run() { + JEditorPane[] panes = ces.getOpenedPanes(); + return panes == null ? 0 : panes.length; + } + }); + } + + + + // + // Implementation of the CloneableEditorSupport.Env + // + private class Env implements CloneableEditorSupport.Env { + // Env variables + private String content = ""; + private boolean valid = true; + private boolean modified = false; + private java.util.Date date = new java.util.Date (); + private List propL = new ArrayList(); + private java.beans.VetoableChangeListener vetoL; + /** the support to work with */ + CloneableEditorSupport support; + + public synchronized void addPropertyChangeListener(PropertyChangeListener l) { + propL.add (l); + } + public synchronized void removePropertyChangeListener(PropertyChangeListener l) { + propL.remove (l); + } + + public synchronized void addVetoableChangeListener(java.beans.VetoableChangeListener l) { + assertNull ("This is the first veto listener", vetoL); + vetoL = l; + } + public void removeVetoableChangeListener(java.beans.VetoableChangeListener l) { + assertEquals ("Removing the right veto one", vetoL, l); + vetoL = null; + } + + public org.openide.windows.CloneableOpenSupport findCloneableOpenSupport() { + return support; + } + + public String getMimeType() { + return "text/plain"; + } + + public java.util.Date getTime() { + return date; + } + + public java.io.InputStream inputStream() throws java.io.IOException { + return new java.io.ByteArrayInputStream (content.getBytes ()); + } + public java.io.OutputStream outputStream() throws java.io.IOException { + class ContentStream extends java.io.ByteArrayOutputStream { + public void close () throws java.io.IOException { + super.close (); + content = new String (toByteArray ()); + } + } + + return new ContentStream (); + } + + public boolean isValid() { + return valid; + } + + public boolean isModified() { + return modified; + } + + public void markModified() throws java.io.IOException { + modified = true; + } + + public void unmarkModified() { + modified = false; + } + } + + /** Implementation of the CES */ + private static final class CES extends CloneableEditorSupport { + public CES (Env env, Lookup l) { + super (env, l); + } + + protected String messageName() { + return "Name"; + } + + protected String messageOpened() { + return "Opened"; + } + + protected String messageOpening() { + return "Opening"; + } + + protected String messageSave() { + return "Save"; + } + + protected String messageToolTip() { + return "ToolTip"; + } + + } + +}