Lines 93-100
Link Here
|
93 |
import javax.swing.event.DocumentListener; |
93 |
import javax.swing.event.DocumentListener; |
94 |
import javax.swing.event.UndoableEditEvent; |
94 |
import javax.swing.event.UndoableEditEvent; |
95 |
import javax.swing.text.*; |
95 |
import javax.swing.text.*; |
|
|
96 |
import javax.swing.undo.AbstractUndoableEdit; |
96 |
import javax.swing.undo.CannotRedoException; |
97 |
import javax.swing.undo.CannotRedoException; |
97 |
import javax.swing.undo.CannotUndoException; |
98 |
import javax.swing.undo.CannotUndoException; |
|
|
99 |
import javax.swing.undo.CompoundEdit; |
100 |
import javax.swing.undo.UndoManager; |
98 |
import javax.swing.undo.UndoableEdit; |
101 |
import javax.swing.undo.UndoableEdit; |
99 |
import org.netbeans.api.editor.mimelookup.MimeLookup; |
102 |
import org.netbeans.api.editor.mimelookup.MimeLookup; |
100 |
import org.netbeans.api.editor.mimelookup.MimePath; |
103 |
import org.netbeans.api.editor.mimelookup.MimePath; |
Lines 120-125
Link Here
|
120 |
* but does not implement |
123 |
* but does not implement |
121 |
* those interfaces. It is up to the subclass to decide which interfaces |
124 |
* those interfaces. It is up to the subclass to decide which interfaces |
122 |
* really implement and which not. |
125 |
* really implement and which not. |
|
|
126 |
* <P> |
127 |
* This class supports collecting multiple edits into a group which is treated |
128 |
* as a single edit by undo/redo. Send {@BEGIN_COMMIT_GROUP} and |
129 |
* {@END_COMMIT_GROUP} to UndoableEditListener. These must always be paired. |
123 |
* |
130 |
* |
124 |
* @author Jaroslav Tulach |
131 |
* @author Jaroslav Tulach |
125 |
*/ |
132 |
*/ |
Lines 128-133
Link Here
|
128 |
|
135 |
|
129 |
/** Common name for editor mode. */ |
136 |
/** Common name for editor mode. */ |
130 |
public static final String EDITOR_MODE = "editor"; // NOI18N |
137 |
public static final String EDITOR_MODE = "editor"; // NOI18N |
|
|
138 |
/** |
139 |
* Start a group of edits which will be committed as a single edit |
140 |
* for purpose of undo/redo. |
141 |
* Nesting semantics are that any BEGIN_COMMIT_GROUP and |
142 |
* END_COMMIT_GROUP delimits a commit-group. |
143 |
* While coalescing edits, any undo/redo/save implicitly delimits |
144 |
* a commit-group. |
145 |
*/ |
146 |
public static final UndoableEdit BEGIN_COMMIT_GROUP = UndoGroupManager.BEGIN_COMMIT_GROUP; |
147 |
/** End a group of edits. */ |
148 |
public static final UndoableEdit END_COMMIT_GROUP = UndoGroupManager.END_COMMIT_GROUP; |
131 |
private static final String PROP_PANE = "CloneableEditorSupport.Pane"; //NOI18N |
149 |
private static final String PROP_PANE = "CloneableEditorSupport.Pane"; //NOI18N |
132 |
private static final int DOCUMENT_NO = 0; |
150 |
private static final int DOCUMENT_NO = 0; |
133 |
private static final int DOCUMENT_LOADING = 1; |
151 |
private static final int DOCUMENT_LOADING = 1; |
Lines 2986-2992
Link Here
|
2986 |
} |
3004 |
} |
2987 |
|
3005 |
|
2988 |
/** Generic undoable edit that delegates to the given undoable edit. */ |
3006 |
/** Generic undoable edit that delegates to the given undoable edit. */ |
2989 |
private class FilterUndoableEdit implements UndoableEdit { |
3007 |
private class FilterUndoableEdit |
|
|
3008 |
implements UndoableEdit, UndoGroupManager.SeparateEdit |
3009 |
{ |
2990 |
protected UndoableEdit delegate; |
3010 |
protected UndoableEdit delegate; |
2991 |
|
3011 |
|
2992 |
FilterUndoableEdit() { |
3012 |
FilterUndoableEdit() { |
Lines 3187-3193
Link Here
|
3187 |
/** An improved version of UndoRedo manager that locks document before |
3207 |
/** An improved version of UndoRedo manager that locks document before |
3188 |
* doing any other operations. |
3208 |
* doing any other operations. |
3189 |
*/ |
3209 |
*/ |
3190 |
private final static class CESUndoRedoManager extends UndoRedo.Manager { |
3210 |
private final static class CESUndoRedoManager extends UndoGroupManager { |
3191 |
private CloneableEditorSupport support; |
3211 |
private CloneableEditorSupport support; |
3192 |
|
3212 |
|
3193 |
public CESUndoRedoManager(CloneableEditorSupport c) { |
3213 |
public CESUndoRedoManager(CloneableEditorSupport c) { |
Lines 3421-3426
Link Here
|
3421 |
} |
3441 |
} |
3422 |
} |
3442 |
} |
3423 |
|
3443 |
|
|
|
3444 |
/** |
3445 |
* <tt>UndoGroupManager</tt> extends {@link UndoManager} |
3446 |
* and allows explicit control of what |
3447 |
* <tt>UndoableEdit</tt>s are coalesced into compound edits, |
3448 |
* rather than using the rules defined by the edits themselves. |
3449 |
* Groups are defined using BEGIN_COMMIT_GROUP and END_COMMIT_GROUP. |
3450 |
* Send these to UndoableEditListener. These must always be paired. |
3451 |
* <p> |
3452 |
* These use cases are supported. |
3453 |
* </p> |
3454 |
* <ol> |
3455 |
* <li> Default behavior is defined by {@link UndoManager}.</li> |
3456 |
* <li> <tt>UnddoableEdit</tt>s issued between {@link #BEGIN_COMMIT_GROUP} |
3457 |
* and {@link END_COMMIT_GROUP} are placed into a single |
3458 |
* {@link CompoundEdit}. |
3459 |
* Thus <tt>undo()</tt> and <tt>redo()</tt> treat them |
3460 |
* as a single undo/redo.</li> |
3461 |
* <li> Use {@link commitUndoGroup} to commit accumulated |
3462 |
* <tt>UndoableEdit</tt>s into a single <tt>CompoundEdit</tt> |
3463 |
* (and to continue accumulating); |
3464 |
* an application could do this at strategic points, such as EndOfLine |
3465 |
* input or cursor movement. In this way, the application can accumulate |
3466 |
* large chunks.</li> |
3467 |
* <li>BEGIN/END nest.</li> |
3468 |
* </ol> |
3469 |
* @see UndoManager |
3470 |
*/ |
3471 |
private static class UndoGroupManager extends UndoRedo.Manager { |
3472 |
/** signals that edits are being accumulated */ |
3473 |
private int buildUndoGroup; |
3474 |
/** accumulate edits here in undoGroup */ |
3475 |
private CompoundEdit undoGroup; |
3476 |
|
3477 |
/** |
3478 |
* Start a group of edits which will be committed as a single edit |
3479 |
* for purpose of undo/redo. |
3480 |
* Nesting semantics are that any BEGIN_COMMIT_GROUP and |
3481 |
* END_COMMIT_GROUP delimits a commit-group. |
3482 |
* While coalescing edits, any undo/redo/save implicitly delimits |
3483 |
* a commit-group. |
3484 |
*/ |
3485 |
static final UndoableEdit BEGIN_COMMIT_GROUP = new CommitGroupEdit(); |
3486 |
/** End a group of edits. */ |
3487 |
static final UndoableEdit END_COMMIT_GROUP = new CommitGroupEdit(); |
3488 |
|
3489 |
/** SeparateEdit tags an UndoableEdit so the |
3490 |
* UndoGroupManager does not coalesce it. |
3491 |
*/ |
3492 |
interface SeparateEdit { |
3493 |
} |
3494 |
|
3495 |
private static class CommitGroupEdit extends AbstractUndoableEdit { |
3496 |
@Override |
3497 |
public boolean isSignificant() { |
3498 |
return false; |
3499 |
} |
3500 |
} |
3501 |
|
3502 |
@Override |
3503 |
public void undoableEditHappened(UndoableEditEvent ue) |
3504 |
{ |
3505 |
if(ue.getEdit() == BEGIN_COMMIT_GROUP) { |
3506 |
beginUndoGroup(); |
3507 |
} else if(ue.getEdit() == END_COMMIT_GROUP) { |
3508 |
endUndoGroup(); |
3509 |
} else { |
3510 |
super.undoableEditHappened(ue); |
3511 |
} |
3512 |
} |
3513 |
|
3514 |
/** |
3515 |
* Direct this <tt>UndoGroupManager</tt> to begin coalescing any |
3516 |
* <tt>UndoableEdit</tt>s that are added into a <tt>CompoundEdit</tt>. |
3517 |
* <p>If edits are already being coalesced and some have been |
3518 |
* accumulated, they are commited as an atomic group and a new |
3519 |
* group is started. |
3520 |
* @see #addEdit |
3521 |
* @see #endUndoGroup |
3522 |
*/ |
3523 |
private synchronized void beginUndoGroup() { |
3524 |
commitUndoGroup(); |
3525 |
ERR.log(Level.FINE, "beginUndoGroup: nesting {0}", buildUndoGroup); |
3526 |
buildUndoGroup++; |
3527 |
} |
3528 |
|
3529 |
/** |
3530 |
* Direct this <tt>UndoGroupManager</tt> to stop coalescing edits. |
3531 |
* Until <tt>beginUndoGroupManager</tt> is invoked, |
3532 |
* any received <tt>UndoableEdit</tt>s are added singly. |
3533 |
* <p> |
3534 |
* This has no effect if edits are not being coalesced, for example |
3535 |
* if <tt>beginUndoGroup</tt> has not been called. |
3536 |
*/ |
3537 |
private synchronized void endUndoGroup() { |
3538 |
buildUndoGroup--; |
3539 |
ERR.log(Level.FINE, "endUndoGroup: nesting {0}", buildUndoGroup); |
3540 |
if(buildUndoGroup < 0) { |
3541 |
ERR.log(Level.INFO, null, new Exception("endUndoGroup without beginUndoGroup")); |
3542 |
buildUndoGroup = 0; |
3543 |
} |
3544 |
// slam buildUndoGroup to 0 to disable nesting |
3545 |
commitUndoGroup(); |
3546 |
} |
3547 |
|
3548 |
/** |
3549 |
* Commit any accumulated <tt>UndoableEdit</tt>s as an atomic |
3550 |
* <tt>undo</tt>/<tt>redo</tt> group. {@link CompoundEdit#end} |
3551 |
* is invoked on the <tt>CompoundEdit</tt> and it is added as a single |
3552 |
* <tt>UndoableEdit</tt> to this <tt>UndoManager</tt>. |
3553 |
* <p> |
3554 |
* If edits are currently being coalesced, a new undo group is started. |
3555 |
* This has no effect if edits are not being coalesced, for example |
3556 |
* <tt>beginUndoGroup</tt> has not been called. |
3557 |
*/ |
3558 |
private synchronized void commitUndoGroup() { |
3559 |
if(undoGroup == null) { |
3560 |
return; |
3561 |
} |
3562 |
// super.addEdit may end up in this.addEdit, |
3563 |
// so buildUndoGroup must be false |
3564 |
int saveBuildUndoGroup = buildUndoGroup; |
3565 |
buildUndoGroup = 0; |
3566 |
|
3567 |
undoGroup.end(); |
3568 |
super.addEdit(undoGroup); |
3569 |
undoGroup = null; |
3570 |
|
3571 |
buildUndoGroup = saveBuildUndoGroup; |
3572 |
} |
3573 |
|
3574 |
/** Add this edit separately, not part of a group. |
3575 |
* @return super.addEdit |
3576 |
*/ |
3577 |
private boolean commitAddEdit(UndoableEdit anEdit) { |
3578 |
commitUndoGroup(); |
3579 |
|
3580 |
int saveBuildUndoGroup = buildUndoGroup; |
3581 |
buildUndoGroup = 0; |
3582 |
boolean f = super.addEdit(anEdit); |
3583 |
//boolean f = addEdit(undoGroup); |
3584 |
buildUndoGroup = saveBuildUndoGroup; |
3585 |
return f; |
3586 |
} |
3587 |
|
3588 |
/** |
3589 |
* If this <tt>UndoManager</tt> is coalescing edits then add |
3590 |
* <tt>anEdit</tt> to the accumulating <tt>CompoundEdit</tt>. |
3591 |
* Otherwise, add it to this UndoManager. In either case the |
3592 |
* edit is saved for later <tt>undo</tt> or <tt>redo</tt>. |
3593 |
* @return {@inheritDoc} |
3594 |
* @see #beginUndoGroup |
3595 |
* @see #endUndoGroup |
3596 |
*/ |
3597 |
@Override |
3598 |
public synchronized boolean addEdit(UndoableEdit anEdit) { |
3599 |
if(!isInProgress()) |
3600 |
return false; |
3601 |
|
3602 |
if(buildUndoGroup > 0) { |
3603 |
if(anEdit instanceof SeparateEdit) |
3604 |
return commitAddEdit(anEdit); |
3605 |
if(undoGroup == null) |
3606 |
undoGroup = new CompoundEdit(); |
3607 |
return undoGroup.addEdit(anEdit); |
3608 |
} else { |
3609 |
return super.addEdit(anEdit); |
3610 |
} |
3611 |
} |
3612 |
|
3613 |
/** {@inheritDoc} */ |
3614 |
@Override |
3615 |
public synchronized void discardAllEdits() { |
3616 |
commitUndoGroup(); |
3617 |
super.discardAllEdits(); |
3618 |
} |
3619 |
|
3620 |
// |
3621 |
// TODO: limits |
3622 |
// |
3623 |
|
3624 |
/** {@inheritDoc} */ |
3625 |
@Override |
3626 |
public synchronized void undoOrRedo() { |
3627 |
commitUndoGroup(); |
3628 |
super.undoOrRedo(); |
3629 |
} |
3630 |
|
3631 |
/** {@inheritDoc} */ |
3632 |
@Override |
3633 |
public synchronized boolean canUndoOrRedo() { |
3634 |
if(undoGroup != null) |
3635 |
return true; |
3636 |
return super.canUndoOrRedo(); |
3637 |
} |
3638 |
|
3639 |
/** {@inheritDoc} */ |
3640 |
@Override |
3641 |
public synchronized void undo() { |
3642 |
commitUndoGroup(); |
3643 |
super.undo(); |
3644 |
} |
3645 |
|
3646 |
/** {@inheritDoc} */ |
3647 |
@Override |
3648 |
public synchronized boolean canUndo() { |
3649 |
if(undoGroup != null) |
3650 |
return true; |
3651 |
return super.canUndo(); |
3652 |
} |
3653 |
|
3654 |
/** {@inheritDoc} */ |
3655 |
@Override |
3656 |
public synchronized void redo() { |
3657 |
if(undoGroup != null) |
3658 |
throw new CannotRedoException(); |
3659 |
super.redo(); |
3660 |
} |
3661 |
|
3662 |
/** {@inheritDoc} */ |
3663 |
@Override |
3664 |
public synchronized boolean canRedo() { |
3665 |
if(undoGroup != null) |
3666 |
return false; |
3667 |
return super.canRedo(); |
3668 |
} |
3669 |
|
3670 |
/** {@inheritDoc} */ |
3671 |
@Override |
3672 |
public synchronized void end() { |
3673 |
commitUndoGroup(); |
3674 |
super.end(); |
3675 |
} |
3676 |
|
3677 |
/** {@inheritDoc} */ |
3678 |
@Override |
3679 |
public synchronized String getUndoOrRedoPresentationName() { |
3680 |
if(undoGroup != null) |
3681 |
return undoGroup.getUndoPresentationName(); |
3682 |
return super.getUndoOrRedoPresentationName(); |
3683 |
} |
3684 |
|
3685 |
/** {@inheritDoc} */ |
3686 |
@Override |
3687 |
public synchronized String getUndoPresentationName() { |
3688 |
if(undoGroup != null) |
3689 |
return undoGroup.getUndoPresentationName(); |
3690 |
return super.getUndoPresentationName(); |
3691 |
} |
3692 |
|
3693 |
/** {@inheritDoc} */ |
3694 |
@Override |
3695 |
public synchronized String getRedoPresentationName() { |
3696 |
if(undoGroup != null) |
3697 |
return undoGroup.getRedoPresentationName(); |
3698 |
return super.getRedoPresentationName(); |
3699 |
} |
3700 |
|
3701 |
/** {@inheritDoc} */ |
3702 |
@Override |
3703 |
public boolean isSignificant() { |
3704 |
if(undoGroup != null && undoGroup.isSignificant()) { |
3705 |
return true; |
3706 |
} |
3707 |
return super.isSignificant(); |
3708 |
} |
3709 |
|
3710 |
/** {@inheritDoc} */ |
3711 |
@Override |
3712 |
public synchronized void die() { |
3713 |
commitUndoGroup(); |
3714 |
super.die(); |
3715 |
} |
3716 |
|
3717 |
/** {@inheritDoc} */ |
3718 |
@Override |
3719 |
public String getPresentationName() { |
3720 |
if(undoGroup != null) |
3721 |
return undoGroup.getPresentationName(); |
3722 |
return super.getPresentationName(); |
3723 |
} |
3724 |
|
3725 |
// The protected methods are only accessed from |
3726 |
// synchronized methods that do commitUndoGroup |
3727 |
// so they do not need to be overridden in this class |
3728 |
} |
3729 |
|
3424 |
/** Special runtime exception that holds the original I/O failure. |
3730 |
/** Special runtime exception that holds the original I/O failure. |
3425 |
*/ |
3731 |
*/ |
3426 |
static final class DelegateIOExc extends IllegalStateException { |
3732 |
static final class DelegateIOExc extends IllegalStateException { |