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