diff --git a/editor.lib/src/org/netbeans/editor/BaseDocument.java b/editor.lib/src/org/netbeans/editor/BaseDocument.java
--- a/editor.lib/src/org/netbeans/editor/BaseDocument.java
+++ b/editor.lib/src/org/netbeans/editor/BaseDocument.java
@@ -73,6 +73,7 @@
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.Document;
+import javax.swing.text.DocumentFilter;
import javax.swing.text.Position;
import javax.swing.text.Element;
import javax.swing.text.AttributeSet;
@@ -336,6 +337,8 @@
private DocumentListener postModificationDocumentListener;
+ private DocumentEvent postModificationEvent;
+
private ListenerList
- * These use cases are supported.
- * If edits are already being coalesced and some have been
- * accumulated, they are flagged for commitment as an atomic group and
- * a new group will be started.
- * @see #addEdit
- * @see #endUndoGroup
- */
- private synchronized void beginUndoGroup() {
- if(undoGroup != null)
- needsNestingCommit++;
- ERR.log(Level.FINE, "beginUndoGroup: nesting {0}", buildUndoGroup);
- buildUndoGroup++;
- }
-
- /**
- * Direct this UndoGroupManager to stop coalescing edits.
- * Until beginUndoGroupManager is invoked,
- * any received UndoableEdits are added singly.
- *
- * This has no effect if edits are not being coalesced, for example
- * if beginUndoGroup has not been called.
- */
- private synchronized void endUndoGroup() {
- buildUndoGroup--;
- ERR.log(Level.FINE, "endUndoGroup: nesting {0}", buildUndoGroup);
- if(buildUndoGroup < 0) {
- ERR.log(Level.INFO, null, new Exception("endUndoGroup without beginUndoGroup"));
- // slam buildUndoGroup to 0 to disable nesting
- buildUndoGroup = 0;
- }
- if(needsNestingCommit <= 0)
- commitUndoGroup();
- if(--needsNestingCommit < 0)
- needsNestingCommit = 0;
- }
-
- /**
- * Commit any accumulated UndoableEdits as an atomic
- * undo/redo group. {@link CompoundEdit#end}
- * is invoked on the CompoundEdit and it is added as a single
- * UndoableEdit to this UndoManager.
- *
- * If edits are currently being coalesced, a new undo group is started.
- * This has no effect if edits are not being coalesced, for example
- * beginUndoGroup has not been called.
- */
- private synchronized void commitUndoGroup() {
- if(undoGroup == null) {
- return;
- }
-
- // undoGroup is being set to null,
- // needsNestingCommit has no meaning now
- needsNestingCommit = 0;
-
- // super.addEdit may end up in this.addEdit,
- // so buildUndoGroup must be false
- int saveBuildUndoGroup = buildUndoGroup;
- buildUndoGroup = 0;
-
- undoGroup.end();
- super.addEdit(undoGroup);
- undoGroup = null;
-
- buildUndoGroup = saveBuildUndoGroup;
- }
-
- /** Add this edit separately, not part of a group.
- * @return super.addEdit
- */
- private boolean commitAddEdit(UndoableEdit anEdit) {
- commitUndoGroup();
-
- int saveBuildUndoGroup = buildUndoGroup;
- buildUndoGroup = 0;
- boolean f = super.addEdit(anEdit);
- //boolean f = addEdit(undoGroup);
- buildUndoGroup = saveBuildUndoGroup;
- return f;
- }
-
- /**
- * If there's a pending undo group that needs to be committed
- * then commit it.
- * If this UndoManager is coalescing edits then add
- * anEdit to the accumulating CompoundEdit.
- * Otherwise, add it to this UndoManager. In either case the
- * edit is saved for later undo or redo.
- * @return {@inheritDoc}
- * @see #beginUndoGroup
- * @see #endUndoGroup
- */
- @Override
- public synchronized boolean addEdit(UndoableEdit anEdit) {
- if(!isInProgress())
- return false;
-
- if(needsNestingCommit > 0) {
- commitUndoGroup();
- }
-
- if(buildUndoGroup > 0) {
- if(anEdit instanceof SeparateEdit)
- return commitAddEdit(anEdit);
- if(undoGroup == null)
- undoGroup = new CompoundEdit();
- return undoGroup.addEdit(anEdit);
- } else {
- return super.addEdit(anEdit);
- }
- }
-
- @Override
- public synchronized void discardAllEdits() {
- commitUndoGroup();
- super.discardAllEdits();
- }
-
- //
- // TODO: limits
- //
-
- @Override
- public synchronized void undoOrRedo() {
- commitUndoGroup();
- super.undoOrRedo();
- }
-
- @Override
- public synchronized boolean canUndoOrRedo() {
- if(undoGroup != null)
- return true;
- return super.canUndoOrRedo();
- }
-
- @Override
- public synchronized void undo() {
- commitUndoGroup();
- super.undo();
- }
-
- @Override
- public synchronized boolean canUndo() {
- if(undoGroup != null)
- return true;
- return super.canUndo();
- }
-
- @Override
- public synchronized void redo() {
- if(undoGroup != null)
- throw new CannotRedoException();
- super.redo();
- }
-
- @Override
- public synchronized boolean canRedo() {
- if(undoGroup != null)
- return false;
- return super.canRedo();
- }
-
- @Override
- public synchronized void end() {
- commitUndoGroup();
- super.end();
- }
-
- @Override
- public synchronized String getUndoOrRedoPresentationName() {
- if(undoGroup != null)
- return undoGroup.getUndoPresentationName();
- return super.getUndoOrRedoPresentationName();
- }
-
- @Override
- public synchronized String getUndoPresentationName() {
- if(undoGroup != null)
- return undoGroup.getUndoPresentationName();
- return super.getUndoPresentationName();
- }
-
- @Override
- public synchronized String getRedoPresentationName() {
- if(undoGroup != null)
- return undoGroup.getRedoPresentationName();
- return super.getRedoPresentationName();
- }
-
- @Override
- public boolean isSignificant() {
- if(undoGroup != null && undoGroup.isSignificant()) {
- return true;
- }
- return super.isSignificant();
- }
-
- @Override
- public synchronized void die() {
- commitUndoGroup();
- super.die();
- }
-
- @Override
- public String getPresentationName() {
- if(undoGroup != null)
- return undoGroup.getPresentationName();
- return super.getPresentationName();
- }
-
- // The protected methods are only accessed from
- // synchronized methods that do commitUndoGroup
- // so they do not need to be overridden in this class
- }
-
/** Special runtime exception that holds the original I/O failure.
*/
static final class DelegateIOExc extends IllegalStateException {
@@ -3791,4 +2954,82 @@
}
}
+ private final class DocFilter extends DocumentFilter {
+
+ final DocumentFilter origFilter;
+
+ DocFilter(DocumentFilter origFilter) {
+ this.origFilter = origFilter;
+ }
+
+ @Override
+ public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
+ boolean origModified = checkModificationAllowed(offset);
+ boolean success = false;
+ try {
+ if (origFilter != null) {
+ origFilter.insertString(fb, offset, string, attr);
+ } else {
+ super.insertString(fb, offset, string, attr);
+ }
+ success = true;
+ } finally {
+ if (!success) {
+ if (!origModified) {
+ callNotifyUnmodified();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
+ boolean origModified = checkModificationAllowed(offset);
+ boolean success = false;
+ try {
+ if (origFilter != null) {
+ origFilter.remove(fb, offset, length);
+ } else {
+ super.remove(fb, offset, length);
+ }
+ success = true;
+ } finally {
+ if (!success) {
+ if (!origModified) {
+ callNotifyUnmodified();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
+ boolean origModified = checkModificationAllowed(offset);
+ boolean success = false;
+ try {
+ if (origFilter != null) {
+ origFilter.replace(fb, offset, length, text, attrs);
+ } else {
+ super.replace(fb, offset, length, text, attrs);
+ }
+ success = true;
+ } finally {
+ if (!success) {
+ if (!origModified) {
+ callNotifyUnmodified();
+ }
+ }
+ }
+ }
+
+ private boolean checkModificationAllowed(int offset) throws BadLocationException {
+ boolean alreadyModified = isAlreadyModified();
+ if (!callNotifyModified()) {
+ throw new BadLocationException("Modification not allowed", offset); // NOI18N
+ }
+ return alreadyModified;
+ }
+
+ }
+
}
diff --git a/openide.text/src/org/openide/text/FilterDocument.java b/openide.text/src/org/openide/text/FilterDocument.java
--- a/openide.text/src/org/openide/text/FilterDocument.java
+++ b/openide.text/src/org/openide/text/FilterDocument.java
@@ -99,12 +99,19 @@
/* Gets document property by key */
public Object getProperty(Object key) {
+ if (key == DocumentFilter.class && original instanceof AbstractDocument) {
+ return ((AbstractDocument)original).getDocumentFilter();
+ }
return original.getProperty(key);
}
/* Puts new property of document */
public void putProperty(Object key, Object value) {
- original.putProperty(key, value);
+ if (key == DocumentFilter.class && original instanceof AbstractDocument) {
+ ((AbstractDocument)original).setDocumentFilter((DocumentFilter)value);
+ } else {
+ original.putProperty(key, value);
+ }
}
/* Removes portion of a document */
diff --git a/openide.text/src/org/openide/text/UndoGroupManager.java b/openide.text/src/org/openide/text/UndoGroupManager.java
new file mode 100644
--- /dev/null
+++ b/openide.text/src/org/openide/text/UndoGroupManager.java
@@ -0,0 +1,373 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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-2007 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.openide.text;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.undo.*;
+import org.openide.awt.UndoRedo;
+
+
+/**
+ * UndoGroupManager extends {@link UndoManager}
+ * and allows explicit control of what
+ * UndoableEdits are coalesced into compound edits,
+ * rather than using the rules defined by the edits themselves.
+ * Groups are defined using BEGIN_COMMIT_GROUP and END_COMMIT_GROUP.
+ * Send these to UndoableEditListener. These must always be paired.
+ *
+ * These use cases are supported.
+ * If edits are already being coalesced and some have been
+ * accumulated, they are flagged for commitment as an atomic group and
+ * a new group will be started.
+ * @see #addEdit
+ * @see #endUndoGroup
+ */
+ private synchronized void beginUndoGroup() {
+ if(undoGroup != null)
+ needsNestingCommit++;
+ LOG.log(Level.FINE, "beginUndoGroup: nesting {0}", buildUndoGroup);
+ buildUndoGroup++;
+ }
+
+ /**
+ * Direct this UndoGroupManager to stop coalescing edits.
+ * Until beginUndoGroupManager is invoked,
+ * any received UndoableEdits are added singly.
+ *
+ * This has no effect if edits are not being coalesced, for example
+ * if beginUndoGroup has not been called.
+ */
+ private synchronized void endUndoGroup() {
+ buildUndoGroup--;
+ LOG.log(Level.FINE, "endUndoGroup: nesting {0}", buildUndoGroup);
+ if(buildUndoGroup < 0) {
+ LOG.log(Level.INFO, null, new Exception("endUndoGroup without beginUndoGroup"));
+ // slam buildUndoGroup to 0 to disable nesting
+ buildUndoGroup = 0;
+ }
+ if(needsNestingCommit <= 0)
+ commitUndoGroup();
+ if(--needsNestingCommit < 0)
+ needsNestingCommit = 0;
+ }
+
+ /**
+ * Commit any accumulated UndoableEdits as an atomic
+ * undo/redo group. {@link CompoundEdit#end}
+ * is invoked on the CompoundEdit and it is added as a single
+ * UndoableEdit to this UndoManager.
+ *
+ * If edits are currently being coalesced, a new undo group is started.
+ * This has no effect if edits are not being coalesced, for example
+ * beginUndoGroup has not been called.
+ */
+ private synchronized void commitUndoGroup() {
+ if(undoGroup == null) {
+ return;
+ }
+
+ // undoGroup is being set to null,
+ // needsNestingCommit has no meaning now
+ needsNestingCommit = 0;
+
+ // super.addEdit may end up in this.addEdit,
+ // so buildUndoGroup must be false
+ int saveBuildUndoGroup = buildUndoGroup;
+ buildUndoGroup = 0;
+
+ undoGroup.end();
+ super.addEdit(undoGroup);
+ undoGroup = null;
+
+ buildUndoGroup = saveBuildUndoGroup;
+ }
+
+ /** Add this edit separately, not part of a group.
+ * @return super.addEdit
+ */
+ private boolean commitAddEdit(UndoableEdit anEdit) {
+ commitUndoGroup();
+
+ int saveBuildUndoGroup = buildUndoGroup;
+ buildUndoGroup = 0;
+ boolean f = super.addEdit(anEdit);
+ //boolean f = addEdit(undoGroup);
+ buildUndoGroup = saveBuildUndoGroup;
+ return f;
+ }
+
+ /**
+ * If there's a pending undo group that needs to be committed
+ * then commit it.
+ * If this UndoManager is coalescing edits then add
+ * anEdit to the accumulating CompoundEdit.
+ * Otherwise, add it to this UndoManager. In either case the
+ * edit is saved for later undo or redo.
+ * @return {@inheritDoc}
+ * @see #beginUndoGroup
+ * @see #endUndoGroup
+ */
+ @Override
+ public synchronized boolean addEdit(UndoableEdit anEdit) {
+ if(!isInProgress())
+ return false;
+
+ if(needsNestingCommit > 0) {
+ commitUndoGroup();
+ }
+
+ if(buildUndoGroup > 0) {
+ if(anEdit instanceof WrapUndoEdit)
+ return commitAddEdit(anEdit);
+ if(undoGroup == null)
+ undoGroup = new CompoundEdit();
+ return undoGroup.addEdit(anEdit);
+ } else {
+ return super.addEdit(anEdit);
+ }
+ }
+
+ @Override
+ public synchronized void discardAllEdits() {
+ commitUndoGroup();
+ super.discardAllEdits();
+ }
+
+ //
+ // TODO: limits
+ //
+
+ @Override
+ public synchronized void undoOrRedo() {
+ commitUndoGroup();
+ super.undoOrRedo();
+ }
+
+ @Override
+ public synchronized boolean canUndoOrRedo() {
+ if(undoGroup != null)
+ return true;
+ return super.canUndoOrRedo();
+ }
+
+ @Override
+ public synchronized void undo() {
+ commitUndoGroup();
+ super.undo();
+ }
+
+ @Override
+ public synchronized boolean canUndo() {
+ if(undoGroup != null)
+ return true;
+ return super.canUndo();
+ }
+
+ @Override
+ public synchronized void redo() {
+ if(undoGroup != null)
+ throw new CannotRedoException();
+ super.redo();
+ }
+
+ @Override
+ public synchronized boolean canRedo() {
+ if(undoGroup != null)
+ return false;
+ return super.canRedo();
+ }
+
+ @Override
+ public synchronized void end() {
+ commitUndoGroup();
+ super.end();
+ }
+
+ @Override
+ public synchronized String getUndoOrRedoPresentationName() {
+ if(undoGroup != null)
+ return undoGroup.getUndoPresentationName();
+ return super.getUndoOrRedoPresentationName();
+ }
+
+ @Override
+ public synchronized String getUndoPresentationName() {
+ if(undoGroup != null)
+ return undoGroup.getUndoPresentationName();
+ return super.getUndoPresentationName();
+ }
+
+ @Override
+ public synchronized String getRedoPresentationName() {
+ if(undoGroup != null)
+ return undoGroup.getRedoPresentationName();
+ return super.getRedoPresentationName();
+ }
+
+ @Override
+ public boolean isSignificant() {
+ if(undoGroup != null && undoGroup.isSignificant()) {
+ return true;
+ }
+ return super.isSignificant();
+ }
+
+ @Override
+ public synchronized void die() {
+ commitUndoGroup();
+ super.die();
+ }
+
+ @Override
+ public String getPresentationName() {
+ if(undoGroup != null)
+ return undoGroup.getPresentationName();
+ return super.getPresentationName();
+ }
+
+ @Override
+ public final WrapUndoEdit lastEdit() { // make lastEdit() accessible
+ return (WrapUndoEdit) super.lastEdit();
+ }
+
+ // The protected methods are only accessed from
+ // synchronized methods that do commitUndoGroup
+ // so they do not need to be overridden in this class
+
+}
diff --git a/openide.text/src/org/openide/text/UndoRedoManager.java b/openide.text/src/org/openide/text/UndoRedoManager.java
new file mode 100644
--- /dev/null
+++ b/openide.text/src/org/openide/text/UndoRedoManager.java
@@ -0,0 +1,527 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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-2007 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.openide.text;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.text.StyledDocument;
+import javax.swing.undo.*;
+
+
+/**
+ * An improved version of UndoRedo manager that locks document before
+ * doing any other operations.
+ *
+ * Following requirements should be met:
+ *
* Also set when document is being reloaded.
*/
- private boolean revertingUndoOrReloading;
- private boolean justRevertedToNotModified;
+ private boolean documentReloading;
private volatile int documentStatus = DOCUMENT_NO;
private Throwable prepareDocumentRuntimeException;
@@ -271,10 +270,12 @@
*/
private Maptrue
if document is ready
*/
- private boolean isDocumentReady() {
+ boolean isDocumentReady() {
CloneableEditorSupport redirect = CloneableEditorSupportRedirector.findRedirect(this);
if (redirect != null) {
return redirect.isDocumentReady();
}
return documentStatus == DOCUMENT_READY;
}
-
+
/**
* Set the MIME type for the document.
* @param s the new MIME type
@@ -1600,7 +1638,7 @@
* @return the undo/redo manager
*/
protected UndoRedo.Manager createUndoRedoManager() {
- return new CESUndoRedoManager(this);
+ return new UndoRedoManager(this);
}
/** Returns an InputStream which reads the current data from this editor, taking into
@@ -1808,6 +1846,7 @@
// XXX do this from AWT???
ERR.fine("task-discardAllEdits");
getUndoRedo().discardAllEdits();
+ getUndoRedoManager().markSavepoint();
ERR.fine("task-check already modified");
// #57104 - if modified previously now it should become unmodified
if (isAlreadyModified()) {
@@ -1925,30 +1964,11 @@
* @return true if the modification was allowed, false if it should be prohibited
*/
final boolean callNotifyModified() {
- // #57104 - when reverting undo the revertingUndoOrReloading flag is set
- // to prevent infinite undoing which could happen now due to fix #56963
- // (undoable edit being undone in the document notifies
- // document's modification listener to mark the file as modified).
- // Maybe clearing alreadyModified flag
- // AFTER revertPreviousOrUpcomingUndo() could suffice as well
- // instead of the revertingUndoOrReloading flag.
- // Also notifyModified() is not called during reloadDocument()
- // to prevent situation when output stream is taken from the file
- // (for which CloneableEditorSupport exists) under file's lock
- // and once closed (still under file's lock) the CES is trying to reload
- // the file calling notifyModified() that tries to grab the lock
- // and fails leading to undoing of the file's content to the one
- // before the reload.
- if (!isAlreadyModified() && !revertingUndoOrReloading) {
+ if (!isAlreadyModified() && !documentReloading) {
setAlreadyModified(true);
if (!notifyModified()) {
- ERR.log(Level.INFO,"callNotifyModified notifyModified returns false this:" + getClass().getName());
setAlreadyModified(false);
- revertingUndoOrReloading = true;
- revertPreviousOrUpcomingUndo();
- revertingUndoOrReloading = false;
-
return false;
}
}
@@ -2036,81 +2056,6 @@
return true;
}
- /** Resets listening on UndoRedo
,
- * and in case next undo edit comes, schedules processesing of it.
- * Used to revert modification e.g. of document of [read-only] env. */
- private void revertPreviousOrUpcomingUndo() {
- UndoRedo.Manager ur = getUndoRedo();
- Listener l = getListener();
-
- if (Boolean.TRUE.equals(getDocument().getProperty("supportsModificationListener"))) { // NOI18N
-
- // revert undos now
- SearchBeforeModificationEdit edit = new SearchBeforeModificationEdit();
-
- try {
- for (;;) {
- edit.delegate = null;
- ur.undoableEditHappened(new UndoableEditEvent(getDocument(), edit));
-
- if (edit.delegate == null) break; // no previous edit
-
- if (edit.delegate instanceof BeforeModificationEdit) {
- if (edit.delegate != null) {
- // undo anyway
- ur.undo();
- }
-
- // and exit
- break;
- }
-
- if (edit.delegate instanceof BeforeSaveEdit) {
- break;
- }
-
- // otherwise remove the edit
- ur.undo();
- }
- } catch (CannotUndoException ex) {
- // ok, cannot undo, just ignore this
- }
- } else {
- // revert upcomming undo
- l.setUndoTask(new Runnable() {
- public void run() {
- undoAll();
- }
- }
- );
- ur.addChangeListener(l);
- }
- }
-
- /** Creates Runnable
which tries to make one undo. Helper method.
- * @see #revertUpcomingUndo */
- final void undoAll() {
- StyledDocument sd = getDoc();
-
- if (sd == null) {
- // #20883, doc can be null(!), doCloseDocument was faster.
- return;
- }
-
- UndoRedo ur = getUndoRedo();
- addRemoveDocListener(sd, false);
-
- try {
- if (ur.canUndo()) {
- ur.undo();
- }
- } catch (CannotUndoException cne) {
- ERR.log(Level.INFO, null, cne);
- } finally {
- addRemoveDocListener(sd, true);
- }
- }
-
/** Method that is called when all components of the support are
* closed. The default implementation closes the document.
*
@@ -2844,18 +2789,15 @@
/** The listener that this support uses to communicate with
* document, environment and also temporarilly on undoredo.
*/
- private final class Listener extends Object implements ChangeListener, DocumentListener, PropertyChangeListener,
+ private final class Listener extends Object implements PropertyChangeListener, DocumentListener,
Runnable, java.beans.VetoableChangeListener {
+
+ /** Stores exception from loadDocument, can be set in run method */
+ private IOException loadExc;
+
/** revert modification if asked */
private boolean revertModifiedFlag;
- /** Stores exception from loadDocument, can be set in run method */
- private IOException loadExc;
-
- /** Stores temporarilly undo task for reverting prohibited changes.
- * @see CloneableEditorSupport#createUndoTask */
- private Runnable undoTask;
-
Listener() {
}
@@ -2868,28 +2810,18 @@
// loadExc = null;
return ret;
}
-
- /** Sets undo task used to revert prohibited change. */
- public void setUndoTask(Runnable undoTask) {
- this.undoTask = undoTask;
+
+ public void insertUpdate(DocumentEvent evt) {
+ callNotifyModified();
+ revertModifiedFlag = false;
}
- /** Schedules reverting(undoing) of prohibited change.
- * Implements ChangeListener
.
- * @see #revertUpcomingUndo */
- public void stateChanged(ChangeEvent evt) {
- getUndoRedo().removeChangeListener(this);
- undoTask.run();
-
- //SwingUtilities.invokeLater(undoTask);
- undoTask = null;
+ public void removeUpdate(DocumentEvent evt) {
+ callNotifyModified();
+ revertModifiedFlag = false;
}
-
- /** Gives notification that an attribute or set of attributes changed.
- * @param ev event describing the action
- */
- public void changedUpdate(DocumentEvent ev) {
- //modified(); (bugfix #1492)
+
+ public void changedUpdate(DocumentEvent evt) {
}
public void vetoableChange(PropertyChangeEvent evt)
@@ -2912,22 +2844,6 @@
}
}
- /** Gives notification that there was an insert into the document.
- * @param ev event describing the action
- */
- public void insertUpdate(DocumentEvent ev) {
- callNotifyModified();
- revertModifiedFlag = false;
- }
-
- /** Gives notification that a portion of the document has been removed.
- * @param ev event describing the action
- */
- public void removeUpdate(DocumentEvent ev) {
- callNotifyModified();
- revertModifiedFlag = false;
- }
-
/** Listener to changes in the Env.
*/
public void propertyChange(PropertyChangeEvent ev) {
@@ -2963,9 +2879,9 @@
}
// #57104 - avoid notifyModified() which takes file lock
- revertingUndoOrReloading = true;
+ documentReloading = true;
NbDocument.runAtomic(sd, this);
- revertingUndoOrReloading = false; // #57104
+ documentReloading = false; // #57104
return;
}
@@ -3022,9 +2938,6 @@
setLastSaveTime(cesEnv().getTime().getTime());
- // Insert before-save undo event to enable unmodifying undo
- getUndoRedo().undoableEditHappened(new UndoableEditEvent(this, new BeforeSaveEdit(lastSaveTime)));
-
// Start listening on changes in document
addRemoveDocListener(getDoc(), true);
}
@@ -3032,756 +2945,6 @@
// }
}
- /** Generic undoable edit that delegates to the given undoable edit. */
- private class FilterUndoableEdit
- implements UndoableEdit, UndoGroupManager.SeparateEdit
- {
- protected UndoableEdit delegate;
-
- FilterUndoableEdit() {
- }
-
- public void undo() throws CannotUndoException {
- if (delegate != null) {
- delegate.undo();
- }
- }
-
- public boolean canUndo() {
- if (delegate != null) {
- return delegate.canUndo();
- } else {
- return false;
- }
- }
-
- public void redo() throws CannotRedoException {
- if (delegate != null) {
- delegate.redo();
- }
- }
-
- public boolean canRedo() {
- if (delegate != null) {
- return delegate.canRedo();
- } else {
- return false;
- }
- }
-
- public void die() {
- if (delegate != null) {
- delegate.die();
- }
- }
-
- public boolean addEdit(UndoableEdit anEdit) {
- if (delegate != null) {
- return delegate.addEdit(anEdit);
- } else {
- return false;
- }
- }
-
- public boolean replaceEdit(UndoableEdit anEdit) {
- if (delegate != null) {
- return delegate.replaceEdit(anEdit);
- } else {
- return false;
- }
- }
-
- public boolean isSignificant() {
- if (delegate != null) {
- return delegate.isSignificant();
- } else {
- return true;
- }
- }
-
- public String getPresentationName() {
- if (delegate != null) {
- return delegate.getPresentationName();
- } else {
- return ""; // NOI18N
- }
- }
-
- public String getUndoPresentationName() {
- if (delegate != null) {
- return delegate.getUndoPresentationName();
- } else {
- return ""; // NOI18N
- }
- }
-
- public String getRedoPresentationName() {
- if (delegate != null) {
- return delegate.getRedoPresentationName();
- } else {
- return ""; // NOI18N
- }
- }
- }
-
- /** Undoable edit that is put before the savepoint. Its replaceEdit()
- * method will consume and wrap the edit that precedes the save.
- * If the edit is added to the begining of the queue then
- * the isSignificant() implementation guarantees that the edit
- * will not be removed from the queue.
- * When redone it marks the document as not modified.
- */
- private class BeforeSaveEdit extends FilterUndoableEdit {
- private long saveTime;
-
- BeforeSaveEdit(long saveTime) {
- this.saveTime = saveTime;
- }
-
- @Override
- public boolean replaceEdit(UndoableEdit anEdit) {
- if (delegate == null) {
- delegate = anEdit;
-
- return true; // signal consumed
- }
-
- return false;
- }
-
- @Override
- public boolean addEdit(UndoableEdit anEdit) {
- if (!(anEdit instanceof BeforeModificationEdit) && !(anEdit instanceof SearchBeforeModificationEdit)) {
- /* UndoRedo.addEdit() must not be done lazily
- * because the edit must be "inserted" before the current one.
- */
- getUndoRedo().addEdit(new BeforeModificationEdit(saveTime, anEdit));
-
- return true;
- }
-
- return false;
- }
-
- @Override
- public void redo() {
- super.redo();
-
- if (saveTime == lastSaveTime) {
- justRevertedToNotModified = true;
- }
- }
-
- @Override
- public boolean isSignificant() {
- return (delegate != null);
- }
- }
-
- /** Edit that is created by wrapping the given edit.
- * When undone it marks the document as not modified.
- */
- private class BeforeModificationEdit extends FilterUndoableEdit {
- private long saveTime;
-
- BeforeModificationEdit(long saveTime, UndoableEdit delegate) {
- this.saveTime = saveTime;
- this.delegate = delegate;
- ERR.log(Level.FINEST, null, new Exception("new BeforeModificationEdit(" + saveTime +")")); // NOI18N
- }
-
- @Override
- public boolean addEdit(UndoableEdit anEdit) {
- if ((delegate == null) && !(anEdit instanceof SearchBeforeModificationEdit)) {
- delegate = anEdit;
-
- return true;
- }
-
- return delegate.addEdit(anEdit);
- }
-
- @Override
- public void undo() {
- super.undo();
-
- boolean res = saveTime == lastSaveTime;
- ERR.fine("Comparing saveTime and lastSaveTime: " + saveTime + "==" + lastSaveTime + " is " + res); // NOI18N
- if (res) {
- justRevertedToNotModified = true;
- }
- }
- }
-
- /** This edit is used to search for BeforeModificationEdit in UndoRedo
- * manager. This is not much nice solution, but well, there is not
- * much other chances to get inside UndoRedo.
- */
- private class SearchBeforeModificationEdit extends FilterUndoableEdit {
- SearchBeforeModificationEdit() {
- }
-
- @Override
- public boolean replaceEdit(UndoableEdit anEdit) {
- if (delegate == null) {
- delegate = anEdit;
-
- return true; // signal consumed
- }
-
- return false;
- }
- }
-
- /** An improved version of UndoRedo manager that locks document before
- * doing any other operations.
- */
- private final static class CESUndoRedoManager extends UndoGroupManager {
- private CloneableEditorSupport support;
-
- public CESUndoRedoManager(CloneableEditorSupport c) {
- this.support = c;
- super.setLimit(1000);
- }
-
- @Override
- public void redo() throws javax.swing.undo.CannotRedoException {
- final StyledDocument myDoc = support.getDocument();
-
- if (myDoc == null) {
- throw new javax.swing.undo.CannotRedoException(); // NOI18N
- }
-
- support.justRevertedToNotModified = false;
- new RenderUndo(0, myDoc);
-
- if (support.justRevertedToNotModified && support.isAlreadyModified()) {
- support.callNotifyUnmodified();
- }
- }
-
- @Override
- public void undo() throws javax.swing.undo.CannotUndoException {
- final StyledDocument myDoc = support.getDocument();
-
- if (myDoc == null) {
- throw new javax.swing.undo.CannotUndoException(); // NOI18N
- }
-
- support.justRevertedToNotModified = false;
- new RenderUndo(1, myDoc);
-
- if (support.justRevertedToNotModified && support.isAlreadyModified()) {
- support.callNotifyUnmodified();
- }
- }
-
- @Override
- public boolean canRedo() {
- final StyledDocument myDoc = support.getDocument();
-
- return new RenderUndo(2, myDoc, 0, true).booleanResult;
- }
-
- @Override
- public boolean canUndo() {
- final StyledDocument myDoc = support.getDocument();
-
- return new RenderUndo(3, myDoc, 0, true).booleanResult;
- }
-
- @Override
- public int getLimit() {
- final StyledDocument myDoc = support.getDocument();
-
- return new RenderUndo(4, myDoc).intResult;
- }
-
- @Override
- public void discardAllEdits() {
- final StyledDocument myDoc = support.getDocument();
- new RenderUndo(5, myDoc);
- // Insert before-save undo event to enable unmodifying undo
- undoableEditHappened(new UndoableEditEvent(support, support.new BeforeSaveEdit(support.lastSaveTime)));
- }
-
- @Override
- public void setLimit(int l) {
- final StyledDocument myDoc = support.getDocument();
- new RenderUndo(6, myDoc, l);
- }
-
- @Override
- public boolean canUndoOrRedo() {
- final StyledDocument myDoc = support.getDocument();
-
- return new RenderUndo(7, myDoc, 0, true).booleanResult;
- }
-
- @Override
- public java.lang.String getUndoOrRedoPresentationName() {
- if (support.isDocumentReady()) {
- final StyledDocument myDoc = support.getDocument();
- return new RenderUndo(8, myDoc, 0, true).stringResult;
- } else {
- return "";
- }
- }
-
- @Override
- public java.lang.String getRedoPresentationName() {
- if (support.isDocumentReady()) {
- final StyledDocument myDoc = support.getDocument();
- return new RenderUndo(9, myDoc, 0, true).stringResult;
- } else {
- return "";
- }
- }
-
- @Override
- public java.lang.String getUndoPresentationName() {
- if (support.isDocumentReady()) {
- final StyledDocument myDoc = support.getDocument();
- return new RenderUndo(10, myDoc, 0, true).stringResult;
- } else {
- return "";
- }
- }
-
- @Override
- public void undoOrRedo() throws javax.swing.undo.CannotUndoException, javax.swing.undo.CannotRedoException {
- final StyledDocument myDoc = support.getDocument();
-
- if (myDoc == null) {
- throw new javax.swing.undo.CannotUndoException(); // NOI18N
- }
-
- support.justRevertedToNotModified = false;
- new RenderUndo(11, myDoc);
-
- if (support.justRevertedToNotModified && support.isAlreadyModified()) {
- support.callNotifyUnmodified();
- }
- }
-
- private final class RenderUndo implements Runnable {
- private final int type;
- public boolean booleanResult;
- public int intResult;
- public String stringResult;
- private final boolean readonly;
-
- public RenderUndo(int type, StyledDocument doc) {
- this(type, doc, 0);
- }
-
- public RenderUndo(int type, StyledDocument doc, int intValue) {
- this(type, doc, intValue, false);
- }
-
- public RenderUndo(int type, StyledDocument doc, int intValue, boolean readonly) {
- this.type = type;
- this.intResult = intValue;
- this.readonly = readonly;
-
- if (!readonly && (doc instanceof NbDocument.WriteLockable)) {
- ((NbDocument.WriteLockable) doc).runAtomic(this);
- } else {
- if (readonly && doc != null) {
- doc.render(this);
- } else {
- // if the document is not one of "NetBeans ready"
- // that supports locking we do not have many
- // chances to do something. Maybe check for AbstractDocument
- // and call writeLock using reflection, but better than
- // that, let's leave this simple for now and wait for
- // bug reports (if any appear)
- run();
- }
- }
- }
-
- public void run() {
- switch (type) {
- case 0:
- CESUndoRedoManager.super.redo();
-
- break;
-
- case 1:
- CESUndoRedoManager.super.undo();
-
- break;
-
- case 2:
- booleanResult = CESUndoRedoManager.super.canRedo();
-
- break;
-
- case 3:
- booleanResult = CESUndoRedoManager.super.canUndo();
-
- break;
-
- case 4:
- intResult = CESUndoRedoManager.super.getLimit();
-
- break;
-
- case 5:
- CESUndoRedoManager.super.discardAllEdits();
-
- break;
-
- case 6:
- CESUndoRedoManager.super.setLimit(intResult);
-
- break;
-
- case 7:
- CESUndoRedoManager.super.canUndoOrRedo();
-
- break;
-
- case 8:
- stringResult = CESUndoRedoManager.super.getUndoOrRedoPresentationName();
-
- break;
-
- case 9:
- stringResult = CESUndoRedoManager.super.getRedoPresentationName();
-
- break;
-
- case 10:
- stringResult = CESUndoRedoManager.super.getUndoPresentationName();
-
- break;
-
- case 11:
- CESUndoRedoManager.super.undoOrRedo();
-
- break;
-
- default:
- throw new IllegalArgumentException("Unknown type: " + type);
- }
- }
- }
- }
-
- /**
- * UndoGroupManager extends {@link UndoManager}
- * and allows explicit control of what
- * UndoableEdits are coalesced into compound edits,
- * rather than using the rules defined by the edits themselves.
- * Groups are defined using BEGIN_COMMIT_GROUP and END_COMMIT_GROUP.
- * Send these to UndoableEditListener. These must always be paired.
- *
- *
- * @see UndoManager
- */
- private static class UndoGroupManager extends UndoRedo.Manager {
- /** signals that edits are being accumulated */
- private int buildUndoGroup;
- /** accumulate edits here in undoGroup */
- private CompoundEdit undoGroup;
- /**
- * Signal that nested group started and that current undo group
- * must be committed if edit is added. Then can avoid doing the commit
- * if the nested group turns out to be empty.
- */
- private int needsNestingCommit;
-
- /**
- * Start a group of edits which will be committed as a single edit
- * for purpose of undo/redo.
- * Nesting semantics are that any BEGIN_COMMIT_GROUP and
- * END_COMMIT_GROUP delimits a commit-group, unless the group is
- * empty in which case the begin/end is ignored.
- * While coalescing edits, any undo/redo/save implicitly delimits
- * a commit-group.
- */
- static final UndoableEdit BEGIN_COMMIT_GROUP = new CommitGroupEdit();
- /** End a group of edits. */
- static final UndoableEdit END_COMMIT_GROUP = new CommitGroupEdit();
- /**
- * Any coalesced edits become a commit-group and a new commit-group
- * is started.
- */
- static final UndoableEdit MARK_COMMIT_GROUP = new CommitGroupEdit();
-
- /** SeparateEdit tags an UndoableEdit so the
- * UndoGroupManager does not coalesce it.
- */
- interface SeparateEdit {
- }
-
- private static class CommitGroupEdit extends AbstractUndoableEdit {
- @Override
- public boolean isSignificant() {
- return false;
- }
-
- @Override
- public boolean canRedo()
- {
- return true;
- }
-
- @Override
- public boolean canUndo()
- {
- return true;
- }
- }
-
- @Override
- public void undoableEditHappened(UndoableEditEvent ue)
- {
- if(ue.getEdit() == BEGIN_COMMIT_GROUP) {
- beginUndoGroup();
- } else if(ue.getEdit() == END_COMMIT_GROUP) {
- endUndoGroup();
- } else if(ue.getEdit() == MARK_COMMIT_GROUP) {
- commitUndoGroup();
- } else {
- super.undoableEditHappened(ue);
- }
- }
-
- /**
- * Direct this UndoGroupManager to begin coalescing any
- * UndoableEdits that are added into a CompoundEdit.
- *
+ *
+ * @see UndoManager
+ * @author Ernie Rael
+ */
+class UndoGroupManager extends UndoRedo.Manager {
+
+ // -J-Dorg.openide.text.UndoGroupManager.level=FINE
+ private static final Logger LOG = Logger.getLogger(UndoGroupManager.class.getName());
+
+ /** signals that edits are being accumulated */
+ private int buildUndoGroup;
+
+ /** accumulate edits here in undoGroup */
+ private CompoundEdit undoGroup;
+
+ /**
+ * Signal that nested group started and that current undo group
+ * must be committed if edit is added. Then can avoid doing the commit
+ * if the nested group turns out to be empty.
+ */
+ private int needsNestingCommit;
+
+ /**
+ * Start a group of edits which will be committed as a single edit
+ * for purpose of undo/redo.
+ * Nesting semantics are that any BEGIN_COMMIT_GROUP and
+ * END_COMMIT_GROUP delimits a commit-group, unless the group is
+ * empty in which case the begin/end is ignored.
+ * While coalescing edits, any undo/redo/save implicitly delimits
+ * a commit-group.
+ */
+ static final UndoableEdit BEGIN_COMMIT_GROUP = new CommitGroupEdit();
+
+ /** End a group of edits. */
+ static final UndoableEdit END_COMMIT_GROUP = new CommitGroupEdit();
+
+ /**
+ * Any coalesced edits become a commit-group and a new commit-group
+ * is started.
+ */
+ static final UndoableEdit MARK_COMMIT_GROUP = new CommitGroupEdit();
+
+ private static class CommitGroupEdit extends AbstractUndoableEdit {
+ @Override
+ public boolean isSignificant() {
+ return false;
+ }
+
+ @Override
+ public boolean canRedo()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean canUndo()
+ {
+ return true;
+ }
+ }
+
+ @Override
+ public void undoableEditHappened(UndoableEditEvent ue)
+ {
+ if(ue.getEdit() == BEGIN_COMMIT_GROUP) {
+ beginUndoGroup();
+ } else if(ue.getEdit() == END_COMMIT_GROUP) {
+ endUndoGroup();
+ } else if(ue.getEdit() == MARK_COMMIT_GROUP) {
+ commitUndoGroup();
+ } else {
+ super.undoableEditHappened(ue);
+ }
+ }
+
+ /**
+ * Direct this UndoGroupManager to begin coalescing any
+ * UndoableEdits that are added into a CompoundEdit.
+ *
+ * It supports grouping of undoable edits by extending UndoGroupManager.
+ *
+ * It supports save actions that produce a compound undoable edit.
+ *
+ *
+ *
+ *
+ *