This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 103467
Collapse All | Expand All

(-)a/openide.text/src/org/openide/text/CloneableEditorSupport.java (-2 / +308 lines)
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 {

Return to bug 103467