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 257893
Collapse All | Expand All

(-)a/.hgtags (+2 lines)
Lines 962-965 Link Here
962
37fc29481ec42daf8347ecc28cdf38079401b033 release81_beta_base
962
37fc29481ec42daf8347ecc28cdf38079401b033 release81_beta_base
963
ca20d6d4ed09fed2af119a1fd21a2eee31751d9e build_27
963
ca20d6d4ed09fed2af119a1fd21a2eee31751d9e build_27
964
294dca090459562fe1bbe59c5a4e61ab95d24d42 release81_base
964
294dca090459562fe1bbe59c5a4e61ab95d24d42 release81_base
965
5cf4b63c4b9823e6fc4a8137827c99b8a753d773 editor_multi_caret_stable
965
8d2520b6f9672304f6326258c34ed8d2509e6d2a jdk9ea
966
8d2520b6f9672304f6326258c34ed8d2509e6d2a jdk9ea
967
(-)a/editor.actions/src/org/netbeans/modules/editor/actions/AddSelectionElseCaretAction.java (+162 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.editor.actions;
43
44
import java.awt.Point;
45
import java.awt.Rectangle;
46
import java.awt.event.ActionEvent;
47
import java.util.ArrayList;
48
import java.util.List;
49
import javax.swing.text.BadLocationException;
50
import javax.swing.text.Caret;
51
import javax.swing.text.JTextComponent;
52
import javax.swing.text.Position;
53
import org.netbeans.api.editor.EditorActionNames;
54
import org.netbeans.api.editor.EditorActionRegistration;
55
import org.netbeans.api.editor.EditorActionRegistrations;
56
import org.netbeans.api.editor.caret.EditorCaret;
57
import org.netbeans.api.editor.caret.CaretMoveContext;
58
import org.netbeans.editor.BaseDocument;
59
import org.netbeans.editor.Utilities;
60
import org.netbeans.spi.editor.AbstractEditorAction;
61
import org.netbeans.spi.editor.caret.CaretMoveHandler;
62
63
/**
64
 *
65
 * @author Ralph Ruijs
66
 */
67
@EditorActionRegistrations({
68
    @EditorActionRegistration(name = EditorActionNames.addSelectionElseCaretUpAction),
69
    @EditorActionRegistration(name = EditorActionNames.addSelectionElseCaretDownAction)
70
})
71
public class AddSelectionElseCaretAction extends AbstractEditorAction {
72
73
    @Override
74
    protected void actionPerformed(ActionEvent evt, final JTextComponent target) {
75
        if (target != null) {
76
            Caret caret = target.getCaret();
77
            if (caret != null && caret instanceof EditorCaret) {
78
                final EditorCaret editorCaret = (EditorCaret) caret;
79
                final BaseDocument doc = (BaseDocument) target.getDocument();
80
                if(EditorActionNames.addSelectionElseCaretUpAction.equals(actionName())) {
81
                    doc.runAtomicAsUser(new Runnable() {
82
                        @Override
83
                        public void run() {
84
                            final List<Position> dots = new ArrayList<>(editorCaret.getCarets().size() << 1);
85
                            editorCaret.moveCarets(new CaretMoveHandler() {
86
                                @Override
87
                                public void moveCarets(CaretMoveContext context) {
88
                                    for (org.netbeans.api.editor.caret.CaretInfo caretInfo : context.getOriginalCarets()) {
89
                                        try {
90
                                            int dot = caretInfo.getDot();
91
                                            Point p = caretInfo.getMagicCaretPosition();
92
                                            if (p == null) {
93
                                                Rectangle r = target.modelToView(dot);
94
                                                if (r != null) {
95
                                                    p = new Point(r.x, r.y);
96
                                                    context.setMagicCaretPosition(caretInfo, p);
97
                                                } else {
98
                                                    return; // model to view failed
99
                                                }
100
                                            }
101
                                            try {
102
                                                dot = Utilities.getPositionAbove(target, dot, p.x);
103
                                                Position dotPos = doc.createPosition(dot);
104
                                                dots.add(dotPos);
105
                                                dots.add(dotPos);
106
                                            } catch (BadLocationException e) {
107
                                                // the position stays the same
108
                                            }
109
                                        } catch (BadLocationException ex) {
110
                                            target.getToolkit().beep();
111
                                        }
112
                                    }
113
                                }
114
                            });
115
116
                            editorCaret.addCarets(dots);
117
                        }
118
                    });
119
                } else {
120
                    doc.runAtomicAsUser(new Runnable() {
121
                        @Override
122
                        public void run() {
123
                            final List<Position> dots = new ArrayList<>(editorCaret.getCarets().size() << 1);
124
                            editorCaret.moveCarets(new CaretMoveHandler() {
125
                                @Override
126
                                public void moveCarets(CaretMoveContext context) {
127
                                    for (org.netbeans.api.editor.caret.CaretInfo caretInfo : context.getOriginalCarets()) {
128
                                        try {
129
                                            int dot = caretInfo.getDot();
130
                                            Point p = caretInfo.getMagicCaretPosition();
131
                                            if (p == null) {
132
                                                Rectangle r = target.modelToView(dot);
133
                                                if (r != null) {
134
                                                    p = new Point(r.x, r.y);
135
                                                    context.setMagicCaretPosition(caretInfo, p);
136
                                                } else {
137
                                                    return; // model to view failed
138
                                                }
139
                                            }
140
                                            try {
141
                                                dot = Utilities.getPositionBelow(target, dot, p.x);
142
                                                Position dotPos = doc.createPosition(dot);
143
                                                dots.add(dotPos);
144
                                                dots.add(dotPos);
145
                                            } catch (BadLocationException e) {
146
                                                // position stays the same
147
                                            }
148
                                        } catch (BadLocationException ex) {
149
                                            target.getToolkit().beep();
150
                                        }
151
                                    }
152
                                }
153
                            });
154
                            editorCaret.addCarets(dots);
155
                        }
156
                    });
157
                }
158
            }
159
        }
160
    }
161
162
}
(-)a/editor.actions/src/org/netbeans/modules/editor/actions/Bundle.properties (-1 / +6 lines)
Lines 52-57 Link Here
52
toggle-line-numbers_menu_text=&Show Line Numbers
52
toggle-line-numbers_menu_text=&Show Line Numbers
53
toggle-non-printable-characters=Toggle Non-printable Characters
53
toggle-non-printable-characters=Toggle Non-printable Characters
54
toggle-non-printable-characters_menu_text=Show &Non-printable Characters
54
toggle-non-printable-characters_menu_text=Show &Non-printable Characters
55
toggle-typing-mode=Toggle Typing Mode
55
transpose-letters=Transpose Letters
56
transpose-letters=Transpose Letters
56
goto-declaration=Go to Declaration
57
goto-declaration=Go to Declaration
57
goto-declaration_menu_text=Go to &Declaration
58
goto-declaration_menu_text=Go to &Declaration
Lines 68-71 Link Here
68
caret-previous-word=Insertion Point to Previous Word
69
caret-previous-word=Insertion Point to Previous Word
69
selection-next-word=Extend Selection to Next Word
70
selection-next-word=Extend Selection to Next Word
70
selection-previous-word=Extend Selection to Previous Word
71
selection-previous-word=Extend Selection to Previous Word
71
toggle-lines-view=Show &Indent Guide Lines
72
toggle-lines-view=Show &Indent Guide Lines
73
remove-last-caret=Remove Last Caret
74
remove-last-caret_menu_text=Remove Last Caret
75
add-selection-else-caret-up=Duplicate Insertion Point Up
76
add-selection-else-caret-down=Duplicate Insertion Point Down
(-)a/editor.actions/src/org/netbeans/modules/editor/actions/RemoveLastCaretAction.java (+74 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.editor.actions;
43
44
import java.awt.event.ActionEvent;
45
import javax.swing.text.Caret;
46
import javax.swing.text.JTextComponent;
47
import org.netbeans.api.editor.EditorActionNames;
48
import org.netbeans.api.editor.EditorActionRegistration;
49
import org.netbeans.api.editor.EditorActionRegistrations;
50
import org.netbeans.api.editor.caret.EditorCaret;
51
import org.netbeans.spi.editor.AbstractEditorAction;
52
53
/**
54
 *
55
 * @author Ralph Ruijs <ralphbenjamin@netbeans.org>
56
 */
57
@EditorActionRegistrations({
58
    @EditorActionRegistration(name = EditorActionNames.removeLastCaret,
59
                              menuPath = "Edit",
60
                              menuPosition = 840,
61
                              menuText = "#" + EditorActionNames.removeLastCaret + "_menu_text")
62
})
63
public class RemoveLastCaretAction extends AbstractEditorAction {
64
65
    @Override
66
    protected void actionPerformed(ActionEvent evt, JTextComponent component) {
67
        Caret caret = component.getCaret();
68
        if(caret instanceof EditorCaret) {
69
            EditorCaret editorCaret = (EditorCaret) caret;
70
            editorCaret.removeLastCaret();
71
        }
72
    }
73
    
74
}
(-)a/editor.actions/src/org/netbeans/modules/editor/actions/ToggleTypingModeAction.java (+76 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.editor.actions;
43
44
import java.awt.event.ActionEvent;
45
import javax.swing.text.JTextComponent;
46
import org.netbeans.api.editor.EditorActionNames;
47
import org.netbeans.api.editor.EditorActionRegistration;
48
import org.netbeans.api.editor.EditorUtilities;
49
import org.netbeans.spi.editor.AbstractEditorAction;
50
51
/**
52
 * Switch caret from insert mode to overwrite mode or vice versa.
53
 *
54
 * @author Miloslav Metelka
55
 */
56
57
@EditorActionRegistration(name = EditorActionNames.toggleTypingMode)
58
public final class ToggleTypingModeAction extends AbstractEditorAction {
59
60
    private static final long serialVersionUID = 1L;
61
62
    public ToggleTypingModeAction() {
63
        super();
64
    }
65
66
    @Override
67
    public void actionPerformed(ActionEvent evt, JTextComponent target) {
68
        if (target != null) {
69
            Boolean overwriteMode = (Boolean) target.getClientProperty(EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY);
70
            // Now toggle
71
            overwriteMode = (overwriteMode == null || !overwriteMode) ? Boolean.TRUE : Boolean.FALSE;
72
            target.putClientProperty(EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY, overwriteMode);
73
        }
74
    }
75
76
}
(-)a/editor.document/apichanges.xml (+18 lines)
Lines 112-117 Link Here
112
<!-- ACTUAL CHANGES BEGIN HERE: -->
112
<!-- ACTUAL CHANGES BEGIN HERE: -->
113
113
114
  <changes>
114
  <changes>
115
      <change id="ShiftPositions">
116
          <api name="api"/>
117
          <summary>Added ShiftPositions</summary>
118
          <version major="1" minor="7"/>
119
          <date day="22" month="2" year="2016"/>
120
          <author login="mmetelka"/>
121
          <compatibility source="compatible" binary="compatible" semantic="compatible" addition="yes"/>
122
          <description>
123
              <p>
124
                  Added ShiftPositions class to create and read ShiftPositions.
125
                  The implementation ShiftPos is a position together with a 
126
                  shift of extra columns. This allows for positions behind
127
                  line's last character (newline) or within a tab character.
128
              </p>
129
          </description>
130
          <class name="ShiftPositions" package="org.netbeans.api.editor.document"/>
131
          <issue number="257889"/>
132
      </change>
115
      <change id="TextSearchUtils-getPreviousWordStart">
133
      <change id="TextSearchUtils-getPreviousWordStart">
116
          <api name="api"/>
134
          <api name="api"/>
117
          <summary>Added TextSearchUtils.getPreviousWordStart() method</summary>
135
          <summary>Added TextSearchUtils.getPreviousWordStart() method</summary>
(-)a/editor.document/nbproject/project.properties (-1 / +1 lines)
Lines 1-6 Link Here
1
javac.source=1.7
1
javac.source=1.7
2
javac.compilerargs=-Xlint -Xlint:-serial
2
javac.compilerargs=-Xlint -Xlint:-serial
3
spec.version.base=1.6.0
3
spec.version.base=1.7.0
4
javadoc.arch=${basedir}/arch.xml
4
javadoc.arch=${basedir}/arch.xml
5
javadoc.apichanges=${basedir}/apichanges.xml
5
javadoc.apichanges=${basedir}/apichanges.xml
6
is.autoload=true
6
is.autoload=true
(-)a/editor.document/src/org/netbeans/api/editor/document/ShiftPositions.java (+135 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.editor.document;
43
44
import javax.swing.text.Position;
45
import org.netbeans.api.annotations.common.NonNull;
46
import org.netbeans.modules.editor.lib2.document.ShiftPos;
47
import org.openide.util.Parameters;
48
49
/**
50
 * A position together with a shift of extra columns.
51
 * This allows for positions behind line's last character (newline) or within a tab character.
52
 *
53
 * @author Miloslav Metelka
54
 * @since 1.7
55
 */
56
public final class ShiftPositions {
57
58
    private ShiftPositions() {
59
        // No instances
60
    }
61
    
62
    /**
63
     * Produce an immutable shift position.
64
     * The returned position acts like the original position and it does not handle in any way
65
     * any subsequent document modifications.
66
     *
67
     * @param pos non-null position. If this is already a shift position its shift
68
     *  gets added to the shift parameter passed to this method.
69
     * @param shift >= 0 number of extra columns added to the position.
70
     *  For 0 the original position gets returned. Negative value throws an IllegalArgumentException.
71
     * @return virtual position whose {@link Position#getOffset()} returns the same value
72
     *  like the getPosition parameter.
73
     */
74
    public static Position create(@NonNull Position pos, int shift) {
75
        Parameters.notNull("pos", pos);   //NOI18N
76
        if (shift > 0) {
77
            if (pos.getClass() == ShiftPos.class) {
78
                return new ShiftPos((ShiftPos)pos, shift);
79
            } else {
80
                return new ShiftPos(pos, shift);
81
            }
82
        } else if (shift == 0) {
83
            return pos;
84
        } else {
85
            throw new IllegalArgumentException("shift=" + shift + " < 0");
86
        }
87
    }
88
89
    /**
90
     * Return shift of a passed virtual position or zero for regular positions.
91
     * @param pos non-null position.
92
     * @return >=0 shift or zero for regular positions.
93
     */
94
    public static int getShift(@NonNull Position pos) {
95
        return getShiftImpl(pos);
96
    }
97
98
    /**
99
     * Compare positions.
100
     * @param pos1 non-null position.
101
     * @param pos2 non-null position.
102
     * @return offset of pos1 minus offset of pos2 or diff of their shifts in case
103
     *  both positions have the same offset.
104
     * @NullPointerException if any passed position is null unless both positions are null
105
     *  in which case the method would return 0.
106
     */
107
    public static int compare(@NonNull Position pos1, @NonNull Position pos2) {
108
        if (pos1 == pos2) {
109
            return 0;
110
        }
111
        int offsetDiff = pos1.getOffset() - pos2.getOffset();
112
        return (offsetDiff != 0) ? offsetDiff : getShiftImpl(pos1) - getShiftImpl(pos2);
113
    }
114
    
115
    /**
116
     * Compare positions by providing their offsets and shifts obtained earlier.
117
     * @param offset1 offset of first position.
118
     * @param shift1 shift of first position.
119
     * @param offset2 offset of second position.
120
     * @param shift2 shift of second position.
121
     * @return offset1 minus offset2 or shift1 minus shift2 in case
122
     *  offset1 and offset2 are equal.
123
     */
124
    public static int compare(int offset1, int shift1, int offset2, int shift2) {
125
        int offsetDiff = offset1 - offset2;
126
        return (offsetDiff != 0) ? offsetDiff : shift1 - shift2;
127
    }
128
    
129
    private static int getShiftImpl(Position pos) {
130
        return (pos.getClass() == ShiftPos.class)
131
                ? ((ShiftPos)pos).getShift()
132
                : 0;
133
    }
134
    
135
}
(-)a/editor.document/src/org/netbeans/modules/editor/lib2/document/ShiftPos.java (+83 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.editor.lib2.document;
43
44
import javax.swing.text.Position;
45
46
47
/**
48
 * Implementation of a shift position. The clients should only use methods
49
 * in {@link org.netbeans.api.editor.document.ShiftPositions} and never check
50
 * for an instance of this particular class.
51
 *
52
 * @author Miloslav Metelka
53
 */
54
public final class ShiftPos implements Position {
55
56
    private final Position pos;
57
    
58
    private final int shift;
59
60
    public ShiftPos(Position pos, int shift) {
61
        this.pos = pos;
62
        this.shift = shift;
63
    }
64
    
65
    public ShiftPos(ShiftPos shiftPos, int shift) {
66
        this.pos = shiftPos.pos;
67
        this.shift = shiftPos.shift + shift;
68
    }
69
70
    public Position getPosition() {
71
        return pos;
72
    }
73
    
74
    public int getShift() {
75
        return shift;
76
    }
77
78
    @Override
79
    public int getOffset() {
80
        return pos.getOffset();
81
    }
82
83
}
(-)a/editor.document/test/unit/src/org/netbeans/api/editor/document/ShiftPositionsTest.java (+100 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.editor.document;
43
44
import javax.swing.text.Document;
45
import javax.swing.text.PlainDocument;
46
import javax.swing.text.Position;
47
import org.junit.Test;
48
import static org.junit.Assert.*;
49
50
/**
51
 *
52
 * @author Miloslav Metelka
53
 */
54
public class ShiftPositionsTest {
55
    
56
    public ShiftPositionsTest() {
57
    }
58
59
    @Test
60
    public void testPos() throws Exception {
61
        Document doc = new PlainDocument();
62
        doc.insertString(0, "\t\t\n\n", null);
63
        Position pos1 = doc.createPosition(1);
64
        Position pos2 = doc.createPosition(2);
65
        
66
        Position pos10 = ShiftPositions.create(pos1, 0);
67
        Position pos11 = ShiftPositions.create(pos1, 1);
68
        Position pos20 = ShiftPositions.create(pos2, 0);
69
        Position pos21 = ShiftPositions.create(pos2, 1);
70
        
71
        assertEquals(0, ShiftPositions.getShift(pos1));
72
        assertEquals(0, ShiftPositions.getShift(pos10));
73
        assertEquals(1, ShiftPositions.getShift(pos11));
74
        comparePos(pos1, pos10, 0);
75
        comparePos(pos10, pos11, -1);
76
        comparePos(pos1, pos2, -1);
77
        comparePos(pos10, pos20, -1);
78
        comparePos(pos20, pos21, -1);
79
    }
80
    
81
    private void comparePos(Position pos1, Position pos2, int expectedResult) {
82
        comparePosImpl(pos1, pos2, expectedResult, true);
83
    }
84
85
    private void comparePosImpl(Position pos1, Position pos2, int expectedResult, boolean reverseCompare) {
86
        int result = ShiftPositions.compare(pos1, pos2);
87
        assertEquals("Invalid result=" + result + " when comparing positions pos1=" +
88
                pos1 + " to pos2=" + pos2, expectedResult, result);
89
90
        result = ShiftPositions.compare(pos1.getOffset(), ShiftPositions.getShift(pos1),
91
                pos2.getOffset(), ShiftPositions.getShift(pos2));
92
        assertEquals("Invalid result=" + result + " when comparing positions pos1=" +
93
                pos1 + " to pos2=" + pos2, expectedResult, result);
94
95
        if (reverseCompare) {
96
            comparePosImpl(pos2, pos1, -expectedResult, false);
97
        }
98
    }
99
100
}
(-)a/editor.errorstripe/nbproject/project.xml (+9 lines)
Lines 84-89 Link Here
84
                    </run-dependency>
84
                    </run-dependency>
85
                </dependency>
85
                </dependency>
86
                <dependency>
86
                <dependency>
87
                    <code-name-base>org.netbeans.modules.editor.lib2</code-name-base>
88
                    <build-prerequisite/>
89
                    <compile-dependency/>
90
                    <run-dependency>
91
                        <release-version>1</release-version>
92
                        <specification-version>2.5</specification-version>
93
                    </run-dependency>
94
                </dependency>
95
                <dependency>
87
                    <code-name-base>org.netbeans.modules.editor.mimelookup</code-name-base>
96
                    <code-name-base>org.netbeans.modules.editor.mimelookup</code-name-base>
88
                    <build-prerequisite/>
97
                    <build-prerequisite/>
89
                    <compile-dependency/>
98
                    <compile-dependency/>
(-)a/editor.errorstripe/src/org/netbeans/modules/editor/errorstripe/caret/CaretMarkProvider.java (-12 / +29 lines)
Lines 44-55 Link Here
44
44
45
package org.netbeans.modules.editor.errorstripe.caret;
45
package org.netbeans.modules.editor.errorstripe.caret;
46
import java.util.Collections;
46
import java.util.Collections;
47
import java.util.LinkedList;
47
import java.util.List;
48
import java.util.List;
48
import javax.swing.event.CaretEvent;
49
import javax.swing.event.CaretEvent;
49
import javax.swing.event.CaretListener;
50
import javax.swing.event.CaretListener;
51
import javax.swing.text.Caret;
50
import javax.swing.text.Document;
52
import javax.swing.text.Document;
51
import javax.swing.text.JTextComponent;
53
import javax.swing.text.JTextComponent;
52
import javax.swing.text.StyledDocument;
54
import javax.swing.text.StyledDocument;
55
import org.netbeans.api.editor.caret.CaretInfo;
56
import org.netbeans.api.editor.caret.EditorCaret;
53
import org.netbeans.modules.editor.errorstripe.privatespi.Mark;
57
import org.netbeans.modules.editor.errorstripe.privatespi.Mark;
54
import org.netbeans.modules.editor.errorstripe.privatespi.MarkProvider;
58
import org.netbeans.modules.editor.errorstripe.privatespi.MarkProvider;
55
import org.openide.text.NbDocument;
59
import org.openide.text.NbDocument;
Lines 64-105 Link Here
64
    
68
    
65
    private static final RequestProcessor RP = new RequestProcessor("CaretMarkProvider");
69
    private static final RequestProcessor RP = new RequestProcessor("CaretMarkProvider");
66
    
70
    
67
    private Mark mark;
71
    private List<Mark> marks;
68
    private JTextComponent component;
72
    private JTextComponent component;
69
    
73
    
70
    /** Creates a new instance of AnnotationMarkProvider */
74
    /** Creates a new instance of AnnotationMarkProvider */
71
    public CaretMarkProvider(JTextComponent component) {
75
    public CaretMarkProvider(JTextComponent component) {
72
        this.component = component;
76
        this.component = component;
73
        component.addCaretListener(this);
77
        component.addCaretListener(this);
74
        mark = createMark();
78
        marks = createMarks();
75
    }
79
    }
76
80
77
    private Mark createMark() {
81
    private List<Mark> createMarks() {
78
        int offset = component.getCaretPosition(); //TODO: AWT?
79
        Document doc = component.getDocument();
82
        Document doc = component.getDocument();
80
        int line = 0;
83
        if(!(doc instanceof StyledDocument)) {
81
        
84
            return Collections.singletonList((Mark)new CaretMark(0));
82
        if (doc instanceof StyledDocument) {
83
            line = NbDocument.findLineNumber((StyledDocument) doc, offset);
84
        }
85
        }
85
        
86
        List<Mark> lines = new LinkedList<>();
86
        return new CaretMark(line);
87
        Caret caret = component.getCaret();
88
        if(caret instanceof EditorCaret) {
89
            EditorCaret editorCaret = (EditorCaret) caret;
90
            for (CaretInfo caretInfo : editorCaret.getCarets()) {
91
                int offset = caretInfo.getDot();
92
                int line = NbDocument.findLineNumber((StyledDocument) doc, offset);
93
                lines.add(new CaretMark(line));
94
            }
95
        } else {
96
            int offset = component.getCaretPosition(); //TODO: AWT?
97
            int line = NbDocument.findLineNumber((StyledDocument) doc, offset);
98
            lines.add(new CaretMark(line));
99
        }
100
        return lines;
87
    }
101
    }
88
    
102
    
103
    @Override
89
    public synchronized List<Mark> getMarks() {
104
    public synchronized List<Mark> getMarks() {
90
        return Collections.singletonList(mark);
105
        return Collections.unmodifiableList(marks);
91
    }
106
    }
92
107
108
    @Override
93
    public void caretUpdate(CaretEvent e) {
109
    public void caretUpdate(CaretEvent e) {
94
        final List<Mark> old = getMarks();
110
        final List<Mark> old = getMarks();
95
        
111
        
96
        mark = createMark();
112
        marks = createMarks();
97
        
113
        
98
        final List<Mark> nue = getMarks();
114
        final List<Mark> nue = getMarks();
99
        
115
        
100
        //Do not fire this event under the document's write lock
116
        //Do not fire this event under the document's write lock
101
        //may deadlock with other providers:
117
        //may deadlock with other providers:
102
        RP.post(new Runnable() {
118
        RP.post(new Runnable() {
119
            @Override
103
            public void run() {
120
            public void run() {
104
                firePropertyChange(PROP_MARKS, old, nue);
121
                firePropertyChange(PROP_MARKS, old, nue);
105
            }
122
            }
(-)a/editor.lib/src/org/netbeans/editor/ActionFactory.java (-5 / +13 lines)
Lines 46-51 Link Here
46
46
47
import java.awt.Component;
47
import java.awt.Component;
48
import java.awt.Cursor;
48
import java.awt.Cursor;
49
import java.awt.Point;
49
import java.awt.Rectangle;
50
import java.awt.Rectangle;
50
import java.awt.Toolkit;
51
import java.awt.Toolkit;
51
import java.awt.event.ActionEvent;
52
import java.awt.event.ActionEvent;
Lines 56-61 Link Here
56
import java.lang.ref.Reference;
57
import java.lang.ref.Reference;
57
import java.lang.ref.WeakReference;
58
import java.lang.ref.WeakReference;
58
import java.text.SimpleDateFormat;
59
import java.text.SimpleDateFormat;
60
import java.util.ArrayList;
59
import java.util.Arrays;
61
import java.util.Arrays;
60
import java.util.Date;
62
import java.util.Date;
61
import java.util.HashSet;
63
import java.util.HashSet;
Lines 87-95 Link Here
87
import javax.swing.text.AbstractDocument;
89
import javax.swing.text.AbstractDocument;
88
import javax.swing.text.View;
90
import javax.swing.text.View;
89
import javax.swing.undo.UndoManager;
91
import javax.swing.undo.UndoManager;
92
import org.netbeans.api.editor.caret.CaretInfo;
90
import org.netbeans.api.editor.EditorActionNames;
93
import org.netbeans.api.editor.EditorActionNames;
91
import org.netbeans.api.editor.EditorActionRegistration;
94
import org.netbeans.api.editor.EditorActionRegistration;
92
import org.netbeans.api.editor.EditorActionRegistrations;
95
import org.netbeans.api.editor.EditorActionRegistrations;
96
import org.netbeans.api.editor.EditorUtilities;
97
import org.netbeans.api.editor.caret.EditorCaret;
93
import org.netbeans.api.editor.fold.FoldHierarchy;
98
import org.netbeans.api.editor.fold.FoldHierarchy;
94
import org.netbeans.api.lexer.TokenHierarchy;
99
import org.netbeans.api.lexer.TokenHierarchy;
95
import org.netbeans.api.progress.ProgressUtils;
100
import org.netbeans.api.progress.ProgressUtils;
Lines 98-103 Link Here
98
import org.netbeans.modules.editor.indent.api.Indent;
103
import org.netbeans.modules.editor.indent.api.Indent;
99
import org.netbeans.modules.editor.indent.api.Reformat;
104
import org.netbeans.modules.editor.indent.api.Reformat;
100
import org.netbeans.api.editor.NavigationHistory;
105
import org.netbeans.api.editor.NavigationHistory;
106
import org.netbeans.api.editor.caret.CaretMoveContext;
107
import org.netbeans.spi.editor.caret.CaretMoveHandler;
101
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils;
108
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils;
102
import org.netbeans.modules.editor.lib2.view.DocumentView;
109
import org.netbeans.modules.editor.lib2.view.DocumentView;
103
import org.netbeans.spi.editor.typinghooks.CamelCaseInterceptor;
110
import org.netbeans.spi.editor.typinghooks.CamelCaseInterceptor;
Lines 105-110 Link Here
105
import org.openide.util.ImageUtilities;
112
import org.openide.util.ImageUtilities;
106
import org.openide.util.Lookup;
113
import org.openide.util.Lookup;
107
import org.openide.util.NbBundle;
114
import org.openide.util.NbBundle;
115
import org.openide.util.Pair;
108
import org.openide.util.actions.Presenter;
116
import org.openide.util.actions.Presenter;
109
117
110
/**
118
/**
Lines 854-861 Link Here
854
        }
862
        }
855
    }
863
    }
856
864
857
    /** Switch to overwrite mode or back to insert mode */
865
    /** Switch to overwrite mode or back to insert mode
858
    @EditorActionRegistration(name = BaseKit.toggleTypingModeAction)
866
     * @deprecated Replaced by ToggleTypingModeAction in editor.actions module
867
     */
859
    public static class ToggleTypingModeAction extends LocalBaseAction {
868
    public static class ToggleTypingModeAction extends LocalBaseAction {
860
869
861
        static final long serialVersionUID =-2431132686507799723L;
870
        static final long serialVersionUID =-2431132686507799723L;
Lines 866-877 Link Here
866
875
867
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
876
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
868
            if (target != null) {
877
            if (target != null) {
869
                EditorUI editorUI = Utilities.getEditorUI(target);
878
                Boolean overwriteMode = (Boolean) target.getClientProperty(EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY);
870
                Boolean overwriteMode = (Boolean)editorUI.getProperty(EditorUI.OVERWRITE_MODE_PROPERTY);
871
                // Now toggle
879
                // Now toggle
872
                overwriteMode = (overwriteMode == null || !overwriteMode.booleanValue())
880
                overwriteMode = (overwriteMode == null || !overwriteMode.booleanValue())
873
                                ? Boolean.TRUE : Boolean.FALSE;
881
                                ? Boolean.TRUE : Boolean.FALSE;
874
                editorUI.putProperty(EditorUI.OVERWRITE_MODE_PROPERTY, overwriteMode);
882
                target.putClientProperty(EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY, overwriteMode);
875
            }
883
            }
876
        }
884
        }
877
    }
885
    }
(-)a/editor.lib/src/org/netbeans/editor/BaseCaret.java (-5 / +5 lines)
Lines 137-150 Link Here
137
    /** Caret type representing block covering current character */
137
    /** Caret type representing block covering current character */
138
    public static final String BLOCK_CARET = EditorPreferencesDefaults.BLOCK_CARET; // NOI18N
138
    public static final String BLOCK_CARET = EditorPreferencesDefaults.BLOCK_CARET; // NOI18N
139
139
140
    /** Default caret type */
141
    public static final String LINE_CARET = EditorPreferencesDefaults.LINE_CARET; // NOI18N
142
143
    /** One dot thin line compatible with Swing default caret */
140
    /** One dot thin line compatible with Swing default caret */
144
    public static final String THIN_LINE_CARET = "thin-line-caret"; // NOI18N
141
    public static final String THIN_LINE_CARET = EditorPreferencesDefaults.THIN_LINE_CARET; // NOI18N
145
142
146
    /** @since 1.23 */
143
    /** @since 1.23 */
147
    public static final String THICK_LINE_CARET = "thick-line-caret"; // NOI18N
144
    public static final String THICK_LINE_CARET = EditorPreferencesDefaults.THICK_LINE_CARET; // NOI18N
145
146
    /** Default caret type */
147
    public static final String LINE_CARET = THICK_LINE_CARET; // NOI18N
148
148
149
    /** Boolean property defining whether selection is being rectangular in a particular text component. */
149
    /** Boolean property defining whether selection is being rectangular in a particular text component. */
150
    private static final String RECTANGULAR_SELECTION_PROPERTY = "rectangular-selection"; // NOI18N
150
    private static final String RECTANGULAR_SELECTION_PROPERTY = "rectangular-selection"; // NOI18N
(-)a/editor.lib/src/org/netbeans/editor/BaseKit.java (-165 / +938 lines)
Lines 59-64 Link Here
59
import java.util.Iterator;
59
import java.util.Iterator;
60
import java.util.List;
60
import java.util.List;
61
import java.util.ArrayList;
61
import java.util.ArrayList;
62
import java.util.LinkedList;
62
import java.util.prefs.PreferenceChangeEvent;
63
import java.util.prefs.PreferenceChangeEvent;
63
import javax.swing.Action;
64
import javax.swing.Action;
64
import javax.swing.InputMap;
65
import javax.swing.InputMap;
Lines 88-98 Link Here
88
import javax.swing.event.ChangeListener;
89
import javax.swing.event.ChangeListener;
89
import javax.swing.plaf.TextUI;
90
import javax.swing.plaf.TextUI;
90
import javax.swing.text.AbstractDocument;
91
import javax.swing.text.AbstractDocument;
92
import static javax.swing.text.DefaultEditorKit.selectionBackwardAction;
93
import static javax.swing.text.DefaultEditorKit.selectionBeginLineAction;
94
import static javax.swing.text.DefaultEditorKit.selectionDownAction;
95
import static javax.swing.text.DefaultEditorKit.selectionEndLineAction;
96
import static javax.swing.text.DefaultEditorKit.selectionForwardAction;
97
import static javax.swing.text.DefaultEditorKit.selectionUpAction;
91
import javax.swing.text.EditorKit;
98
import javax.swing.text.EditorKit;
92
import javax.swing.text.Position;
99
import javax.swing.text.Position;
93
import javax.swing.text.View;
100
import javax.swing.text.View;
101
import javax.swing.undo.AbstractUndoableEdit;
102
import javax.swing.undo.CannotRedoException;
103
import javax.swing.undo.CannotUndoException;
104
import org.netbeans.api.editor.caret.CaretInfo;
94
import org.netbeans.api.editor.EditorActionRegistration;
105
import org.netbeans.api.editor.EditorActionRegistration;
95
import org.netbeans.api.editor.EditorActionRegistrations;
106
import org.netbeans.api.editor.EditorActionRegistrations;
107
import org.netbeans.api.editor.caret.EditorCaret;
96
import org.netbeans.api.editor.mimelookup.MimeLookup;
108
import org.netbeans.api.editor.mimelookup.MimeLookup;
97
import org.netbeans.api.editor.mimelookup.MimePath;
109
import org.netbeans.api.editor.mimelookup.MimePath;
98
import org.netbeans.api.editor.settings.KeyBindingSettings;
110
import org.netbeans.api.editor.settings.KeyBindingSettings;
Lines 108-115 Link Here
108
import org.netbeans.modules.editor.lib2.EditorPreferencesKeys;
120
import org.netbeans.modules.editor.lib2.EditorPreferencesKeys;
109
import org.netbeans.modules.editor.lib.KitsTracker;
121
import org.netbeans.modules.editor.lib.KitsTracker;
110
import org.netbeans.api.editor.NavigationHistory;
122
import org.netbeans.api.editor.NavigationHistory;
123
import org.netbeans.api.editor.caret.CaretMoveContext;
124
import org.netbeans.spi.editor.caret.CaretMoveHandler;
111
import org.netbeans.lib.editor.util.swing.PositionRegion;
125
import org.netbeans.lib.editor.util.swing.PositionRegion;
112
import org.netbeans.modules.editor.lib.SettingsConversions;
126
import org.netbeans.modules.editor.lib.SettingsConversions;
127
import org.netbeans.modules.editor.lib2.RectangularSelectionCaretAccessor;
113
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils;
128
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils;
114
import org.netbeans.modules.editor.lib2.actions.KeyBindingsUpdater;
129
import org.netbeans.modules.editor.lib2.actions.KeyBindingsUpdater;
115
import org.netbeans.modules.editor.lib2.typinghooks.DeletedTextInterceptorsManager;
130
import org.netbeans.modules.editor.lib2.typinghooks.DeletedTextInterceptorsManager;
Lines 122-127 Link Here
122
import org.openide.util.LookupEvent;
137
import org.openide.util.LookupEvent;
123
import org.openide.util.LookupListener;
138
import org.openide.util.LookupListener;
124
import org.openide.util.NbBundle;
139
import org.openide.util.NbBundle;
140
import org.openide.util.Pair;
125
import org.openide.util.WeakListeners;
141
import org.openide.util.WeakListeners;
126
import org.openide.util.WeakSet;
142
import org.openide.util.WeakSet;
127
143
Lines 545-551 Link Here
545
561
546
    /** Create caret to navigate through document */
562
    /** Create caret to navigate through document */
547
    public @Override Caret createCaret() {
563
    public @Override Caret createCaret() {
548
        return new BaseCaret();
564
        return new EditorCaret();
549
    }
565
    }
550
566
551
    /** Create empty document */
567
    /** Create empty document */
Lines 1111-1118 Link Here
1111
                            }
1127
                            }
1112
                        });
1128
                        });
1113
                        Caret caret = target.getCaret();
1129
                        Caret caret = target.getCaret();
1114
                        if (caret instanceof BaseCaret) {
1130
                        if (caret instanceof EditorCaret) {
1115
                            ((BaseCaret)caret).setRectangularSelectionToDotAndMark();
1131
                            RectangularSelectionCaretAccessor.get().setRectangularSelectionToDotAndMark((EditorCaret)caret);
1116
                        }
1132
                        }
1117
                        if (changed[0]) {
1133
                        if (changed[0]) {
1118
                            return;
1134
                            return;
Lines 1120-1185 Link Here
1120
                    }
1136
                    }
1121
1137
1122
                    try {
1138
                    try {
1123
                    final Position insertionOffset = doc.createPosition(computeInsertionOffset(target.getCaret()), Position.Bias.Backward);
1139
//                        for (final CaretInfo caret : ((EditorCaret)target.getCaret()).getCarets()) {
1124
                    String replacedText = "";
1140
//                    final Position insertionOffset = doc.createPosition(computeInsertionOffset(caret), Position.Bias.Backward);
1125
                    if (Utilities.isSelectionShowing(target.getCaret())) {
1141
//                    String replacedText = "";
1126
                        int p0 = Math.min(target.getCaret().getDot(), target.getCaret().getMark());
1142
//                    if (target.getCaret().isSelectionVisible() && caret.getDotPosition() != caret.getMarkPosition()) {
1127
                        int p1 = Math.max(target.getCaret().getDot(), target.getCaret().getMark());
1143
//                        int p0 = Math.min(caret.getDot(), caret.getMark());
1128
                        replacedText = doc.getText(p0, p1 - p0);
1144
//                        int p1 = Math.max(caret.getDot(), caret.getMark());
1129
                    }
1145
//                        replacedText = doc.getText(p0, p1 - p0);
1130
                    final TypedTextInterceptorsManager.Transaction transaction = TypedTextInterceptorsManager.getInstance().openTransaction(
1146
//                    }
1131
                            target, insertionOffset, cmd, replacedText);
1147
//                    final TypedTextInterceptorsManager.Transaction transaction = TypedTextInterceptorsManager.getInstance().openTransaction(
1132
                    
1148
//                            target, insertionOffset, cmd, replacedText);
1133
                    try {
1149
//                    
1134
                        if (!transaction.beforeInsertion()) {
1150
//                    try {
1135
                            final Object [] result = new Object [] { Boolean.FALSE, "" }; //NOI18N
1151
//                        if (!transaction.beforeInsertion()) {
1136
                            doc.runAtomicAsUser (new Runnable () {
1152
//                            final Object [] result = new Object [] { Boolean.FALSE, "" }; //NOI18N
1137
                                public void run () {
1153
//                            doc.runAtomicAsUser (new Runnable () {
1138
                                    boolean alreadyBeeped = false;
1154
//                                public void run () {
1139
                                    if (Utilities.isSelectionShowing(target.getCaret())) { // valid selection
1155
//                                    boolean alreadyBeeped = false;
1140
                                        EditorUI editorUI = Utilities.getEditorUI(target);
1156
//                                    if (target.getCaret().isSelectionVisible() && caret.getDot() != caret.getMark()) { // valid selection
1141
                                        Boolean overwriteMode = (Boolean) editorUI.getProperty(EditorUI.OVERWRITE_MODE_PROPERTY);
1157
//                                        EditorUI editorUI = Utilities.getEditorUI(target);
1142
                                        boolean ovr = (overwriteMode != null && overwriteMode.booleanValue());
1158
//                                        Boolean overwriteMode = (Boolean) editorUI.getProperty(EditorUI.OVERWRITE_MODE_PROPERTY);
1159
//                                        boolean ovr = (overwriteMode != null && overwriteMode.booleanValue());
1160
                        final Caret[] caret = {target.getCaret()};
1161
                        
1162
                        if(caret[0] instanceof EditorCaret) {
1163
                            EditorCaret editorCaret = (EditorCaret) caret[0];
1164
                            final List<CaretInfo> carets = editorCaret.getCarets();
1165
                            if(carets.size() > 1) {
1166
                                doc.runAtomicAsUser(new Runnable() {
1167
                                    public void run() {
1168
                                        boolean alreadyBeeped = false;
1169
                                        DocumentUtilities.setTypingModification(doc, true);
1143
                                        try {
1170
                                        try {
1144
                                            doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, true);
1171
                                        List<Position> dotAndMarkPosPairs = new ArrayList<>(carets.size() << 1);
1145
                                            replaceSelection(target, insertionOffset.getOffset(), target.getCaret(), "", ovr);
1172
                                        for (CaretInfo c : carets) {
1173
                                            dotAndMarkPosPairs.add(c.getDotPosition());
1174
                                            dotAndMarkPosPairs.add(c.getMarkPosition());
1175
                                            if (c.isSelection()) { // valid selection
1176
                                                int p0 = Math.min(c.getDot(), c.getMark());
1177
                                                int p1 = Math.max(c.getDot(), c.getMark());
1178
                                                String replacedText = null;
1179
                                                try {
1180
                                                    replacedText = doc.getText(p0, p1 - p0);
1181
                                                } catch (BadLocationException ble) {
1182
                                                    LOG.log(Level.FINE, null, ble);
1183
                                                    if (!alreadyBeeped) {
1184
                                                        target.getToolkit().beep();
1185
                                                    }
1186
                                                    alreadyBeeped = true;
1187
                                                }
1188
                                                EditorUI editorUI = Utilities.getEditorUI(target);
1189
                                                Boolean overwriteMode = (Boolean) editorUI.getProperty(EditorUI.OVERWRITE_MODE_PROPERTY);
1190
                                                boolean ovr = (overwriteMode != null && overwriteMode.booleanValue());
1191
                                                try {
1192
                                                    doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, true);
1193
                                                    doc.remove(p0, p1 - p0);
1194
                                                } catch (BadLocationException ble) {
1195
                                                    LOG.log(Level.FINE, null, ble);
1196
                                                    if (!alreadyBeeped) {
1197
                                                        target.getToolkit().beep();
1198
                                                    }
1199
                                                    alreadyBeeped = true;
1200
                                                } finally {
1201
                                                    doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, null);
1202
                                                }
1203
                                            }
1204
                                            try {
1205
//                                                TBD: Mark for the last caret?
1206
//                                                try {
1207
//                                                    NavigationHistory.getEdits().markWaypoint(target, insertionOffset, false, true);
1208
//                                                } catch (BadLocationException e) {
1209
//                                                    LOG.log(Level.WARNING, "Can't add position to the history of edits.", e); //NOI18N
1210
//                                                }
1211
                                                
1212
                                                final BaseDocument doc = (BaseDocument) target.getDocument();
1213
                                                EditorUI editorUI = Utilities.getEditorUI(target);
1214
                                                editorUI.getWordMatch().clear(); // reset word matching
1215
                                                
1216
                                                int insertionOffset = c.getDot();
1217
                                                Boolean overwriteMode = (Boolean) editorUI.getProperty(EditorUI.OVERWRITE_MODE_PROPERTY);
1218
                                                boolean ovr = (overwriteMode != null && overwriteMode.booleanValue());
1219
                                                if (ovr && insertionOffset < doc.getLength() && doc.getChars(insertionOffset, 1)[0] != '\n') { //NOI18N
1220
                                                    // overwrite current char
1221
                                                    doc.remove(insertionOffset, 1);
1222
                                                    doc.insertString(insertionOffset, cmd, null);
1223
                                                } else { // insert mode
1224
                                                    doc.insertString(insertionOffset, cmd, null);
1225
                                                }
1226
                                            } catch (BadLocationException ble) {
1227
                                                LOG.log(Level.FINE, null, ble);
1228
                                                if (!alreadyBeeped) {
1229
                                                    target.getToolkit().beep();
1230
                                                }
1231
                                            }
1232
                                        }
1233
                                        doc.addUndoableEdit(new CaretEdit(dotAndMarkPosPairs, target));
1234
                                        } finally {
1235
                                            DocumentUtilities.setTypingModification(doc, false);
1236
                                        }
1237
                                    }
1238
                                });
1239
                                return;
1240
                            }
1241
                        }
1242
                        
1243
                        final Position insertionOffset = doc.createPosition(computeInsertionOffset(caret[0]), Position.Bias.Backward);
1244
                        String replacedText = "";
1245
                        if (target.getCaret().isSelectionVisible() && caret[0].getDot() != caret[0].getMark()) {
1246
                            int p0 = Math.min(caret[0].getDot(), caret[0].getMark());
1247
                            int p1 = Math.max(caret[0].getDot(), caret[0].getMark());
1248
                            replacedText = doc.getText(p0, p1 - p0);
1249
                        }
1250
                        final TypedTextInterceptorsManager.Transaction transaction = TypedTextInterceptorsManager.getInstance().openTransaction(
1251
                                target, insertionOffset, cmd, replacedText);
1252
1253
                        try {
1254
                            if (!transaction.beforeInsertion()) {
1255
                                final Object[] result = new Object[]{Boolean.FALSE, ""}; //NOI18N
1256
                                doc.runAtomicAsUser(new Runnable() {
1257
                                    public void run() {
1258
                                        boolean alreadyBeeped = false;
1259
                                        if (target.getCaret().isSelectionVisible() && caret[0].getDot() != caret[0].getMark()) { // valid selection
1260
                                            EditorUI editorUI = Utilities.getEditorUI(target);
1261
                                            Boolean overwriteMode = (Boolean) editorUI.getProperty(EditorUI.OVERWRITE_MODE_PROPERTY);
1262
                                            boolean ovr = (overwriteMode != null && overwriteMode.booleanValue());
1263
                                            try {
1264
                                                doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, true);
1265
                                                replaceSelection(target, insertionOffset.getOffset(), target.getCaret(), "", ovr);
1266
                                            } catch (BadLocationException ble) {
1267
                                                LOG.log(Level.FINE, null, ble);
1268
                                                target.getToolkit().beep();
1269
                                                alreadyBeeped = true;
1270
                                            } finally {
1271
                                                doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, null);
1272
                                            }
1273
                                        }
1274
                                        Object[] r = transaction.textTyped();
1275
                                        String insertionText = r == null ? cmd : (String) r[0];
1276
                                        int caretPosition = r == null ? -1 : (Integer) r[1];
1277
1278
                                        try {
1279
                                            performTextInsertion(target, insertionOffset.getOffset(), insertionText, caretPosition);
1280
                                            result[0] = Boolean.TRUE;
1281
                                            result[1] = insertionText;
1146
                                        } catch (BadLocationException ble) {
1282
                                        } catch (BadLocationException ble) {
1147
                                            LOG.log(Level.FINE, null, ble);
1283
                                            LOG.log(Level.FINE, null, ble);
1148
                                            target.getToolkit().beep();
1284
                                            if (!alreadyBeeped) {
1149
                                            alreadyBeeped = true;
1285
                                                target.getToolkit().beep();
1150
                                        }
1286
                                            }
1151
                                        finally {
1152
                                            doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, null);
1153
                                        }
1287
                                        }
1154
                                    }
1288
                                    }
1155
                                    Object [] r = transaction.textTyped();
1289
                                });
1156
                                    String insertionText = r == null ? cmd : (String) r[0];
1290
1157
                                    int caretPosition = r == null ? -1 : (Integer) r[1];
1291
                                if (((Boolean) result[0]).booleanValue()) {
1158
                                    
1292
                                    transaction.afterInsertion();
1159
                                    try {
1293
1160
                                        performTextInsertion(target, insertionOffset.getOffset(), insertionText, caretPosition);
1294
                                    // XXX: this is potentially wrong and we may need to call this with
1161
                                        result[0] = Boolean.TRUE;
1295
                                    // the original cmd; or maybe only if insertionText == cmd; but maybe
1162
                                        result[1] = insertionText;
1296
                                    // it does not matter, because nobody seems to be overwriting this method anyway
1163
                                    } catch (BadLocationException ble) {
1297
                                    checkIndent(target, (String) result[1]);
1164
                                        LOG.log(Level.FINE, null, ble);
1298
                                } // else text insertion failed
1165
                                        if (!alreadyBeeped)
1299
                            }
1166
                                            target.getToolkit().beep();
1300
                        } finally {
1167
                                    }
1301
                            transaction.close();
1168
                                }
1169
                            });
1170
                            
1171
                            if (((Boolean)result[0]).booleanValue()) {
1172
                                transaction.afterInsertion();
1173
1174
                                // XXX: this is potentially wrong and we may need to call this with
1175
                                // the original cmd; or maybe only if insertionText == cmd; but maybe
1176
                                // it does not matter, because nobody seems to be overwriting this method anyway
1177
                                checkIndent(target, (String)result[1]);
1178
                            } // else text insertion failed
1179
                        }
1302
                        }
1180
                    } finally {
1181
                        transaction.close();
1182
                    }
1183
                    } catch (BadLocationException ble) {
1303
                    } catch (BadLocationException ble) {
1184
                        LOG.log(Level.FINE, null, ble);
1304
                        LOG.log(Level.FINE, null, ble);
1185
                        target.getToolkit().beep();
1305
                        target.getToolkit().beep();
Lines 1548-1553 Link Here
1548
                        public void run () {
1668
                        public void run () {
1549
                            DocumentUtilities.setTypingModification(doc, true);
1669
                            DocumentUtilities.setTypingModification(doc, true);
1550
                            try {
1670
                            try {
1671
                                if(caret instanceof EditorCaret) {
1672
                                    EditorCaret editorCaret = (EditorCaret) caret;
1673
                                    editorCaret.moveCarets(new CaretMoveHandler() {
1674
                                        @Override
1675
                                        public void moveCarets(CaretMoveContext context) {
1676
                                            for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
1677
                                                if (caretInfo.isSelection()) { // block selected
1678
                                                    try {
1679
                                                        int start = Math.min(caretInfo.getDot(), caretInfo.getMark());
1680
                                                        int end = Math.max(caretInfo.getDot(), caretInfo.getMark());
1681
                                                        String replacedText = doc.getText(start, end - start);
1682
                                                        if (replacedText.trim().isEmpty()) {
1683
                                                            doc.remove(start, end - start);
1684
                                                            insertTabString(doc, start);
1685
                                                        } else {
1686
                                                            boolean selectionAtLineStart = Utilities.getRowStart(doc, start) == start;
1687
                                                            changeBlockIndent(doc, start, end, +1);
1688
                                                            if (selectionAtLineStart) {
1689
                                                                int newSelectionStartOffset = start;
1690
                                                                int lineStartOffset = Utilities.getRowStart(doc, start);
1691
                                                                if (lineStartOffset != newSelectionStartOffset) {
1692
                                                                    target.select(lineStartOffset, end);
1693
                                                                }
1694
                                                            }
1695
                                                        }
1696
                                                    } catch (GuardedException ge) {
1697
                                                        LOG.log(Level.FINE, null, ge);
1698
                                                        target.getToolkit().beep();
1699
                                                    } catch (BadLocationException e) {
1700
                                                        LOG.log(Level.WARNING, null, e);
1701
                                                    }
1702
                                                } else { // no selected text
1703
                                                    int dotOffset = caretInfo.getDot();
1704
                                                    try {
1705
                                                        // is there any char on this line before cursor?
1706
                                                        int indent = Utilities.getRowIndent(doc, dotOffset);
1707
                                                        // test whether we should indent
1708
                                                        if (indent == -1) {
1709
                                                            // find caret column
1710
                                                            int caretCol = Utilities.getVisualColumn(doc, dotOffset);
1711
                                                            // find next tab column
1712
                                                            int nextTabCol = Utilities.getNextTabColumn(doc, dotOffset);
1713
1714
                                                            indenter.reindent(dotOffset);
1715
1716
                                                            dotOffset = caretInfo.getDot();
1717
                                                            int newCaretCol = Utilities.getVisualColumn(doc, dotOffset);
1718
                                                            if (newCaretCol <= caretCol) {
1719
                                                                // find indent of the first previous non-white row
1720
                                                                int upperCol = Utilities.getRowIndent(doc, dotOffset, false);
1721
                                                                changeRowIndent(doc, dotOffset, upperCol > nextTabCol ? upperCol : nextTabCol);
1722
                                                                // Fix of #32240
1723
                                                                dotOffset = caretInfo.getDot();
1724
                                                                Position newDotPos = doc.createPosition(Utilities.getRowEnd(doc, dotOffset));
1725
                                                                context.setDot(caretInfo, newDotPos);
1726
                                                            }
1727
                                                        } else { // already chars on the line
1728
                                                            insertTabString(doc, dotOffset);
1729
                                                        }
1730
                                                    } catch (GuardedException ge) {
1731
                                                        LOG.log(Level.FINE, null, ge);
1732
                                                        target.getToolkit().beep();
1733
                                                    } catch (BadLocationException e) {
1734
                                                        // use the same pos
1735
                                                        target.getToolkit().beep();
1736
                                                        LOG.log(Level.FINE, null, e);
1737
                                                    }
1738
                                                }
1739
                                            }
1740
                                        }
1741
                                    });
1742
                                } else {
1551
                                if (Utilities.isSelectionShowing(caret)) { // block selected
1743
                                if (Utilities.isSelectionShowing(caret)) { // block selected
1552
                                    try {
1744
                                    try {
1553
                                        if (target.getSelectedText().trim().isEmpty()) {
1745
                                        if (target.getSelectedText().trim().isEmpty()) {
Lines 1605-1610 Link Here
1605
                                        LOG.log(Level.FINE, null, e);
1797
                                        LOG.log(Level.FINE, null, e);
1606
                                    }
1798
                                    }
1607
                                }
1799
                                }
1800
                                }
1608
                            } finally {
1801
                            } finally {
1609
                                DocumentUtilities.setTypingModification(doc, false);
1802
                                DocumentUtilities.setTypingModification(doc, false);
1610
                            }
1803
                            }
Lines 1802-1807 Link Here
1802
1995
1803
		final BaseDocument doc = (BaseDocument)target.getDocument();
1996
		final BaseDocument doc = (BaseDocument)target.getDocument();
1804
		final Caret caret = target.getCaret();
1997
		final Caret caret = target.getCaret();
1998
                if (caret instanceof EditorCaret) {
1999
                    final EditorCaret editorCaret = (EditorCaret) caret;
2000
                    final List<CaretInfo> carets = editorCaret.getSortedCarets();
2001
                    if (carets.size() > 1) {
2002
                        doc.runAtomicAsUser(new Runnable() {
2003
                            public void run() {
2004
                                    boolean alreadyBeeped = false;
2005
                                    DocumentUtilities.setTypingModification(doc, true);
2006
                                    try {
2007
                                        List<Position> caretsDotAndMarkPositions = new ArrayList<>(carets.size() << 1);
2008
                                        for (CaretInfo c : carets) {
2009
                                            caretsDotAndMarkPositions.add(c.getDotPosition());
2010
                                            caretsDotAndMarkPositions.add(c.getMarkPosition());
2011
                                            if (c.isSelection()) {
2012
                                                // remove selection
2013
                                                final int dot = c.getDot();
2014
                                                final int mark = c.getMark();
2015
                                                try {
2016
                                                    if (RectangularSelectionUtils.isRectangularSelection(target)) {
2017
                                                        if (!RectangularSelectionUtils.removeSelection(target)) {
2018
                                                            RectangularSelectionUtils.removeChar(target, nextChar);
2019
                                                        }
2020
                                                        RectangularSelectionCaretAccessor.get().setRectangularSelectionToDotAndMark((EditorCaret)caret);
2021
                                                    } else {
2022
                                                        doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
2023
                                                    }
2024
                                                } catch (BadLocationException ble) {
2025
                                                    LOG.log(Level.FINE, null, ble);
2026
                                                    if (!alreadyBeeped) {
2027
                                                        target.getToolkit().beep();
2028
                                                    }
2029
                                                    alreadyBeeped = true;
2030
                                                }
2031
                                            } else {
2032
                                                final int dot = c.getDot();
2033
                                                char[] removedChar = null;
2034
2035
                                                try {
2036
                                                    removedChar = nextChar
2037
                                                            ? dot < doc.getLength() ? doc.getChars(dot, 1) : null
2038
                                                            : dot > 0 ? doc.getChars(dot - 1, 1) : null;
2039
                                                } catch (BadLocationException ble) {
2040
                                                    LOG.log(Level.FINE, null, ble);
2041
                                                    if (!alreadyBeeped) {
2042
                                                        target.getToolkit().beep();
2043
                                                    }
2044
                                                    alreadyBeeped = true;
2045
                                                }
2046
2047
                                                if (removedChar != null) {
2048
                                                    final String removedText = String.valueOf(removedChar);
2049
                                                    try {
2050
                                                        if (nextChar) { // remove next char
2051
                                                            doc.remove(dot, 1);
2052
                                                        } else { // remove previous char
2053
                                                            doc.remove(dot - 1, 1);
2054
                                                        }
2055
                                                    } catch (BadLocationException ble) {
2056
                                                        LOG.log(Level.FINE, null, ble);
2057
                                                        if (!alreadyBeeped) {
2058
                                                            target.getToolkit().beep();
2059
                                                        }
2060
                                                        alreadyBeeped = true;
2061
                                                    }
2062
                                                }
2063
                                            }
2064
                                        }
2065
                                        doc.addUndoableEdit(new CaretEdit(caretsDotAndMarkPositions, target));
2066
                                    } finally {
2067
                                        DocumentUtilities.setTypingModification(doc, false);
2068
                                    }
2069
                                }
2070
                            }
2071
                        );
2072
                        return;
2073
                    }
2074
                }
2075
                    
2076
                // Non-multicaret case
1805
		final int dot = caret.getDot();
2077
		final int dot = caret.getDot();
1806
		final int mark = caret.getMark();
2078
		final int mark = caret.getMark();
1807
                
2079
                
Lines 1811-1826 Link Here
1811
                        public void run () {
2083
                        public void run () {
1812
                            DocumentUtilities.setTypingModification(doc, true);
2084
                            DocumentUtilities.setTypingModification(doc, true);
1813
                            try {
2085
                            try {
2086
                                List<Position> dotAndMarkPosPairs = new ArrayList<>(2);
2087
                                dotAndMarkPosPairs.add(doc.createPosition(caret.getDot()));
2088
                                dotAndMarkPosPairs.add(doc.createPosition(caret.getMark()));
1814
                                if (RectangularSelectionUtils.isRectangularSelection(target)) {
2089
                                if (RectangularSelectionUtils.isRectangularSelection(target)) {
1815
                                    if (!RectangularSelectionUtils.removeSelection(target)) {
2090
                                    if (!RectangularSelectionUtils.removeSelection(target)) {
1816
                                        RectangularSelectionUtils.removeChar(target, nextChar);
2091
                                        RectangularSelectionUtils.removeChar(target, nextChar);
1817
                                    }
2092
                                    }
1818
                                    if (caret instanceof BaseCaret) {
2093
                                    if (caret instanceof EditorCaret) {
1819
                                        ((BaseCaret)caret).setRectangularSelectionToDotAndMark();
2094
                                        RectangularSelectionCaretAccessor.get().setRectangularSelectionToDotAndMark((EditorCaret)caret);
1820
                                    }
2095
                                    }
1821
                                } else {
2096
                                } else {
1822
                                    doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
2097
                                    doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
1823
                                }
2098
                                }
2099
                                doc.addUndoableEdit(new CaretEdit(dotAndMarkPosPairs, target));
1824
                            } catch (BadLocationException e) {
2100
                            } catch (BadLocationException e) {
1825
                                target.getToolkit().beep();
2101
                                target.getToolkit().beep();
1826
                            } finally {
2102
                            } finally {
Lines 1972-1994 Link Here
1972
                        // If on last line (without newline) then insert newline at very end temporarily
2248
                        // If on last line (without newline) then insert newline at very end temporarily
1973
                        // then cut and remove previous newline.
2249
                        // then cut and remove previous newline.
1974
                        int removeNewlineOffset = -1;
2250
                        int removeNewlineOffset = -1;
1975
                        if (!Utilities.isSelectionShowing(target)) {
2251
                        Caret caret = target.getCaret();
1976
                            Element elem = ((AbstractDocument) target.getDocument()).getParagraphElement(
2252
                        boolean disableNoSelectionCopy =
1977
                                    target.getCaretPosition());
2253
                            Boolean.getBoolean("org.netbeans.editor.disable.no.selection.copy");
1978
                            int lineStartOffset = elem.getStartOffset();
2254
                        if(!disableNoSelectionCopy &&
1979
                            int lineEndOffset = elem.getEndOffset();
2255
                                (!(caret instanceof EditorCaret)) || !(((EditorCaret)caret).getCarets().size() > 1)) {
1980
                            if (lineEndOffset == doc.getLength() + 1) { // Very end
2256
                            if (!Utilities.isSelectionShowing(target)) {
1981
                                // Temporarily insert extra newline
2257
                                Element elem = ((AbstractDocument) target.getDocument()).getParagraphElement(
1982
                                try {
2258
                                        target.getCaretPosition());
1983
                                    doc.insertString(lineEndOffset - 1, "\n", null);
2259
                                int lineStartOffset = elem.getStartOffset();
1984
                                    if (lineStartOffset > 0) { // Only when not on first line
2260
                                int lineEndOffset = elem.getEndOffset();
1985
                                        removeNewlineOffset = lineStartOffset - 1;
2261
                                if (lineEndOffset == doc.getLength() + 1) { // Very end
2262
                                    // Temporarily insert extra newline
2263
                                    try {
2264
                                        doc.insertString(lineEndOffset - 1, "\n", null);
2265
                                        if (lineStartOffset > 0) { // Only when not on first line
2266
                                            removeNewlineOffset = lineStartOffset - 1;
2267
                                        }
2268
                                    } catch (BadLocationException e) {
2269
                                        // could not insert extra newline
1986
                                    }
2270
                                    }
1987
                                } catch (BadLocationException e) {
1988
                                    // could not insert extra newline
1989
                                }
2271
                                }
2272
                                target.select(lineStartOffset, lineEndOffset);
1990
                            }
2273
                            }
1991
                            target.select(lineStartOffset, lineEndOffset);
1992
                        }
2274
                        }
1993
2275
1994
                        target.cut();
2276
                        target.cut();
Lines 2023-2038 Link Here
2023
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
2305
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
2024
            if (target != null) {
2306
            if (target != null) {
2025
                try {
2307
                try {
2026
                    int caretPosition = target.getCaretPosition();
2308
                    Caret caret = target.getCaret();
2027
                    boolean emptySelection = !Utilities.isSelectionShowing(target);
2309
                    boolean emptySelection = false;
2028
                    boolean disableNoSelectionCopy =
2310
                    boolean disableNoSelectionCopy =
2029
                            Boolean.getBoolean("org.netbeans.editor.disable.no.selection.copy");
2311
                            Boolean.getBoolean("org.netbeans.editor.disable.no.selection.copy");
2030
                    // If there is no selection then pre-select a current line including newline
2312
                    int caretPosition = caret.getDot();
2031
                    if (emptySelection && !disableNoSelectionCopy) {
2313
                    if(!disableNoSelectionCopy &&
2032
                        Element elem = ((AbstractDocument) target.getDocument()).getParagraphElement(
2314
                            (!(caret instanceof EditorCaret)) || !(((EditorCaret)caret).getCarets().size() > 1)) {
2033
                                caretPosition);
2315
                        emptySelection = !Utilities.isSelectionShowing(target);
2034
                        if (!Utilities.isRowWhite((BaseDocument) target.getDocument(), elem.getStartOffset())) {
2316
                        // If there is no selection then pre-select a current line including newline
2035
                            target.select(elem.getStartOffset(), elem.getEndOffset());
2317
                        if (emptySelection && !disableNoSelectionCopy) {
2318
                            Element elem = ((AbstractDocument) target.getDocument()).getParagraphElement(
2319
                                    caretPosition);
2320
                            if (!Utilities.isRowWhite((BaseDocument) target.getDocument(), elem.getStartOffset())) {
2321
                                target.select(elem.getStartOffset(), elem.getEndOffset());
2322
                            }
2036
                        }
2323
                        }
2037
                    }
2324
                    }
2038
                    target.copy();
2325
                    target.copy();
Lines 2214-2223 Link Here
2214
            super(ABBREV_RESET | UNDO_MERGE_RESET | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2501
            super(ABBREV_RESET | UNDO_MERGE_RESET | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2215
        }
2502
        }
2216
2503
2217
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
2504
        public void actionPerformed(ActionEvent evt, final JTextComponent target) {
2218
            if (target != null) {
2505
            if (target != null) {
2506
                Caret caret = target.getCaret();
2507
                final Document doc = target.getDocument();
2508
                if(doc != null && caret instanceof EditorCaret) {
2509
                    final EditorCaret editorCaret = (EditorCaret) caret;
2510
                    editorCaret.moveCarets(new CaretMoveHandler() {
2511
                        @Override
2512
                        public void moveCarets(CaretMoveContext context) {
2513
                            for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
2514
                                try {
2515
                                    int dot = caretInfo.getDot();
2516
                                    Point p = caretInfo.getMagicCaretPosition();
2517
                                    if (p == null) {
2518
                                        Rectangle r = target.modelToView(dot);
2519
                                        if (r != null) {
2520
                                            p = new Point(r.x, r.y);
2521
                                            context.setMagicCaretPosition(caretInfo, p);
2522
                                        } else {
2523
                                            return; // model to view failed
2524
                                        }
2525
                                    }
2526
                                    try {
2527
                                        dot = Utilities.getPositionAbove(target, dot, p.x);
2528
                                        Position dotPos = doc.createPosition(dot);
2529
                                        boolean select = selectionUpAction.equals(getValue(Action.NAME));
2530
                                        if (select) {
2531
                                            context.moveDot(caretInfo, dotPos);
2532
                                            if (RectangularSelectionUtils.isRectangularSelection(target)) {
2533
                                                RectangularSelectionCaretAccessor.get().updateRectangularUpDownSelection(editorCaret);
2534
                                            }
2535
                                        } else {
2536
                                            context.setDot(caretInfo, dotPos);
2537
                                        }
2538
                                    } catch (BadLocationException e) {
2539
                                        // the position stays the same
2540
                                    }
2541
                                } catch (BadLocationException ex) {
2542
                                    target.getToolkit().beep();
2543
                                }
2544
                            }
2545
                        }
2546
                    });
2547
                } else {
2219
                try {
2548
                try {
2220
                    Caret caret = target.getCaret();
2221
                    int dot = caret.getDot();
2549
                    int dot = caret.getDot();
2222
                    Point p = caret.getMagicCaretPosition();
2550
                    Point p = caret.getMagicCaretPosition();
2223
                    if (p == null) {
2551
                    if (p == null) {
Lines 2235-2242 Link Here
2235
                        if (select) {
2563
                        if (select) {
2236
                            caret.moveDot(dot);
2564
                            caret.moveDot(dot);
2237
                            if (RectangularSelectionUtils.isRectangularSelection(target)) {
2565
                            if (RectangularSelectionUtils.isRectangularSelection(target)) {
2238
                                if (caret instanceof BaseCaret) {
2566
                                if (caret instanceof EditorCaret) {
2239
                                    ((BaseCaret) caret).updateRectangularUpDownSelection();
2567
                                    RectangularSelectionCaretAccessor.get().updateRectangularUpDownSelection((EditorCaret)caret);
2240
                                }
2568
                                }
2241
                            }
2569
                            }
2242
                        } else {
2570
                        } else {
Lines 2249-2254 Link Here
2249
                    target.getToolkit().beep();
2577
                    target.getToolkit().beep();
2250
                }
2578
                }
2251
            }
2579
            }
2580
            }
2252
        }
2581
        }
2253
    }
2582
    }
2254
2583
Lines 2264-2273 Link Here
2264
            super(ABBREV_RESET | UNDO_MERGE_RESET | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2593
            super(ABBREV_RESET | UNDO_MERGE_RESET | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2265
        }
2594
        }
2266
2595
2267
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
2596
        public void actionPerformed(ActionEvent evt, final JTextComponent target) {
2268
            if (target != null) {
2597
            if (target != null) {
2598
                Caret caret = target.getCaret();
2599
                final Document doc = target.getDocument();
2600
                if (doc != null && caret instanceof EditorCaret) {
2601
                    final EditorCaret editorCaret = (EditorCaret) caret;
2602
                    editorCaret.moveCarets(new CaretMoveHandler() {
2603
                        @Override
2604
                        public void moveCarets(CaretMoveContext context) {
2605
                            for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
2606
                                try {
2607
                                    int dot = caretInfo.getDot();
2608
                                    Point p = caretInfo.getMagicCaretPosition();
2609
                                    if (p == null) {
2610
                                        Rectangle r = target.modelToView(dot);
2611
                                        if (r != null) {
2612
                                            p = new Point(r.x, r.y);
2613
                                            context.setMagicCaretPosition(caretInfo, p);
2614
                                        } else {
2615
                                            return; // model to view failed
2616
                                        }
2617
                                    }
2618
                                    try {
2619
                                        dot = Utilities.getPositionBelow(target, dot, p.x);
2620
                                        Position dotPos = doc.createPosition(dot);
2621
                                        boolean select = selectionDownAction.equals(getValue(Action.NAME));
2622
                                        if (select) {
2623
                                            context.moveDot(caretInfo, dotPos);
2624
                                            if (RectangularSelectionUtils.isRectangularSelection(target)) {
2625
                                                RectangularSelectionCaretAccessor.get().updateRectangularUpDownSelection(editorCaret);
2626
                                            }
2627
                                        } else {
2628
                                            context.setDot(caretInfo, dotPos);
2629
                                        }
2630
                                    } catch (BadLocationException e) {
2631
                                        // position stays the same
2632
                                    }
2633
                                } catch (BadLocationException ex) {
2634
                                    target.getToolkit().beep();
2635
                                }
2636
                            }
2637
                        }
2638
                    });
2639
                } else {
2269
                try {
2640
                try {
2270
                    Caret caret = target.getCaret();
2271
                    int dot = caret.getDot();
2641
                    int dot = caret.getDot();
2272
                    Point p = caret.getMagicCaretPosition();
2642
                    Point p = caret.getMagicCaretPosition();
2273
                    if (p == null) {
2643
                    if (p == null) {
Lines 2285-2292 Link Here
2285
                        if (select) {
2655
                        if (select) {
2286
                            caret.moveDot(dot);
2656
                            caret.moveDot(dot);
2287
                            if (RectangularSelectionUtils.isRectangularSelection(target)) {
2657
                            if (RectangularSelectionUtils.isRectangularSelection(target)) {
2288
                                if (caret instanceof BaseCaret) {
2658
                                if (caret instanceof EditorCaret) {
2289
                                    ((BaseCaret)caret).updateRectangularUpDownSelection();
2659
                                    RectangularSelectionCaretAccessor.get().updateRectangularUpDownSelection((EditorCaret)caret);
2290
                                }
2660
                                }
2291
                            }
2661
                            }
2292
                        } else {
2662
                        } else {
Lines 2298-2303 Link Here
2298
                } catch (BadLocationException ex) {
2668
                } catch (BadLocationException ex) {
2299
                    target.getToolkit().beep();
2669
                    target.getToolkit().beep();
2300
                }
2670
                }
2671
                }
2301
            }
2672
            }
2302
        }
2673
        }
2303
    }
2674
    }
Lines 2315-2325 Link Here
2315
            super(ABBREV_RESET | UNDO_MERGE_RESET | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2686
            super(ABBREV_RESET | UNDO_MERGE_RESET | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2316
        }
2687
        }
2317
2688
2318
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
2689
        public void actionPerformed(ActionEvent evt, final JTextComponent target) {
2319
            if (target != null) {
2690
            if (target != null) {
2320
                try {
2691
                try {
2321
                    Caret caret = target.getCaret();
2692
                    Caret caret = target.getCaret();
2322
                    BaseDocument doc = (BaseDocument)target.getDocument();
2693
                    final Document doc = target.getDocument();
2694
                    if(doc != null && caret instanceof EditorCaret) {
2695
                        final EditorCaret editorCaret = (EditorCaret) caret;
2696
                        editorCaret.moveCarets(new CaretMoveHandler() {
2697
                            @Override
2698
                            public void moveCarets(CaretMoveContext context) {
2699
                                try {
2700
                                    for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
2701
                                        int caretOffset = caretInfo.getDot();
2702
                                        Rectangle caretBounds = ((BaseTextUI) target.getUI()).modelToView(target, caretOffset);
2703
                                        if (caretBounds == null) {
2704
                                            return; // Cannot continue reasonably
2705
                                        }
2706
2707
                                        // Retrieve caret magic position and attempt to retain
2708
                                        // the x-coordinate information and use it
2709
                                        // for setting of the new caret position
2710
                                        Point magicCaretPosition = caretInfo.getMagicCaretPosition();
2711
                                        if (magicCaretPosition == null) {
2712
                                            magicCaretPosition = new Point(caretBounds.x, caretBounds.y);
2713
                                        }
2714
2715
                                        Rectangle visibleBounds = target.getVisibleRect();
2716
                                        int newCaretOffset;
2717
                                        Rectangle newCaretBounds;
2718
2719
                                        // Check whether caret was contained in the original visible window
2720
                                        if (visibleBounds.contains(caretBounds)) {
2721
                                            // Clone present view bounds
2722
                                            Rectangle newVisibleBounds = new Rectangle(visibleBounds);
2723
                                            // Do viewToModel() and modelToView() with the left top corner
2724
                                            // of the currently visible view. If that line is not fully visible
2725
                                            // then it should be the bottom line of the previous page
2726
                                            // (if it's fully visible then the line above it).
2727
                                            int topLeftOffset = target.viewToModel(new Point(
2728
                                                    visibleBounds.x, visibleBounds.y));
2729
                                            Rectangle topLeftLineBounds = target.modelToView(topLeftOffset);
2730
2731
                                            // newVisibleBounds.y will hold bottom of new view
2732
                                            if (topLeftLineBounds.y != visibleBounds.y) {
2733
                                                newVisibleBounds.y = topLeftLineBounds.y + topLeftLineBounds.height;
2734
                                            } // Component view starts right at the line boundary
2735
                                            // Go back by the view height
2736
                                            newVisibleBounds.y -= visibleBounds.height;
2737
2738
                                            // Find the new caret bounds by using relative y position
2739
                                            // on the original caret bounds. If the caret's new relative bounds
2740
                                            // would be visually above the old bounds
2741
                                            // the view should be shifted so that the relative bounds
2742
                                            // are the same (user's eyes do not need to move).
2743
                                            int caretRelY = caretBounds.y - visibleBounds.y;
2744
                                            int caretNewY = newVisibleBounds.y + caretRelY;
2745
                                            newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x, caretNewY));
2746
                                            newCaretBounds = target.modelToView(newCaretOffset);
2747
                                            if (newCaretBounds.y < caretNewY) {
2748
                                                // Need to go one line down to retain the top line
2749
                                                // of the present newVisibleBounds to be fully visible.
2750
                                                // Attempt to go forward by height of caret
2751
                                                newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x,
2752
                                                        newCaretBounds.y + newCaretBounds.height));
2753
                                                newCaretBounds = target.modelToView(newCaretOffset);
2754
                                            }
2755
2756
                                            // Shift the new visible bounds so that the caret
2757
                                            // does not visually move
2758
                                            newVisibleBounds.y = newCaretBounds.y - caretRelY;
2759
2760
                                            // Scroll the window to the requested rectangle
2761
                                            target.scrollRectToVisible(newVisibleBounds);
2762
2763
                                        } else { // Caret outside of originally visible window
2764
                                            // Shift the dot by the visible bounds height
2765
                                            Point newCaretPoint = new Point(magicCaretPosition.x,
2766
                                                    caretBounds.y - visibleBounds.height);
2767
                                            newCaretOffset = target.viewToModel(newCaretPoint);
2768
                                            newCaretBounds = target.modelToView(newCaretOffset);
2769
                                        }
2770
2771
                                        boolean select = selectionPageUpAction.equals(getValue(Action.NAME));
2772
                                        Position newCaretPos = doc.createPosition(newCaretOffset);
2773
                                        if (select) {
2774
                                            context.moveDot(caretInfo, newCaretPos);
2775
                                        } else {
2776
                                            context.setDot(caretInfo, newCaretPos);
2777
                                        }
2778
2779
                                        // Update magic caret position
2780
                                        magicCaretPosition.y = newCaretBounds.y;
2781
                                        context.setMagicCaretPosition(caretInfo, magicCaretPosition);
2782
                                    }
2783
                                } catch (BadLocationException ex) {
2784
                                    target.getToolkit().beep();
2785
                                }
2786
                            }
2787
                        });
2788
                    } else {
2323
                    int caretOffset = caret.getDot();
2789
                    int caretOffset = caret.getDot();
2324
                    Rectangle caretBounds = ((BaseTextUI)target.getUI()).modelToView(target, caretOffset);
2790
                    Rectangle caretBounds = ((BaseTextUI)target.getUI()).modelToView(target, caretOffset);
2325
                    if (caretBounds == null) {
2791
                    if (caretBounds == null) {
Lines 2400-2406 Link Here
2400
                    // Update magic caret position
2866
                    // Update magic caret position
2401
                    magicCaretPosition.y = newCaretBounds.y;
2867
                    magicCaretPosition.y = newCaretBounds.y;
2402
                    caret.setMagicCaretPosition(magicCaretPosition);
2868
                    caret.setMagicCaretPosition(magicCaretPosition);
2403
                    
2869
                    }
2404
                } catch (BadLocationException ex) {
2870
                } catch (BadLocationException ex) {
2405
                    target.getToolkit().beep();
2871
                    target.getToolkit().beep();
2406
                }
2872
                }
Lines 2421-2429 Link Here
2421
                | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2887
                | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2422
        }
2888
        }
2423
2889
2424
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
2890
        public void actionPerformed(ActionEvent evt, final JTextComponent target) {
2425
            if (target != null) {
2891
            if (target != null) {
2426
                Caret caret = target.getCaret();
2892
                Caret caret = target.getCaret();
2893
                final Document doc = target.getDocument();
2894
                if (doc != null && caret instanceof EditorCaret) {
2895
                    final EditorCaret editorCaret = (EditorCaret) caret;
2896
                    editorCaret.moveCarets(new CaretMoveHandler() {
2897
                        @Override
2898
                        public void moveCarets(CaretMoveContext context) {
2899
                            for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
2900
                                try {
2901
                                    int offset;
2902
                                    boolean select = selectionForwardAction.equals(getValue(Action.NAME));
2903
                                    if (!select && Utilities.isSelectionShowing(editorCaret)) {
2904
                                        offset = target.getSelectionEnd();
2905
                                        if (offset != caretInfo.getDot()) {
2906
                                            offset--;
2907
                                        } else {
2908
                                            // clear the selection, but do not move the cursor
2909
                                            Position pos = doc.createPosition(offset);
2910
                                            context.setDot(caretInfo, pos);
2911
                                            return;
2912
                                        }
2913
                                    } else {
2914
                                        offset = caretInfo.getDot();
2915
                                    }
2916
                                    int dot = target.getUI().getNextVisualPositionFrom(target,
2917
                                            offset, Position.Bias.Forward, SwingConstants.EAST, null);
2918
                                    Position dotPos = doc.createPosition(dot);
2919
                                    if (select) {
2920
                                        if (RectangularSelectionUtils.isRectangularSelection(target)) {
2921
                                            RectangularSelectionCaretAccessor.get().extendRectangularSelection(editorCaret, true, false);
2922
                                        } else {
2923
                                            context.moveDot(caretInfo, dotPos);
2924
                                        }
2925
                                    } else {
2926
                                        context.setDot(caretInfo, dotPos);
2927
                                    }
2928
                                } catch (BadLocationException ex) {
2929
                                    target.getToolkit().beep();
2930
                                }
2931
                            }
2932
                        }
2933
                    });
2934
                } else {
2427
                try {
2935
                try {
2428
                    int pos;
2936
                    int pos;
2429
                    boolean select = selectionForwardAction.equals(getValue(Action.NAME));
2937
                    boolean select = selectionForwardAction.equals(getValue(Action.NAME));
Lines 2443-2450 Link Here
2443
                    int dot = target.getUI().getNextVisualPositionFrom(target,
2951
                    int dot = target.getUI().getNextVisualPositionFrom(target,
2444
                              pos, Position.Bias.Forward, SwingConstants.EAST, null);
2952
                              pos, Position.Bias.Forward, SwingConstants.EAST, null);
2445
                    if (select) {
2953
                    if (select) {
2446
                        if (caret instanceof BaseCaret && RectangularSelectionUtils.isRectangularSelection(target)) {
2954
                        if (caret instanceof EditorCaret && RectangularSelectionUtils.isRectangularSelection(target)) {
2447
                            ((BaseCaret)caret).extendRectangularSelection(true, false);
2955
                            RectangularSelectionCaretAccessor.get().extendRectangularSelection((EditorCaret)caret, true, false);
2448
                        } else {
2956
                        } else {
2449
                            caret.moveDot(dot);
2957
                            caret.moveDot(dot);
2450
                        }
2958
                        }
Lines 2455-2460 Link Here
2455
                    target.getToolkit().beep();
2963
                    target.getToolkit().beep();
2456
                }
2964
                }
2457
            }
2965
            }
2966
            }
2458
        }
2967
        }
2459
    }
2968
    }
2460
2969
Lines 2472-2481 Link Here
2472
                | CLEAR_STATUS_TEXT);
2981
                | CLEAR_STATUS_TEXT);
2473
        }
2982
        }
2474
2983
2475
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
2984
        public void actionPerformed(ActionEvent evt, final JTextComponent target) {
2476
            if (target != null) {
2985
            if (target != null) {
2477
                try {
2986
                try {
2478
                    Caret caret = target.getCaret();
2987
                    Caret caret = target.getCaret();
2988
                    final Document doc = target.getDocument();
2989
                    if(doc != null && caret instanceof EditorCaret) {
2990
                        final EditorCaret editorCaret = (EditorCaret) caret;
2991
                        editorCaret.moveCarets(new CaretMoveHandler() {
2992
                            @Override
2993
                            public void moveCarets(CaretMoveContext context) {
2994
                                try {
2995
                                    for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
2996
                                        int caretOffset = caretInfo.getDot();
2997
                                        Rectangle caretBounds = ((BaseTextUI) target.getUI()).modelToView(target, caretOffset);
2998
                                        if (caretBounds == null) {
2999
                                            return; // Cannot continue reasonably
3000
                                        }
3001
3002
                                        // Retrieve caret magic position and attempt to retain
3003
                                        // the x-coordinate information and use it
3004
                                        // for setting of the new caret position
3005
                                        Point magicCaretPosition = caretInfo.getMagicCaretPosition();
3006
                                        if (magicCaretPosition == null) {
3007
                                            magicCaretPosition = new Point(caretBounds.x, caretBounds.y);
3008
                                        }
3009
3010
                                        Rectangle visibleBounds = target.getVisibleRect();
3011
                                        int newCaretOffset;
3012
                                        Rectangle newCaretBounds;
3013
3014
                                        // Check whether caret was contained in the original visible window
3015
                                        if (visibleBounds.contains(caretBounds)) {
3016
                                            // Clone present view bounds
3017
                                            Rectangle newVisibleBounds = new Rectangle(visibleBounds);
3018
                                            // Do viewToModel() and modelToView() with the left bottom corner
3019
                                            // of the currently visible view.
3020
                                            // That line should be the top line of the next page.
3021
                                            int bottomLeftOffset = target.viewToModel(new Point(
3022
                                                    visibleBounds.x, visibleBounds.y + visibleBounds.height));
3023
                                            Rectangle bottomLeftLineBounds = target.modelToView(bottomLeftOffset);
3024
3025
                                            // newVisibleBounds.y will hold bottom of new view
3026
                                            newVisibleBounds.y = bottomLeftLineBounds.y;
3027
3028
                                            // Find the new caret bounds by using relative y position
3029
                                            // on the original caret bounds. If the caret's new relative bounds
3030
                                            // would be visually below the old bounds
3031
                                            // the view should be shifted so that the relative bounds
3032
                                            // are the same (user's eyes do not need to move).
3033
                                            int caretRelY = caretBounds.y - visibleBounds.y;
3034
                                            int caretNewY = newVisibleBounds.y + caretRelY;
3035
                                            newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x, caretNewY));
3036
                                            newCaretBounds = target.modelToView(newCaretOffset);
3037
                                            if (newCaretBounds.y > caretNewY) {
3038
                                                // Need to go one line above to retain the top line
3039
                                                // of the present newVisibleBounds to be fully visible.
3040
                                                // Attempt to go up by height of caret.
3041
                                                newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x,
3042
                                                        newCaretBounds.y - newCaretBounds.height));
3043
                                                newCaretBounds = target.modelToView(newCaretOffset);
3044
                                            }
3045
3046
                                            // Shift the new visible bounds so that the caret
3047
                                            // does not visually move
3048
                                            newVisibleBounds.y = newCaretBounds.y - caretRelY;
3049
3050
                                            // Scroll the window to the requested rectangle
3051
                                            target.scrollRectToVisible(newVisibleBounds);
3052
3053
                                        } else { // Caret outside of originally visible window
3054
                                            // Shift the dot by the visible bounds height
3055
                                            Point newCaretPoint = new Point(magicCaretPosition.x,
3056
                                                    caretBounds.y + visibleBounds.height);
3057
                                            newCaretOffset = target.viewToModel(newCaretPoint);
3058
                                            newCaretBounds = target.modelToView(newCaretOffset);
3059
                                        }
3060
3061
                                        boolean select = selectionPageDownAction.equals(getValue(Action.NAME));
3062
                                        Position newCaretPos = doc.createPosition(newCaretOffset);
3063
                                        if (select) {
3064
                                            context.moveDot(caretInfo, newCaretPos);
3065
                                        } else {
3066
                                            context.setDot(caretInfo, newCaretPos);
3067
                                        }
3068
3069
                                        // Update magic caret position
3070
                                        magicCaretPosition.y = newCaretBounds.y;
3071
                                        context.setMagicCaretPosition(caretInfo, magicCaretPosition);
3072
                                    }
3073
                                } catch (BadLocationException ex) {
3074
                                    target.getToolkit().beep();
3075
                                }
3076
                            }
3077
                        });
3078
                    } else {
2479
                    int caretOffset = caret.getDot();
3079
                    int caretOffset = caret.getDot();
2480
                    Rectangle caretBounds = ((BaseTextUI)target.getUI()).modelToView(target, caretOffset);
3080
                    Rectangle caretBounds = ((BaseTextUI)target.getUI()).modelToView(target, caretOffset);
2481
                    if (caretBounds == null) {
3081
                    if (caretBounds == null) {
Lines 2551-2606 Link Here
2551
                    // Update magic caret position
3151
                    // Update magic caret position
2552
                    magicCaretPosition.y = newCaretBounds.y;
3152
                    magicCaretPosition.y = newCaretBounds.y;
2553
                    caret.setMagicCaretPosition(magicCaretPosition);
3153
                    caret.setMagicCaretPosition(magicCaretPosition);
2554
                    
2555
                } catch (BadLocationException ex) {
2556
                    target.getToolkit().beep();
2557
                }
2558
            }
2559
        }
2560
    }
2561
2562
    @EditorActionRegistrations({
2563
        @EditorActionRegistration(name = backwardAction),
2564
        @EditorActionRegistration(name = selectionBackwardAction)
2565
    })
2566
    public static class BackwardAction extends LocalBaseAction {
2567
2568
        static final long serialVersionUID =-3048379822817847356L;
2569
2570
        public BackwardAction() {
2571
            super(MAGIC_POSITION_RESET | ABBREV_RESET | UNDO_MERGE_RESET
2572
                  | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2573
        }
2574
2575
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
2576
            if (target != null) {
2577
                Caret caret = target.getCaret();
2578
                try {
2579
                    int pos;
2580
                    boolean select = selectionBackwardAction.equals(getValue(Action.NAME));
2581
                    if (!select && Utilities.isSelectionShowing(caret))
2582
                    {
2583
                        pos = target.getSelectionStart(); 
2584
                        if (pos != caret.getDot()) {
2585
                            pos++;
2586
                        } else {
2587
                            // clear the selection, but do not move the cursor
2588
                            caret.setDot(pos);
2589
                            return;
2590
                        }
2591
                    }
2592
                    else
2593
                        pos = caret.getDot();
2594
                    int dot = target.getUI().getNextVisualPositionFrom(target,
2595
                              pos, Position.Bias.Backward, SwingConstants.WEST, null);
2596
                    if (select) {
2597
                        if (caret instanceof BaseCaret && RectangularSelectionUtils.isRectangularSelection(target)) {
2598
                            ((BaseCaret)caret).extendRectangularSelection(false, false);
2599
                        } else {
2600
                            caret.moveDot(dot);
2601
                        }
2602
                    } else {
2603
                        caret.setDot(dot);
2604
                    }
3154
                    }
2605
                } catch (BadLocationException ex) {
3155
                } catch (BadLocationException ex) {
2606
                    target.getToolkit().beep();
3156
                    target.getToolkit().beep();
Lines 2609-2614 Link Here
2609
        }
3159
        }
2610
    }
3160
    }
2611
3161
3162
    @EditorActionRegistrations({
3163
        @EditorActionRegistration(name = backwardAction),
3164
        @EditorActionRegistration(name = selectionBackwardAction)
3165
    })
3166
    public static class BackwardAction extends LocalBaseAction {
3167
3168
        static final long serialVersionUID = -3048379822817847356L;
3169
3170
        public BackwardAction() {
3171
            super(MAGIC_POSITION_RESET | ABBREV_RESET | UNDO_MERGE_RESET
3172
                    | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
3173
        }
3174
3175
        public void actionPerformed(ActionEvent evt, final JTextComponent target) {
3176
            if (target != null) {
3177
                Caret caret = target.getCaret();
3178
                final Document doc = target.getDocument();
3179
                if (doc != null && caret instanceof EditorCaret) {
3180
                    EditorCaret editorCaret = (EditorCaret) caret;
3181
                    editorCaret.moveCarets(new CaretMoveHandler() {
3182
                        @Override
3183
                        public void moveCarets(CaretMoveContext context) {
3184
                            for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
3185
                                try {
3186
                                    int offset;
3187
                                    boolean select = selectionBackwardAction.equals(getValue(Action.NAME));
3188
                                    if (!select && caretInfo.isSelectionShowing()) {
3189
                                        offset = caretInfo.getSelectionStart();
3190
                                        if (offset != caretInfo.getDot()) {
3191
                                            offset++;
3192
                                        } else {
3193
                                            // clear the selection, but do not move the cursor
3194
                                            context.setDot(caretInfo, doc.createPosition(offset));
3195
                                            return;
3196
                                        }
3197
                                    } else {
3198
                                        offset = caretInfo.getDot();
3199
                                    }
3200
                                    int dot = target.getUI().getNextVisualPositionFrom(target,
3201
                                            offset, Position.Bias.Backward, SwingConstants.WEST, null);
3202
                                    Position dotPos = doc.createPosition(dot);
3203
                                    if (select) {
3204
                                        context.moveDot(caretInfo, dotPos);
3205
                                    } else {
3206
                                        context.setDot(caretInfo, dotPos);
3207
                                    }
3208
                                } catch (BadLocationException ex) {
3209
                                    target.getToolkit().beep();
3210
                                }
3211
                            }
3212
                        }
3213
                    });
3214
                } else {
3215
                    try {
3216
                        int pos;
3217
                        boolean select = selectionBackwardAction.equals(getValue(Action.NAME));
3218
                        if (!select && Utilities.isSelectionShowing(caret)) {
3219
                            pos = target.getSelectionStart();
3220
                            if (pos != caret.getDot()) {
3221
                                pos++;
3222
                            } else {
3223
                                // clear the selection, but do not move the cursor
3224
                                caret.setDot(pos);
3225
                                return;
3226
                            }
3227
                        } else {
3228
                            pos = caret.getDot();
3229
                        }
3230
                        int dot = target.getUI().getNextVisualPositionFrom(target,
3231
                                pos, Position.Bias.Backward, SwingConstants.WEST, null);
3232
                        if (select) {
3233
                            if (caret instanceof EditorCaret && RectangularSelectionUtils.isRectangularSelection(target)) {
3234
                                RectangularSelectionCaretAccessor.get().extendRectangularSelection((EditorCaret)caret, false, false);
3235
                            } else {
3236
                                caret.moveDot(dot);
3237
                            }
3238
                        } else {
3239
                            caret.setDot(dot);
3240
                        }
3241
                    } catch (BadLocationException ex) {
3242
                        target.getToolkit().beep();
3243
                    }
3244
                }
3245
            }
3246
        }
3247
    }
3248
2612
    public static class BeginLineAction extends LocalBaseAction {
3249
    public static class BeginLineAction extends LocalBaseAction {
2613
3250
2614
        @EditorActionRegistrations({
3251
        @EditorActionRegistrations({
Lines 2639-2648 Link Here
2639
            homeKeyColumnOne = columnOne;
3276
            homeKeyColumnOne = columnOne;
2640
        }
3277
        }
2641
3278
2642
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
3279
        public void actionPerformed(ActionEvent evt, final JTextComponent target) {
2643
            if (target != null) {
3280
            if (target != null) {
2644
                Caret caret = target.getCaret();
3281
                Caret caret = target.getCaret();
2645
                BaseDocument doc = (BaseDocument)target.getDocument();
3282
                final Document doc = target.getDocument();
3283
                if (doc != null && caret instanceof EditorCaret) {
3284
                    EditorCaret editorCaret = (EditorCaret) caret;
3285
                    editorCaret.moveCarets(new CaretMoveHandler() {
3286
                        @Override
3287
                        public void moveCarets(CaretMoveContext context) {
3288
                            for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
3289
                                try {
3290
                                    int dot = caretInfo.getDot();
3291
                                    // #232675: if bounds are defined, use them rather than line start/end
3292
                                    Object o = target.getClientProperty(PROP_NAVIGATE_BOUNDARIES);
3293
                                    PositionRegion bounds = null;
3294
                                    if (o instanceof PositionRegion) {
3295
                                        bounds = (PositionRegion) o;
3296
                                        int start = bounds.getStartOffset();
3297
                                        int end = bounds.getEndOffset();
3298
                                        if (dot > start && dot <= end) {
3299
                                            // move to the region start
3300
                                            dot = start;
3301
                                        } else {
3302
                                            bounds = null;
3303
                                        }
3304
                                    }
3305
3306
                                    if (bounds == null) {
3307
                                        int lineStartPos = Utilities.getRowStart(target, dot);
3308
                                        if (homeKeyColumnOne) { // to first column
3309
                                            dot = lineStartPos;
3310
                                        } else { // either to line start or text start
3311
                                            BaseDocument doc = (BaseDocument) target.getDocument();
3312
                                            int textStartPos = Utilities.getRowFirstNonWhite(doc, lineStartPos);
3313
                                            if (textStartPos < 0) { // no text on the line
3314
                                                textStartPos = Utilities.getRowEnd(target, lineStartPos);
3315
                                            }
3316
                                            if (dot == lineStartPos) { // go to the text start pos
3317
                                                dot = textStartPos;
3318
                                            } else if (dot <= textStartPos) {
3319
                                                dot = lineStartPos;
3320
                                            } else {
3321
                                                dot = textStartPos;
3322
                                            }
3323
                                        }
3324
                                    }
3325
                                    // For partial view hierarchy check bounds
3326
                                    dot = Math.max(dot, target.getUI().getRootView(target).getStartOffset());
3327
                                    String actionName = (String) getValue(Action.NAME);
3328
                                    boolean select = selectionBeginLineAction.equals(actionName)
3329
                                            || selectionLineFirstColumnAction.equals(actionName);
3330
3331
                                    // If possible scroll the view to its begining horizontally
3332
                                    // to ease user's orientation in the code.
3333
                                    Rectangle r = target.modelToView(dot);
3334
                                    Rectangle visRect = target.getVisibleRect();
3335
                                    if (r.getMaxX() < visRect.getWidth()) {
3336
                                        r.x = 0;
3337
                                        target.scrollRectToVisible(r);
3338
                                    }
3339
3340
                                    Position dotPos = doc.createPosition(dot);
3341
                                    if (select) {
3342
                                        context.moveDot(caretInfo, dotPos);
3343
                                    } else {
3344
                                        context.setDot(caretInfo, dotPos);
3345
                                    }
3346
                                } catch (BadLocationException e) {
3347
                                    target.getToolkit().beep();
3348
                                }
3349
                            }
3350
                        }
3351
                    });
3352
                } else {
2646
                try {
3353
                try {
2647
                    int dot = caret.getDot();
3354
                    int dot = caret.getDot();
2648
                    // #232675: if bounds are defined, use them rather than line start/end
3355
                    // #232675: if bounds are defined, use them rather than line start/end
Lines 2665-2671 Link Here
2665
                        if (homeKeyColumnOne) { // to first column
3372
                        if (homeKeyColumnOne) { // to first column
2666
                            dot = lineStartPos;
3373
                            dot = lineStartPos;
2667
                        } else { // either to line start or text start
3374
                        } else { // either to line start or text start
2668
                            int textStartPos = Utilities.getRowFirstNonWhite(doc, lineStartPos);
3375
                            int textStartPos = Utilities.getRowFirstNonWhite(((BaseDocument)doc), lineStartPos);
2669
                            if (textStartPos < 0) { // no text on the line
3376
                            if (textStartPos < 0) { // no text on the line
2670
                                textStartPos = Utilities.getRowEnd(target, lineStartPos);
3377
                                textStartPos = Utilities.getRowEnd(target, lineStartPos);
2671
                            }
3378
                            }
Lines 2702-2707 Link Here
2702
                    target.getToolkit().beep();
3409
                    target.getToolkit().beep();
2703
                }
3410
                }
2704
            }
3411
            }
3412
            }
2705
        }
3413
        }
2706
    }
3414
    }
2707
3415
Lines 2718-2726 Link Here
2718
                  | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
3426
                  | WORD_MATCH_RESET | CLEAR_STATUS_TEXT);
2719
        }
3427
        }
2720
3428
2721
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
3429
        public void actionPerformed(ActionEvent evt, final JTextComponent target) {
2722
            if (target != null) {
3430
            if (target != null) {
2723
                Caret caret = target.getCaret();
3431
                Caret caret = target.getCaret();
3432
                if(caret instanceof EditorCaret) {
3433
                    ((EditorCaret) caret).moveCarets(new org.netbeans.spi.editor.caret.CaretMoveHandler() {
3434
                        @Override
3435
                        public void moveCarets(CaretMoveContext context) {
3436
                            for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
3437
                                try {
3438
                                    // #232675: if bounds are defined, use them rather than line start/end
3439
                                    Object o = target.getClientProperty(PROP_NAVIGATE_BOUNDARIES);
3440
                                    int dot = -1;
3441
3442
                                    if (o instanceof PositionRegion) {
3443
                                        PositionRegion bounds = (PositionRegion) o;
3444
                                        int start = bounds.getStartOffset();
3445
                                        int end = bounds.getEndOffset();
3446
                                        int d = caretInfo.getDot();
3447
                                        if (d >= start && d < end) {
3448
                                            // move to the region start
3449
                                            dot = end;
3450
                                        }
3451
                                    }
3452
3453
                                    if (dot == -1) {
3454
                                        dot = Utilities.getRowEnd(target, caretInfo.getDot());
3455
                                    }
3456
3457
                                    // For partial view hierarchy check bounds
3458
                                    dot = Math.min(dot, target.getUI().getRootView(target).getEndOffset());
3459
                                    boolean select = selectionEndLineAction.equals(getValue(Action.NAME));
3460
                                    Position dotPos = context.getDocument().createPosition(dot);
3461
                                    if (select) {
3462
                                        context.moveDot(caretInfo, dotPos);
3463
                                    } else {
3464
                                        context.setDot(caretInfo, dotPos);
3465
                                    }
3466
                                    // now move the magic caret position far to the right
3467
                                    Rectangle r = target.modelToView(dot);
3468
                                    if (r != null) {
3469
                                        Point p = new Point(MAGIC_POSITION_MAX, r.y);
3470
                                        context.setMagicCaretPosition(caretInfo, p);
3471
                                    }
3472
                                } catch (BadLocationException e) {
3473
                                    e.printStackTrace();
3474
                                    target.getToolkit().beep();
3475
                                }
3476
                            }
3477
                        }
3478
                    });
3479
                    
3480
                } else {
2724
                try {
3481
                try {
2725
                    // #232675: if bounds are defined, use them rather than line start/end
3482
                    // #232675: if bounds are defined, use them rather than line start/end
2726
                    Object o = target.getClientProperty(PROP_NAVIGATE_BOUNDARIES);
3483
                    Object o = target.getClientProperty(PROP_NAVIGATE_BOUNDARIES);
Lines 2760-2765 Link Here
2760
                    target.getToolkit().beep();
3517
                    target.getToolkit().beep();
2761
                }
3518
                }
2762
            }
3519
            }
3520
            }
2763
        }
3521
        }
2764
    }
3522
    }
2765
3523
Lines 2852-2868 Link Here
2852
                    int newDotOffset = getNextWordOffset(target);
3610
                    int newDotOffset = getNextWordOffset(target);
2853
                    boolean select = selectionNextWordAction.equals(getValue(Action.NAME));
3611
                    boolean select = selectionNextWordAction.equals(getValue(Action.NAME));
2854
                    if (select) {
3612
                    if (select) {
2855
                        if (caret instanceof BaseCaret && RectangularSelectionUtils.isRectangularSelection(target)) {
3613
                        if (caret instanceof EditorCaret && RectangularSelectionUtils.isRectangularSelection(target)) {
2856
                            ((BaseCaret) caret).extendRectangularSelection(true, true);
3614
                            RectangularSelectionCaretAccessor.get().extendRectangularSelection((EditorCaret)caret, true, true);
2857
                        } else {
3615
                        } else {
2858
                            caret.moveDot(newDotOffset);
3616
                            caret.moveDot(newDotOffset);
2859
                        }
3617
                        }
2860
                    } else {
3618
                    } else {
2861
                        if (caret instanceof BaseCaret) {
3619
                        caret.setDot(newDotOffset);
2862
                            ((BaseCaret)caret).setDot(newDotOffset, false);
2863
                        } else {
2864
                            caret.setDot(newDotOffset);
2865
                        }
2866
                    }
3620
                    }
2867
                } catch (BadLocationException ex) {
3621
                } catch (BadLocationException ex) {
2868
                    target.getToolkit().beep();
3622
                    target.getToolkit().beep();
Lines 2898-2914 Link Here
2898
                    int newDotOffset = getPreviousWordOffset(target);
3652
                    int newDotOffset = getPreviousWordOffset(target);
2899
                    boolean select = selectionPreviousWordAction.equals(getValue(Action.NAME));
3653
                    boolean select = selectionPreviousWordAction.equals(getValue(Action.NAME));
2900
                    if (select) {
3654
                    if (select) {
2901
                        if (caret instanceof BaseCaret && RectangularSelectionUtils.isRectangularSelection(target)) {
3655
                        if (caret instanceof EditorCaret && RectangularSelectionUtils.isRectangularSelection(target)) {
2902
                            ((BaseCaret) caret).extendRectangularSelection(false, true);
3656
                            RectangularSelectionCaretAccessor.get().extendRectangularSelection((EditorCaret)caret, false, true);
2903
                        } else {
3657
                        } else {
2904
                            caret.moveDot(newDotOffset);
3658
                            caret.moveDot(newDotOffset);
2905
                        }
3659
                        }
2906
                    } else {
3660
                    } else {
2907
                        if (caret instanceof BaseCaret) {
3661
                        caret.setDot(newDotOffset);
2908
                            ((BaseCaret)caret).setDot(newDotOffset, false);
2909
                        } else {
2910
                            caret.setDot(newDotOffset);
2911
                        }
2912
                    }
3662
                    }
2913
                } catch (BadLocationException ex) {
3663
                } catch (BadLocationException ex) {
2914
                    target.getToolkit().beep();
3664
                    target.getToolkit().beep();
Lines 3008-3014 Link Here
3008
            if (target != null) {
3758
            if (target != null) {
3009
                final Caret caret = target.getCaret();
3759
                final Caret caret = target.getCaret();
3010
                final BaseDocument doc = (BaseDocument)target.getDocument();
3760
                final BaseDocument doc = (BaseDocument)target.getDocument();
3011
                doc.runAtomicAsUser (new Runnable () {
3761
                doc.render(new Runnable () {
3012
                    public void run () {
3762
                    public void run () {
3013
                        DocumentUtilities.setTypingModification(doc, true);
3763
                        DocumentUtilities.setTypingModification(doc, true);
3014
                        try {
3764
                        try {
Lines 3171-3176 Link Here
3171
            }
3921
            }
3172
        }
3922
        }
3173
    } // End of DefaultSyntaxTokenContext class
3923
    } // End of DefaultSyntaxTokenContext class
3924
3925
    private static class CaretEdit extends AbstractUndoableEdit {
3926
3927
        private final JTextComponent target;
3928
        private final List<Position> offsets;
3929
3930
        public CaretEdit(List<Position> offsets, JTextComponent target) {
3931
            this.target = target;
3932
            this.offsets = offsets;
3933
        }
3934
3935
        @Override
3936
        public void undo() throws CannotUndoException {
3937
            EditorCaret caret = (EditorCaret) target.getCaret();
3938
            caret.replaceCarets(offsets);
3939
        }
3940
3941
        @Override
3942
        public void redo() throws CannotRedoException {
3943
            EditorCaret caret = (EditorCaret) target.getCaret();
3944
            caret.replaceCarets(offsets);
3945
        }
3946
    }
3174
    
3947
    
3175
    private class KeybindingsAndPreferencesTracker implements LookupListener, PreferenceChangeListener {
3948
    private class KeybindingsAndPreferencesTracker implements LookupListener, PreferenceChangeListener {
3176
        
3949
        
(-)a/editor.lib/src/org/netbeans/editor/EditorUI.java (-1 / +5 lines)
Lines 82-87 Link Here
82
import javax.swing.plaf.TextUI;
82
import javax.swing.plaf.TextUI;
83
import javax.swing.text.*;
83
import javax.swing.text.*;
84
import javax.swing.undo.UndoManager;
84
import javax.swing.undo.UndoManager;
85
import org.netbeans.api.editor.EditorUtilities;
85
import org.netbeans.api.editor.mimelookup.MimeLookup;
86
import org.netbeans.api.editor.mimelookup.MimeLookup;
86
import org.netbeans.api.editor.mimelookup.MimePath;
87
import org.netbeans.api.editor.mimelookup.MimePath;
87
import org.netbeans.api.editor.settings.EditorStyleConstants;
88
import org.netbeans.api.editor.settings.EditorStyleConstants;
Lines 655-661 Link Here
655
            if (!component.isEnabled()) {
656
            if (!component.isEnabled()) {
656
                component.getCaret().setVisible(false);
657
                component.getCaret().setVisible(false);
657
            }
658
            }
658
        } 
659
        } else if (EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY.equals(propName)) {
660
            // Mirror the property into EditorUI
661
            putProperty(OVERWRITE_MODE_PROPERTY, component.getClientProperty(EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY));
662
        }
659
        
663
        
660
        if (propName == null || ColoringMap.PROP_COLORING_MAP.equals(propName)) {
664
        if (propName == null || ColoringMap.PROP_COLORING_MAP.equals(propName)) {
661
            listener.preferenceChange(null);
665
            listener.preferenceChange(null);
(-)a/editor.lib/src/org/netbeans/editor/ext/ExtKit.java (-5 lines)
Lines 177-187 Link Here
177
    public ExtKit() {
177
    public ExtKit() {
178
    }
178
    }
179
179
180
    /** Create caret to navigate through document */
181
    public @Override Caret createCaret() {
182
        return new ExtCaret();
183
    }
184
185
    public @Override SyntaxSupport createSyntaxSupport(BaseDocument doc) {
180
    public @Override SyntaxSupport createSyntaxSupport(BaseDocument doc) {
186
        return new ExtSyntaxSupport(doc);
181
        return new ExtSyntaxSupport(doc);
187
    }
182
    }
(-)a/editor.lib2/apichanges.xml (-1 / +35 lines)
Lines 105-112 Link Here
105
    </apidefs>
105
    </apidefs>
106
106
107
    <!-- ACTUAL CHANGES BEGIN HERE: -->
107
    <!-- ACTUAL CHANGES BEGIN HERE: -->
108
108
    
109
    <changes>
109
    <changes>
110
        <change id="editor-caret">
111
            <summary>Caret API introduced</summary>
112
            <version major="2" minor="7"/>
113
            <date day="22" month="2" year="2016"/>
114
            <author login="mmetelka"/>
115
            <author login="ralphbenjamin"/>
116
            <compatibility binary="compatible" semantic="compatible" source="compatible" addition="yes" deprecation="no" deletion="no"/>
117
            <description>
118
                <p>
119
                    The Caret API was introduced to allow working with multiple
120
                    carets within one document.
121
                </p>
122
            </description>
123
            <class name="EditorActionNames" package="org.netbeans.api.editor" />
124
            <class name="EditorUtilities" package="org.netbeans.api.editor" />
125
            <package name="org.netbeans.api.editor.caret" />
126
            <issue number="257893"/>
127
        </change>
110
        <change id="occurrences-names">
128
        <change id="occurrences-names">
111
            <summary>Added IDs for goto prev/next occurrence actions</summary>
129
            <summary>Added IDs for goto prev/next occurrence actions</summary>
112
            <version major="2" minor="6"/>
130
            <version major="2" minor="6"/>
Lines 122-127 Link Here
122
            <class name="EditorActionNames" package="org.netbeans.api.editor"/>
140
            <class name="EditorActionNames" package="org.netbeans.api.editor"/>
123
            <issue number="194356"/>
141
            <issue number="194356"/>
124
        </change>
142
        </change>
143
        <change id="shift-highlights-sequence">
144
            <summary>Extended HightlightsSequence by ShiftHighlightsSequence</summary>
145
            <version major="2" minor="5"/>
146
            <date day="22" month="2" year="2016"/>
147
            <author login="mmetelka"/>
148
            <compatibility binary="compatible" semantic="compatible" source="compatible" addition="yes" deprecation="no" deletion="no"/>
149
            <description>
150
                <p>
151
        Highlights sequence that supports shifts in addition to regular offsets.
152
        This allows to color individual spaces within a tab character
153
        or to color extra virtual characters beyond a newline character.
154
                </p>
155
            </description>
156
            <class name="ShiftHighlightsSequence" package="org.netbeans.spi.editor.highlighting"/>
157
            <issue number="XXX"/>
158
        </change>
125
        <change id="releasable-highlights-container">
159
        <change id="releasable-highlights-container">
126
            <summary>ReleasableHighlightsContainer added</summary>
160
            <summary>ReleasableHighlightsContainer added</summary>
127
            <version major="2" minor="4"/>
161
            <version major="2" minor="4"/>
(-)a/editor.lib2/arch.xml (-3 / +12 lines)
Lines 87-93 Link Here
87
</li>    
87
</li>    
88
<li>
88
<li>
89
    <api name="editor-typing-hooks" group="java" type="export" category="official" url="@org-netbeans-modules-editor-lib2@/org/netbeans/spi/editor/typinghooks/package-summary.html">Typing Hooks SPI</api>
89
    <api name="editor-typing-hooks" group="java" type="export" category="official" url="@org-netbeans-modules-editor-lib2@/org/netbeans/spi/editor/typinghooks/package-summary.html">Typing Hooks SPI</api>
90
</li>    
90
</li>
91
<li>
92
    <api name="editor-caret" group="java" type="export" category="official" url="@org-netbeans-modules-editor-lib2@/org/netbeans/api/editor/caret/package-summary.html">Editor Caret API</api>
93
</li>
91
</ul>
94
</ul>
92
</answer>
95
</answer>
93
96
Lines 126-133 Link Here
126
        </question>
129
        </question>
127
-->
130
-->
128
<answer id="arch-usecases">
131
<answer id="arch-usecases">
129
    At the moment the Editor Library 2 module contains three distinct SPIs. Each SPI lives
132
    At the moment the Editor Library 2 module contains distinct APIs/SPIs. They live
130
    in its own package and the usecases can be found in the packages overview.
133
    in their own package and the usecases can be found in the packages overview.
131
    <ul>
134
    <ul>
132
    <li>Code Generator SPI -
135
    <li>Code Generator SPI -
133
        <a href="@org-netbeans-modules-editor-lib2@/org/netbeans/spi/editor/codegen/package-summary.html#usecases">org.netbeans.spi.editor.codegen</a>
136
        <a href="@org-netbeans-modules-editor-lib2@/org/netbeans/spi/editor/codegen/package-summary.html#usecases">org.netbeans.spi.editor.codegen</a>
Lines 138-143 Link Here
138
    <li>Typing Hooks SPI -
141
    <li>Typing Hooks SPI -
139
        <a href="@org-netbeans-modules-editor-lib2@/org/netbeans/spi/editor/typinghooks/package-summary.html#usecases">org.netbeans.spi.editor.typinghooks</a>
142
        <a href="@org-netbeans-modules-editor-lib2@/org/netbeans/spi/editor/typinghooks/package-summary.html#usecases">org.netbeans.spi.editor.typinghooks</a>
140
    </li>
143
    </li>
144
    <li>Caret API -
145
        <a href="@org-netbeans-modules-editor-lib2@/org/netbeans/api/editor/caret/package-summary.html#usecases">org.netbeans.api.editor.caret</a>
146
    </li>
147
    <li>Caret SPI -
148
        <a href="@org-netbeans-modules-editor-lib2@/org/netbeans/spi/editor/caret/package-summary.html#usecases">org.netbeans.spi.editor.caret</a>
149
    </li>
141
    </ul>
150
    </ul>
142
</answer>
151
</answer>
143
152
(-)a/editor.lib2/nbproject/project.xml (-10 / +11 lines)
Lines 90-96 Link Here
90
                    <compile-dependency/>
90
                    <compile-dependency/>
91
                    <run-dependency>
91
                    <run-dependency>
92
                        <release-version>1</release-version>
92
                        <release-version>1</release-version>
93
                        <specification-version>1.44</specification-version>
93
                        <specification-version>1.63</specification-version>
94
                    </run-dependency>
94
                    </run-dependency>
95
                </dependency>
95
                </dependency>
96
                <dependency>
96
                <dependency>
Lines 127-140 Link Here
127
                    </run-dependency>
127
                    </run-dependency>
128
                </dependency>
128
                </dependency>
129
                <dependency>
129
                <dependency>
130
                    <code-name-base>org.openide.util.ui</code-name-base>
131
                    <build-prerequisite/>
132
                    <compile-dependency/>
133
                    <run-dependency>
134
                        <specification-version>9.3</specification-version>
135
                    </run-dependency>
136
                </dependency>
137
                <dependency>
138
                    <code-name-base>org.openide.util</code-name-base>
130
                    <code-name-base>org.openide.util</code-name-base>
139
                    <build-prerequisite/>
131
                    <build-prerequisite/>
140
                    <compile-dependency/>
132
                    <compile-dependency/>
Lines 150-155 Link Here
150
                        <specification-version>8.0</specification-version>
142
                        <specification-version>8.0</specification-version>
151
                    </run-dependency>
143
                    </run-dependency>
152
                </dependency>
144
                </dependency>
145
                <dependency>
146
                    <code-name-base>org.openide.util.ui</code-name-base>
147
                    <build-prerequisite/>
148
                    <compile-dependency/>
149
                    <run-dependency>
150
                        <specification-version>9.3</specification-version>
151
                    </run-dependency>
152
                </dependency>
153
            </module-dependencies>
153
            </module-dependencies>
154
            <test-dependencies>
154
            <test-dependencies>
155
                <test-type>
155
                <test-type>
Lines 209-216 Link Here
209
            </test-dependencies>
209
            </test-dependencies>
210
            <public-packages>
210
            <public-packages>
211
                <package>org.netbeans.api.editor</package>
211
                <package>org.netbeans.api.editor</package>
212
                <package>org.netbeans.api.editor.document</package>
212
                <package>org.netbeans.api.editor.caret</package>
213
                <package>org.netbeans.spi.editor</package>
213
                <package>org.netbeans.spi.editor</package>
214
                <package>org.netbeans.spi.editor.caret</package>
214
                <package>org.netbeans.spi.editor.codegen</package>
215
                <package>org.netbeans.spi.editor.codegen</package>
215
                <package>org.netbeans.spi.editor.document</package>
216
                <package>org.netbeans.spi.editor.document</package>
216
                <package>org.netbeans.spi.editor.highlighting</package>
217
                <package>org.netbeans.spi.editor.highlighting</package>
(-)a/editor.lib2/src/org/netbeans/api/editor/EditorActionNames.java (-1 / +27 lines)
Lines 81-87 Link Here
81
81
82
    /**
82
    /**
83
     * Zoom text in by increasing default font size.
83
     * Zoom text in by increasing default font size.
84
     * <br/>
84
     * <br>
85
     * textComponent.getClientProperty("text-zoom") contains positive (or negative)
85
     * textComponent.getClientProperty("text-zoom") contains positive (or negative)
86
     * integer of how many points the font size should be increased (decreased).
86
     * integer of how many points the font size should be increased (decreased).
87
     * @since 1.45
87
     * @since 1.45
Lines 136-141 Link Here
136
     * @since 1.64
136
     * @since 1.64
137
     */
137
     */
138
    public static final String organizeMembers = "organize-members"; // NOI18N
138
    public static final String organizeMembers = "organize-members"; // NOI18N
139
140
141
    /**
142
     * Toggle caret between regular insert mode and overwrite mode.
143
     * @since 1.31
144
     */
145
    public static final String toggleTypingMode = "toggle-typing-mode"; // NOI18N
146
    
147
    /**
148
     * Remove the last added caret.
149
     * @since 2.6
150
     */
151
    public static final String removeLastCaret = "remove-last-caret"; // NOI18N
139
    
152
    
140
    /**
153
    /**
141
     * Navigates to the previous occurence of the symbol under the caret. The action
154
     * Navigates to the previous occurence of the symbol under the caret. The action
Lines 150-153 Link Here
150
     * @since 2.3
163
     * @since 2.3
151
     */
164
     */
152
    public static final String gotoNextOccurrence = "next-marked-occurrence"; // NOI18N
165
    public static final String gotoNextOccurrence = "next-marked-occurrence"; // NOI18N
166
    
167
    /**
168
     * Add a caret to the line above at the same position.
169
     * @since 2.6
170
     */
171
    public static final String addSelectionElseCaretUpAction = "add-selection-else-caret-up"; // NOI18N
172
    
173
    /**
174
     * Add a caret to the line below at the same position.
175
     * @since 2.6
176
     */
177
    public static final String addSelectionElseCaretDownAction = "add-selection-else-caret-down"; // NOI18N
178
153
}
179
}
(-)a/editor.lib2/src/org/netbeans/api/editor/EditorActionRegistration.java (-14 / +14 lines)
Lines 52-58 Link Here
52
/**
52
/**
53
 * Registration of an editor action so that it's automatically added into the list
53
 * Registration of an editor action so that it's automatically added into the list
54
 * of editor actions even without being explicitly created by <code>BaseKit.createActions()</code>.
54
 * of editor actions even without being explicitly created by <code>BaseKit.createActions()</code>.
55
 * <br/>
55
 * <br>
56
 * The corresponding annotation processor will build a xml-layer entry file
56
 * The corresponding annotation processor will build a xml-layer entry file
57
 * in the corresponding <i>/Editors/&lt;mime-type&gt;/Actions</code> folder.
57
 * in the corresponding <i>/Editors/&lt;mime-type&gt;/Actions</code> folder.
58
 *
58
 *
Lines 65-71 Link Here
65
65
66
    /**
66
    /**
67
     * Name of the action that will appear as <code>Action.NAME</code> attribute's value.
67
     * Name of the action that will appear as <code>Action.NAME</code> attribute's value.
68
     * <br/>
68
     * <br>
69
     * The Swing's text package actions use convention of lowercase letters with hyphens
69
     * The Swing's text package actions use convention of lowercase letters with hyphens
70
     * e.g. "caret-end-word" - see String constants in {@link javax.swing.text.DefaultEditorKit}.
70
     * e.g. "caret-end-word" - see String constants in {@link javax.swing.text.DefaultEditorKit}.
71
     *
71
     *
Lines 75-81 Link Here
75
75
76
    /**
76
    /**
77
     * Mime type for which the action will be registered.
77
     * Mime type for which the action will be registered.
78
     * <br/>
78
     * <br>
79
     * It implies the target folder of the registration <i>/Editors/&lt;mime-type&gt;/Actions</code>.
79
     * It implies the target folder of the registration <i>/Editors/&lt;mime-type&gt;/Actions</code>.
80
     *
80
     *
81
     * @return mime-type of the action registration (for example "text/x-java"
81
     * @return mime-type of the action registration (for example "text/x-java"
Lines 117-126 Link Here
117
     * Menu text bundle key of the registered action.
117
     * Menu text bundle key of the registered action.
118
     * If an empty string is used (the default) it will be set to the same value
118
     * If an empty string is used (the default) it will be set to the same value
119
     * like action's short description.
119
     * like action's short description.
120
     * <br/>
120
     * <br>
121
     * Value starting with a hash "#key" searches in a bundle in the same package
121
     * Value starting with a hash "#key" searches in a bundle in the same package
122
     * as the action's class.
122
     * as the action's class.
123
     * <br/>
123
     * <br>
124
     * "bundle#key" allows specification of both bundle and a corresponding key.
124
     * "bundle#key" allows specification of both bundle and a corresponding key.
125
     */
125
     */
126
    String menuText() default "";
126
    String menuText() default "";
Lines 129-138 Link Here
129
     * Popup menu text bundle key of the registered action.
129
     * Popup menu text bundle key of the registered action.
130
     * If an empty string is used (the default) it will be set to the same value
130
     * If an empty string is used (the default) it will be set to the same value
131
     * like menu text.
131
     * like menu text.
132
     * <br/>
132
     * <br>
133
     * Value starting with a hash "#key" searches in a bundle in the same package
133
     * Value starting with a hash "#key" searches in a bundle in the same package
134
     * as the action's class.
134
     * as the action's class.
135
     * <br/>
135
     * <br>
136
     * "bundle#key" allows specification of both bundle and a corresponding key.
136
     * "bundle#key" allows specification of both bundle and a corresponding key.
137
     */
137
     */
138
    String popupText() default "";
138
    String popupText() default "";
Lines 144-150 Link Here
144
144
145
    /**
145
    /**
146
     * Integer position of the main menu item among the other menu items.
146
     * Integer position of the main menu item among the other menu items.
147
     * <br/>
147
     * <br>
148
     * The default Integer.MAX_VALUE value means no menu representation.
148
     * The default Integer.MAX_VALUE value means no menu representation.
149
     */
149
     */
150
    int menuPosition() default Integer.MAX_VALUE;
150
    int menuPosition() default Integer.MAX_VALUE;
Lines 157-177 Link Here
157
157
158
    /**
158
    /**
159
     * Integer position of the popup menu item among the other popup menu (or submenu) items.
159
     * Integer position of the popup menu item among the other popup menu (or submenu) items.
160
     * <br/>
160
     * <br>
161
     * The default Integer.MAX_VALUE value means no popup menu representation.
161
     * The default Integer.MAX_VALUE value means no popup menu representation.
162
     */
162
     */
163
    int popupPosition() default Integer.MAX_VALUE;
163
    int popupPosition() default Integer.MAX_VALUE;
164
164
165
    /**
165
    /**
166
     * Integer position of this action in editor toolbar.
166
     * Integer position of this action in editor toolbar.
167
     * <br/>
167
     * <br>
168
     * The default Integer.MAX_VALUE value means no toolbar representation.
168
     * The default Integer.MAX_VALUE value means no toolbar representation.
169
     */
169
     */
170
    int toolBarPosition() default Integer.MAX_VALUE;
170
    int toolBarPosition() default Integer.MAX_VALUE;
171
    
171
    
172
    /**
172
    /**
173
     * True if the action should not display its icon in menu.
173
     * True if the action should not display its icon in menu.
174
     * <br/>
174
     * <br>
175
     * False by default (icon visible in menu).
175
     * False by default (icon visible in menu).
176
     * @since 1.74
176
     * @since 1.74
177
     */
177
     */
Lines 179-185 Link Here
179
    
179
    
180
    /**
180
    /**
181
     * True if the action should not be displayed in customizer for key bindings assignment.
181
     * True if the action should not be displayed in customizer for key bindings assignment.
182
     * <br/>
182
     * <br>
183
     * False by default (key binding can be configured for the action).
183
     * False by default (key binding can be configured for the action).
184
     * @since 1.74
184
     * @since 1.74
185
     */
185
     */
Lines 187-193 Link Here
187
187
188
    /**
188
    /**
189
     * Boolean key in preferences that corresponds to action's selected state.
189
     * Boolean key in preferences that corresponds to action's selected state.
190
     * <br/>
190
     * <br>
191
     * If set to non-empty string the action will be represented by a check-box
191
     * If set to non-empty string the action will be represented by a check-box
192
     * in menu and popup menu and the corresponding key will be set in
192
     * in menu and popup menu and the corresponding key will be set in
193
     * global mime-lookup <code>MimeLookup.getLookup(MimePath.EMPTY)</code>.
193
     * global mime-lookup <code>MimeLookup.getLookup(MimePath.EMPTY)</code>.
Lines 196-202 Link Here
196
    
196
    
197
    /**
197
    /**
198
     * Whether or not the action should be in checked state by default.
198
     * Whether or not the action should be in checked state by default.
199
     * <br/>
199
     * <br>
200
     * If the preference should default to true or to false. Only valid in conjunction
200
     * If the preference should default to true or to false. Only valid in conjunction
201
     * with {@link #preferencesKey() }.
201
     * with {@link #preferencesKey() }.
202
     * 
202
     * 
(-)a/editor.lib2/src/org/netbeans/api/editor/EditorActionRegistrations.java (-1 / +1 lines)
Lines 52-58 Link Here
52
/**
52
/**
53
 * Annotation allowing to annotate one action class by multiple
53
 * Annotation allowing to annotate one action class by multiple
54
 * {@link EditorActionRegistration} annotations.
54
 * {@link EditorActionRegistration} annotations.
55
 * <br/>
55
 * <br>
56
 * Example:
56
 * Example:
57
 * <pre>
57
 * <pre>
58
 * @EditorActionRegistrations({ @EditorActionRegistration(name = "name1", ...),
58
 * @EditorActionRegistrations({ @EditorActionRegistration(name = "name1", ...),
(-)a/editor.lib2/src/org/netbeans/api/editor/EditorRegistry.java (-17 / +17 lines)
Lines 72-83 Link Here
72
72
73
/**
73
/**
74
 * Registry maintaining {@link JTextComponent}s in most-recently-used order.
74
 * Registry maintaining {@link JTextComponent}s in most-recently-used order.
75
 * <br/>
75
 * <br>
76
 * The particular text component needs to register itself first (to avoid dealing
76
 * The particular text component needs to register itself first (to avoid dealing
77
 * with all the JTextFields etc.). Then the registry will attach
77
 * with all the JTextFields etc.). Then the registry will attach
78
 * a focus listener to the text component and once the component gains
78
 * a focus listener to the text component and once the component gains
79
 * the focus it will move to the head of the components list.
79
 * the focus it will move to the head of the components list.
80
 * <br/>
80
 * <br>
81
 * The registry will also fire a change in case a document property
81
 * The registry will also fire a change in case a document property
82
 * of the focused component changes (by calling component.setDocument()).
82
 * of the focused component changes (by calling component.setDocument()).
83
 *
83
 *
Lines 98-106 Link Here
98
98
99
    /**
99
    /**
100
     * Fired when focus was delivered to a registered text component.
100
     * Fired when focus was delivered to a registered text component.
101
     * <br/>
101
     * <br>
102
     * The focused component will become the first in the components list.
102
     * The focused component will become the first in the components list.
103
     * <br/>
103
     * <br>
104
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be a component
104
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be a component
105
     * losing the focus {@link FocusEvent#getOppositeComponent()}.
105
     * losing the focus {@link FocusEvent#getOppositeComponent()}.
106
     * The {@link java.beans.PropertyChangeEvent#getNewValue()} will be the text component gaining the focus.
106
     * The {@link java.beans.PropertyChangeEvent#getNewValue()} will be the text component gaining the focus.
Lines 109-117 Link Here
109
    
109
    
110
    /**
110
    /**
111
     * Fired when a registered focused component has lost the focus.
111
     * Fired when a registered focused component has lost the focus.
112
     * <br/>
112
     * <br>
113
     * The focused component will remain the first in the components list.
113
     * The focused component will remain the first in the components list.
114
     * <br/>
114
     * <br>
115
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the text component
115
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the text component
116
     * losing the focus and the {@link java.beans.PropertyChangeEvent#getNewValue()}
116
     * losing the focus and the {@link java.beans.PropertyChangeEvent#getNewValue()}
117
     * will be the component gaining the focus {@link FocusEvent#getOppositeComponent()}.
117
     * will be the component gaining the focus {@link FocusEvent#getOppositeComponent()}.
Lines 121-127 Link Here
121
    /**
121
    /**
122
     * Fired when document property of the focused component changes
122
     * Fired when document property of the focused component changes
123
     * i.e. someone has called {@link JTextComponent#setDocument(Document)}.
123
     * i.e. someone has called {@link JTextComponent#setDocument(Document)}.
124
     * <br/>
124
     * <br>
125
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the original document
125
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the original document
126
     * of the focused text component and the {@link java.beans.PropertyChangeEvent#getNewValue()}
126
     * of the focused text component and the {@link java.beans.PropertyChangeEvent#getNewValue()}
127
     * will be the new document set to the focused text component.
127
     * will be the new document set to the focused text component.
Lines 132-144 Link Here
132
     * Fired when a component (returned previously from {@link #componentList()})
132
     * Fired when a component (returned previously from {@link #componentList()})
133
     * is removed from component hierarchy (so it's likely that the component will be released completely
133
     * is removed from component hierarchy (so it's likely that the component will be released completely
134
     * and garbage-collected).
134
     * and garbage-collected).
135
     * <br/>
135
     * <br>
136
     * Such component will no longer be returned from {@link #componentList()}
136
     * Such component will no longer be returned from {@link #componentList()}
137
     * or {@link #lastFocusedComponent()}.
137
     * or {@link #lastFocusedComponent()}.
138
     * <br/>
138
     * <br>
139
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the removed
139
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the removed
140
     * component.
140
     * component.
141
     * <br/>
141
     * <br>
142
     * The {@link java.beans.PropertyChangeEvent#getNewValue()} returns <code>null</code>
142
     * The {@link java.beans.PropertyChangeEvent#getNewValue()} returns <code>null</code>
143
     */
143
     */
144
    public static final String COMPONENT_REMOVED_PROPERTY = "componentRemoved"; //NOI18N
144
    public static final String COMPONENT_REMOVED_PROPERTY = "componentRemoved"; //NOI18N
Lines 147-160 Link Here
147
     * Fired when the last focused component (returned previously from {@link #lastFocusedComponent()})
147
     * Fired when the last focused component (returned previously from {@link #lastFocusedComponent()})
148
     * was removed from component hierarchy (so it's likely that the component will be released completely
148
     * was removed from component hierarchy (so it's likely that the component will be released completely
149
     * and garbage-collected).
149
     * and garbage-collected).
150
     * <br/>
150
     * <br>
151
     * Such component will no longer be returned from {@link #componentList()}
151
     * Such component will no longer be returned from {@link #componentList()}
152
     * or {@link #lastFocusedComponent()}.
152
     * or {@link #lastFocusedComponent()}.
153
     * <br/>
153
     * <br>
154
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the removed
154
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the removed
155
     * last focused component and the {@link java.beans.PropertyChangeEvent#getNewValue()}
155
     * last focused component and the {@link java.beans.PropertyChangeEvent#getNewValue()}
156
     * will be the component that would currently be returned from {@link #lastFocusedComponent()}.
156
     * will be the component that would currently be returned from {@link #lastFocusedComponent()}.
157
     * <br/>
157
     * <br>
158
     * If {@link java.beans.PropertyChangeEvent#getNewValue()} returns <code>null</code>
158
     * If {@link java.beans.PropertyChangeEvent#getNewValue()} returns <code>null</code>
159
     * then there are no longer any registered components
159
     * then there are no longer any registered components
160
     * ({@link #componentList()} would return empty list). If the client
160
     * ({@link #componentList()} would return empty list). If the client
Lines 180-186 Link Here
180
180
181
    /**
181
    /**
182
     * Return last focused text component (from the ones included in the registry).
182
     * Return last focused text component (from the ones included in the registry).
183
     * <br/>
183
     * <br>
184
     * It may or may not currently have a focus.
184
     * It may or may not currently have a focus.
185
     * 
185
     * 
186
     * @return last focused text component or null if no text components
186
     * @return last focused text component or null if no text components
Lines 193-199 Link Here
193
    /**
193
    /**
194
     * Return the last focused component if it currently has a focus
194
     * Return the last focused component if it currently has a focus
195
     * or return null if none of the registered components currently have the focus.
195
     * or return null if none of the registered components currently have the focus.
196
     * <br/>
196
     * <br>
197
     * @return focused component or null if none of the registered components
197
     * @return focused component or null if none of the registered components
198
     *  is currently focused.
198
     *  is currently focused.
199
     */
199
     */
Lines 205-211 Link Here
205
    /**
205
    /**
206
     * Get list of all components present in the registry starting with the most active
206
     * Get list of all components present in the registry starting with the most active
207
     * and ending with least active component.
207
     * and ending with least active component.
208
     * <br/>
208
     * <br>
209
     * The list is a snapshot of the current state and it may be modified
209
     * The list is a snapshot of the current state and it may be modified
210
     * by the caller if desired.
210
     * by the caller if desired.
211
     * 
211
     * 
Lines 240-246 Link Here
240
     *   <li>{@link #FOCUS_LOST_PROPERTY}</li>
240
     *   <li>{@link #FOCUS_LOST_PROPERTY}</li>
241
     *   <li>{@link #FOCUSED_DOCUMENT_PROPERTY}</li>
241
     *   <li>{@link #FOCUSED_DOCUMENT_PROPERTY}</li>
242
     * </ul>.
242
     * </ul>.
243
     * <br/>
243
     * <br>
244
     * All the firing should occur in AWT thread only
244
     * All the firing should occur in AWT thread only
245
     * (assuming the JTextComponent.setDocument() is done properly in AWT).
245
     * (assuming the JTextComponent.setDocument() is done properly in AWT).
246
     * 
246
     * 
(-)a/editor.lib2/src/org/netbeans/api/editor/EditorUtilities.java (+9 lines)
Lines 57-62 Link Here
57
 */
57
 */
58
58
59
public final class EditorUtilities {
59
public final class EditorUtilities {
60
    
61
    /**
62
     * Client property of editor component which determines
63
     * whether caret is currently in overwrite mode (Boolean.TRUE) or insert mode (Boolean.FALSE or null).
64
     * <br>
65
     * It's modified by appropriate editor actions.
66
     * @since 2.6
67
     */
68
    public static final String CARET_OVERWRITE_MODE_PROPERTY = "caret-overwrite-mode";
60
69
61
    private EditorUtilities() {
70
    private EditorUtilities() {
62
        // No instances
71
        // No instances
(-)a/editor.lib2/src/org/netbeans/api/editor/caret/CaretInfo.java (+182 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.editor.caret;
43
44
import java.awt.Point;
45
import java.util.logging.Logger;
46
import javax.swing.text.Position;
47
import org.netbeans.api.annotations.common.CheckForNull;
48
49
/**
50
 * Immutable info about a single caret used in caret API - see {@link EditorCaret}.
51
 * <br>
52
 * There is one-to-one reference between immutable caret info and mutable {@link CaretItem}.
53
 * CaretItem is not accessible through the caret API and it's managed privately by EditorCaret.
54
 * Once the caret item gets mutated its corresponding caret info becomes obsolete
55
 * and new caret info instance gets created lazily.
56
 *
57
 * @author Miloslav Metelka
58
 * @author Ralph Ruijs
59
 * @see EditorCaret
60
 * @since 2.6
61
 */
62
public final class CaretInfo {
63
    
64
    // -J-Dorg.netbeans.api.editor.CaretInfo.level=FINEST
65
    private static final Logger LOG = Logger.getLogger(CaretInfo.class.getName());
66
    
67
    private final CaretItem caretItem;
68
69
    private final Position dotPos;
70
71
    private final Position markPos;
72
73
    private final Point magicCaretPosition;
74
75
    CaretInfo(CaretItem caretItem) {
76
        this.caretItem = caretItem;
77
        this.dotPos = caretItem.getDotPosition();
78
        this.markPos = caretItem.getMarkPosition();
79
        this.magicCaretPosition = caretItem.getMagicCaretPosition();
80
    }
81
82
    /**
83
     * Get position of the caret itself.
84
     * @return non-null position of the caret placement. The position may be virtual
85
     *  so methods in {@link org.netbeans.api.editor.document.ShiftPositions} may be used if necessary.
86
     */
87
    @CheckForNull
88
    public Position getDotPosition() {
89
        return dotPos;
90
    }
91
92
    /**
93
     * Return either the same object like {@link #getDotPosition()} if there's no selection
94
     * or return position denoting the other end of an existing selection (which is either before
95
     * or after the dot position depending of how the selection was created).
96
     * @return non-null position of the caret placement. The position may be virtual
97
     *  so methods in {@link org.netbeans.api.editor.document.ShiftPositions} may be used if necessary.
98
     */
99
    @CheckForNull
100
    public Position getMarkPosition() {
101
        return markPos;
102
    }
103
    
104
    /**
105
     * Fetches the current position of the caret.
106
     *
107
     * @return the position &gt;=0
108
     */
109
    public int getDot() {
110
        return (dotPos != null) ? dotPos.getOffset() : 0;
111
    }
112
113
    /**
114
     * Fetches the current position of the mark.  If there
115
     * is a selection, the mark will not be the same as
116
     * the dot.
117
     *
118
     * @return the position &gt;=0
119
     */
120
    public int getMark() {
121
        return (markPos != null) ? markPos.getOffset() : getDot();
122
    }
123
    
124
    /**
125
     * Determines if there currently is a selection.
126
     * 
127
     * @return true if there's a selection or false if there's no selection for this caret.
128
     */
129
    public boolean isSelection() {
130
        return (dotPos != null && markPos != null &&
131
                markPos != dotPos && dotPos.getOffset() != markPos.getOffset());
132
    }
133
    
134
    /**
135
     * Determines if the selection is currently visible.
136
     * 
137
     * @return true if both {@link #isSelection() } and {@link EditorCaret#isSelectionVisible() } are true.
138
     */
139
    public boolean isSelectionShowing() {
140
        return caretItem.editorCaret().isSelectionVisible() && isSelection();
141
    }
142
143
    /**
144
     * Returns the selected text's start position.  Return 0 for an
145
     * empty document, or the value of dot if no selection.
146
     *
147
     * @return the start position &ge; 0
148
     */
149
    public int getSelectionStart() {
150
        return Math.min(getDot(), getMark());
151
    }
152
153
    /**
154
     * Returns the selected text's end position.  Return 0 if the document
155
     * is empty, or the value of dot if there is no selection.
156
     *
157
     * @return the end position &ge; 0
158
     */
159
    public int getSelectionEnd() {
160
        return Math.max(getDot(), getMark());
161
    }
162
    
163
    /**
164
     * Gets the current caret visual location.
165
     *
166
     * @return the visual position.
167
     */
168
    public @CheckForNull Point getMagicCaretPosition() {
169
        return magicCaretPosition;
170
    }
171
    
172
    CaretItem getCaretItem() {
173
        return caretItem;
174
    }
175
    
176
    @Override
177
    public String toString() {
178
        return "dotPos=" + dotPos + ", markPos=" + markPos + ", magicCaretPosition=" + magicCaretPosition + // NOI18N
179
                "\n  caretItem=" + caretItem; // NOI18N
180
    }
181
    
182
}
(-)a/editor.lib2/src/org/netbeans/api/editor/caret/CaretItem.java (+254 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.editor.caret;
43
44
import java.awt.Point;
45
import java.awt.Rectangle;
46
import java.util.logging.Logger;
47
import javax.swing.text.Position;
48
import org.netbeans.api.annotations.common.CheckForNull;
49
50
/**
51
 * A single caret inside {@link EditorCaret} handled internally by EditorCaret.
52
 * <br>
53
 * For API client methods {@link CaretInfo} class is used. There is one-to-one reference
54
 * between caret item and caret info. But the info is immutable so once the caret item
55
 * gets mutated its corresponding caret info becomes obsolete and new info gets created
56
 * lazily.
57
 *
58
 * @author Miloslav Metelka
59
 */
60
final class CaretItem implements Comparable {
61
    
62
    // -J-Dorg.netbeans.modules.editor.lib2.CaretItem.level=FINEST
63
    private static final Logger LOG = Logger.getLogger(CaretItem.class.getName());
64
65
    private static final int TRANSACTION_MARK_REMOVED = 1;
66
    
67
    private static final int UPDATE_VISUAL_BOUNDS = 2;
68
69
    private final EditorCaret editorCaret;
70
    
71
    private Position dotPos;
72
73
    private Position markPos;
74
75
    private Point magicCaretPosition;
76
    
77
    /**
78
     * Last info or null if info became obsolete and should be recomputed.
79
     */
80
    private CaretInfo info;
81
    
82
    private Rectangle caretBounds;
83
    
84
    /**
85
     * Hint of index of this caret item in replaceCaretItems in transaction context.
86
     */
87
    private int transactionIndexHint;
88
    
89
    /**
90
     * Transaction uses this flag to mark this item for removal.
91
     */
92
    private int statusBits;
93
94
    CaretItem(EditorCaret editorCaret, Position dotPos, Position markPos) {
95
        this.editorCaret = editorCaret;
96
        this.dotPos = dotPos;
97
        this.markPos = markPos;
98
        this.statusBits = UPDATE_VISUAL_BOUNDS; // Request visual bounds updating automatically
99
    }
100
    
101
    EditorCaret editorCaret() {
102
        return editorCaret;
103
    }
104
    
105
    void ensureValidInfo() {
106
        // No explicit locking - locking managed by EditorCaret
107
        if (info == null) {
108
            info = new CaretInfo(this);
109
        }
110
    }
111
    
112
    void clearInfo() {
113
        // No explicit locking - locking managed by EditorCaret
114
        this.info = null;
115
    }
116
117
    CaretInfo getValidInfo() {
118
        ensureValidInfo();
119
        return info;
120
    }
121
122
//    void clearTransactionInfo() {
123
//        // No explicit locking - locking managed by EditorCaret
124
//        this.transactionInfo = null;
125
//    }
126
//
127
//    CaretInfo getValidTransactionInfo() {
128
//        if (transactionInfo == null) {
129
//            transactionInfo = new CaretInfo(this);
130
//        }
131
//        return transactionInfo;
132
//    }
133
    
134
    /**
135
     * Get position of the caret itself.
136
     *
137
     * @return non-null position of the caret placement. The position may be
138
     * virtual so methods in {@link org.netbeans.api.editor.document.ShiftPositions} may be used if necessary.
139
     */
140
    @CheckForNull
141
    Position getDotPosition() {
142
        return dotPos;
143
    }
144
145
    /**
146
     * Return either the same object like {@link #getDotPosition()} if there's
147
     * no selection or return position denoting the other end of an existing
148
     * selection (which is either before or after the dot position depending of
149
     * how the selection was created).
150
     *
151
     * @return non-null position of the caret placement. The position may be
152
     * virtual so methods in {@link org.netbeans.api.editor.document.ShiftPositions} may be used if necessary.
153
     */
154
    @CheckForNull
155
    Position getMarkPosition() {
156
        return markPos;
157
    }
158
159
    int getDot() {
160
        return (dotPos != null) ? dotPos.getOffset() : 0;
161
    }
162
163
    int getMark() {
164
        return (markPos != null) ? markPos.getOffset() : 0;
165
    }
166
167
    /**
168
     * @return true if there's a selection or false if there's no selection for
169
     * this caret.
170
     */
171
    boolean isSelection() {
172
        return (dotPos != null && markPos != null &&
173
                markPos != dotPos && dotPos.getOffset() != markPos.getOffset());
174
    }
175
    
176
    boolean isSelectionShowing() {
177
        return editorCaret.isSelectionVisible() && isSelection();
178
    }
179
180
    Position getSelectionStart() {
181
        return dotPos; // TBD - possibly inspect virtual columns etc.
182
    }
183
184
    Position getSelectionEnd() {
185
        return dotPos; // TBD - possibly inspect virtual columns etc.
186
    }
187
188
    Point getMagicCaretPosition() {
189
        return magicCaretPosition;
190
    }
191
192
    void setDotPos(Position dotPos) {
193
        this.dotPos = dotPos;
194
    }
195
196
    void setMarkPos(Position markPos) {
197
        this.markPos = markPos;
198
    }
199
200
    void setMagicCaretPosition(Point newMagicCaretPosition) { // [TODO] move to transaction context
201
        this.magicCaretPosition = newMagicCaretPosition;
202
    }
203
204
    void setCaretBounds(Rectangle newCaretBounds) {
205
        this.caretBounds = newCaretBounds;
206
    }
207
208
    Rectangle getCaretBounds() {
209
        return this.caretBounds;
210
    }
211
212
    int getTransactionIndexHint() {
213
        return transactionIndexHint;
214
    }
215
216
    void setTransactionIndexHint(int transactionIndexHint) {
217
        this.transactionIndexHint = transactionIndexHint;
218
    }
219
220
    void markTransactionMarkRemoved() {
221
        this.statusBits |= TRANSACTION_MARK_REMOVED;
222
    }
223
224
    boolean isTransactionMarkRemoved() {
225
        return (this.statusBits & TRANSACTION_MARK_REMOVED) != 0;
226
    }
227
228
    void clearTransactionMarkRemoved() {
229
        this.statusBits &= ~TRANSACTION_MARK_REMOVED;
230
    }
231
    
232
    void markUpdateVisualBounds() {
233
        this.statusBits |= UPDATE_VISUAL_BOUNDS;
234
    }
235
    
236
    boolean isUpdateVisualBounds() {
237
        return (this.statusBits & UPDATE_VISUAL_BOUNDS) != 0;
238
    }
239
    
240
    void clearUpdateVisualBounds() {
241
        this.statusBits &= ~UPDATE_VISUAL_BOUNDS;
242
    }
243
    
244
    @Override
245
    public int compareTo(Object o) {
246
        return getDot() - ((CaretItem)o).getDot();
247
    }
248
249
    @Override
250
    public String toString() {
251
        return "dotPos=" + dotPos + ", markPos=" + markPos + ", magicCaretPosition=" + magicCaretPosition; // NOI18N
252
    }
253
254
}
(-)a/editor.lib2/src/org/netbeans/api/editor/caret/CaretMoveContext.java (+172 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.editor.caret;
43
44
import java.awt.Point;
45
import java.util.List;
46
import javax.swing.text.Document;
47
import javax.swing.text.JTextComponent;
48
import javax.swing.text.Position;
49
import org.netbeans.api.annotations.common.NonNull;
50
51
/**
52
 * Context for carets moving within {@link CaretMoveHandler}.
53
 *
54
 * @author Miloslav Metelka
55
 * @since 2.6
56
 */
57
public final class CaretMoveContext {
58
    
59
    private CaretTransaction transaction;
60
    
61
    CaretMoveContext(CaretTransaction transactionContext) {
62
        this.transaction = transactionContext;
63
    }
64
65
    /**
66
     * Get list of carets at the time when transaction started.
67
     * <br>
68
     * <b>Note</b>: information contained in the returned list will not reflect changes
69
     * performed by the dot/selection modification methods contained in this class.
70
     * It will always return the original state from the beginning of the move transaction.
71
     * All the performed modifications will be incorporated at the end of the move transaction.
72
     *
73
     * @return list of carets at the time when moving transaction has started.
74
     */
75
    public @NonNull List<CaretInfo> getOriginalCarets() {
76
        return transaction.getOriginalCarets();
77
    }
78
    
79
    /**
80
     * Get last item from the list returned by {@link #getOriginalCarets()}.
81
     * @return last caret at the time when caret moving transaction has started.
82
     * @see #getOriginalCarets()
83
     */
84
    public @NonNull CaretInfo getOriginalLastCaret() {
85
        List<CaretInfo> origCarets = getOriginalCarets();
86
        return origCarets.get(origCarets.size() - 1);
87
    }
88
    
89
    /**
90
     * Get list of carets sorted ascending order at the time when transaction started.
91
     * <br>
92
     * <b>Note</b>: information contained in the returned list will not reflect changes
93
     * performed by the dot/selection modification methods contained in this class.
94
     * It will always return the original state from the beginning of the move transaction.
95
     * All the performed modifications will be incorporated at the end of the move transaction.
96
     *
97
     * @return list of carets at the time when caret moving transaction has started.
98
     */
99
    public @NonNull List<CaretInfo> getOriginalSortedCarets() {
100
        return transaction.getOriginalSortedCarets();
101
    }
102
    
103
    /**
104
     * Change dot of the given caret.
105
     *
106
     * @param caret non-null caret.
107
     * @param dotPos new dot position.
108
     * @return false if passed caret is obsolete or invalid (e.g. a member of another {@link EditorCaret})
109
     *  or true otherwise.
110
     */
111
    public boolean setDot(@NonNull CaretInfo caret, @NonNull Position dotPos) {
112
        return setDotAndMark(caret, dotPos, dotPos);
113
    }
114
    
115
    /**
116
     * Move dot of the given getCaret so getCaret selection gets created or changed.
117
     *
118
     * @param caret non-null getCaret.
119
     * @param dotPos new dot position.
120
     * @return false if passed caret is obsolete or invalid (e.g. a member of another {@link EditorCaret})
121
     *  or true otherwise.
122
     */
123
    public boolean moveDot(@NonNull CaretInfo caret, @NonNull Position dotPos) {
124
        return transaction.moveDot(caret.getCaretItem(), dotPos);
125
    }
126
    
127
    /**
128
     * Move dot of the given getCaret so getCaret selection gets created or changed.
129
     *
130
     * @param caret non-null getCaret.
131
     * @param dotPos new dot position.
132
     * @param markPos starting position of the selection or the same position like dotPos if there should be no selection.
133
     *  <br>
134
     *  The position may point to a lower offset than dotPos in case the selection
135
     *  should extend from a higher offset to a lower offset.
136
     * @return false if passed caret is obsolete or invalid (e.g. a member of another {@link EditorCaret})
137
     *  or true otherwise.
138
     */
139
    public boolean setDotAndMark(@NonNull CaretInfo caret, @NonNull Position dotPos, @NonNull Position markPos) {
140
        return transaction.setDotAndMark(caret.getCaretItem(), dotPos, markPos);
141
    }
142
143
    /**
144
     * Set magic caret position of the given caret.
145
     *
146
     * @param caret non-null caret.
147
     * @param p new magic caret position.
148
     * @return false if passed caret is obsolete or invalid (e.g. a member of another {@link EditorCaret})
149
     *  or true otherwise.
150
     */
151
    public boolean setMagicCaretPosition(@NonNull CaretInfo caret, Point p) {
152
        return transaction.setMagicCaretPosition(caret.getCaretItem(), p);
153
    }
154
155
    /**
156
     * Return component in which the caret is currently installed.
157
     * @return non-null component in which the caret is installed.
158
     */
159
    public @NonNull JTextComponent getComponent() {
160
        return transaction.getComponent();
161
    }
162
    
163
    /**
164
     * Get document associated with the text component.
165
     * @return document of the associated component.
166
     * @see #getComponent()
167
     */
168
    public Document getDocument() {
169
        return getComponent().getDocument();
170
    }
171
172
}
(-)a/editor.lib2/src/org/netbeans/api/editor/caret/CaretTransaction.java (+627 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.editor.caret;
43
44
import org.netbeans.spi.editor.caret.CaretMoveHandler;
45
import java.awt.Point;
46
import java.util.List;
47
import javax.swing.text.BadLocationException;
48
import javax.swing.text.Document;
49
import javax.swing.text.JTextComponent;
50
import javax.swing.text.Position;
51
import org.netbeans.api.annotations.common.NonNull;
52
import org.netbeans.api.editor.document.ShiftPositions;
53
import org.netbeans.lib.editor.util.GapList;
54
55
/**
56
 * Context passed to getCaret transaction allowing to create/remove/modify the carets during the transaction.
57
 *
58
 * @author Miloslav Metelka
59
 */
60
final class CaretTransaction {
61
    
62
    private final EditorCaret editorCaret;
63
    
64
    private final JTextComponent component;
65
    
66
    private final Document doc;
67
    
68
    /**
69
     * Original items held here mainly due to latter repaints of the removed items.
70
     */
71
    private final GapList<CaretItem> origCaretItems;
72
    
73
    private int modIndex;
74
    
75
    /**
76
     * For MOVE_HANDLER and CARETS_REMOVE this is end index of modified area.
77
     * For CARETS_ADD and MIXED this is not filled.
78
     */
79
    private int modEndIndex;
80
    
81
    private CaretItem[] addCaretItems;
82
    
83
    private int affectedStartIndex = Integer.MAX_VALUE;
84
85
    private int affectedEndIndex;
86
87
    private int affectedStartOffset = Integer.MAX_VALUE;
88
    
89
    private int affectedEndOffset;
90
    
91
    private boolean addOrRemove;
92
    
93
    private boolean dotOrMarkChanged;
94
    
95
    private GapList<CaretItem> replaceItems;
96
    
97
    private GapList<CaretItem> replaceSortedItems;
98
    
99
    private GapList<CaretItem> extraRemovedItems;
100
    
101
    private int[] indexes;
102
    
103
    private int indexesLength;
104
    
105
    /**
106
     * End of area where index hints were updated.
107
     */
108
    private int indexHintEnd;
109
    
110
    private boolean fullResort;
111
    
112
    
113
    CaretTransaction(EditorCaret caret, JTextComponent component, Document doc) {
114
        this.editorCaret = caret;
115
        this.component = component;
116
        this.doc = doc;
117
        this.origCaretItems = editorCaret.getCaretItems();
118
    }
119
    
120
121
    @NonNull EditorCaret getCaret() {
122
        return editorCaret;
123
    }
124
    
125
    @NonNull JTextComponent getComponent() {
126
        return component;
127
    }
128
    
129
    @NonNull Document getDocument() {
130
        return doc;
131
    }
132
    
133
    boolean isAnyChange() {
134
        return addOrRemove || dotOrMarkChanged;
135
    }
136
    
137
    boolean moveDot(@NonNull CaretItem caret, @NonNull Position dotPos) {
138
        return setDotAndMark(caret, dotPos, caret.getMarkPosition());
139
    }
140
141
    boolean setDotAndMark(@NonNull CaretItem caretItem, @NonNull Position dotPos, @NonNull Position markPos) {
142
        assert (dotPos != null) : "dotPos must not be null";
143
        assert (markPos != null) : "markPos must not be null";
144
        int index = findCaretItemIndex(origCaretItems, caretItem);
145
        if (index != -1) {
146
            Position origDotPos = caretItem.getDotPosition();
147
            Position origMarkPos = caretItem.getMarkPosition();
148
            boolean changed = false;
149
            if (origDotPos == null || ShiftPositions.compare(dotPos, origDotPos) != 0) {
150
                caretItem.setDotPos(dotPos);
151
                changed = true;
152
            }
153
            if (origMarkPos == null || ShiftPositions.compare(markPos, origMarkPos) != 0) {
154
                caretItem.setMarkPos(markPos);
155
                changed = true;
156
            }
157
            if (changed) {
158
                updateAffectedIndexes(index, index + 1);
159
                caretItem.markUpdateVisualBounds();
160
                caretItem.clearInfo();
161
                dotOrMarkChanged = true;
162
            }
163
            return changed;
164
        }
165
        return false;
166
        //caret.setDotCaret(offset, this, true);
167
    }
168
    
169
    boolean setMagicCaretPosition(@NonNull CaretItem caretItem, Point p) {
170
        int index = findCaretItemIndex(origCaretItems, caretItem);
171
        if (index != -1) {
172
            caretItem.setMagicCaretPosition(p);
173
            caretItem.clearInfo();
174
            updateAffectedIndexes(index, index + 1);
175
            return true;
176
        }
177
        return false;
178
    }
179
    
180
    void documentInsertAtZeroOffset(int insertEndOffset) {
181
        // Nested insert inside active transaction for caret moving
182
        // Since carets may already be moved - do the operations with CaretItems directly
183
        Position insertEndPos = null;
184
        for (CaretItem caretItem : editorCaret.getSortedCaretItems()) {
185
            Position dotPos = caretItem.getDotPosition();
186
            boolean modifyDot = (dotPos == null || dotPos.getOffset() == 0);
187
            Position markPos = caretItem.getMarkPosition();
188
            boolean modifyMark = (markPos == null || markPos.getOffset() == 0);
189
            if (modifyDot || modifyMark) {
190
                if (insertEndPos == null) {
191
                    try {
192
                        insertEndPos = doc.createPosition(insertEndOffset);
193
                    } catch (BadLocationException ex) {
194
                        // Should never happen
195
                        return;
196
                    }
197
                }
198
                if (modifyDot) {
199
                    dotPos = insertEndPos;
200
                }
201
                if (modifyMark) {
202
                    markPos = insertEndPos;
203
                }
204
                setDotAndMark(caretItem, dotPos, markPos);
205
            }
206
            // Do not break the loop when caret's pos is above zero offset
207
            // since the carets may be already moved during the transaction
208
            // - possibly to offset zero. But there could be optimization
209
            // at least scan position of only the caret items that were modified and not others.
210
        }
211
        if (insertEndPos != null) {
212
            updateAffectedOffsets(0, insertEndOffset); // TODO isn't this extra work that setDotAndMark() already did??
213
            fullResort = true;
214
        }
215
    }
216
    
217
    void documentRemove(int offset) {
218
        fullResort = true; // TODO modify to more specific update
219
    }
220
221
    private void setSelectionStartEnd(@NonNull CaretItemInfo info, @NonNull Position pos, boolean start) {
222
        int index = findCaretItemIndex(origCaretItems, info.caretItem);
223
        assert (index != -1) : "Index=" + index + " should be valid";
224
        if (start == info.dotAtStart) {
225
            info.caretItem.setDotPos(pos);
226
        } else {
227
            info.caretItem.setMarkPos(pos);
228
        }
229
        updateAffectedIndexes(index, index + 1);
230
    }
231
    
232
    void handleCaretRemove(@NonNull CaretInfo caret) {
233
        
234
    }
235
236
    GapList<CaretItem> getReplaceItems() {
237
        return replaceItems;
238
    }
239
    
240
    GapList<CaretItem> getSortedCaretItems() {
241
        return replaceSortedItems;
242
    }
243
    
244
    List<CaretInfo> getOriginalCarets() {
245
        // Current impl ignores possible replaceItems content since client transactions only operate over
246
        // original infos from editorCaret. Internal transactions know the type of transaction
247
        // that was performed so they will skip carets possibly removed by the transaction.
248
        return editorCaret.getCarets();
249
    }
250
251
    List<CaretInfo> getOriginalSortedCarets() {
252
        // Current impl ignores possible replaceItems content - see getOriginalCarets()
253
        return editorCaret.getSortedCarets();
254
    }
255
256
    void replaceCarets(RemoveType removeType, int offset, CaretItem[] addCaretItems) {
257
        int size = origCaretItems.size();
258
        switch (removeType) {
259
            case NO_REMOVE:
260
                break;
261
            case REMOVE_LAST_CARET:
262
                if (size > 1) {
263
                    modIndex = size - 1;
264
                    modEndIndex = size;
265
                    addOrRemove = true;
266
                }
267
                break;
268
            case RETAIN_LAST_CARET:
269
                if (size > 1) {
270
                    modEndIndex = size - 1;
271
                    addOrRemove = true;
272
                }
273
                break;
274
            case REMOVE_ALL_CARETS:
275
                if (size > 0) {
276
                    modEndIndex = size;
277
                    addOrRemove = true;
278
                }
279
                break;
280
            case DOCUMENT_REMOVE:
281
                break;
282
            case DOCUMENT_INSERT_ZERO_OFFSET:
283
                documentInsertAtZeroOffset(offset);
284
                break;
285
286
            default:
287
                throw new AssertionError("Unhandled removeType=" + removeType); // NOI18N
288
        }
289
        if (addCaretItems != null) {
290
            this.addCaretItems = addCaretItems;
291
            addOrRemove = true;
292
        }
293
    }
294
    
295
    void runCaretMoveHandler(CaretMoveHandler handler) {
296
        CaretMoveContext context = new CaretMoveContext(this);
297
        handler.moveCarets(context);
298
    }
299
    
300
    private static int getOffset(Position pos) {
301
        return (pos != null) ? pos.getOffset() : 0;
302
    }
303
    
304
    void removeOverlappingRegions() {
305
        removeOverlappingRegions(0, Integer.MAX_VALUE);
306
    }
307
308
    void removeOverlappingRegions(int removeOffset) {
309
        removeOverlappingRegions(0, removeOffset); // TODO compute startIndex by binary search
310
    }
311
    
312
    void removeOverlappingRegions(int startIndex, int stopOffset) {
313
        if (addOrRemove) {
314
            initReplaceItems();
315
        } else if (dotOrMarkChanged) {
316
            initReplaceItems(); // TODO optimize for low number of changed items
317
        }
318
        GapList<CaretItem> origSortedItems = (replaceSortedItems != null)
319
                ? replaceSortedItems
320
                : editorCaret.getSortedCaretItems();
321
        int origSortedItemsSize = origSortedItems.size();
322
        GapList<CaretItem> nonOverlappingItems = null;
323
        int copyStartIndex = 0;
324
        int i = startIndex - 1;
325
        boolean itemsRemoved = false;
326
        CaretItemInfo lastInfo = new CaretItemInfo();
327
        if (i >= 0) {
328
            lastInfo.update(origSortedItems.get(i));
329
        } // Otherwise leave the default zeros in lastInfo
330
        
331
        CaretItemInfo itemInfo = new CaretItemInfo();
332
        while (++i < origSortedItemsSize) {
333
            itemInfo.update(origSortedItems.get(i));
334
            if (itemInfo.overlapsAtStart(lastInfo)) {
335
                if (nonOverlappingItems == null) {
336
                    nonOverlappingItems = new GapList<CaretItem>(origSortedItemsSize - 1); // At least one will be skipped
337
                }
338
                itemsRemoved = true;
339
                // Determine type of overlap
340
                if (!lastInfo.dotAtStart) { // Caret of lastInfo moved into next block
341
                    if (lastInfo.startsBelow(itemInfo)) {
342
                        // Extend selection of itemInfo to start of lastInfo
343
                        updateAffectedOffsets(lastInfo.startOffset, itemInfo.startOffset);
344
                        setSelectionStartEnd(itemInfo, lastInfo.startPos, true);
345
                    }
346
                    // Remove lastInfo's getCaret item
347
                    lastInfo.caretItem.markTransactionMarkRemoved();
348
                    origSortedItems.copyElements(copyStartIndex, i - 1, nonOverlappingItems);
349
                    copyStartIndex = i;
350
                    
351
                } else { // Remove itemInfo and set selection of lastInfo to end of itemInfo
352
                    if (itemInfo.endsAbove(lastInfo)) {
353
                        updateAffectedOffsets(lastInfo.endOffset, itemInfo.endOffset);
354
                        setSelectionStartEnd(lastInfo, itemInfo.endPos, false);
355
                    }
356
                    // Remove itemInfo's getCaret item
357
                    itemInfo.caretItem.markTransactionMarkRemoved();
358
                    origSortedItems.copyElements(copyStartIndex, i, nonOverlappingItems);
359
                    copyStartIndex = i + 1;
360
                }
361
            } else { // No overlapping
362
                // Swap the items to reuse original lastInfo
363
                CaretItemInfo tmp = lastInfo;
364
                lastInfo = itemInfo;
365
                itemInfo = tmp;
366
                if (lastInfo.endOffset > stopOffset) {
367
                    break;
368
                }
369
            }
370
        }
371
372
        if (itemsRemoved) { // At least one item removed
373
            if (copyStartIndex < origSortedItemsSize) {
374
                origSortedItems.copyElements(copyStartIndex, origSortedItemsSize, nonOverlappingItems);
375
            }
376
            GapList<CaretItem> origItems = resultItems();
377
            int origItemsSize = origItems.size();
378
            replaceItems = new GapList<>(origItemsSize);
379
            for (i = 0; i < origItemsSize; i++) {
380
                CaretItem caretItem = origItems.get(i);
381
                if (caretItem.isTransactionMarkRemoved()) {
382
                    caretItem.clearTransactionMarkRemoved();
383
                    if (extraRemovedItems == null) {
384
                        extraRemovedItems = new GapList<>();
385
                    }
386
                    extraRemovedItems.add(caretItem);
387
                } else {
388
                    replaceItems.add(caretItem);
389
                }
390
            }
391
            replaceSortedItems = nonOverlappingItems;
392
        }
393
    }
394
    
395
    GapList<CaretItem> addRemovedItems(GapList<CaretItem> toItems) {
396
        int removeSize = modEndIndex - modIndex;
397
        int extraRemovedSize = (extraRemovedItems != null) ? extraRemovedItems.size() : 0;
398
        if (removeSize + extraRemovedSize > 0) {
399
            if (toItems == null) {
400
                toItems = new GapList<>(removeSize + extraRemovedSize);
401
            }
402
            if (removeSize > 0) {
403
                toItems.addAll(origCaretItems, modIndex, removeSize);
404
            }
405
            if (extraRemovedSize > 0) {
406
                toItems.addAll(extraRemovedItems);
407
            }
408
        }
409
        return toItems;
410
    }
411
    
412
    GapList<CaretItem> addUpdateVisualBoundsItems(GapList<CaretItem> toItems) {
413
        GapList<CaretItem> items = resultItems();
414
        int size = items.size();
415
        for (int i = 0; i < size; i++) {
416
            CaretItem caretItem = items.get(i);
417
            if (caretItem.isUpdateVisualBounds()) {
418
                caretItem.clearUpdateVisualBounds();
419
                if (toItems == null) {
420
                    toItems = new GapList<>();
421
                }
422
                toItems.add(caretItem);
423
            }
424
        }
425
        return toItems;
426
    }
427
428
    private GapList<CaretItem> resultItems() {
429
        return (replaceItems != null) ? replaceItems : origCaretItems;
430
    }
431
432
    private void initReplaceItems() {
433
        assert (replaceItems == null) : "replaceItems already inited to " + replaceItems; // NOI18N
434
        int size = origCaretItems.size();
435
        int removeSize = modEndIndex - modIndex;
436
        int addSize = (addCaretItems != null) ? addCaretItems.length : 0;
437
        int newSize = size - removeSize + addSize;
438
        replaceItems = new GapList<>(newSize);
439
        if (removeSize > 0) {
440
            replaceItems.addAll(origCaretItems, 0, modIndex);
441
            replaceItems.addAll(origCaretItems, modEndIndex, size - modEndIndex);
442
        } else {
443
            replaceItems.addAll(origCaretItems);
444
        }
445
        if (addCaretItems != null) {
446
            replaceItems.addArray(replaceItems.size(), addCaretItems);
447
        }
448
449
        assert (replaceItems.size() == newSize);
450
        boolean updateIndividual = (removeSize + addSize) < (newSize >> 2); // Threshold 1/4 of total size for full resort
451
        if (fullResort || true) { // Force full resort
452
            replaceSortedItems = replaceItems.copy();
453
            if (newSize > 1) {
454
                
455
            }
456
        } else { // Partial resort TODO
457
            
458
        }
459
    }
460
461
    private void resetIndexes() {
462
        indexesLength = 0;
463
    }
464
    
465
    private void addToIndexes(int index) {
466
        if (indexes == null) {
467
            indexes = new int[8];
468
        } else if (indexesLength == indexes.length) {
469
            int[] orig = indexes;
470
            indexes = new int[indexesLength << 1];
471
            System.arraycopy(orig, 0, indexes, 0, indexesLength);
472
        }
473
        indexes[indexesLength++] = index;
474
    }
475
    
476
    
477
    private int findCaretItemIndex(GapList<CaretItem> caretItems, CaretItem caretItem) {
478
        // Method only resolves existing items not added items
479
        int i = caretItem.getTransactionIndexHint();
480
        int size = caretItems.size();
481
        if (i >= size || caretItems.get(i) != caretItem) {
482
            while (indexHintEnd < size) {
483
                CaretItem c = caretItems.get(indexHintEnd);
484
                c.setTransactionIndexHint(indexHintEnd++);
485
                if (c == caretItem) {
486
                    return indexHintEnd - 1;
487
                }
488
            }
489
            return -1;
490
        }
491
        return i;
492
    }
493
    
494
    private void updateAffectedIndexes(int startIndex, int endIndex) {
495
        if (affectedStartIndex == Integer.MAX_VALUE) {
496
            affectedStartIndex = startIndex;
497
            affectedEndIndex = endIndex;
498
        } else {
499
            affectedStartIndex = Math.min(affectedStartIndex, startIndex);
500
            affectedEndIndex = Math.max(affectedEndIndex, endIndex);
501
        }
502
    }
503
504
    private void updateAffectedOffsets(int startOffset, int endOffset) {
505
        if (affectedStartOffset == Integer.MAX_VALUE) { // Affected range not inited yet
506
            affectedStartOffset = startOffset;
507
            affectedEndOffset = endOffset;
508
        } else { // Affected range already inited
509
            if (startOffset < affectedStartOffset) {
510
                affectedStartOffset = startOffset;
511
            }
512
            if (endOffset > affectedEndOffset) {
513
                affectedEndOffset = endOffset;
514
            }
515
        }
516
    }
517
518
    static CaretItem[] asCaretItems(EditorCaret caret, @NonNull List<Position> dotAndSelectionStartPosPairs) {
519
        int size = dotAndSelectionStartPosPairs.size();
520
        if ((size & 1) != 0) {
521
            throw new IllegalStateException("Passed list has size=" + size + " which is not an even number.");
522
        }
523
        CaretItem[] addedCarets = new CaretItem[size >> 1];
524
        int listIndex = 0;
525
        for (int j = 0; j < addedCarets.length; j++) {
526
            Position dotPos = dotAndSelectionStartPosPairs.get(listIndex++);
527
            Position selectionStartPos = dotAndSelectionStartPosPairs.get(listIndex++);
528
            CaretItem caretItem = new CaretItem(caret, dotPos, selectionStartPos);
529
            addedCarets[j] = caretItem;
530
        }
531
        return addedCarets;
532
    }
533
534
    enum RemoveType {
535
        NO_REMOVE,
536
        REMOVE_LAST_CARET,
537
        RETAIN_LAST_CARET,
538
        REMOVE_ALL_CARETS,
539
        DOCUMENT_REMOVE,
540
        DOCUMENT_INSERT_ZERO_OFFSET
541
    }
542
    
543
    /**
544
     * Helper class for resolving overlapping getCaret selections.
545
     */
546
    private static final class CaretItemInfo {
547
        
548
        CaretItem caretItem;
549
        
550
        Position startPos;
551
        
552
        Position endPos;
553
554
        int startOffset;
555
556
        int startShift;
557
        
558
        int endOffset;
559
560
        int endShift;
561
        
562
        boolean dotAtStart;
563
        
564
        void update(CaretItem caret) {
565
            this.caretItem = caret;
566
            Position dotPos = caret.getDotPosition();
567
            if (dotPos != null) {
568
                int dotOffset = dotPos.getOffset();
569
                int dotShift = ShiftPositions.getShift(dotPos);
570
                Position markPos = caret.getMarkPosition();
571
                if (markPos != null && markPos != dotPos) { // Still they may be equal which means no selection
572
                    int markOffset = markPos.getOffset();
573
                    int markShift = ShiftPositions.getShift(markPos);
574
                    if (markOffset < dotOffset || (markOffset == dotOffset && markShift <= dotShift)) {
575
                        startPos = markPos;
576
                        endPos = dotPos;
577
                        startOffset = markOffset;
578
                        startShift = markShift;
579
                        endOffset = dotOffset;
580
                        endShift = dotShift;
581
                        dotAtStart = false;
582
                    } else {
583
                        startPos = dotPos;
584
                        endPos = markPos;
585
                        startOffset = dotOffset;
586
                        startShift = dotShift;
587
                        endOffset = markOffset;
588
                        endShift = markShift;
589
                        dotAtStart = true;
590
                    }
591
                } else {
592
                    startPos = endPos = dotPos;
593
                    startOffset = endOffset = dotOffset;
594
                    startShift = startShift = dotShift;
595
                    dotAtStart = false;
596
                }
597
            } else {
598
                clear();
599
            }
600
        }
601
602
        private void clear() {
603
            caretItem = null;
604
            startPos = endPos = null;
605
            startOffset = endOffset = 0;
606
            startShift = startShift = 0;
607
            dotAtStart = false;
608
        }
609
        
610
        private boolean overlapsAtStart(CaretItemInfo info) {
611
            return (ShiftPositions.compare(info.endOffset, info.endShift,
612
                    startOffset, startShift) > 0);
613
        }
614
        
615
        private boolean startsBelow(CaretItemInfo info) {
616
            return (ShiftPositions.compare(startOffset, startShift,
617
                    info.startOffset, info.startShift) < 0);
618
        }
619
        
620
        private boolean endsAbove(CaretItemInfo info) {
621
            return (ShiftPositions.compare(endOffset, endShift,
622
                    info.endOffset, info.endShift) > 0);
623
        }
624
        
625
    }
626
627
}
(-)a/editor.lib2/src/org/netbeans/api/editor/caret/EditorCaret.java (+2585 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
    package org.netbeans.api.editor.caret;
43
44
import org.netbeans.spi.editor.caret.CaretMoveHandler;
45
import java.awt.AlphaComposite;
46
import java.awt.BasicStroke;
47
import java.awt.Color;
48
import java.awt.Component;
49
import java.awt.Composite;
50
import java.awt.Container;
51
import java.awt.Cursor;
52
import java.awt.Dimension;
53
import java.awt.Graphics;
54
import java.awt.Graphics2D;
55
import java.awt.Point;
56
import java.awt.Rectangle;
57
import java.awt.Stroke;
58
import java.awt.datatransfer.Clipboard;
59
import java.awt.datatransfer.DataFlavor;
60
import java.awt.datatransfer.Transferable;
61
import java.awt.datatransfer.UnsupportedFlavorException;
62
import java.awt.event.ActionEvent;
63
import java.awt.event.ActionListener;
64
import java.awt.event.ComponentAdapter;
65
import java.awt.event.ComponentEvent;
66
import java.awt.event.ComponentListener;
67
import java.awt.event.FocusEvent;
68
import java.awt.event.FocusListener;
69
import java.awt.event.InputEvent;
70
import java.awt.event.KeyEvent;
71
import java.awt.event.KeyListener;
72
import java.awt.event.MouseEvent;
73
import java.awt.event.MouseListener;
74
import java.awt.event.MouseMotionListener;
75
import java.beans.PropertyChangeEvent;
76
import java.beans.PropertyChangeListener;
77
import java.io.IOException;
78
import java.util.ArrayList;
79
import java.util.List;
80
import java.util.concurrent.Callable;
81
import java.util.logging.Level;
82
import java.util.logging.Logger;
83
import java.util.prefs.PreferenceChangeEvent;
84
import java.util.prefs.PreferenceChangeListener;
85
import java.util.prefs.Preferences;
86
import javax.swing.Action;
87
import javax.swing.JComponent;
88
import javax.swing.JScrollBar;
89
import javax.swing.JScrollPane;
90
import javax.swing.JViewport;
91
import javax.swing.SwingUtilities;
92
import javax.swing.Timer;
93
import javax.swing.TransferHandler;
94
import javax.swing.event.ChangeEvent;
95
import javax.swing.event.ChangeListener;
96
import javax.swing.event.DocumentEvent;
97
import javax.swing.event.DocumentListener;
98
import javax.swing.text.AbstractDocument;
99
import javax.swing.text.AttributeSet;
100
import javax.swing.text.BadLocationException;
101
import javax.swing.text.Caret;
102
import javax.swing.text.DefaultEditorKit;
103
import javax.swing.text.Document;
104
import javax.swing.text.Element;
105
import javax.swing.text.JTextComponent;
106
import javax.swing.text.Position;
107
import javax.swing.text.StyleConstants;
108
import org.netbeans.api.annotations.common.CheckForNull;
109
import org.netbeans.api.annotations.common.NonNull;
110
import org.netbeans.api.editor.EditorUtilities;
111
import org.netbeans.api.editor.document.AtomicLockDocument;
112
import org.netbeans.api.editor.document.AtomicLockEvent;
113
import org.netbeans.api.editor.document.AtomicLockListener;
114
import org.netbeans.api.editor.document.LineDocument;
115
import org.netbeans.api.editor.document.LineDocumentUtils;
116
import org.netbeans.api.editor.mimelookup.MimeLookup;
117
import org.netbeans.api.editor.settings.FontColorNames;
118
import org.netbeans.api.editor.settings.FontColorSettings;
119
import org.netbeans.api.editor.settings.SimpleValueNames;
120
import org.netbeans.lib.editor.util.ArrayUtilities;
121
import org.netbeans.lib.editor.util.GapList;
122
import org.netbeans.lib.editor.util.ListenerList;
123
import org.netbeans.lib.editor.util.swing.DocumentListenerPriority;
124
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
125
import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults;
126
import org.netbeans.modules.editor.lib2.RectangularSelectionCaretAccessor;
127
import org.netbeans.modules.editor.lib2.RectangularSelectionTransferHandler;
128
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils;
129
import org.netbeans.modules.editor.lib2.actions.EditorActionUtilities;
130
import org.netbeans.modules.editor.lib2.highlighting.CaretOverwriteModeHighlighting;
131
import org.netbeans.modules.editor.lib2.view.DocumentView;
132
import org.netbeans.modules.editor.lib2.view.LockedViewHierarchy;
133
import org.netbeans.modules.editor.lib2.view.ViewHierarchy;
134
import org.netbeans.modules.editor.lib2.view.ViewHierarchyEvent;
135
import org.netbeans.modules.editor.lib2.view.ViewHierarchyListener;
136
import org.netbeans.modules.editor.lib2.view.ViewUtils;
137
import org.openide.util.Exceptions;
138
import org.openide.util.WeakListeners;
139
140
/**
141
 * Extension to standard Swing caret used by all NetBeans editors.
142
 * <br>
143
 * It supports multi-caret editing mode where an arbitrary number of carets
144
 * is placed at arbitrary positions throughout a document.
145
 * In this mode each caret is described by its <code>CaretInfo</code> object.
146
 * <br>
147
 * The caret works over text components having {@link AbstractDocument} based document.
148
 *
149
 * @author Miloslav Metelka
150
 * @author Ralph Ruijs
151
 * @since 2.6
152
 */
153
public final class EditorCaret implements Caret {
154
    
155
    // Temporary until rectangular selection gets ported to multi-caret support
156
    private static final String RECTANGULAR_SELECTION_PROPERTY = "rectangular-selection"; // NOI18N
157
    private static final String RECTANGULAR_SELECTION_REGIONS_PROPERTY = "rectangular-selection-regions"; // NOI18N
158
    
159
    // -J-Dorg.netbeans.editor.BaseCaret.level=FINEST
160
    private static final Logger LOG = Logger.getLogger(EditorCaret.class.getName());
161
    
162
    static {
163
        RectangularSelectionCaretAccessor.register(new RectangularSelectionCaretAccessor() {
164
            @Override
165
            public void setRectangularSelectionToDotAndMark(EditorCaret editorCaret) {
166
                editorCaret.setRectangularSelectionToDotAndMark();
167
            }
168
169
            @Override
170
            public void updateRectangularUpDownSelection(EditorCaret editorCaret) {
171
                editorCaret.updateRectangularUpDownSelection();
172
            }
173
174
            @Override
175
            public void extendRectangularSelection(EditorCaret editorCaret, boolean toRight, boolean ctrl) {
176
                editorCaret.extendRectangularSelection(toRight, ctrl);
177
            }
178
        });
179
    }
180
181
    static final long serialVersionUID = 0L;
182
183
    /**
184
     * Non-empty list of individual carets in the order they were created.
185
     * At least one item is always present.
186
     */
187
    private @NonNull GapList<CaretItem> caretItems;
188
    
189
    /**
190
     * Non-empty list of individual carets in the order they were created.
191
     * At least one item is always present.
192
     */
193
    private @NonNull GapList<CaretItem> sortedCaretItems;
194
    
195
    /**
196
     * Cached infos corresponding to caret items or null if any of the items were changed
197
     *  so another copy of the caret items- (translated into caret infos) will get created
198
     * upon query to {@link #getCarets() }.
199
     * <br>
200
     * Once the list gets created both the list content and information in each caret info are immutable.
201
     */
202
    private List<CaretInfo> caretInfos;
203
    
204
    /**
205
     * Cached infos corresponding to sorted caret items or null if any of the sorted items were changed
206
     * so another copy of the sorted caret items (translated into caret infos) will get created
207
     * upon query to {@link #getSortedCarets() }.
208
     * <br>
209
     * Once the list gets created both the list content and information in each caret info are immutable.
210
     */
211
    private List<CaretInfo> sortedCaretInfos;
212
    
213
    /** Component this caret is bound to */
214
    private JTextComponent component;
215
    
216
    /** List of individual carets */
217
    private boolean overwriteMode;
218
219
    private final ListenerList<EditorCaretListener> listenerList;
220
221
    private final ListenerList<ChangeListener> changeListenerList;
222
223
    /**
224
     * Implementor of various listeners.
225
     */
226
    private final ListenerImpl listenerImpl;
227
    
228
    /** Is the caret visible (after <code>setVisible(true)</code> call? */
229
    private boolean visible;
230
231
    /**
232
     * Whether blinking caret is currently visible on the screen.
233
     * <br>
234
     * This changes from true to false after each tick of a timer
235
     * (assuming <code>visible == true</code>).
236
     */
237
    private boolean blinkVisible;
238
239
    /**
240
     * Determine if a possible selection would be displayed or not.
241
     */
242
    private boolean selectionVisible;
243
244
    /** Type of the caret */
245
    private CaretType type = CaretType.THICK_LINE_CARET;
246
247
    /** Width of caret */
248
    private int thickCaretWidth = EditorPreferencesDefaults.defaultThickCaretWidth;
249
    
250
    private MouseState mouseState = MouseState.DEFAULT;
251
    
252
    /** Timer used for blinking the caret */
253
    private Timer flasher;
254
255
    private Action selectWordAction;
256
    private Action selectLineAction;
257
258
    private AbstractDocument activeDoc;
259
    
260
    private Thread lockThread;
261
    
262
    private int lockDepth;
263
    
264
    private CaretTransaction activeTransaction;
265
    
266
    /**
267
     * Items from previous transaction(s) that need their visual rectangle
268
     * to get repainted to clear the previous caret representation visually.
269
     */
270
    private GapList<CaretItem> pendingRepaintRemovedItemsList;
271
272
    /**
273
     * Items from previous transaction(s) that need their visual bounds
274
     * to be recomputed and caret to be repainted then.
275
     */
276
    private GapList<CaretItem> pendingUpdateVisualBoundsItemsList;
277
    
278
    /**
279
     * Caret item to which the view should scroll or null for no scrolling.
280
     */
281
    private CaretItem scrollToItem;
282
283
    /**
284
     * Whether the text is being modified under atomic lock.
285
     * If so just one caret change is fired at the end of all modifications.
286
     */
287
    private transient boolean inAtomicLock = false;
288
    private transient boolean inAtomicUnlock = false;
289
    
290
    /**
291
     * Helps to check whether there was modification performed
292
     * and so the caret change needs to be fired.
293
     */
294
    private transient boolean modified;
295
    
296
    /**
297
     * Set to true once the folds have changed. The caret should retain
298
     * its relative visual position on the screen.
299
     */
300
    private boolean updateAfterFoldHierarchyChange;
301
    
302
    /**
303
     * Whether at least one typing change occurred during possibly several atomic operations.
304
     */
305
    private boolean typingModificationOccurred;
306
307
    private Preferences prefs = null;
308
309
    private PreferenceChangeListener weakPrefsListener = null;
310
    
311
    private boolean caretUpdatePending;
312
    
313
    /**
314
     * Minimum selection start for word and line selections.
315
     * This helps to ensure that when extending word (or line) selections
316
     * the selection will always include at least the initially selected word (or line).
317
     */
318
    private int minSelectionStartOffset;
319
    
320
    private int minSelectionEndOffset;
321
    
322
    private boolean rectangularSelection;
323
    
324
    /**
325
     * Rectangle that corresponds to model2View of current point of selection.
326
     */
327
    private Rectangle rsDotRect;
328
329
    /**
330
     * Rectangle that corresponds to model2View of beginning of selection.
331
     */
332
    private Rectangle rsMarkRect;
333
    
334
    /**
335
     * Rectangle marking rectangular selection.
336
     */
337
    private Rectangle rsPaintRect;
338
    
339
    /**
340
     * List of start-pos and end-pos pairs that denote rectangular selection
341
     * on the selected lines.
342
     */
343
    private List<Position> rsRegions;
344
345
    /**
346
     * Used for showing the default cursor instead of the text cursor when the
347
     * mouse is over a block of selected text.
348
     * This field is used to prevent repeated calls to component.setCursor()
349
     * with the same cursor.
350
     */
351
    private boolean showingTextCursor = true;
352
    
353
    public EditorCaret() {
354
        caretItems = new GapList<>();
355
        sortedCaretItems = new GapList<>();
356
        CaretItem singleCaret = new CaretItem(this, null, null);
357
        caretItems.add(singleCaret);
358
        sortedCaretItems.add(singleCaret);
359
360
        listenerList = new ListenerList<>();
361
        changeListenerList = new ListenerList<>();
362
        listenerImpl = new ListenerImpl();
363
    }
364
365
    @Override
366
    public int getDot() {
367
        return getLastCaret().getDot();
368
    }
369
    
370
    @Override
371
    public int getMark() {
372
        return getLastCaret().getMark();
373
    }
374
    
375
    /**
376
     * Get information about all existing carets in the order they were created.
377
     * <br>
378
     * The list always has at least one item. The last caret (last item of the list)
379
     * is the most recent caret.
380
     * <br>
381
     * The list is a snapshot of the current state of the carets. The list content itself and its contained
382
     * caret infos are guaranteed not change after subsequent calls to caret API or document modifications.
383
     * <br>
384
     * The list is nonmodifiable.
385
     * <br>
386
     * This method should be called with document's read-lock acquired which will guarantee
387
     * stability of {@link CaretInfo#getDot() } and {@link CaretInfo#getMark() } and prevent
388
     * caret merging as a possible effect of document modifications.
389
     * 
390
     * @return copy of caret list with size &gt;= 1 containing information about all carets.
391
     */
392
    public @NonNull List<CaretInfo> getCarets() {
393
        synchronized (listenerList) {
394
            if (caretInfos == null) {
395
                int i = caretItems.size();
396
                CaretInfo[] infos = new CaretInfo[i--];
397
                for (; i >= 0; i--) {
398
                    infos[i] = caretItems.get(i).getValidInfo();
399
                }
400
                caretInfos = ArrayUtilities.unmodifiableList(infos);
401
            }
402
            return caretInfos;
403
        }
404
    }
405
    
406
    /**
407
     * Get information about all existing carets sorted by dot positions in ascending order.
408
     * <br>
409
     * The list is a snapshot of the current state of the carets. The list content itself and its contained
410
     * caret infos are guaranteed not change after subsequent calls to caret API or document modifications.
411
     * <br>
412
     * The list is nonmodifiable.
413
     * <br>
414
     * This method should be called with document's read-lock acquired which will guarantee
415
     * stability of {@link CaretInfo#getDot() } and {@link CaretInfo#getMark() } and prevent
416
     * caret merging as a possible effect of document modifications.
417
     * 
418
     * @return copy of caret list with size &gt;= 1 sorted by dot positions in ascending order.
419
     */
420
    public @NonNull List<CaretInfo> getSortedCarets() {
421
        synchronized (listenerList) {
422
            if (sortedCaretInfos == null) {
423
                int i = sortedCaretItems.size();
424
                CaretInfo[] sortedInfos = new CaretInfo[i--];
425
                for (; i >= 0; i--) {
426
                    sortedInfos[i] = sortedCaretItems.get(i).getValidInfo();
427
                }
428
                sortedCaretInfos = ArrayUtilities.unmodifiableList(sortedInfos);
429
            }
430
            return sortedCaretInfos;
431
        }
432
    }
433
    
434
    /**
435
     * Get info about the most recently created caret.
436
     * <br>
437
     * For normal mode this is the only caret returned by {@link #getCarets() }.
438
     * <br>
439
     * For multi-caret mode this is the last item in the list returned by {@link #getCarets() }.
440
     * 
441
     * @return last caret (the most recently added caret).
442
     */
443
    public @NonNull CaretInfo getLastCaret() {
444
        synchronized (listenerList) {
445
            return caretItems.get(caretItems.size() - 1).getValidInfo();
446
        }
447
    }
448
    
449
    /**
450
     * Get information about the caret at the specified offset.
451
     *
452
     * @param offset the offset of the caret
453
     * @return CaretInfo for the caret at offset, null if there is no caret or
454
     * the offset is invalid
455
     */
456
    public @CheckForNull CaretInfo getCaretAt(int offset) {
457
        return null; // TBD
458
    }
459
    
460
    /**
461
     * Assign a new offset to the caret in the underlying document.
462
     * <br>
463
     * This method implicitly sets the selection range to zero.
464
     * <br>
465
     * If multiple carets are present this method retains only last caret
466
     * which then moves to the given offset.
467
     * 
468
     * @param offset {@inheritDoc}
469
     * @see Caret#setDot(int) 
470
     */
471
    public @Override void setDot(final int offset) {
472
        if (LOG.isLoggable(Level.FINE)) {
473
            LOG.fine("setDot: offset=" + offset); //NOI18N
474
            if (LOG.isLoggable(Level.FINEST)) {
475
                LOG.log(Level.INFO, "setDot call stack", new Exception());
476
            }
477
        }
478
        runTransaction(CaretTransaction.RemoveType.RETAIN_LAST_CARET, 0, null, new CaretMoveHandler() {
479
            @Override
480
            public void moveCarets(CaretMoveContext context) {
481
                Document doc = context.getComponent().getDocument();
482
                if (doc != null) {
483
                    try {
484
                        Position pos = doc.createPosition(offset);
485
                        context.setDot(context.getOriginalLastCaret(), pos);
486
                    } catch (BadLocationException ex) {
487
                        // Ignore the setDot() request
488
                    }
489
                }
490
            }
491
        });
492
    }
493
494
    /**
495
     * Moves the caret position (dot) to some other position, leaving behind the
496
     * mark. This is useful for making selections.
497
     * <br>
498
     * If multiple carets are present this method retains all carets
499
     * and moves the dot of the last caret to the given offset.
500
     * 
501
     * @param offset {@inheritDoc}
502
     * @see Caret#moveDot(int) 
503
     */
504
    public @Override void moveDot(final int offset) {
505
        if (LOG.isLoggable(Level.FINE)) {
506
            LOG.fine("moveDot: offset=" + offset); //NOI18N
507
        }
508
        
509
        runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0, null, new CaretMoveHandler() {
510
            @Override
511
            public void moveCarets(CaretMoveContext context) {
512
                Document doc = context.getComponent().getDocument();
513
                if (doc != null) {
514
                    try {
515
                        Position pos = doc.createPosition(offset);
516
                        context.moveDot(context.getOriginalLastCaret(), pos);
517
                    } catch (BadLocationException ex) {
518
                        // Ignore the setDot() request
519
                    }
520
                }
521
            }
522
        });
523
    }
524
525
    /**
526
     * Move multiple carets or create/modify selections.
527
     * <br>
528
     * For performance reasons this is made as a single transaction over the caret
529
     * with only one change event being fired.
530
     * <br>
531
     * Note that the move handler does not permit to add or remove carets - this has to be performed
532
     * by other methods present in this class (as another transaction over the editor caret).
533
     * <br>
534
     * <pre>
535
     * <code>
536
     *   // Go one line up with all carets
537
     *   editorCaret.moveCarets(new CaretMoveHandler() {
538
     *     &#64;Override public void moveCarets(CaretMoveContext context) {
539
     *       for (CaretInfo caretInfo : context.getOriginalSortedCarets()) {
540
     *         try {
541
     *           int dot = caretInfo.getDot();
542
     *           dot = Utilities.getPositionAbove(target, dot, p.x);
543
     *           Position dotPos = doc.createPosition(dot);
544
     *           context.setDot(caretInfo, dotPos);
545
     *         } catch (BadLocationException e) {
546
     *           // the position stays the same
547
     *         }
548
     *       }
549
     *     }
550
     *   });
551
     * </code>
552
     * </pre>
553
     *
554
     * @param moveHandler non-null move handler to perform the changes. The handler's methods
555
     *  will be given a context to operate on.
556
     * @return difference between current count of carets and the number of carets when the operation started.
557
     *  Returns Integer.MIN_VALUE if the operation was cancelled due to the caret not being installed in any text component
558
     *  or no document installed in the text component.
559
     */
560
    public int moveCarets(@NonNull CaretMoveHandler moveHandler) {
561
        return runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0, null, moveHandler);
562
    }
563
564
    /**
565
     * Create a new caret at the given position with a possible selection.
566
     * <br>
567
     * The caret will become the last caret of the list returned by {@link #getCarets() }.
568
     * <br>
569
     * This method requires the caller to have either read lock or write lock acquired
570
     * over the underlying document.
571
     * <br>
572
     * <pre>
573
     * <code>
574
     *   editorCaret.addCaret(pos, pos); // Add a new caret at pos.getOffset()
575
     * 
576
     *   Position pos2 = doc.createPosition(pos.getOffset() + 2);
577
     *   // Add a new caret with selection starting at pos and extending to pos2 with caret located at pos2
578
     *   editorCaret.addCaret(pos2, pos);
579
     *   // Add a new caret with selection starting at pos and extending to pos2 with caret located at pos
580
     *   editorCaret.addCaret(pos, pos2);
581
     * </code>
582
     * </pre>
583
     * 
584
     * @param dotPos position of the newly created caret.
585
     * @param markPos beginning of the selection (the other end is dotPos) or the same position like dotPos for no selection.
586
     *  The markPos may have higher offset than dotPos to select in a backward direction.
587
     * @return difference between current count of carets and the number of carets when the operation started.
588
     *  Returns Integer.MIN_VALUE if the operation was cancelled due to the caret not being installed in any text component
589
     *  or no document installed in the text component.
590
     *  <br>
591
     *  Note that adding a new caret to offset where another caret is already located may lead
592
     *  to its immediate removal.
593
     */
594
    public int addCaret(@NonNull Position dotPos, @NonNull Position markPos) {
595
        return runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0,
596
                new CaretItem[] { new CaretItem(this, dotPos, markPos) }, null);
597
    }
598
    
599
    /**
600
     * Add multiple carets at once.
601
     * <br>
602
     * It is similar to calling {@link #addCaret(javax.swing.text.Position, javax.swing.text.Position) }
603
     * multiple times but this method is more efficient (it only fires caret change once).
604
     * <br>
605
     * This method requires the caller to have either read lock or write lock acquired
606
     * over the underlying document.
607
     * <br>
608
     * <pre>
609
     * <code>
610
     *   List&lt;Position&gt; pairs = new ArrayList&lt;&gt;();
611
     *   pairs.add(dotPos);
612
     *   pairs.add(dotPos);
613
     *   pairs.add(dot2Pos);
614
     *   pairs.add(mark2Pos);
615
     *   // Add caret located at dotPos.getOffset() and another one with selection
616
     *   // starting at mark2Pos and extending to dot2Pos with caret located at dot2Pos
617
     *   editorCaret.addCaret(pairs);
618
     * </code>
619
     * </pre>
620
     * 
621
     * @param dotAndMarkPosPairs list of position pairs consisting of dot position
622
     *  and mark position (selection start position) which may be the same position like the dot
623
     *  if the particular caret has no selection. The list must have even size.
624
     * @return difference between current count of carets and the number of carets when the operation started.
625
     *  Returns Integer.MIN_VALUE if the operation was cancelled due to the caret not being installed in any text component
626
     *  or no document installed in the text component.
627
     */
628
    public int addCarets(@NonNull List<Position> dotAndMarkPosPairs) {
629
        return runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0,
630
                CaretTransaction.asCaretItems(this, dotAndMarkPosPairs), null);
631
    }
632
633
    /**
634
     * Replace all current carets with the new ones.
635
     * <br>
636
     * This method requires the caller to have either read lock or write lock acquired
637
     * over the underlying document.
638
     * <br>
639
     * @param dotAndMarkPosPairs list of position pairs consisting of dot position
640
     *  and mark position (selection start position) which may be the same position like dot
641
     *  if the particular caret has no selection. The list must have even size.
642
     * @return difference between current count of carets and the number of carets when the operation started.
643
     *  Returns Integer.MIN_VALUE if the operation was cancelled due to the caret not being installed in any text component
644
     *  or no document installed in the text component.
645
     */
646
    public int replaceCarets(@NonNull List<Position> dotAndMarkPosPairs) {
647
        if (dotAndMarkPosPairs.isEmpty()) {
648
            throw new IllegalArgumentException("dotAndSelectionStartPosPairs list must not be empty");
649
        }
650
        CaretItem[] addedItems = CaretTransaction.asCaretItems(this, dotAndMarkPosPairs);
651
        return runTransaction(CaretTransaction.RemoveType.REMOVE_ALL_CARETS, 0, addedItems, null);
652
    }
653
654
    /**
655
     * Remove last added caret (determined by {@link #getLastCaret() }).
656
     * <br>
657
     * If there is just one caret the method has no effect.
658
     * 
659
     * @return difference between current count of carets and the number of carets when the operation started.
660
     *  Returns Integer.MIN_VALUE if the operation was cancelled due to the caret not being installed in any text component
661
     *  or no document installed in the text component.
662
     */
663
    public int removeLastCaret() {
664
        return runTransaction(CaretTransaction.RemoveType.REMOVE_LAST_CARET, 0, null, null);
665
    }
666
667
    /**
668
     * Switch to single caret mode by removing all carets except the last caret.
669
     * @return difference between current count of carets and the number of carets when the operation started.
670
     *  Returns Integer.MIN_VALUE if the operation was cancelled due to the caret not being installed in any text component
671
     *  or no document installed in the text component.
672
     */
673
    public int retainLastCaretOnly() {
674
        return runTransaction(CaretTransaction.RemoveType.RETAIN_LAST_CARET, 0, null, null);
675
    }
676
    
677
    /**
678
     * Adds listener to track caret changes in detail.
679
     * 
680
     * @param listener non-null listener.
681
     */
682
    public void addEditorCaretListener(@NonNull EditorCaretListener listener) {
683
        listenerList.add(listener);
684
    }
685
    
686
    /**
687
     * Adds listener to track caret position changes (to fulfil {@link Caret} interface).
688
     */
689
    @Override
690
    public void addChangeListener(@NonNull ChangeListener l) {
691
        changeListenerList.add(l);
692
    }
693
694
    public void removeEditorCaretListener(@NonNull EditorCaretListener listener) {
695
        listenerList.remove(listener);
696
    }
697
    
698
    /**
699
     * Removes listener to track caret position changes (to fulfil {@link Caret} interface).
700
     */
701
    @Override
702
    public void removeChangeListener(@NonNull ChangeListener l) {
703
        changeListenerList.remove(l);
704
    }
705
706
    /**
707
     * Determines if the caret is currently visible (it may be blinking depending on settings).
708
     * <p>
709
     * Caret becomes visible after <code>setVisible(true)</code> gets called on it.
710
     *
711
     * @return <code>true</code> if visible else <code>false</code>
712
     */
713
    @Override
714
    public boolean isVisible() {
715
        synchronized (listenerList) {
716
            return visible;
717
        }
718
    }
719
720
    /**
721
     * Sets the caret visibility, and repaints the caret.
722
     *
723
     * @param visible the visibility specifier
724
     * @see Caret#setVisible
725
     */
726
    @Override
727
    public void setVisible(boolean visible) {
728
        if (LOG.isLoggable(Level.FINER)) {
729
            LOG.finer("BaseCaret.setVisible(" + visible + ")\n");
730
            if (LOG.isLoggable(Level.FINEST)) {
731
                LOG.log(Level.INFO, "", new Exception());
732
            }
733
        }
734
        synchronized (listenerList) {
735
            if (flasher != null) {
736
                if (this.visible) {
737
                    flasher.stop();
738
                }
739
                if (LOG.isLoggable(Level.FINER)) {
740
                    LOG.finer((visible ? "Starting" : "Stopping") + // NOI18N
741
                            " the caret blinking timer: " + dumpVisibility() + '\n'); // NOI18N
742
                }
743
                this.visible = visible;
744
                if (visible) {
745
                    flasher.start();
746
                } else {
747
                    flasher.stop();
748
                }
749
            }
750
        }
751
        JTextComponent c = component;
752
        if (c != null) {
753
            // TODO only paint carets showing on screen
754
            List<CaretInfo> sortedCarets = getSortedCarets();
755
            for (CaretInfo caret : sortedCarets) {
756
                CaretItem caretItem = caret.getCaretItem();
757
                if (caretItem.getCaretBounds() != null) {
758
                    Rectangle repaintRect = caretItem.getCaretBounds();
759
                    c.repaint(repaintRect);
760
                }
761
            }
762
        }
763
    }
764
765
    @Override
766
    public boolean isSelectionVisible() {
767
        return selectionVisible;
768
    }
769
770
    @Override
771
    public void setSelectionVisible(boolean v) {
772
        if (selectionVisible == v) {
773
            return;
774
        }
775
        JTextComponent c = component;
776
        Document doc;
777
        if (c != null && (doc = c.getDocument()) != null) {
778
            selectionVisible = v;
779
            // [TODO] ensure to repaint 
780
        }
781
    }
782
783
    @Override
784
    public void install(JTextComponent c) {
785
        assert (SwingUtilities.isEventDispatchThread()); // must be done in AWT
786
        if (LOG.isLoggable(Level.FINE)) {
787
            LOG.fine("Installing to " + s2s(c)); //NOI18N
788
        }
789
        
790
        component = c;
791
        visible = true;
792
        modelChanged(null, c.getDocument());
793
794
        Boolean b = (Boolean) c.getClientProperty(EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY);
795
        overwriteMode = (b != null) ? b : false;
796
        updateOverwriteModeLayer(true);
797
        setBlinkVisible(true);
798
        
799
        // Attempt to assign initial bounds - usually here the component
800
        // is not yet added to the component hierarchy.
801
        updateAllCaretsBounds();
802
        
803
        if(getLastCaretItem().getCaretBounds() == null) {
804
            // For null bounds wait for the component to get resized
805
            // and attempt to recompute bounds then
806
            component.addComponentListener(listenerImpl);
807
        }
808
809
        component.addPropertyChangeListener(listenerImpl);
810
        component.addFocusListener(listenerImpl);
811
        component.addMouseListener(listenerImpl);
812
        component.addMouseMotionListener(listenerImpl);
813
        component.addKeyListener(listenerImpl);
814
        ViewHierarchy.get(component).addViewHierarchyListener(listenerImpl);
815
816
        if (component.hasFocus()) {
817
            if (LOG.isLoggable(Level.FINE)) {
818
                LOG.fine("Component has focus, calling BaseCaret.focusGained(); doc=" // NOI18N
819
                    + component.getDocument().getProperty(Document.TitleProperty) + '\n');
820
            }
821
            listenerImpl.focusGained(null); // emulate focus gained
822
        }
823
824
        dispatchUpdate(false);
825
    }
826
827
    @Override
828
    public void deinstall(JTextComponent c) {
829
        if (LOG.isLoggable(Level.FINE)) {
830
            LOG.fine("Deinstalling from " + s2s(c)); //NOI18N
831
        }
832
        
833
        synchronized (listenerList) {
834
            if (flasher != null) {
835
                setBlinkRate(0);
836
            }
837
        }
838
        
839
        c.removeComponentListener(listenerImpl);
840
        c.removePropertyChangeListener(listenerImpl);
841
        c.removeFocusListener(listenerImpl);
842
        c.removeMouseListener(listenerImpl);
843
        c.removeMouseMotionListener(listenerImpl);
844
        ViewHierarchy.get(c).removeViewHierarchyListener(listenerImpl);
845
846
        
847
        modelChanged(activeDoc, null);
848
    }
849
850
    @Override
851
    public void paint(Graphics g) {
852
        JTextComponent c = component;
853
        if (c == null) return;
854
        
855
        // Check whether the caret was moved but the component was not
856
        // validated yet and therefore the caret bounds are still null
857
        // and if so compute the bounds and scroll the view if necessary.
858
        // TODO - could this be done by an extra flag rather than bounds checking??
859
        CaretItem lastCaret = getLastCaretItem();
860
        if (getDot() != 0 && lastCaret.getCaretBounds() == null) {
861
            update(true);
862
        }
863
        
864
        List<CaretInfo> carets = getSortedCarets();
865
        for (CaretInfo caretInfo : carets) { // TODO only paint the items in the clipped area - use binary search to located first item
866
            CaretItem caretItem = caretInfo.getCaretItem();
867
            if (LOG.isLoggable(Level.FINEST)) {
868
                LOG.finest("BaseCaret.paint(): caretBounds=" + caretItem.getCaretBounds() + dumpVisibility() + '\n');
869
            }
870
            if (caretItem.getCaretBounds() != null && isVisible() && blinkVisible) {
871
                paintCaret(g, caretItem);
872
            }
873
            if (rectangularSelection && rsPaintRect != null && g instanceof Graphics2D) {
874
                Graphics2D g2d = (Graphics2D) g;
875
                Stroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] {4, 2}, 0);
876
                Stroke origStroke = g2d.getStroke();
877
                Color origColor = g2d.getColor();
878
                try {
879
                    // Render translucent rectangle
880
                    Color selColor = c.getSelectionColor();
881
                    g2d.setColor(selColor);
882
                    Composite origComposite = g2d.getComposite();
883
                    try {
884
                        g2d.setComposite(AlphaComposite.SrcOver.derive(0.2f));
885
                        g2d.fill(rsPaintRect);
886
                    } finally {
887
                        g2d.setComposite(origComposite);
888
                    }
889
                    // Paint stroked line around rectangular selection rectangle
890
                    g.setColor(c.getCaretColor());
891
                    g2d.setStroke(stroke);
892
                    Rectangle onePointSmallerRect = new Rectangle(rsPaintRect);
893
                    onePointSmallerRect.width--;
894
                    onePointSmallerRect.height--;
895
                    g2d.draw(onePointSmallerRect);
896
897
                } finally {
898
                    g2d.setStroke(origStroke);
899
                    g2d.setColor(origColor);
900
                }
901
            }
902
        }
903
    }
904
905
    public @Override void setMagicCaretPosition(Point p) {
906
        getLastCaretItem().setMagicCaretPosition(p);
907
    }
908
909
    public @Override final Point getMagicCaretPosition() {
910
        return getLastCaretItem().getMagicCaretPosition();
911
    }
912
913
    public @Override void setBlinkRate(int rate) {
914
        if (LOG.isLoggable(Level.FINER)) {
915
            LOG.finer("setBlinkRate(" + rate + ")" + dumpVisibility() + '\n'); // NOI18N
916
        }
917
        synchronized (listenerList) {
918
            if (flasher == null && rate > 0) {
919
                flasher = new Timer(rate, listenerImpl);
920
            }
921
            if (flasher != null) {
922
                if (rate > 0) {
923
                    if (flasher.getDelay() != rate) {
924
                        flasher.setDelay(rate);
925
                    }
926
                } else { // zero rate - don't blink
927
                    flasher.stop();
928
                    flasher.removeActionListener(listenerImpl);
929
                    flasher = null;
930
                    setBlinkVisible(true);
931
                    if (LOG.isLoggable(Level.FINER)){
932
                        LOG.finer("Zero blink rate - no blinking. flasher=null; blinkVisible=true"); // NOI18N
933
                    }
934
                }
935
            }
936
        }
937
    }
938
939
    @Override
940
    public int getBlinkRate() {
941
        synchronized (listenerList) {
942
            return (flasher != null) ? flasher.getDelay() : 0;
943
        }
944
    }
945
946
    /**
947
     * 
948
     */
949
    void setRectangularSelectionToDotAndMark() {
950
        int dotOffset = getDot();
951
        int markOffset = getMark();
952
        try {
953
            rsDotRect = component.modelToView(dotOffset);
954
            rsMarkRect = component.modelToView(markOffset);
955
        } catch (BadLocationException ex) {
956
            rsDotRect = rsMarkRect = null;
957
        }
958
        updateRectangularSelectionPaintRect();
959
    }
960
961
    /**
962
     * 
963
     */
964
    void updateRectangularUpDownSelection() {
965
        JTextComponent c = component;
966
        int dotOffset = getDot();
967
        try {
968
            Rectangle r = c.modelToView(dotOffset);
969
            rsDotRect.y = r.y;
970
            rsDotRect.height = r.height;
971
        } catch (BadLocationException ex) {
972
            // Leave rsDotRect unchanged
973
        }
974
    }
975
    
976
    /**
977
     * Extend rectangular selection either by char in a specified selection
978
     * or by word (if ctrl is pressed).
979
     *
980
     * @param toRight true for right or false for left.
981
     * @param ctrl true for ctrl pressed.
982
     */
983
    void extendRectangularSelection(boolean toRight, boolean ctrl) {
984
        JTextComponent c = component;
985
        Document doc = c.getDocument();
986
        int dotOffset = getDot();
987
        Element lineRoot = doc.getDefaultRootElement();
988
        int lineIndex = lineRoot.getElementIndex(dotOffset);
989
        Element lineElement = lineRoot.getElement(lineIndex);
990
        float charWidth;
991
        LockedViewHierarchy lvh = ViewHierarchy.get(c).lock();
992
        try {
993
            charWidth = lvh.getDefaultCharWidth();
994
        } finally {
995
            lvh.unlock();
996
        }
997
        int newDotOffset = -1;
998
        try {
999
            int newlineOffset = lineElement.getEndOffset() - 1;
1000
            Rectangle newlineRect = c.modelToView(newlineOffset);
1001
            if (!ctrl) {
1002
                if (toRight) {
1003
                    if (rsDotRect.x < newlineRect.x) {
1004
                        newDotOffset = dotOffset + 1;
1005
                    } else {
1006
                        rsDotRect.x += charWidth;
1007
                    }
1008
                } else { // toLeft
1009
                    if (rsDotRect.x > newlineRect.x) {
1010
                        rsDotRect.x -= charWidth;
1011
                        if (rsDotRect.x < newlineRect.x) { // Fix on rsDotRect
1012
                            newDotOffset = newlineOffset;
1013
                        }
1014
                    } else {
1015
                        newDotOffset = Math.max(dotOffset - 1, lineElement.getStartOffset());
1016
                    }
1017
                }
1018
1019
            } else { // With Ctrl
1020
                int numVirtualChars = 8; // Number of virtual characters per one Ctrl+Shift+Arrow press
1021
                if (toRight) {
1022
                    if (rsDotRect.x < newlineRect.x) {
1023
//[TODO] fix                        newDotOffset = Math.min(Utilities.getNextWord(c, dotOffset), lineElement.getEndOffset() - 1);
1024
                    } else { // Extend virtually
1025
                        rsDotRect.x += numVirtualChars * charWidth;
1026
                    }
1027
                } else { // toLeft
1028
                    if (rsDotRect.x > newlineRect.x) { // Virtually extended
1029
                        rsDotRect.x -= numVirtualChars * charWidth;
1030
                        if (rsDotRect.x < newlineRect.x) {
1031
                            newDotOffset = newlineOffset;
1032
                        }
1033
                    } else {
1034
//[TODO] fix                        newDotOffset = Math.max(Utilities.getPreviousWord(c, dotOffset), lineElement.getStartOffset());
1035
                    }
1036
                }
1037
            }
1038
1039
            if (newDotOffset != -1) {
1040
                rsDotRect = c.modelToView(newDotOffset);
1041
                moveDot(newDotOffset); // updates rs and fires state change
1042
            } else {
1043
                updateRectangularSelectionPaintRect();
1044
                fireStateChanged();
1045
            }
1046
        } catch (BadLocationException ex) {
1047
            // Leave selection as is
1048
        }
1049
    }
1050
    
1051
    
1052
    // Private implementation
1053
    
1054
    /** This method should only be accessed by transaction's methods */
1055
    GapList<CaretItem> getCaretItems() {
1056
        return caretItems; // No sync as this should only be accessed by transaction's methods
1057
    }
1058
    
1059
    /** This method should only be accessed by transaction's methods */
1060
    GapList<CaretItem> getSortedCaretItems() {
1061
        return sortedCaretItems; // No sync as this should only be accessed by transaction's methods
1062
    }
1063
1064
    /** This method may be accessed arbitrarily */
1065
    private CaretItem getLastCaretItem() {
1066
        synchronized (listenerList) {
1067
            return caretItems.get(caretItems.size() - 1);
1068
        }
1069
    }
1070
1071
    /**
1072
     * Run a transaction to modify number of carets or their dots or selections.
1073
     * @param removeType type of carets removal.
1074
     * @param offset offset of document text removal otherwise the value is ignored.
1075
     *  Internally this is also used for an end offset of the insertion at offset zero once it happens.
1076
     * @param addCarets carets to be added if any.
1077
     * @param moveHandler caret move handler or null if there's no caret moving. API client's move handlers
1078
     *  are only invoked without any extra removals or additions so the original caret infos are used.
1079
     *  Internal transactions may use caret additions or removals together with caret move handlers
1080
     *  but they are also passed with original caret infos so the handlers must be aware of the removal
1081
     *  and only pick the valid non-removed items.
1082
     * @return 
1083
     */
1084
    private int runTransaction(CaretTransaction.RemoveType removeType, int offset, CaretItem[] addCarets, CaretMoveHandler moveHandler) {
1085
        lock();
1086
        try {
1087
            if (activeTransaction == null) {
1088
                JTextComponent c = component;
1089
                Document d = activeDoc;
1090
                if (c != null && d != null) {
1091
                    activeTransaction = new CaretTransaction(this, c, d);
1092
                    try {
1093
                        activeTransaction.replaceCarets(removeType, offset, addCarets);
1094
                        if (moveHandler != null) {
1095
                            activeTransaction.runCaretMoveHandler(moveHandler);
1096
                        }
1097
                        activeTransaction.removeOverlappingRegions();
1098
                        int diffCount = 0;
1099
                        synchronized (listenerList) {
1100
                            GapList<CaretItem> replaceItems = activeTransaction.getReplaceItems();
1101
                            if (replaceItems != null) {
1102
                                diffCount = replaceItems.size() - caretItems.size();
1103
                                caretItems = replaceItems;
1104
                                sortedCaretItems = activeTransaction.getSortedCaretItems();
1105
                                assert (sortedCaretItems != null) : "Null sortedCaretItems! removeType=" + removeType; // NOI18N
1106
                            }
1107
                        }
1108
                        if (activeTransaction.isAnyChange()) {
1109
                            caretInfos = null;
1110
                            sortedCaretInfos = null;
1111
                        }
1112
                        pendingRepaintRemovedItemsList = activeTransaction.
1113
                                addRemovedItems(pendingRepaintRemovedItemsList);
1114
                        pendingUpdateVisualBoundsItemsList = activeTransaction.
1115
                                addUpdateVisualBoundsItems(pendingUpdateVisualBoundsItemsList);
1116
                        if (pendingUpdateVisualBoundsItemsList != null || pendingRepaintRemovedItemsList != null) {
1117
                            // For now clear the lists and use old way TODO update to selective updating and rendering
1118
                            fireStateChanged();
1119
                            dispatchUpdate(true);
1120
                            pendingRepaintRemovedItemsList = null;
1121
                            pendingUpdateVisualBoundsItemsList = null;
1122
                        }
1123
                        return diffCount;
1124
                    } finally {
1125
                        activeTransaction = null;
1126
                    }
1127
                }
1128
                return Integer.MIN_VALUE;
1129
                
1130
            } else { // Nested transaction - document insert/remove within transaction
1131
                switch (removeType) {
1132
                    case DOCUMENT_REMOVE:
1133
                        activeTransaction.documentRemove(offset);
1134
                        break;
1135
                    case DOCUMENT_INSERT_ZERO_OFFSET:
1136
                        activeTransaction.documentInsertAtZeroOffset(offset);
1137
                        break;
1138
                    default:
1139
                        throw new AssertionError("Unsupported removeType=" + removeType + " in nested transaction"); // NOI18N
1140
                }
1141
                return 0;
1142
            }
1143
        } finally {
1144
            unlock();
1145
        }
1146
    }
1147
    
1148
    private void moveDotCaret(int offset, CaretItem caret) throws IllegalStateException {
1149
        JTextComponent c = component;
1150
        AbstractDocument doc;
1151
        if (c != null && (doc = activeDoc) != null) {
1152
            if (offset >= 0 && offset <= doc.getLength()) {
1153
                doc.readLock();
1154
                try {
1155
                    int oldCaretPos = caret.getDot();
1156
                    if (offset == oldCaretPos) { // no change
1157
                        return;
1158
                    }
1159
                    caret.setDotPos(doc.createPosition(offset));
1160
                    // Selection highlighting should be handled automatically by highlighting layers
1161
                    if (rectangularSelection) {
1162
                        Rectangle r = c.modelToView(offset);
1163
                        if (rsDotRect != null) {
1164
                            rsDotRect.y = r.y;
1165
                            rsDotRect.height = r.height;
1166
                        } else {
1167
                            rsDotRect = r;
1168
                        }
1169
                        updateRectangularSelectionPaintRect();
1170
                    }
1171
                } catch (BadLocationException e) {
1172
                    throw new IllegalStateException(e.toString());
1173
                    // position is incorrect
1174
                } finally {
1175
                    doc.readUnlock();
1176
                }
1177
            }
1178
            fireStateChanged();
1179
            dispatchUpdate(true);
1180
        }
1181
    }
1182
    
1183
    private void fireEditorCaretChange(EditorCaretEvent evt) {
1184
        for (EditorCaretListener listener : listenerList.getListeners()) {
1185
            listener.caretChanged(evt);
1186
        }
1187
    }
1188
    
1189
    /**
1190
     * Notifies listeners that caret position has changed.
1191
     */
1192
    private void fireStateChanged() {
1193
        Runnable runnable = new Runnable() {
1194
            public @Override void run() {
1195
                JTextComponent c = component;
1196
                if (c == null || c.getCaret() != EditorCaret.this) {
1197
                    return;
1198
                }
1199
                fireEditorCaretChange(new EditorCaretEvent(EditorCaret.this, 0, Integer.MAX_VALUE)); // [TODO] temp firing without detailed info
1200
                ChangeEvent evt = new ChangeEvent(EditorCaret.this);
1201
                List<ChangeListener> listeners = changeListenerList.getListeners();
1202
                for (ChangeListener l : listeners) {
1203
                    l.stateChanged(evt);
1204
                }
1205
            }
1206
        };
1207
        
1208
        // Always fire in EDT
1209
        if (inAtomicUnlock) { // Cannot fire within atomic lock9
1210
            SwingUtilities.invokeLater(runnable);
1211
        } else {
1212
            ViewUtils.runInEDT(runnable);
1213
        }
1214
        updateSystemSelection();
1215
    }
1216
1217
    private void lock() {
1218
        Thread curThread = Thread.currentThread();
1219
        try {
1220
            synchronized (listenerList) {
1221
                while (lockThread != null) {
1222
                    if (curThread == lockThread) {
1223
                        lockDepth++;
1224
                        return;
1225
                    }
1226
                    listenerList.wait();
1227
                }
1228
                lockThread = curThread;
1229
                lockDepth = 1;
1230
1231
            }
1232
        } catch (InterruptedException e) {
1233
            throw new Error("Interrupted attempt to acquire lock"); // NOI18N
1234
        }
1235
    }
1236
    
1237
    private void unlock() {
1238
        Thread curThread = Thread.currentThread();
1239
        synchronized (listenerList) {
1240
            if (lockThread == curThread) {
1241
                lockDepth--;
1242
                if (lockDepth == 0) {
1243
                    lockThread = null;
1244
                    listenerList.notifyAll();
1245
                }
1246
            } else {
1247
                throw new IllegalStateException("Invalid thread called EditorCaret.unlock():  thread=" + // NOI18N
1248
                        curThread + ", lockThread=" + lockThread); // NOI18N
1249
            }
1250
        }
1251
    }
1252
1253
    private void updateType() {
1254
        final JTextComponent c = component;
1255
        if (c != null) {
1256
            Color caretColor = null;
1257
            CaretType newType;
1258
            boolean cIsTextField = Boolean.TRUE.equals(c.getClientProperty("AsTextField"));
1259
            
1260
            if (cIsTextField) {
1261
                newType = CaretType.THIN_LINE_CARET;
1262
            } else if (prefs != null) {
1263
                String newTypeStr;
1264
                if (overwriteMode) {
1265
                    newTypeStr = prefs.get(SimpleValueNames.CARET_TYPE_OVERWRITE_MODE, EditorPreferencesDefaults.defaultCaretTypeOverwriteMode);
1266
                } else { // insert mode
1267
                    newTypeStr = prefs.get(SimpleValueNames.CARET_TYPE_INSERT_MODE, EditorPreferencesDefaults.defaultCaretTypeInsertMode);
1268
                    this.thickCaretWidth = prefs.getInt(SimpleValueNames.THICK_CARET_WIDTH, EditorPreferencesDefaults.defaultThickCaretWidth);
1269
                }
1270
                newType = CaretType.decode(newTypeStr);
1271
            } else {
1272
                newType = CaretType.THICK_LINE_CARET;
1273
            }
1274
1275
            String mimeType = DocumentUtilities.getMimeType(c);
1276
            FontColorSettings fcs = (mimeType != null) ? MimeLookup.getLookup(mimeType).lookup(FontColorSettings.class) : null;
1277
            if (fcs != null) {
1278
                AttributeSet attribs = fcs.getFontColors(overwriteMode
1279
                        ? FontColorNames.CARET_COLOR_OVERWRITE_MODE
1280
                        : FontColorNames.CARET_COLOR_INSERT_MODE); //NOI18N
1281
                if (attribs != null) {
1282
                    caretColor = (Color) attribs.getAttribute(StyleConstants.Foreground);
1283
                }
1284
            }
1285
            
1286
            this.type = newType;
1287
            final Color caretColorFinal = caretColor;
1288
            ViewUtils.runInEDT(
1289
                new Runnable() {
1290
                    public @Override void run() {
1291
                        if (caretColorFinal != null) {
1292
                            c.setCaretColor(caretColorFinal);
1293
                            if (LOG.isLoggable(Level.FINER)) {
1294
                                LOG.finer("Updating caret color:" + caretColorFinal + '\n'); // NOI18N
1295
                            }
1296
                        }
1297
1298
                        resetBlink();
1299
                        dispatchUpdate(false);
1300
                    }
1301
                }
1302
            );
1303
        }
1304
    }
1305
1306
    /**
1307
     * Assign new caret bounds into <code>caretBounds</code> variable.
1308
     *
1309
     * @return true if the new caret bounds were successfully computed
1310
     *  and assigned or false otherwise.
1311
     */
1312
    private boolean updateAllCaretsBounds() {
1313
        JTextComponent c = component;
1314
        AbstractDocument doc;
1315
        boolean ret = false;
1316
        if (c != null && (doc = activeDoc) != null) {
1317
            doc.readLock();
1318
            try {
1319
                List<CaretInfo> sortedCarets = getSortedCarets();
1320
                for (CaretInfo caret : sortedCarets) {
1321
                    ret |= updateRealCaretBounds(caret.getCaretItem(), doc, c);
1322
                }
1323
            } finally {
1324
                doc.readUnlock();
1325
            }
1326
        }
1327
        return ret;
1328
    }
1329
    
1330
    private boolean updateCaretBounds(CaretItem caret) {
1331
        JTextComponent c = component;
1332
        boolean ret = false;
1333
        AbstractDocument doc;
1334
        if (c != null && (doc = activeDoc) != null) {
1335
            doc.readLock();
1336
            try {
1337
                ret = updateRealCaretBounds(caret, doc, c);
1338
            } finally {
1339
                doc.readUnlock();
1340
            }
1341
        }
1342
        return ret;
1343
    }
1344
    
1345
    private boolean updateRealCaretBounds(CaretItem caret, Document doc, JTextComponent c) {
1346
        Position dotPos = caret.getDotPosition();
1347
        int offset = dotPos == null? 0 : dotPos.getOffset();
1348
        if (offset > doc.getLength()) {
1349
            offset = doc.getLength();
1350
        }
1351
        Rectangle newCaretBounds;
1352
        try {
1353
            DocumentView docView = DocumentView.get(c);
1354
            if (docView != null) {
1355
                // docView.syncViewsRebuild(); // Make sure pending views changes are resolved
1356
            }
1357
            newCaretBounds = c.getUI().modelToView(
1358
                    c, offset, Position.Bias.Forward);
1359
            // [TODO] Temporary fix - impl should remember real bounds computed by paintCustomCaret()
1360
            if (newCaretBounds != null) {
1361
                newCaretBounds.width = Math.max(newCaretBounds.width, 2);
1362
            }
1363
1364
        } catch (BadLocationException e) {
1365
            
1366
            newCaretBounds = null;
1367
        }
1368
        if (newCaretBounds != null) {
1369
            if (LOG.isLoggable(Level.FINE)) {
1370
                LOG.log(Level.FINE, "updateCaretBounds: old={0}, new={1}, offset={2}",
1371
                        new Object[]{caret.getCaretBounds(), newCaretBounds, offset}); //NOI18N
1372
            }
1373
            caret.setCaretBounds(newCaretBounds);
1374
            return true;
1375
        } else {
1376
            return false;
1377
        }
1378
    }
1379
1380
    private void modelChanged(Document oldDoc, Document newDoc) {
1381
        if (oldDoc != null) {
1382
            // ideally the oldDoc param shouldn't exist and only listenDoc should be used
1383
            assert (oldDoc == activeDoc);
1384
1385
            DocumentUtilities.removeDocumentListener(
1386
                    oldDoc, listenerImpl, DocumentListenerPriority.CARET_UPDATE);
1387
            AtomicLockDocument oldAtomicDoc = LineDocumentUtils.as(oldDoc, AtomicLockDocument.class);
1388
            if (oldAtomicDoc != null) {
1389
                oldAtomicDoc.removeAtomicLockListener(listenerImpl);
1390
            }
1391
1392
            activeDoc = null;
1393
            if (prefs != null && weakPrefsListener != null) {
1394
                prefs.removePreferenceChangeListener(weakPrefsListener);
1395
            }
1396
        }
1397
1398
        // EditorCaret only installs successfully into AbstractDocument based documents that carry a mime-type
1399
        if (newDoc instanceof AbstractDocument) {
1400
            String mimeType = DocumentUtilities.getMimeType(newDoc);
1401
            activeDoc = (AbstractDocument) newDoc;
1402
            DocumentUtilities.addDocumentListener(
1403
                    newDoc, listenerImpl, DocumentListenerPriority.CARET_UPDATE);
1404
            AtomicLockDocument newAtomicDoc = LineDocumentUtils.as(oldDoc, AtomicLockDocument.class);
1405
            if (newAtomicDoc != null) {
1406
                newAtomicDoc.addAtomicLockListener(listenerImpl);
1407
            }
1408
1409
            // Set caret to zero position upon document change (DefaultCaret impl does this too)
1410
            runTransaction(CaretTransaction.RemoveType.REMOVE_ALL_CARETS, 0,
1411
                    new CaretItem[] { new CaretItem(this, newDoc.getStartPosition(), null) }, null);
1412
            
1413
            // Leave caretPos and markPos null => offset==0
1414
            prefs = (mimeType != null) ? MimeLookup.getLookup(mimeType).lookup(Preferences.class) : null;
1415
            if (prefs != null) {
1416
                weakPrefsListener = WeakListeners.create(PreferenceChangeListener.class, listenerImpl, prefs);
1417
                prefs.addPreferenceChangeListener(weakPrefsListener);
1418
            }
1419
            
1420
            updateType();
1421
        }
1422
    }
1423
    
1424
    private void paintCaret(Graphics g, CaretItem caret) {
1425
        JTextComponent c = component;
1426
        if (c != null) {
1427
            g.setColor(c.getCaretColor());
1428
            Rectangle caretBounds = caret.getCaretBounds();
1429
            switch (type) {
1430
                case THICK_LINE_CARET:
1431
                    g.fillRect(caretBounds.x, caretBounds.y, this.thickCaretWidth, caretBounds.height - 1);
1432
                    break;
1433
1434
                case THIN_LINE_CARET:
1435
                    int upperX = caret.getCaretBounds().x;
1436
                    g.drawLine((int) upperX, caret.getCaretBounds().y, caret.getCaretBounds().x,
1437
                            (caret.getCaretBounds().y + caret.getCaretBounds().height - 1));
1438
                    break;
1439
1440
                case BLOCK_CARET:
1441
                    // Use a CaretOverwriteModeHighlighting layer to paint the caret
1442
                    break;
1443
1444
                default:
1445
                    throw new IllegalStateException("Invalid caret type=" + type);
1446
            }
1447
        }
1448
    }
1449
1450
    void dispatchUpdate() {
1451
        JTextComponent c = component;
1452
        if (c != null) {
1453
            dispatchUpdate(c.hasFocus()); // Scroll to caret only for component with focus
1454
        }
1455
    }
1456
    /** Update visual position of caret(s) */
1457
    private void dispatchUpdate(final boolean scrollViewToCaret) {
1458
        /* Ensure that the caret's document listener will be added AFTER the views hierarchy's
1459
         * document listener so the code can run synchronously again
1460
         * which should eliminate the problem with caret lag.
1461
         * However the document can be modified from non-AWT thread
1462
         * which is the case in #57316 and in that case the code
1463
         * must run asynchronously in AWT thread.
1464
         */
1465
        ViewUtils.runInEDT(
1466
            new Runnable() {
1467
                public @Override void run() {
1468
                    AbstractDocument doc = activeDoc;
1469
                    if (doc != null) {
1470
                        doc.readLock();
1471
                        try {
1472
                            update(scrollViewToCaret);
1473
                        } finally {
1474
                            doc.readUnlock();
1475
                        }
1476
                    }
1477
                }
1478
            }
1479
        );
1480
    }
1481
1482
    /**
1483
     * Update the caret's visual position.
1484
     * <br>
1485
     * The document is read-locked while calling this method.
1486
     *
1487
     * @param scrollViewToCaret whether the view of the text component should be
1488
     *  scrolled to the position of the caret.
1489
     */
1490
    private void update(boolean scrollViewToCaret) {
1491
        caretUpdatePending = false;
1492
        JTextComponent c = component;
1493
        if (c != null) {
1494
            if (!c.isValid()) {
1495
                c.validate();
1496
            }
1497
            Document doc = c.getDocument();
1498
            if (doc != null) {
1499
                List<CaretInfo> sortedCarets = getSortedCarets();
1500
                for (CaretInfo caret : sortedCarets) {
1501
                    CaretItem caretItem = caret.getCaretItem();
1502
                    Rectangle oldCaretBounds = caretItem.getCaretBounds(); // no need to deep copy
1503
                    if (oldCaretBounds != null) {
1504
                        c.repaint(oldCaretBounds);
1505
                    }
1506
1507
                    // note - the order is important ! caret bounds must be updated even if the fold flag is true.
1508
                    if (updateCaretBounds(caretItem) || updateAfterFoldHierarchyChange) {
1509
                        Rectangle scrollBounds = new Rectangle(caretItem.getCaretBounds());
1510
1511
                        // Optimization to avoid extra repaint:
1512
                        // If the caret bounds were not yet assigned then attempt
1513
                        // to scroll the window so that there is an extra vertical space 
1514
                        // for the possible horizontal scrollbar that may appear
1515
                        // if the line-view creation process finds line-view that
1516
                        // is too wide and so the horizontal scrollbar will appear
1517
                        // consuming an extra vertical space at the bottom.
1518
                        if (oldCaretBounds == null) {
1519
                            Component viewport = c.getParent();
1520
                            if (viewport instanceof JViewport) {
1521
                                Component scrollPane = viewport.getParent();
1522
                                if (scrollPane instanceof JScrollPane) {
1523
                                    JScrollBar hScrollBar = ((JScrollPane) scrollPane).getHorizontalScrollBar();
1524
                                    if (hScrollBar != null) {
1525
                                        int hScrollBarHeight = hScrollBar.getPreferredSize().height;
1526
                                        Dimension extentSize = ((JViewport) viewport).getExtentSize();
1527
                                        // If the extent size is high enough then extend
1528
                                        // the scroll region by extra vertical space
1529
                                        if (extentSize.height >= caretItem.getCaretBounds().height + hScrollBarHeight) {
1530
                                            scrollBounds.height += hScrollBarHeight;
1531
                                        }
1532
                                    }
1533
                                }
1534
                            }
1535
                        }
1536
1537
                        Rectangle visibleBounds = c.getVisibleRect();
1538
1539
                        // If folds have changed attempt to scroll the view so that 
1540
                        // relative caret's visual position gets retained
1541
                        // (the absolute position will change because of collapsed/expanded folds).
1542
                        boolean doScroll = scrollViewToCaret;
1543
                        boolean explicit = false;
1544
                        if (oldCaretBounds != null && (!scrollViewToCaret || updateAfterFoldHierarchyChange)) {
1545
                            int oldRelY = oldCaretBounds.y - visibleBounds.y;
1546
                            // Only fix if the caret is within visible bounds and the new x or y coord differs from the old one
1547
                            if (LOG.isLoggable(Level.FINER)) {
1548
                                LOG.log(Level.FINER, "oldCaretBounds: {0}, visibleBounds: {1}, caretBounds: {2}",
1549
                                        new Object[]{oldCaretBounds, visibleBounds, caretItem.getCaretBounds()});
1550
                            }
1551
                            if (oldRelY >= 0 && oldRelY < visibleBounds.height
1552
                                    && (oldCaretBounds.y != caretItem.getCaretBounds().y || oldCaretBounds.x != caretItem.getCaretBounds().x)) {
1553
                                doScroll = true; // Perform explicit scrolling
1554
                                explicit = true;
1555
                                int oldRelX = oldCaretBounds.x - visibleBounds.x;
1556
                                // Do not retain the horizontal caret bounds by scrolling
1557
                                // since many modifications do not explicitly say that they are typing modifications
1558
                                // and this would cause problems like #176268
1559
//                            scrollBounds.x = Math.max(caretBounds.x - oldRelX, 0);
1560
                                scrollBounds.y = Math.max(caretItem.getCaretBounds().y - oldRelY, 0);
1561
//                            scrollBounds.width = visibleBounds.width;
1562
                                scrollBounds.height = visibleBounds.height;
1563
                            }
1564
                        }
1565
1566
                        // Historically the caret is expected to appear
1567
                        // in the middle of the window if setDot() gets called
1568
                        // e.g. by double-clicking in Navigator.
1569
                        // If the caret bounds are more than a caret height below the present
1570
                        // visible view bounds (or above the view bounds)
1571
                        // then scroll the window so that the caret is in the middle
1572
                        // of the visible window to see the context around the caret.
1573
                        // This should work fine with PgUp/Down because these
1574
                        // scroll the view explicitly.
1575
                        if (scrollViewToCaret
1576
                                && !explicit
1577
                                && // #219580: if the preceding if-block computed new scrollBounds, it cannot be offset yet more
1578
                                /* # 70915 !updateAfterFoldHierarchyChange && */ (caretItem.getCaretBounds().y > visibleBounds.y + visibleBounds.height + caretItem.getCaretBounds().height
1579
                                || caretItem.getCaretBounds().y + caretItem.getCaretBounds().height < visibleBounds.y - caretItem.getCaretBounds().height)) {
1580
                            // Scroll into the middle
1581
                            scrollBounds.y -= (visibleBounds.height - caretItem.getCaretBounds().height) / 2;
1582
                            scrollBounds.height = visibleBounds.height;
1583
                        }
1584
                        if (LOG.isLoggable(Level.FINER)) {
1585
                            LOG.finer("Resetting fold flag, current: " + updateAfterFoldHierarchyChange);
1586
                        }
1587
                        updateAfterFoldHierarchyChange = false;
1588
1589
                        // Ensure that the viewport will be scrolled either to make the caret visible
1590
                        // or to retain cart's relative visual position against the begining of the viewport's visible rectangle.
1591
                        if (doScroll) {
1592
                            if (LOG.isLoggable(Level.FINER)) {
1593
                                LOG.finer("Scrolling to: " + scrollBounds);
1594
                            }
1595
                            c.scrollRectToVisible(scrollBounds);
1596
                            if (!c.getVisibleRect().intersects(scrollBounds)) {
1597
                                // HACK: see #219580: for some reason, the scrollRectToVisible may fail.
1598
                                c.scrollRectToVisible(scrollBounds);
1599
                            }
1600
                        }
1601
                        resetBlink();
1602
                        c.repaint(caretItem.getCaretBounds());
1603
                    }
1604
                }
1605
            }
1606
        }
1607
    }
1608
1609
    private void updateSystemSelection() {
1610
        if(component == null) return;
1611
        Clipboard clip = null;
1612
        try {
1613
            clip = component.getToolkit().getSystemSelection();
1614
        } catch (SecurityException ex) {
1615
            // XXX: ignore for now, there is no ExClipboard for SystemSelection Clipboard
1616
        }
1617
        if(clip != null) {
1618
            StringBuilder builder = new StringBuilder();
1619
            boolean first = true;
1620
            List<CaretInfo> sortedCarets = getSortedCarets();
1621
            for (CaretInfo caret : sortedCarets) {
1622
                CaretItem caretItem = caret.getCaretItem();
1623
                if(caretItem.isSelection()) {
1624
                    if(!first) {
1625
                        builder.append("\n");
1626
                    } else {
1627
                        first = false;
1628
                    }
1629
                    builder.append(getSelectedText(caretItem));
1630
                }
1631
            }
1632
            if(builder.length() > 0) {
1633
                clip.setContents(new java.awt.datatransfer.StringSelection(builder.toString()), null);
1634
            }
1635
        }
1636
    }
1637
    
1638
    /**
1639
     * Returns the selected text contained for this
1640
     * <code>Caret</code>.  If the selection is
1641
     * <code>null</code> or the document empty, returns <code>null</code>.
1642
     *
1643
     * @param caret
1644
     * @return the text
1645
     * @exception IllegalArgumentException if the selection doesn't
1646
     *  have a valid mapping into the document for some reason
1647
     */
1648
    private String getSelectedText(CaretItem caret) {
1649
        String txt = null;
1650
        int p0 = Math.min(caret.getDot(), caret.getMark());
1651
        int p1 = Math.max(caret.getDot(), caret.getMark());
1652
        if (p0 != p1) {
1653
            try {
1654
                Document doc = component.getDocument();
1655
                txt = doc.getText(p0, p1 - p0);
1656
            } catch (BadLocationException e) {
1657
                throw new IllegalArgumentException(e.getMessage());
1658
            }
1659
        }
1660
        return txt;
1661
    }
1662
1663
    private void updateRectangularSelectionPositionBlocks() {
1664
        JTextComponent c = component;
1665
        if (rectangularSelection) {
1666
            AbstractDocument doc = activeDoc;
1667
            if (doc != null) {
1668
                doc.readLock();
1669
                try {
1670
                    if (rsRegions == null) {
1671
                        rsRegions = new ArrayList<Position>();
1672
                        component.putClientProperty(RECTANGULAR_SELECTION_REGIONS_PROPERTY, rsRegions);
1673
                    }
1674
                    synchronized (rsRegions) {
1675
                        if (LOG.isLoggable(Level.FINE)) {
1676
                            LOG.fine("Rectangular-selection position regions:\n");
1677
                        }
1678
                        rsRegions.clear();
1679
                        if (rsPaintRect != null) {
1680
                            LockedViewHierarchy lvh = ViewHierarchy.get(c).lock();
1681
                            try {
1682
                                float rowHeight = lvh.getDefaultRowHeight();
1683
                                double y = rsPaintRect.y;
1684
                                double maxY = y + rsPaintRect.height;
1685
                                double minX = rsPaintRect.getMinX();
1686
                                double maxX = rsPaintRect.getMaxX();
1687
                                do {
1688
                                    int startOffset = lvh.viewToModel(minX, y, null);
1689
                                    int endOffset = lvh.viewToModel(maxX, y, null);
1690
                                    // They could be swapped due to RTL text
1691
                                    if (startOffset > endOffset) {
1692
                                        int tmp = startOffset;
1693
                                        startOffset = endOffset;
1694
                                        endOffset = tmp;
1695
                                    }
1696
                                    Position startPos = activeDoc.createPosition(startOffset);
1697
                                    Position endPos = activeDoc.createPosition(endOffset);
1698
                                    rsRegions.add(startPos);
1699
                                    rsRegions.add(endPos);
1700
                                    if (LOG.isLoggable(Level.FINE)) {
1701
                                        LOG.fine("  <" + startOffset + "," + endOffset + ">\n");
1702
                                    }
1703
                                    y += rowHeight;
1704
                                } while (y < maxY);
1705
                                c.putClientProperty(RECTANGULAR_SELECTION_REGIONS_PROPERTY, rsRegions);
1706
                            } finally {
1707
                                lvh.unlock();
1708
                            }
1709
                        }
1710
                    }
1711
                } catch (BadLocationException ex) {
1712
                    Exceptions.printStackTrace(ex);
1713
                } finally {
1714
                    doc.readUnlock();
1715
                }
1716
            }
1717
        }
1718
    }
1719
1720
    private String dumpVisibility() {
1721
        return "visible=" + isVisible() + ", blinkVisible=" + blinkVisible;
1722
    }
1723
1724
    /*private*/ void resetBlink() {
1725
        boolean visible = isVisible();
1726
        synchronized (listenerList) {
1727
            if (flasher != null) {
1728
                flasher.stop();
1729
                setBlinkVisible(true);
1730
                if (visible) {
1731
                    if (LOG.isLoggable(Level.FINER)){
1732
                        LOG.finer("Reset blinking (caret already visible)" + // NOI18N
1733
                                " - starting the caret blinking timer: " + dumpVisibility() + '\n'); // NOI18N
1734
                    }
1735
                    flasher.start();
1736
                } else {
1737
                    if (LOG.isLoggable(Level.FINER)){
1738
                        LOG.finer("Reset blinking (caret not visible)" + // NOI18N
1739
                                " - caret blinking timer not started: " + dumpVisibility() + '\n'); // NOI18N
1740
                    }
1741
                }
1742
            }
1743
        }
1744
    }
1745
    
1746
    /*private*/ void setBlinkVisible(boolean blinkVisible) {
1747
        synchronized (listenerList) {
1748
            this.blinkVisible = blinkVisible;
1749
        }
1750
        updateOverwriteModeLayer(false);
1751
    }
1752
    
1753
    private void updateOverwriteModeLayer(boolean forceUpdate) {
1754
        JTextComponent c;
1755
        if ((forceUpdate || overwriteMode) && (c = component) != null) {
1756
            CaretOverwriteModeHighlighting overwriteModeHighlighting = (CaretOverwriteModeHighlighting)
1757
                    c.getClientProperty(CaretOverwriteModeHighlighting.class);
1758
            if (overwriteModeHighlighting != null) {
1759
                overwriteModeHighlighting.setVisible(visible && blinkVisible);
1760
            }
1761
        }
1762
    }
1763
1764
    private void adjustRectangularSelectionMouseX(int x, int y) {
1765
        if (!rectangularSelection) {
1766
            return;
1767
        }
1768
        JTextComponent c = component;
1769
        int offset = c.viewToModel(new Point(x, y));
1770
        Rectangle r = null;;
1771
        if (offset >= 0) {
1772
            try {
1773
                r = c.modelToView(offset);
1774
            } catch (BadLocationException ex) {
1775
                r = null;
1776
            }
1777
        }
1778
        if (r != null) {
1779
            float xDiff = x - r.x;
1780
            if (xDiff > 0) {
1781
                float charWidth;
1782
                LockedViewHierarchy lvh = ViewHierarchy.get(c).lock();
1783
                try {
1784
                    charWidth = lvh.getDefaultCharWidth();
1785
                } finally {
1786
                    lvh.unlock();
1787
                }
1788
                int n = (int) (xDiff / charWidth);
1789
                r.x += n * charWidth;
1790
                r.width = (int) charWidth;
1791
            }
1792
            rsDotRect.x = r.x;
1793
            rsDotRect.width = r.width;
1794
            updateRectangularSelectionPaintRect();
1795
            fireStateChanged();
1796
        }
1797
    }
1798
    
1799
    private void updateRectangularSelectionPaintRect() {
1800
        // Repaint current rect
1801
        JTextComponent c = component;
1802
        Rectangle repaintRect = rsPaintRect;
1803
        if (rsDotRect == null || rsMarkRect == null) {
1804
            return;
1805
        }
1806
        Rectangle newRect = new Rectangle();
1807
        if (rsDotRect.x < rsMarkRect.x) { // Swap selection to left
1808
            newRect.x = rsDotRect.x; // -1 to make the visual selection non-empty
1809
            newRect.width = rsMarkRect.x - newRect.x;
1810
        } else { // Extend or shrink on right
1811
            newRect.x = rsMarkRect.x;
1812
            newRect.width = rsDotRect.x - newRect.x;
1813
        }
1814
        if (rsDotRect.y < rsMarkRect.y) {
1815
            newRect.y = rsDotRect.y;
1816
            newRect.height = (rsMarkRect.y + rsMarkRect.height) - newRect.y;
1817
        } else {
1818
            newRect.y = rsMarkRect.y;
1819
            newRect.height = (rsDotRect.y + rsDotRect.height) - newRect.y;
1820
        }
1821
        if (newRect.width < 2) {
1822
            newRect.width = 2;
1823
        }
1824
        rsPaintRect = newRect;
1825
1826
        // Repaint merged region with original rect
1827
        if (repaintRect == null) {
1828
            repaintRect = rsPaintRect;
1829
        } else {
1830
            repaintRect = repaintRect.union(rsPaintRect);
1831
        }
1832
        c.repaint(repaintRect);
1833
        
1834
        updateRectangularSelectionPositionBlocks();
1835
    }
1836
1837
    private void selectEnsureMinSelection(int mark, int dot, int newDot) {
1838
        if (LOG.isLoggable(Level.FINE)) {
1839
            LOG.fine("selectEnsureMinSelection: mark=" + mark + ", dot=" + dot + ", newDot=" + newDot); // NOI18N
1840
        }
1841
        if (dot >= mark) { // Existing forward selection
1842
            if (newDot >= mark) {
1843
                moveDot(Math.max(newDot, minSelectionEndOffset));
1844
            } else { // newDot < mark => swap mark and dot
1845
                setDot(minSelectionEndOffset);
1846
                moveDot(Math.min(newDot, minSelectionStartOffset));
1847
            }
1848
1849
        } else { // Existing backward selection 
1850
            if (newDot <= mark) {
1851
                moveDot(Math.min(newDot, minSelectionStartOffset));
1852
            } else { // newDot > mark => swap mark and dot
1853
                setDot(minSelectionStartOffset);
1854
                moveDot(Math.max(newDot, minSelectionEndOffset));
1855
            }
1856
        }
1857
    }
1858
    
1859
    private boolean isLeftMouseButtonExt(MouseEvent evt) {
1860
        return (SwingUtilities.isLeftMouseButton(evt)
1861
                && !(evt.isPopupTrigger())
1862
                && (evt.getModifiers() & (InputEvent.META_MASK/* | InputEvent.ALT_MASK*/)) == 0);
1863
    }
1864
    
1865
    private boolean isMiddleMouseButtonExt(MouseEvent evt) {
1866
        return (evt.getButton() == MouseEvent.BUTTON2) &&
1867
                (evt.getModifiersEx() & (InputEvent.CTRL_DOWN_MASK | InputEvent.META_DOWN_MASK | /* cannot be tested bcs of bug in JDK InputEvent.ALT_DOWN_MASK | */ InputEvent.ALT_GRAPH_DOWN_MASK)) == 0;
1868
    }
1869
1870
    private int mapDragOperationFromModifiers(MouseEvent e) {
1871
        int mods = e.getModifiersEx();
1872
        
1873
        if ((mods & InputEvent.BUTTON1_DOWN_MASK) == 0) {
1874
            return TransferHandler.NONE;
1875
        }
1876
        
1877
        return TransferHandler.COPY_OR_MOVE;
1878
    }
1879
1880
    /**
1881
     * Determines if the following are true:
1882
     * <ul>
1883
     * <li>the press event is located over a selection
1884
     * <li>the dragEnabled property is true
1885
     * <li>A TranferHandler is installed
1886
     * </ul>
1887
     * <p>
1888
     * This is implemented to check for a TransferHandler.
1889
     * Subclasses should perform the remaining conditions.
1890
     */
1891
    private boolean isDragPossible(MouseEvent e) {
1892
	Object src = e.getSource();
1893
	if (src instanceof JComponent) {
1894
	    JComponent comp = (JComponent) src;
1895
            boolean possible =  (comp == null) ? false : (comp.getTransferHandler() != null);
1896
            if (possible && comp instanceof JTextComponent) {
1897
                JTextComponent c = (JTextComponent) comp;
1898
                if (c.getDragEnabled()) {
1899
                    Caret caret = c.getCaret();
1900
                    int dot = caret.getDot();
1901
                    int mark = caret.getMark();
1902
                    if (dot != mark) {
1903
                        Point p = new Point(e.getX(), e.getY());
1904
                        int pos = c.viewToModel(p);
1905
1906
                        int p0 = Math.min(dot, mark);
1907
                        int p1 = Math.max(dot, mark);
1908
                        if ((pos >= p0) && (pos < p1)) {
1909
                            return true;
1910
                        }
1911
                    }
1912
                }
1913
            }
1914
        }
1915
        return false;
1916
    }
1917
    
1918
    private void refresh() {
1919
        updateType();
1920
        SwingUtilities.invokeLater(new Runnable() {
1921
            public @Override void run() {
1922
                updateAllCaretsBounds(); // the line height etc. may have change
1923
            }
1924
        });
1925
    }
1926
    
1927
    private static String logMouseEvent(MouseEvent evt) {
1928
        return "x=" + evt.getX() + ", y=" + evt.getY() + ", clicks=" + evt.getClickCount() //NOI18N
1929
            + ", component=" + s2s(evt.getComponent()) //NOI18N
1930
            + ", source=" + s2s(evt.getSource()) + ", button=" + evt.getButton() + ", mods=" + evt.getModifiers() + ", modsEx=" + evt.getModifiersEx(); //NOI18N
1931
    }
1932
1933
    private static String s2s(Object o) {
1934
        return o == null ? "null" : o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o)); //NOI18N
1935
    }
1936
1937
    private final class ListenerImpl extends ComponentAdapter
1938
    implements DocumentListener, AtomicLockListener, MouseListener, MouseMotionListener, FocusListener, ViewHierarchyListener,
1939
            PropertyChangeListener, ActionListener, PreferenceChangeListener, KeyListener
1940
    {
1941
1942
        ListenerImpl() {
1943
        }
1944
1945
        public @Override void preferenceChange(PreferenceChangeEvent evt) {
1946
            String setingName = evt == null ? null : evt.getKey();
1947
            if (setingName == null || SimpleValueNames.CARET_BLINK_RATE.equals(setingName)) {
1948
                int rate = prefs.getInt(SimpleValueNames.CARET_BLINK_RATE, -1);
1949
                if (rate == -1) {
1950
                    rate = EditorPreferencesDefaults.defaultCaretBlinkRate;
1951
                }
1952
                setBlinkRate(rate);
1953
                refresh();
1954
            }
1955
        }
1956
1957
        public @Override void propertyChange(PropertyChangeEvent evt) {
1958
            String propName = evt.getPropertyName();
1959
            JTextComponent c = component;
1960
            if ("document".equals(propName)) { // NOI18N
1961
                if (c != null) {
1962
                    modelChanged(activeDoc, c.getDocument());
1963
                }
1964
1965
            } else if (EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY.equals(propName)) {
1966
                Boolean b = (Boolean) evt.getNewValue();
1967
                overwriteMode = (b != null) ? b : false;
1968
                updateOverwriteModeLayer(true);
1969
                updateType();
1970
1971
            } else if ("ancestor".equals(propName) && evt.getSource() == component) { // NOI18N
1972
                // The following code ensures that when the width of the line views
1973
                // gets computed on background after the file gets opened
1974
                // (so the horizontal scrollbar gets added after several seconds
1975
                // for larger files) that the suddenly added horizontal scrollbar
1976
                // will not hide the caret laying on the last line of the viewport.
1977
                // A component listener gets installed into horizontal scrollbar
1978
                // and if it's fired the caret's bounds will be checked whether
1979
                // they intersect with the horizontal scrollbar
1980
                // and if so the view will be scrolled.
1981
                Container parent = component.getParent();
1982
                if (parent instanceof JViewport) {
1983
                    parent = parent.getParent(); // parent of viewport
1984
                    if (parent instanceof JScrollPane) {
1985
                        JScrollPane scrollPane = (JScrollPane) parent;
1986
                        JScrollBar hScrollBar = scrollPane.getHorizontalScrollBar();
1987
                        if (hScrollBar != null) {
1988
                            // Add weak listener so that editor pane could be removed
1989
                            // from scrollpane without being held by scrollbar
1990
                            hScrollBar.addComponentListener(
1991
                                    (ComponentListener) WeakListeners.create(
1992
                                            ComponentListener.class, listenerImpl, hScrollBar));
1993
                        }
1994
                    }
1995
                }
1996
            } else if ("enabled".equals(propName)) {
1997
                Boolean enabled = (Boolean) evt.getNewValue();
1998
                if (component.isFocusOwner()) {
1999
                    if (enabled == Boolean.TRUE) {
2000
                        if (component.isEditable()) {
2001
                            setVisible(true);
2002
                        }
2003
                        setSelectionVisible(true);
2004
                    } else {
2005
                        setVisible(false);
2006
                        setSelectionVisible(false);
2007
                    }
2008
                }
2009
            } else if (RECTANGULAR_SELECTION_PROPERTY.equals(propName)) {
2010
                boolean origRectangularSelection = rectangularSelection;
2011
                rectangularSelection = Boolean.TRUE.equals(component.getClientProperty(RECTANGULAR_SELECTION_PROPERTY));
2012
                if (rectangularSelection != origRectangularSelection) {
2013
                    if (rectangularSelection) {
2014
                        setRectangularSelectionToDotAndMark();
2015
                        RectangularSelectionTransferHandler.install(component);
2016
2017
                    } else { // No rectangular selection
2018
                        RectangularSelectionTransferHandler.uninstall(component);
2019
                    }
2020
                    fireStateChanged();
2021
                }
2022
            }
2023
        }
2024
2025
        // ActionListener methods
2026
        /**
2027
         * Fired when blink timer fires
2028
         */
2029
        public @Override void actionPerformed(ActionEvent evt) {
2030
            JTextComponent c = component;
2031
            if (c != null) {
2032
                setBlinkVisible(!blinkVisible);
2033
                List<CaretInfo> sortedCarets = getSortedCarets(); // TODO only repaint carets showing on screen
2034
                for (CaretInfo caret : sortedCarets) {
2035
                    CaretItem caretItem = caret.getCaretItem();
2036
                    if (caretItem.getCaretBounds() != null) {
2037
                        Rectangle repaintRect = caretItem.getCaretBounds();
2038
                        c.repaint(repaintRect);
2039
                    }
2040
                }
2041
            }
2042
        }
2043
2044
        // DocumentListener methods
2045
        public @Override void insertUpdate(DocumentEvent evt) {
2046
            JTextComponent c = component;
2047
            if (c != null) {
2048
                Document doc = evt.getDocument();
2049
                int offset = evt.getOffset();
2050
                final int endOffset = offset + evt.getLength();
2051
                if (offset == 0) {
2052
                    // Manually shift carets at offset zero
2053
                    runTransaction(CaretTransaction.RemoveType.DOCUMENT_INSERT_ZERO_OFFSET, endOffset, null, null);
2054
                }
2055
                // [TODO] proper undo solution
2056
                modified = true;
2057
                modifiedUpdate(true);
2058
                
2059
            }
2060
        }
2061
2062
        public @Override void removeUpdate(DocumentEvent evt) {
2063
            JTextComponent c = component;
2064
            if (c != null) {
2065
                // [TODO] proper undo solution
2066
                modified = true;
2067
                int offset = evt.getOffset();
2068
                runTransaction(CaretTransaction.RemoveType.DOCUMENT_REMOVE, offset, null, null);
2069
                modifiedUpdate(true);
2070
            }
2071
        }
2072
2073
        public @Override void changedUpdate(DocumentEvent evt) {
2074
        }
2075
2076
        public @Override
2077
        void atomicLock(AtomicLockEvent evt) {
2078
            inAtomicLock = true;
2079
        }
2080
2081
        public @Override
2082
        void atomicUnlock(AtomicLockEvent evt) {
2083
            inAtomicLock = false;
2084
            inAtomicUnlock = true;
2085
            try {
2086
                modifiedUpdate(typingModificationOccurred);
2087
            } finally {
2088
                inAtomicUnlock = false;
2089
                typingModificationOccurred = false;
2090
            }
2091
        }
2092
2093
        private void modifiedUpdate(boolean typingModification) {
2094
            if (!inAtomicLock) {
2095
                JTextComponent c = component;
2096
                if (modified && c != null) {
2097
                    fireStateChanged();
2098
                    modified = false;
2099
                }
2100
            } else {
2101
                typingModificationOccurred |= typingModification;
2102
            }
2103
        }
2104
2105
        // MouseListener methods
2106
        @Override
2107
        public void mousePressed(MouseEvent evt) {
2108
            if (LOG.isLoggable(Level.FINE)) {
2109
                LOG.fine("mousePressed: " + logMouseEvent(evt) + ", state=" + mouseState + '\n'); // NOI18N
2110
            }
2111
2112
            JTextComponent c = component;
2113
            AbstractDocument doc = activeDoc;
2114
            if (c != null && doc != null && isLeftMouseButtonExt(evt)) {
2115
                doc.readLock();
2116
                try {
2117
                    // Expand fold if offset is in collapsed fold
2118
                    int offset = mouse2Offset(evt);
2119
                    switch (evt.getClickCount()) {
2120
                        case 1: // Single press
2121
                            if (c.isEnabled() && !c.hasFocus()) {
2122
                                c.requestFocus();
2123
                            }
2124
                            c.setDragEnabled(true);
2125
                            if (evt.isAltDown() && evt.isShiftDown()) {
2126
                                mouseState = MouseState.CHAR_SELECTION;
2127
                                try {
2128
                                    Position pos = doc.createPosition(offset);
2129
                                    runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0, 
2130
                                            new CaretItem[] { new CaretItem(EditorCaret.this, pos, pos) }, null);
2131
                                } catch (BadLocationException ex) {
2132
                                    // Do nothing
2133
                                }
2134
                            } else if (evt.isShiftDown()) { // Select till offset
2135
                                moveDot(offset);
2136
                                adjustRectangularSelectionMouseX(evt.getX(), evt.getY()); // also fires state change
2137
                                mouseState = MouseState.CHAR_SELECTION;
2138
                            } else // Regular press
2139
                            // check whether selection drag is possible
2140
                            if (isDragPossible(evt) && mapDragOperationFromModifiers(evt) != TransferHandler.NONE) {
2141
                                mouseState = MouseState.DRAG_SELECTION_POSSIBLE;
2142
                            } else { // Drag not possible
2143
                                mouseState = MouseState.CHAR_SELECTION;
2144
                                setDot(offset);
2145
                            }
2146
                            break;
2147
2148
                        case 2: // double-click => word selection
2149
                            mouseState = MouseState.WORD_SELECTION;
2150
                            // Disable drag which would otherwise occur when mouse would be over text
2151
                            c.setDragEnabled(false);
2152
                            // Check possible fold expansion
2153
                            try {
2154
                                // hack, to get knowledge of possible expansion. Editor depends on Folding, so it's not really possible
2155
                                // to have Folding depend on BaseCaret (= a cycle). If BaseCaret moves to editor.lib2, this contract
2156
                                // can be formalized as an interface.
2157
                                @SuppressWarnings("unchecked")
2158
                                Callable<Boolean> cc = (Callable<Boolean>) c.getClientProperty("org.netbeans.api.fold.expander");
2159
                                if (cc == null || !cc.equals(this)) {
2160
                                    if (selectWordAction == null) {
2161
                                        selectWordAction = EditorActionUtilities.getAction(
2162
                                                c.getUI().getEditorKit(c), DefaultEditorKit.selectWordAction);
2163
                                    }
2164
                                    if (selectWordAction != null) {
2165
                                        selectWordAction.actionPerformed(null);
2166
                                    }
2167
                                    // Select word action selects forward i.e. dot > mark
2168
                                    minSelectionStartOffset = getMark();
2169
                                    minSelectionEndOffset = getDot();
2170
                                }
2171
                            } catch (Exception ex) {
2172
                                Exceptions.printStackTrace(ex);
2173
                            }
2174
                            break;
2175
2176
                        case 3: // triple-click => line selection
2177
                            mouseState = MouseState.LINE_SELECTION;
2178
                            // Disable drag which would otherwise occur when mouse would be over text
2179
                            c.setDragEnabled(false);
2180
                            if (selectLineAction == null) {
2181
                                selectLineAction = EditorActionUtilities.getAction(
2182
                                                c.getUI().getEditorKit(c), DefaultEditorKit.selectLineAction);
2183
                            }
2184
                            if (selectLineAction != null) {
2185
                                selectLineAction.actionPerformed(null);
2186
                                // Select word action selects forward i.e. dot > mark
2187
                                minSelectionStartOffset = getMark();
2188
                                minSelectionEndOffset = getDot();
2189
                            }
2190
                            break;
2191
2192
                        default: // multi-click
2193
                    }
2194
                } finally {
2195
                    doc.readUnlock();
2196
                }
2197
            }
2198
        }
2199
2200
        @Override
2201
        public void mouseReleased(MouseEvent evt) {
2202
            if (LOG.isLoggable(Level.FINE)) {
2203
                LOG.fine("mouseReleased: " + logMouseEvent(evt) + ", state=" + mouseState + '\n'); // NOI18N
2204
            }
2205
2206
            int offset = mouse2Offset(evt);
2207
            switch (mouseState) {
2208
                case DRAG_SELECTION_POSSIBLE:
2209
                    setDot(offset);
2210
                    adjustRectangularSelectionMouseX(evt.getX(), evt.getY()); // also fires state change
2211
                    break;
2212
2213
                case CHAR_SELECTION:
2214
                    if (evt.isAltDown() && evt.isShiftDown()) {
2215
                        moveDotCaret(offset, getLastCaretItem());
2216
                    } else {
2217
                        moveDot(offset); // Will do setDot() if no selection
2218
                        adjustRectangularSelectionMouseX(evt.getX(), evt.getY()); // also fires state change
2219
                    }
2220
                    break;
2221
            }
2222
            // Set DEFAULT state; after next mouse press the state may change
2223
            // to another state according to particular click count
2224
            mouseState = MouseState.DEFAULT;
2225
            component.setDragEnabled(true);
2226
        }
2227
2228
        /**
2229
         * Translates mouse event to text offset
2230
         */
2231
        int mouse2Offset(MouseEvent evt) {
2232
            JTextComponent c = component;
2233
            int offset = 0;
2234
            if (c != null) {
2235
                int y = evt.getY();
2236
                if (y < 0) {
2237
                    offset = 0;
2238
                } else if (y > c.getSize().getHeight()) {
2239
                    offset = c.getDocument().getLength();
2240
                } else {
2241
                    offset = c.viewToModel(new Point(evt.getX(), evt.getY()));
2242
                }
2243
            }
2244
            return offset;
2245
        }
2246
2247
        @Override
2248
        public void mouseClicked(MouseEvent evt) {
2249
            if (LOG.isLoggable(Level.FINE)) {
2250
                LOG.fine("mouseClicked: " + logMouseEvent(evt) + ", state=" + mouseState + '\n'); // NOI18N
2251
            }
2252
2253
            JTextComponent c = component;
2254
            if (c != null) {
2255
                if (isMiddleMouseButtonExt(evt)) {
2256
                    if (evt.getClickCount() == 1) {
2257
                        if (c == null) {
2258
                            return;
2259
                        }
2260
                        Clipboard buffer = component.getToolkit().getSystemSelection();
2261
2262
                        if (buffer == null) {
2263
                            return;
2264
                        }
2265
2266
                        Transferable trans = buffer.getContents(null);
2267
                        if (trans == null) {
2268
                            return;
2269
                        }
2270
2271
                        final Document doc = c.getDocument();
2272
                        if (doc == null) {
2273
                            return;
2274
                        }
2275
2276
                        final int offset = c.getUI().viewToModel(c, new Point(evt.getX(), evt.getY()));
2277
2278
                        try {
2279
                            final String pastingString = (String) trans.getTransferData(DataFlavor.stringFlavor);
2280
                            if (pastingString == null) {
2281
                                return;
2282
                            }
2283
                            Runnable pasteRunnable = new Runnable() {
2284
                                public @Override
2285
                                void run() {
2286
                                    try {
2287
                                        doc.insertString(offset, pastingString, null);
2288
                                        setDot(offset + pastingString.length());
2289
                                    } catch (BadLocationException exc) {
2290
                                    }
2291
                                }
2292
                            };
2293
                            AtomicLockDocument ald = LineDocumentUtils.as(doc, AtomicLockDocument.class);
2294
                            if (ald != null) {
2295
                                ald.runAtomic(pasteRunnable);
2296
                            } else {
2297
                                pasteRunnable.run();
2298
                            }
2299
                        } catch (UnsupportedFlavorException ufe) {
2300
                        } catch (IOException ioe) {
2301
                        }
2302
                    }
2303
                }
2304
            }
2305
        }
2306
2307
        @Override
2308
        public void mouseEntered(MouseEvent evt) {
2309
        }
2310
2311
        @Override
2312
        public void mouseExited(MouseEvent evt) {
2313
        }
2314
2315
        // MouseMotionListener methods
2316
        @Override
2317
        public void mouseMoved(MouseEvent evt) {
2318
            if (mouseState == MouseState.DEFAULT) {
2319
                boolean textCursor = true;
2320
                int position = component.viewToModel(evt.getPoint());
2321
                if (RectangularSelectionUtils.isRectangularSelection(component)) {
2322
                    List<Position> positions = RectangularSelectionUtils.regionsCopy(component);
2323
                    for (int i = 0; textCursor && i < positions.size(); i += 2) {
2324
                        int a = positions.get(i).getOffset();
2325
                        int b = positions.get(i + 1).getOffset();
2326
                        if (a == b) {
2327
                            continue;
2328
                        }
2329
2330
                        textCursor &= !(position >= a && position <= b || position >= b && position <= a);
2331
                    }
2332
                } else // stream selection
2333
                if (getDot() == getMark()) {
2334
                    // empty selection
2335
                    textCursor = true;
2336
                } else {
2337
                    int dot = getDot();
2338
                    int mark = getMark();
2339
                    if (position >= dot && position <= mark || position >= mark && position <= dot) {
2340
                        textCursor = false;
2341
                    } else {
2342
                        textCursor = true;
2343
                    }
2344
                }
2345
2346
                if (textCursor != showingTextCursor) {
2347
                    int cursorType = textCursor ? Cursor.TEXT_CURSOR : Cursor.DEFAULT_CURSOR;
2348
                    component.setCursor(Cursor.getPredefinedCursor(cursorType));
2349
                    showingTextCursor = textCursor;
2350
                }
2351
            }
2352
        }
2353
2354
        @Override
2355
        public void mouseDragged(MouseEvent evt) {
2356
            if (LOG.isLoggable(Level.FINE)) {
2357
                LOG.fine("mouseDragged: " + logMouseEvent(evt) + ", state=" + mouseState + '\n'); //NOI18N
2358
            }
2359
2360
            if (isLeftMouseButtonExt(evt)) {
2361
                JTextComponent c = component;
2362
                int offset = mouse2Offset(evt);
2363
                int dot = getDot();
2364
                int mark = getMark();
2365
                LineDocument lineDoc = LineDocumentUtils.asRequired(c.getDocument(), LineDocument.class);
2366
                
2367
                try {
2368
                    switch (mouseState) {
2369
                        case DEFAULT:
2370
                        case DRAG_SELECTION:
2371
                            break;
2372
2373
                        case DRAG_SELECTION_POSSIBLE:
2374
                            mouseState = MouseState.DRAG_SELECTION;
2375
                            break;
2376
2377
                        case CHAR_SELECTION:
2378
                            if (evt.isAltDown() && evt.isShiftDown()) {
2379
                                moveDotCaret(offset, getLastCaretItem());
2380
                            } else {
2381
                                moveDot(offset);
2382
                                adjustRectangularSelectionMouseX(evt.getX(), evt.getY());
2383
                            }
2384
                            break; // Use the offset under mouse pointer
2385
2386
                        case WORD_SELECTION:
2387
                            // Increase selection if at least in the middle of a word.
2388
                            // It depends whether selection direction is from lower offsets upward or back.
2389
                            if (offset >= mark) { // Selection extends forward.
2390
                                offset = LineDocumentUtils.getWordEnd(lineDoc, offset);
2391
                            } else { // Selection extends backward.
2392
                                offset = LineDocumentUtils.getWordStart(lineDoc, offset);
2393
                            }
2394
                            selectEnsureMinSelection(mark, dot, offset);
2395
                            break;
2396
2397
                        case LINE_SELECTION:
2398
                            if (offset >= mark) { // Selection extends forward
2399
                                offset = Math.min(LineDocumentUtils.getLineEnd(lineDoc, offset) + 1, c.getDocument().getLength());
2400
                            } else { // Selection extends backward
2401
                                offset = LineDocumentUtils.getLineStart(lineDoc, offset);
2402
                            }
2403
                            selectEnsureMinSelection(mark, dot, offset);
2404
                            break;
2405
2406
                        default:
2407
                            throw new AssertionError("Invalid state " + mouseState); // NOI18N
2408
                    }
2409
                } catch (BadLocationException ex) {
2410
                    Exceptions.printStackTrace(ex);
2411
                }
2412
            }
2413
        }
2414
2415
        // FocusListener methods
2416
        public @Override void focusGained(FocusEvent evt) {
2417
            if (LOG.isLoggable(Level.FINE)) {
2418
                LOG.fine(
2419
                        "BaseCaret.focusGained(); doc=" + // NOI18N
2420
                        component.getDocument().getProperty(Document.TitleProperty) + '\n'
2421
                );
2422
            }
2423
            
2424
            JTextComponent c = component;
2425
            if (c != null) {
2426
                updateType();
2427
                if (component.isEnabled()) {
2428
                    if (component.isEditable()) {
2429
                        setVisible(true);
2430
                }
2431
                    setSelectionVisible(true);
2432
                }
2433
                if (LOG.isLoggable(Level.FINER)) {
2434
                    LOG.finer("Caret visibility: " + isVisible() + '\n'); // NOI18N
2435
                }
2436
            } else {
2437
                if (LOG.isLoggable(Level.FINER)) {
2438
                    LOG.finer("Text component is null, caret will not be visible" + '\n'); // NOI18N
2439
                }
2440
            }
2441
        }
2442
2443
        public @Override void focusLost(FocusEvent evt) {
2444
            if (LOG.isLoggable(Level.FINE)) {
2445
                LOG.fine("BaseCaret.focusLost(); doc=" + // NOI18N
2446
                        component.getDocument().getProperty(Document.TitleProperty) +
2447
                        "\nFOCUS GAINER: " + evt.getOppositeComponent() + '\n' // NOI18N
2448
                );
2449
                if (LOG.isLoggable(Level.FINER)) {
2450
                    LOG.finer("FOCUS EVENT: " + evt + '\n'); // NOI18N
2451
                }
2452
            }
2453
	    setVisible(false);
2454
            setSelectionVisible(evt.isTemporary());
2455
        }
2456
2457
        // ComponentListener methods
2458
        /**
2459
         * May be called for either component or horizontal scrollbar.
2460
         */
2461
        public @Override void componentShown(ComponentEvent e) {
2462
            // Called when horizontal scrollbar gets visible
2463
            // (but the same listener added to component as well so must check first)
2464
            // Check whether present caret position will not get hidden
2465
            // under horizontal scrollbar and if so scroll the view
2466
            Component hScrollBar = e.getComponent();
2467
            if (hScrollBar != component) { // really called for horizontal scrollbar
2468
                Component scrollPane = hScrollBar.getParent();
2469
                boolean needsUpdate = false;
2470
                List<CaretInfo> sortedCarets = getSortedCarets();
2471
                for (CaretInfo caret : sortedCarets) { // TODO This is wrong, but a quick prototype
2472
                    CaretItem caretItem = caret.getCaretItem();
2473
                    if (caretItem.getCaretBounds() != null && scrollPane instanceof JScrollPane) {
2474
                        Rectangle viewRect = ((JScrollPane)scrollPane).getViewport().getViewRect();
2475
                        Rectangle hScrollBarRect = new Rectangle(
2476
                                viewRect.x,
2477
                                viewRect.y + viewRect.height,
2478
                                hScrollBar.getWidth(),
2479
                                hScrollBar.getHeight()
2480
                                );
2481
                        if (hScrollBarRect.intersects(caretItem.getCaretBounds())) {
2482
                            // Update caret's position
2483
                            needsUpdate = true;
2484
                        }
2485
                    }
2486
                }
2487
                if(needsUpdate) {
2488
                    dispatchUpdate(true); // should be visible so scroll the view
2489
                }
2490
            }
2491
        }
2492
        
2493
        /**
2494
         * May be called for either component or horizontal scrollbar.
2495
         */
2496
        public @Override void componentResized(ComponentEvent e) {
2497
            Component c = e.getComponent();
2498
            if (c == component) { // called for component
2499
                // In case the caretBounds are still null
2500
                // (component not connected to hierarchy yet or it has zero size
2501
                // so the modelToView() returned null) re-attempt to compute the bounds.
2502
                CaretItem caret = getLastCaretItem();
2503
                if (caret.getCaretBounds() == null) {
2504
                    dispatchUpdate(true);
2505
                    if (caret.getCaretBounds() != null) { // detach the listener - no longer necessary
2506
                        c.removeComponentListener(this);
2507
                    }
2508
                }
2509
            }
2510
        }
2511
2512
        @Override
2513
        public void viewHierarchyChanged(ViewHierarchyEvent evt) {
2514
            if (!caretUpdatePending) {
2515
                caretUpdatePending = true;
2516
                SwingUtilities.invokeLater(new Runnable() {
2517
                    @Override
2518
                    public void run() {
2519
                        update(false);
2520
                    }
2521
                });
2522
            }
2523
        }
2524
2525
        @Override
2526
        public void keyPressed(KeyEvent e) {
2527
            if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
2528
                // Retain just the last caret
2529
                retainLastCaretOnly();
2530
            }
2531
        }
2532
2533
        @Override
2534
        public void keyTyped(KeyEvent e) {
2535
        }
2536
2537
        @Override
2538
        public void keyReleased(KeyEvent e) {
2539
        }
2540
        
2541
    } // End of ListenerImpl class
2542
    
2543
    
2544
    private enum CaretType {
2545
        
2546
        /**
2547
         * Two-pixel line caret (the default).
2548
         */
2549
        THICK_LINE_CARET,
2550
        
2551
        /**
2552
         * Thin one-pixel line caret.
2553
         */
2554
        THIN_LINE_CARET,
2555
2556
        /**
2557
         * Rectangle corresponding to a single character.
2558
         */
2559
        BLOCK_CARET;
2560
        
2561
        static CaretType decode(String typeStr) {
2562
            switch (typeStr) {
2563
                case EditorPreferencesDefaults.THIN_LINE_CARET:
2564
                    return THIN_LINE_CARET;
2565
                case EditorPreferencesDefaults.BLOCK_CARET:
2566
                    return BLOCK_CARET;
2567
                default:
2568
                    return THICK_LINE_CARET;
2569
            }
2570
        };
2571
2572
    }
2573
    
2574
    private static enum MouseState {
2575
2576
        DEFAULT, // Mouse released; not extending any selection
2577
        CHAR_SELECTION, // Extending character selection after single mouse press 
2578
        WORD_SELECTION, // Extending word selection after double-click when mouse button still pressed
2579
        LINE_SELECTION, // Extending line selection after triple-click when mouse button still pressed
2580
        DRAG_SELECTION_POSSIBLE,  // There was a selected text when mouse press arrived so drag is possible
2581
        DRAG_SELECTION  // Drag is being done (text selection existed at the mouse press)
2582
        
2583
    }
2584
    
2585
}
(-)a/editor.lib2/src/org/netbeans/api/editor/caret/EditorCaretEvent.java (+94 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.editor.caret;
43
44
import org.netbeans.api.annotations.common.NonNull;
45
46
/**
47
 * Notification event about changes in editor caret.
48
 *
49
 * @author Miloslav Metelka
50
 * @since 2.6
51
 */
52
public final class EditorCaretEvent extends java.util.EventObject {
53
    
54
    private final int affectedStartOffset;
55
    
56
    private final int affectedEndOffset;
57
    
58
    EditorCaretEvent(EditorCaret source, int affectedStartOffset, int affectedEndOffset) {
59
        super(source);
60
        this.affectedStartOffset = affectedStartOffset;
61
        this.affectedEndOffset = affectedEndOffset;
62
    }
63
    
64
    /**
65
     * Get caret instance to which this event relates.
66
     *
67
     * @return caret instance.
68
     */
69
    public @NonNull EditorCaret getCaret() {
70
        return (EditorCaret) getSource();
71
    }
72
73
    /**
74
     * Get start of the region that was affected by caret change.
75
     * <br>
76
     * This offset region will be repainted automatically by the editor infrastructure.
77
     *
78
     * @return &gt;= 0 offset.
79
     */
80
    public int getAffectedStartOffset() {
81
        return affectedStartOffset;
82
    }
83
    
84
    /**
85
     * Get end of the region that was affected by caret change.
86
     * <br>
87
     * This offset region will be repainted automatically by the editor infrastructure.
88
     *
89
     * @return &gt;= 0 offset.
90
     */
91
    public int getAffectedEndOffset() {
92
        return affectedEndOffset;
93
    }
94
}
(-)a/editor.lib2/src/org/netbeans/api/editor/caret/EditorCaretListener.java (+61 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.editor.caret;
43
44
import org.netbeans.api.annotations.common.NonNull;
45
46
/**
47
 * Listener for changes in editor caret.
48
 *
49
 * @author Miloslav Metelka
50
 * @since 2.6
51
 */
52
public interface EditorCaretListener extends java.util.EventListener {
53
54
    /**
55
     * Notification about change in terms of caret addition/removal or movement.
56
     *
57
     * @param evt caret event describing the change.
58
     */
59
    void caretChanged(@NonNull EditorCaretEvent evt);
60
61
}
(-)a/editor.lib2/src/org/netbeans/api/editor/caret/package.html (+158 lines)
Line 0 Link Here
1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2
<!--
3
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
5
Copyright 2016 Oracle and/or its affiliates. All rights reserved.
6
7
Oracle and Java are registered trademarks of Oracle and/or its affiliates.
8
Other names may be trademarks of their respective owners.
9
10
The contents of this file are subject to the terms of either the GNU
11
General Public License Version 2 only ("GPL") or the Common
12
Development and Distribution License("CDDL") (collectively, the
13
"License"). You may not use this file except in compliance with the
14
License. You can obtain a copy of the License at
15
http://www.netbeans.org/cddl-gplv2.html
16
or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
17
specific language governing permissions and limitations under the
18
License.  When distributing the software, include this License Header
19
Notice in each file and include the License file at
20
nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
21
particular file as subject to the "Classpath" exception as provided
22
by Oracle in the GPL Version 2 section of the License file that
23
accompanied this code. If applicable, add the following below the
24
License Header, with the fields enclosed by brackets [] replaced by
25
your own identifying information:
26
"Portions Copyrighted [year] [name of copyright owner]"
27
28
If you wish your version of this file to be governed by only the CDDL
29
or only the GPL Version 2, indicate your decision by adding
30
"[Contributor] elects to include this software in this distribution
31
under the [CDDL or GPL Version 2] license." If you do not indicate a
32
single choice of license, a recipient has the option to distribute
33
your version of this file under either the CDDL, the GPL Version 2 or
34
to extend the choice of license to its licensees as provided above.
35
However, if you add GPL Version 2 code and therefore, elected the GPL
36
Version 2 license, then the option applies only if the new code is
37
made subject to such option by the copyright holder.
38
39
Contributor(s):
40
41
Portions Copyrighted 2016 Sun Microsystems, Inc.
42
-->
43
44
<html>
45
  <head>
46
    <title>org.netbeans.api.editor.caret</title>
47
  </head>
48
  <body>
49
50
  <p>
51
  The Editor Caret API opens up the editor to give information about its carets
52
  and to manipulate them.
53
  </p>
54
  
55
  <h3>Key parts of the API</h3>
56
  
57
  <p>
58
  The whole API is organized around the
59
  <code><a href="@org-netbeans-modules-editor-lib2@/org/netbeans/api/editor/caret/EditorCaret.html">EditorCaret</a></code>
60
  class, an implementation of the <code>javax.swing.text.Caret</code> managing
61
  all the carets in a single document. Information about carets maintained by
62
  <code>EditorCaret</code> is stored in the immutable class
63
  <code><a href="@org-netbeans-modules-editor-lib2@/org/netbeans/api/editor/caret/CaretInfo.html">CaretInfo</a></code>.
64
  Once a caret gets mutated its corresponding caret info becomes obsolete
65
  and new caret info instance gets created lazily.
66
  <code><a href="@org-netbeans-modules-editor-lib2@/org/netbeans/api/editor/caret/EditorCaretListener.html">EditorCaretListener</a></code>
67
  can be registered to an <code>EditorCaret</code> to be informed about caret
68
  addition/removal or movement with an
69
  <code><a href="@org-netbeans-modules-editor-lib2@/org/netbeans/api/editor/caret/EditorCaretEvent.html">EditorCaretEvent</a></code>.
70
  </p>
71
  
72
  <p>
73
  The <code>EditorCaret</code> mutates its carets in a single transaction.
74
  <code><a href="@org-netbeans-modules-editor-lib2@/org/netbeans/api/editor/caret/CaretMoveContext.html">CaretMoveContext</a></code>
75
  allows clients implementing
76
  <code><a href="@org-netbeans-modules-editor-lib2@/org/netbeans/spi/editor/caret/CaretMoveHandler.html">CaretMoveHandler</a></code>
77
  to manipulate carets. The following code shows how all carets are moved to the
78
  end of the word they are currently on.
79
  
80
  <pre><code>
81
    editorCaret.moveCarets((CaretMoveContext context) -> {
82
        for (CaretInfo ci : context.getOriginalCarets()) {
83
            Position pos = target.getDocument().createPosition(Utilities.getWordEnd(target, ci.getDot()));
84
            context.setDot(ci, pos);
85
        }
86
    });
87
  </code>
88
  </pre>
89
  </p>
90
91
  <h3>Locking and <code>Document</code> changes</h3>
92
  
93
  <p>
94
  The basics of the locking and events model of Swing documents is
95
  described in the javadoc of the
96
  <a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/text/AbstractDocument.html">javax.swing.text.AbstractDocument</a>
97
  class. Netbeans documents use the same patterns and so does the Caret API,
98
  because of its tight relation to documents. The fundamentals of the Swing documents
99
  locking model are that any changes to a document are done under the
100
  document's write lock, the document's listeners are notified synchronously on the
101
  mutating thread and have full read access to the document, but can't modify it.
102
  </p>
103
  
104
  <ul>
105
    <li>
106
    Any calls from the infrastructure to <code>EditorCaret</code> to query or
107
    mutate its carets, should be called with document's read-lock acquired which
108
    will guarantee stability of <code>CaretInfo#getDot()</code> and
109
    <code>CaretInfo#getMark()</code> and prevent caret merging as a possible
110
    effect of document modifications.
111
    </li>
112
    <li>
113
    When <code>EditorCaret</code> needs to notify its listeners that some
114
    of its carets have changed, all the events have to be fired under the
115
    layer's document's read lock. Obviously, the listeners are not allowed to
116
    modify the document from the event notification methods.
117
    </li>
118
  </ul>
119
  
120
  <p>
121
  The Caret API does not use any special threads and any processing it
122
  does is always done on the caller's thread. This means that the above described
123
  constraints hardly cause any limitation for practical use.
124
  </p>
125
  
126
  <p>
127
  The Caret API is generally thread-safe meaning that it can be used
128
  simultaneously from multiple threads if not stated otherwise. This doesn't
129
  change in any way the rule of acquiring a read lock before calling the API.
130
  Swing documents generally allow access for multiple readers that can run
131
  concurrently.
132
  </p>
133
  
134
  <h3><a name="compatibilty">Backwards compatibility</a></h3>
135
  <p>
136
  As a technical limitation, <code>EditorCaret</code> has to implement <code>Caret</code>
137
  to be able to work with Swings Text API. The <code>Caret</code> interface is not
138
  aware of multiple carets and any call to its method will work on the last added
139
  caret. Manipulating the caret using <code>setDot(int)</code> or <code>moveDot(int)</code>
140
  will retain only the last added caret and move it accordingly.
141
  <br>
142
  <code>editorTextComponent.getCaret()</code> will now always return <code>EditorCaret</code>
143
  instance instead of the original caret implementation <code>org.netbeans.editor.BaseCaret</code>.
144
  Since clients were expected to only rely on <code>javax.swing.text.Caret</code> returned type
145
  (except internal code in editor modules) they should not be affected by the change
146
  since <code>EditorCaret</code> implements <code>Caret</code> interface.
147
  <br>
148
  With official Caret API the clients can start to cast
149
  <code>editorTextComponent.getCaret()</code> to <code>EditorCaret</code> type
150
  and use its extended functionality.
151
  </p>
152
  
153
  <h3><a name="usecases">Use cases</a></h3>
154
  <p>
155
  Use cases are shown in javadoc documentation of particular methods.
156
  </p>
157
  </body>
158
</html>
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/Bundle.properties (-1 / +2 lines)
Lines 47-50 Link Here
47
47
48
#DocUtils.debugPosition
48
#DocUtils.debugPosition
49
wrong_position=Wrong position
49
wrong_position=Wrong position
50
MSG_RectangularSelectionClipboardFlavor=Rectangular Selection
50
MSG_RectangularSelectionClipboardFlavor=Rectangular Selection
51
MSG_MultiCaretClipboardFlavor=Multi Caret
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/RectangularSelectionTransferHandler.java (-16 / +209 lines)
Lines 1-4 Link Here
1
2
/*
1
/*
3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
 *
3
 *
Lines 50-104 Link Here
50
import java.util.Arrays;
49
import java.util.Arrays;
51
import java.util.List;
50
import java.util.List;
52
import java.util.StringTokenizer;
51
import java.util.StringTokenizer;
52
import java.util.logging.Level;
53
import java.util.logging.Logger;
53
import java.util.logging.Logger;
54
import javax.swing.Icon;
54
import javax.swing.Icon;
55
import javax.swing.JComponent;
55
import javax.swing.JComponent;
56
import javax.swing.TransferHandler;
56
import javax.swing.TransferHandler;
57
import javax.swing.text.AbstractDocument;
57
import javax.swing.text.AbstractDocument;
58
import javax.swing.text.BadLocationException;
58
import javax.swing.text.BadLocationException;
59
import javax.swing.text.Caret;
59
import javax.swing.text.Document;
60
import javax.swing.text.Document;
60
import javax.swing.text.JTextComponent;
61
import javax.swing.text.JTextComponent;
61
import javax.swing.text.Position;
62
import javax.swing.text.Position;
63
import org.netbeans.api.editor.caret.CaretInfo;
64
import org.netbeans.api.editor.caret.EditorCaret;
62
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
65
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
63
import org.openide.util.Exceptions;
66
import org.openide.util.Exceptions;
64
import org.openide.util.NbBundle;
67
import org.openide.util.NbBundle;
65
68
66
/**
69
/**
67
 * Clipboard transfer handler for rectangular selection.
70
 * Clipboard transfer handler for rectangular selection.
68
 * <br/>
71
 * <br>
69
 * It overrides the original transfer handler during the rectangular selection.
72
 * It overrides the original transfer handler during the rectangular selection.
70
 *
73
 *
71
 * @author Miloslav Metelka
74
 * @author Miloslav Metelka
72
 */
75
 */
73
public class RectangularSelectionTransferHandler extends TransferHandler {
76
public class EditorCaretTransferHandler extends TransferHandler {
74
    
77
    
75
    public static void install(JTextComponent c) {
78
    public static void install(JTextComponent c) {
76
        TransferHandler origHandler = c.getTransferHandler();
79
        TransferHandler origHandler = c.getTransferHandler();
77
        if (!(origHandler instanceof RectangularSelectionTransferHandler)) {
80
        if (!(origHandler instanceof EditorCaretTransferHandler)) {
78
            c.setTransferHandler(new RectangularSelectionTransferHandler(c.getTransferHandler()));
81
            c.setTransferHandler(new EditorCaretTransferHandler(c.getTransferHandler()));
79
        }
82
        }
80
    }
83
    }
81
    
84
    
82
    public static void uninstall(JTextComponent c) {
85
    public static void uninstall(JTextComponent c) {
83
        TransferHandler origHandler = c.getTransferHandler();
86
        TransferHandler origHandler = c.getTransferHandler();
84
        if (origHandler instanceof RectangularSelectionTransferHandler) {
87
        if (origHandler instanceof EditorCaretTransferHandler) {
85
            c.setTransferHandler(((RectangularSelectionTransferHandler)origHandler).getDelegate());
88
            c.setTransferHandler(((EditorCaretTransferHandler)origHandler).getDelegate());
86
        }
89
        }
87
    }
90
    }
88
    
91
    
89
    private static final DataFlavor RECTANGULAR_SELECTION_FLAVOR = new DataFlavor(RectangularSelectionData.class,
92
    private static final DataFlavor RECTANGULAR_SELECTION_FLAVOR = new DataFlavor(RectangularSelectionData.class,
90
            NbBundle.getMessage(RectangularSelectionTransferHandler.class, "MSG_RectangularSelectionClipboardFlavor"));
93
            NbBundle.getMessage(EditorCaretTransferHandler.class, "MSG_RectangularSelectionClipboardFlavor"));
94
    
95
    private static final DataFlavor MULTI_CARET_FLAVOR = new DataFlavor(MultiCaretData.class,
96
            NbBundle.getMessage(EditorCaretTransferHandler.class, "MSG_MultiCaretClipboardFlavor"));
91
97
92
    /** Boolean property defining whether selection is being rectangular in a particular text component. */
98
    /** Boolean property defining whether selection is being rectangular in a particular text component. */
93
    private static final String RECTANGULAR_SELECTION_PROPERTY = "rectangular-selection"; // NOI18N
99
    private static final String RECTANGULAR_SELECTION_PROPERTY = "rectangular-selection"; // NOI18N
94
100
95
    // -J-Dorg.netbeans.modules.editor.lib2.RectangularSelectionClipboardHandler.level=FINE
101
    // -J-Dorg.netbeans.modules.editor.lib2.RectangularSelectionClipboardHandler.level=FINE
96
    private static final Logger LOG = Logger.getLogger(RectangularSelectionTransferHandler.class.getName());
102
    private static final Logger LOG = Logger.getLogger(EditorCaretTransferHandler.class.getName());
97
98
103
99
    private final TransferHandler delegate;
104
    private final TransferHandler delegate;
100
105
101
    public RectangularSelectionTransferHandler(TransferHandler delegate) {
106
    public EditorCaretTransferHandler(TransferHandler delegate) {
102
        this.delegate = delegate;
107
        this.delegate = delegate;
103
    }
108
    }
104
    
109
    
Lines 195-201 Link Here
195
            }
200
            }
196
201
197
            clip.setContents(
202
            clip.setContents(
198
                    new WrappedTransferable(
203
                    new RectangularTransferable(
199
                        new StringSelection(stringSelectionBuffer.toString()),
204
                        new StringSelection(stringSelectionBuffer.toString()),
200
                        new RectangularSelectionData(data)),
205
                        new RectangularSelectionData(data)),
201
                    null);
206
                    null);
Lines 209-215 Link Here
209
            }
214
            }
210
            return;
215
            return;
211
216
212
        } else { // No rectangular selection
217
        } else if (c instanceof JTextComponent &&
218
                ((JTextComponent)c).getCaret() instanceof EditorCaret &&
219
                ((EditorCaret)((JTextComponent)c).getCaret()).getCarets().size() > 1)
220
        {
221
            EditorCaret editorCaret = (EditorCaret) ((JTextComponent)c).getCaret();
222
            final JTextComponent tc = (JTextComponent) c;
223
            StringBuilder stringSelectionBuffer;
224
            String[] lines = null;
225
            AbstractDocument doc = (AbstractDocument) tc.getDocument();
226
            doc.readLock();
227
            try {
228
                CharSequence docText = DocumentUtilities.getText(doc);
229
                stringSelectionBuffer = new StringBuilder(100);
230
                List<CaretInfo> carets = editorCaret.getSortedCarets();
231
                lines = new String[carets.size()];
232
                boolean newline = false;
233
                for (int i = 0; i < carets.size(); i++) {
234
                    CaretInfo caret = carets.get(i);
235
                    if(!caret.isSelection()) continue;
236
                    int startOffset = caret.getSelectionStart();
237
                    int endOffset = caret.getSelectionEnd();
238
                    CharSequence lineSel = docText.subSequence(startOffset, endOffset);
239
                    if (newline) {
240
                        stringSelectionBuffer.append('\n');
241
                    } else {
242
                        newline = true;
243
                    }
244
                    stringSelectionBuffer.append(lineSel);
245
                    lines[i] = lineSel.toString();
246
                }
247
            } finally {
248
                doc.readUnlock();
249
            }
250
251
            clip.setContents(
252
                    new MultiCaretTransferable(
253
                        new StringSelection(stringSelectionBuffer.toString()),
254
                        new MultiCaretData(lines)),
255
                    null);
256
257
            if (action == TransferHandler.MOVE) {
258
                List<CaretInfo> carets = editorCaret.getSortedCarets();
259
                for (CaretInfo caret : carets) {
260
                    if (caret.isSelection()) {
261
                        // remove selection
262
                        final int dot = caret.getDot();
263
                        final int mark = caret.getMark();
264
                        try {
265
                            if (RectangularSelectionUtils.isRectangularSelection(tc)) {
266
                                RectangularSelectionUtils.removeSelection(tc);
267
                                RectangularSelectionCaretAccessor.get().setRectangularSelectionToDotAndMark(editorCaret);
268
                            } else {
269
                                doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
270
                            }
271
                        } catch (BadLocationException ble) {
272
                            LOG.log(Level.FINE, null, ble);
273
                        }
274
                    }
275
                }
276
            }
277
            return;
278
        } else { // No rectangular selection or multiple carets
213
            delegate.exportToClipboard(c, clip, action);
279
            delegate.exportToClipboard(c, clip, action);
214
        }
280
        }
215
    }
281
    }
Lines 262-268 Link Here
262
                    } else { // Regular selection
328
                    } else { // Regular selection
263
                        String s = (String) t.getTransferData(DataFlavor.stringFlavor); // There should be string flavor
329
                        String s = (String) t.getTransferData(DataFlavor.stringFlavor); // There should be string flavor
264
                        if (s != null) {
330
                        if (s != null) {
265
                            tc.replaceSelection("");
331
                            tc.replaceSelection(s);
266
                        }
332
                        }
267
                    }
333
                    }
268
                    result = true;
334
                    result = true;
Lines 315-325 Link Here
315
                        Exceptions.printStackTrace(ex);
381
                        Exceptions.printStackTrace(ex);
316
                    }
382
                    }
317
                }
383
                }
384
            } else {
385
                Caret caret = ((JTextComponent) c).getCaret();
386
                if(caret instanceof EditorCaret && ((EditorCaret) caret).getSortedCarets().size() > 1) {
387
                    final EditorCaret editorCaret = (EditorCaret) caret;
388
                    boolean result = false;
389
                    MultiCaretData multiCaretData = null;
390
                    if(t.isDataFlavorSupported(MULTI_CARET_FLAVOR)) {
391
                        try {
392
                            multiCaretData = (MultiCaretData) t.getTransferData(MULTI_CARET_FLAVOR);
393
                        } catch (UnsupportedFlavorException | IOException ex) {
394
                            Exceptions.printStackTrace(ex);
395
                        }
396
                    }
397
                    if(multiCaretData != null && multiCaretData.strings().length == editorCaret.getCarets().size()) {
398
                        final MultiCaretData content = multiCaretData;
399
                        final Document doc = ((JTextComponent) c).getDocument();
400
                        DocUtils.runAtomicAsUser(doc, new Runnable() {
401
                            @Override
402
                            public void run() {
403
                                for (int i = 0; i < editorCaret.getSortedCarets().size(); i++) {
404
                                    CaretInfo ci = editorCaret.getSortedCarets().get(i);
405
                                    try {
406
                                        int startOffset = ci.getSelectionStart();
407
                                        int endOffset = ci.getSelectionEnd();
408
                                        if (startOffset != endOffset) {
409
                                            doc.remove(startOffset, endOffset - startOffset);
410
                                        }
411
                                        if (content.strings()[i] != null && content.strings()[i].length() > 0) {
412
                                            doc.insertString(startOffset, content.strings()[i], null);
413
                                        }
414
                                    } catch (BadLocationException ex) {
415
                                        //ignore ?
416
                                    }
417
                                }
418
                            }
419
                        });
420
                    } else {
421
                        try {
422
                            final String content = (String) t.getTransferData(DataFlavor.stringFlavor); // There should be string flavor
423
                            final Document doc = ((JTextComponent) c).getDocument();
424
                            DocUtils.runAtomicAsUser(doc, new Runnable() {
425
                                @Override
426
                                public void run() {
427
                                    for (CaretInfo ci : editorCaret.getSortedCarets()) {
428
                                        try {
429
                                            int startOffset = ci.getSelectionStart();
430
                                            int endOffset = ci.getSelectionEnd();
431
                                            if (startOffset != endOffset) {
432
                                                doc.remove(startOffset, endOffset - startOffset);
433
                                            }
434
                                            if (content != null && content.length() > 0) {
435
                                                doc.insertString(startOffset, content, null);
436
                                            }
437
                                        } catch (BadLocationException ex) {
438
                                            //ignore ?
439
                                        }
440
                                    }
441
                                }
442
                            });
443
                            result = true;
444
                        } catch (UnsupportedFlavorException | IOException ex) {
445
                            Exceptions.printStackTrace(ex);
446
                        }
447
                    }
448
449
                    return result;
450
                }
318
            }
451
            }
319
        }
452
        }
320
        return delegate.importData(support); // Regular importData()
453
        return delegate.importData(support); // Regular importData()
321
    }
454
    }
322
455
456
    @Override
457
    public boolean importData(JComponent comp, Transferable t) {
458
        return delegate.importData(comp, t);
459
    }
460
323
    private static List<String> splitByLines(String s) {
461
    private static List<String> splitByLines(String s) {
324
        StringTokenizer splitByLines = new StringTokenizer(s, "\n", false);
462
        StringTokenizer splitByLines = new StringTokenizer(s, "\n", false);
325
        List<String> lines = new ArrayList<String>();
463
        List<String> lines = new ArrayList<String>();
Lines 329-335 Link Here
329
        return lines;
467
        return lines;
330
    }
468
    }
331
469
332
    private static final class WrappedTransferable implements Transferable {
470
    private static final class RectangularTransferable implements Transferable {
333
471
334
        private final Transferable delegate;
472
        private final Transferable delegate;
335
473
Lines 337-343 Link Here
337
475
338
        private DataFlavor[] transferDataFlavorsCache;
476
        private DataFlavor[] transferDataFlavorsCache;
339
477
340
        public WrappedTransferable(Transferable delegate, RectangularSelectionData rectangularSelectionData) {
478
        public RectangularTransferable(Transferable delegate, RectangularSelectionData rectangularSelectionData) {
341
            this.delegate = delegate;
479
            this.delegate = delegate;
342
            this.rectangularSelectionData = rectangularSelectionData;
480
            this.rectangularSelectionData = rectangularSelectionData;
343
        }
481
        }
Lines 367-372 Link Here
367
            return delegate.getTransferData(flavor);
505
            return delegate.getTransferData(flavor);
368
        }
506
        }
369
    }
507
    }
508
    
509
    private static final class MultiCaretTransferable implements Transferable {
510
511
        private final Transferable delegate;
512
513
        private final MultiCaretData multiCaretData;
514
515
        private DataFlavor[] transferDataFlavorsCache;
516
517
        public MultiCaretTransferable(Transferable delegate, MultiCaretData multiCaretData) {
518
            this.delegate = delegate;
519
            this.multiCaretData = multiCaretData;
520
        }
521
522
        @Override
523
        public synchronized DataFlavor[] getTransferDataFlavors() {
524
            if (transferDataFlavorsCache != null) {
525
                return transferDataFlavorsCache;
526
            }
527
            DataFlavor[] flavors = delegate.getTransferDataFlavors();
528
            DataFlavor[] result = Arrays.copyOf(flavors, flavors.length + 1);
529
            result[flavors.length] = MULTI_CARET_FLAVOR;
530
531
            return transferDataFlavorsCache = result;
532
        }
533
534
        @Override
535
        public boolean isDataFlavorSupported(DataFlavor flavor) {
536
            return MULTI_CARET_FLAVOR.equals(flavor) || delegate.isDataFlavorSupported(flavor);
537
        }
538
539
        @Override
540
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
541
            if (MULTI_CARET_FLAVOR.equals(flavor)) {
542
                return multiCaretData;
543
            }
544
            return delegate.getTransferData(flavor);
545
        }
546
    }
547
    
548
    public static final class MultiCaretData {
549
        
550
        private final String[] strings;
551
        
552
        public MultiCaretData(String[] strings) {
553
            this.strings = strings;
554
        }
555
        
556
        /**
557
         * Strings containing rectangular selection (on particular selected line).
558
         */
559
        public String[] strings() {
560
            return strings;
561
        }
562
    }
370
563
371
    public static final class RectangularSelectionData {
564
    public static final class RectangularSelectionData {
372
565
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorPreferencesDefaults.java (-3 / +15 lines)
Lines 58-64 Link Here
58
        // no-op
58
        // no-op
59
    }
59
    }
60
60
61
    public static final String LINE_CARET = "line-caret";
61
    /**
62
     * One dot thin line compatible with Swing default caret.
63
     */
64
    public static final String THIN_LINE_CARET = "thin-line-caret"; // NOI18N
65
66
    /**
67
     * Two dots line - the default for the editor.
68
     */
69
    public static final String THICK_LINE_CARET = "thick-line-caret"; // NOI18N
70
71
    /**
72
     * Rectangle covering character to possibly be overwritten.
73
     */
62
    public static final String BLOCK_CARET = "block-caret";
74
    public static final String BLOCK_CARET = "block-caret";
63
    
75
    
64
    public static final boolean defaultToolbarVisible = true; // Currently unused - see ToggleAction in editor.actions
76
    public static final boolean defaultToolbarVisible = true; // Currently unused - see ToggleAction in editor.actions
Lines 100-111 Link Here
100
112
101
    public static final boolean defaultExpandTabs = true;
113
    public static final boolean defaultExpandTabs = true;
102
114
103
    public static final String defaultCaretTypeInsertMode = LINE_CARET;
115
    public static final String defaultCaretTypeInsertMode = THICK_LINE_CARET;
104
    public static final String defaultCaretTypeOverwriteMode = BLOCK_CARET;
116
    public static final String defaultCaretTypeOverwriteMode = BLOCK_CARET;
105
    public static final boolean defaultCaretItalicInsertMode = false;
117
    public static final boolean defaultCaretItalicInsertMode = false;
106
    public static final boolean defaultCaretItalicOverwriteMode = false;
118
    public static final boolean defaultCaretItalicOverwriteMode = false;
107
    /** @since 1.23 */
119
    /** @since 1.23 */
108
    public static final int defaultThickCaretWidth = 5;
120
    public static final int defaultThickCaretWidth = 2;
109
    public static final Acceptor defaultAbbrevExpandAcceptor = AcceptorFactory.WHITESPACE;
121
    public static final Acceptor defaultAbbrevExpandAcceptor = AcceptorFactory.WHITESPACE;
110
    public static final Acceptor defaultAbbrevAddTypedCharAcceptor = AcceptorFactory.NL;
122
    public static final Acceptor defaultAbbrevAddTypedCharAcceptor = AcceptorFactory.NL;
111
    public static final Acceptor defaultAbbrevResetAcceptor = AcceptorFactory.NON_JAVA_IDENTIFIER;
123
    public static final Acceptor defaultAbbrevResetAcceptor = AcceptorFactory.NON_JAVA_IDENTIFIER;
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/RectangularSelectionCaretAccessor.java (+77 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.editor.lib2;
43
44
import org.netbeans.api.editor.caret.EditorCaret;
45
46
/**
47
 * Accessor of EditorCaret's rectangular selection methods.
48
 *
49
 * @author Miloslav Metelka
50
 */
51
public abstract class RectangularSelectionCaretAccessor {
52
    
53
    private static RectangularSelectionCaretAccessor INSTANCE;
54
55
    public static RectangularSelectionCaretAccessor get() {
56
        if (INSTANCE == null) {
57
            // Cause api accessor impl to get initialized
58
            try {
59
                Class.forName(EditorCaret.class.getName(), true, RectangularSelectionCaretAccessor.class.getClassLoader());
60
            } catch (ClassNotFoundException e) {
61
                // Should never happen
62
            }
63
        }
64
        return INSTANCE;
65
    }
66
67
    public static void register(RectangularSelectionCaretAccessor accessor) {
68
        INSTANCE = accessor;
69
    }
70
71
    public abstract void setRectangularSelectionToDotAndMark(EditorCaret editorCaret);
72
73
    public abstract void updateRectangularUpDownSelection(EditorCaret editorCaret);
74
75
    public abstract void extendRectangularSelection(EditorCaret editorCaret, boolean toRight, boolean ctrl);
76
77
}
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/RectangularSelectionTransferHandler.java (-1 / +3 lines)
Lines 65-75 Link Here
65
65
66
/**
66
/**
67
 * Clipboard transfer handler for rectangular selection.
67
 * Clipboard transfer handler for rectangular selection.
68
 * <br/>
68
 * <br>
69
 * It overrides the original transfer handler during the rectangular selection.
69
 * It overrides the original transfer handler during the rectangular selection.
70
 *
70
 *
71
 * @author Miloslav Metelka
71
 * @author Miloslav Metelka
72
 * @deprecated replaced by {@link EditorCaretTransferHandler}, kept in place for BaseCaret
72
 */
73
 */
74
@Deprecated
73
public class RectangularSelectionTransferHandler extends TransferHandler {
75
public class RectangularSelectionTransferHandler extends TransferHandler {
74
    
76
    
75
    public static void install(JTextComponent c) {
77
    public static void install(JTextComponent c) {
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/WeakReferenceStableList.java (-1 / +1 lines)
Lines 50-56 Link Here
50
50
51
/**
51
/**
52
 * List holding non-null items weakly with automatic collection of GCed items.
52
 * List holding non-null items weakly with automatic collection of GCed items.
53
 * <br/>
53
 * <br>
54
 * Add and remove operations do not affect a list previously obtained by {@link #getList() }
54
 * Add and remove operations do not affect a list previously obtained by {@link #getList() }
55
 * so it may be used in a similar way like ListenerList holding all listener instances
55
 * so it may be used in a similar way like ListenerList holding all listener instances
56
 * by a weak reference.
56
 * by a weak reference.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/EditorActionUtilities.java (-1 / +1 lines)
Lines 382-388 Link Here
382
382
383
    /**
383
    /**
384
     * Get single-key accelerator for a given declared action.
384
     * Get single-key accelerator for a given declared action.
385
     * <br/>
385
     * <br>
386
     * Unfortunately currently there's no easy way to display multi-keybinding in menu-item
386
     * Unfortunately currently there's no easy way to display multi-keybinding in menu-item
387
     * (there's just JMenuItem.setAccelerator() and its impl is L&F-based)
387
     * (there's just JMenuItem.setAccelerator() and its impl is L&F-based)
388
     * so just display single-keystroke accelerators.
388
     * so just display single-keystroke accelerators.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/PresenterEditorAction.java (-1 / +1 lines)
Lines 73-79 Link Here
73
/**
73
/**
74
 * Action that represents a named editor action in main menu, popup menu
74
 * Action that represents a named editor action in main menu, popup menu
75
 * and editor toolbar.
75
 * and editor toolbar.
76
 * <br/>
76
 * <br>
77
 * The actions are registered into "Editors/ActionPresenters" regardless
77
 * The actions are registered into "Editors/ActionPresenters" regardless
78
 * of the mime-type for which the actions get created.
78
 * of the mime-type for which the actions get created.
79
 */
79
 */
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/PresenterUpdater.java (-1 / +1 lines)
Lines 106-112 Link Here
106
106
107
    /**
107
    /**
108
     * For menu presenters hold action of last focused editor component.
108
     * For menu presenters hold action of last focused editor component.
109
     * <br/>
109
     * <br>
110
     * Hold reference strongly but may be changed to weak reference in case
110
     * Hold reference strongly but may be changed to weak reference in case
111
     * even actions of last activated text component are desired to be released.
111
     * even actions of last activated text component are desired to be released.
112
     */
112
     */
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/AbstractOffsetGapList.java (-2 / +2 lines)
Lines 643-653 Link Here
643
    /**
643
    /**
644
     * This method updates element's offset (shifts it below offset gap if necessary)
644
     * This method updates element's offset (shifts it below offset gap if necessary)
645
     * before (or after) the element gets removed from the list.
645
     * before (or after) the element gets removed from the list.
646
     * <br/>
646
     * <br>
647
     * This method should be called after the element is physically removed
647
     * This method should be called after the element is physically removed
648
     * from the list and it's desired that it retains its natural offset
648
     * from the list and it's desired that it retains its natural offset
649
     * (not possibly shifted by the offset gap length).
649
     * (not possibly shifted by the offset gap length).
650
     * <br/>
650
     * <br>
651
     * If the element was located below the offset gap prior removal
651
     * If the element was located below the offset gap prior removal
652
     * then calling of this method is not necessary.
652
     * then calling of this method is not necessary.
653
     */
653
     */
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/CaretBasedBlockHighlighting.java (-179 / +100 lines)
Lines 64-81 Link Here
64
import javax.swing.text.Position;
64
import javax.swing.text.Position;
65
import javax.swing.text.SimpleAttributeSet;
65
import javax.swing.text.SimpleAttributeSet;
66
import javax.swing.text.StyleConstants;
66
import javax.swing.text.StyleConstants;
67
import org.netbeans.api.editor.caret.CaretInfo;
68
import org.netbeans.api.editor.caret.EditorCaret;
67
import org.netbeans.api.editor.mimelookup.MimeLookup;
69
import org.netbeans.api.editor.mimelookup.MimeLookup;
68
import org.netbeans.api.editor.mimelookup.MimePath;
70
import org.netbeans.api.editor.mimelookup.MimePath;
69
import org.netbeans.api.editor.settings.AttributesUtilities;
71
import org.netbeans.api.editor.settings.AttributesUtilities;
70
import org.netbeans.api.editor.settings.FontColorNames;
72
import org.netbeans.api.editor.settings.FontColorNames;
71
import org.netbeans.api.editor.settings.FontColorSettings;
73
import org.netbeans.api.editor.settings.FontColorSettings;
72
import org.netbeans.lib.editor.util.swing.BlockCompare;
73
import org.netbeans.modules.editor.lib2.DocUtils;
74
import org.netbeans.modules.editor.lib2.DocUtils;
74
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils;
75
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils;
75
import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent;
76
import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent;
76
import org.netbeans.spi.editor.highlighting.HighlightsChangeListener;
77
import org.netbeans.spi.editor.highlighting.HighlightsChangeListener;
78
import org.netbeans.spi.editor.highlighting.HighlightsContainer;
77
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
79
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
78
import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer;
79
import org.netbeans.spi.editor.highlighting.support.PositionsBag;
80
import org.netbeans.spi.editor.highlighting.support.PositionsBag;
80
import org.openide.util.Lookup;
81
import org.openide.util.Lookup;
81
import org.openide.util.LookupEvent;
82
import org.openide.util.LookupEvent;
Lines 87-93 Link Here
87
 * 
88
 * 
88
 * @author Vita Stejskal
89
 * @author Vita Stejskal
89
 */
90
 */
90
public abstract class CaretBasedBlockHighlighting extends AbstractHighlightsContainer implements ChangeListener, PropertyChangeListener {
91
public abstract class CaretBasedBlockHighlighting implements HighlightsContainer, ChangeListener, PropertyChangeListener {
91
92
92
    // -J-Dorg.netbeans.modules.editor.lib2.highlighting.CaretBasedBlockHighlighting.level=FINE
93
    // -J-Dorg.netbeans.modules.editor.lib2.highlighting.CaretBasedBlockHighlighting.level=FINE
93
    private static final Logger LOG = Logger.getLogger(CaretBasedBlockHighlighting.class.getName());
94
    private static final Logger LOG = Logger.getLogger(CaretBasedBlockHighlighting.class.getName());
Lines 103-110 Link Here
103
    private final boolean extendsEOL;
104
    private final boolean extendsEOL;
104
    private final boolean extendsEmptyLine;
105
    private final boolean extendsEmptyLine;
105
    
106
    
106
    private Position currentBlockStart;
107
    private final PositionsBag selectionsBag;
107
    private Position currentBlockEnd;
108
    
108
    
109
    private AttributeSet attribs;
109
    private AttributeSet attribs;
110
    
110
    
Lines 122-127 Link Here
122
        
122
        
123
        // Hook up the component
123
        // Hook up the component
124
        this.component = component;
124
        this.component = component;
125
        
126
        selectionsBag = new PositionsBag(component.getDocument(), false);
125
    }
127
    }
126
    
128
    
127
    private void init() {
129
    private void init() {
Lines 156-186 Link Here
156
            inited = true;
158
            inited = true;
157
            init();
159
            init();
158
        }
160
        }
161
        
162
        return selectionsBag.getHighlights(startOffset, endOffset);
163
    }
159
164
160
        Position blockStart;
165
    @Override
161
        Position blockEnd;
166
    public void addHighlightsChangeListener(HighlightsChangeListener listener) {
162
        synchronized (this) {
167
        selectionsBag.addHighlightsChangeListener(listener);
163
            blockStart = currentBlockStart;
168
    }
164
            blockEnd = currentBlockEnd;
165
        }
166
        if (blockStart != null && blockEnd != null &&
167
            endOffset >= blockStart.getOffset() && startOffset <= blockEnd.getOffset())
168
        {
169
            if (LOG.isLoggable(Level.FINE)) {
170
                LOG.fine("Queried for highlights in <" //NOI18N
171
                    + startOffset + ", " + endOffset + ">, returning <" //NOI18N
172
                    + positionToString(blockStart) + ", " + positionToString(blockEnd) + ">" //NOI18N
173
                    + ", layer=" + s2s(this) + '\n'); //NOI18N
174
            }
175
169
176
            return new SimpleHighlightsSequence(
170
    @Override
177
                Math.max(blockStart.getOffset(), startOffset), 
171
    public void removeHighlightsChangeListener(HighlightsChangeListener listener) {
178
                Math.min(blockEnd.getOffset(), endOffset), 
172
        selectionsBag.removeHighlightsChangeListener(listener);
179
                getAttribs()
180
            );
181
        } else {
182
            return HighlightsSequence.EMPTY;
183
        }
184
    }
173
    }
185
174
186
    // ------------------------------------------------
175
    // ------------------------------------------------
Lines 215-221 Link Here
215
        updateLineInfo(true);
204
        updateLineInfo(true);
216
    }
205
    }
217
206
218
    protected abstract Position [] getCurrentBlockPositions(Document document);
207
    protected abstract PositionsBag getCurrentBlockPositions(Document document);
219
    
208
    
220
    // ------------------------------------------------
209
    // ------------------------------------------------
221
    // private implementation
210
    // private implementation
Lines 224-301 Link Here
224
    private final void updateLineInfo(boolean fire) {
213
    private final void updateLineInfo(boolean fire) {
225
        ((AbstractDocument) component.getDocument()).readLock();
214
        ((AbstractDocument) component.getDocument()).readLock();
226
        try {
215
        try {
227
            Position newStart;
216
            PositionsBag newPositions = getCurrentBlockPositions(component.getDocument());
228
            Position newEnd;
229
            Position changeStart;
230
            Position changeEnd;
231
            Position[] newBlock = getCurrentBlockPositions(component.getDocument());
232
            synchronized (this) {
217
            synchronized (this) {
233
                if (newBlock != null) {
218
                if (newPositions != null) {
234
                    newStart = newBlock[0];
219
                    selectionsBag.setHighlights(newPositions);
235
                    newEnd = newBlock[1];
236
                    if (currentBlockStart == null) { // not valid yet
237
                        if (newStart.getOffset() < newEnd.getOffset()) { // Valid non-empty block
238
                            changeStart = newStart;
239
                            changeEnd = newEnd;
240
                        } else {
241
                            changeStart = null; // Invalid or empty block => no change
242
                            changeEnd = null;
243
                        }
244
                    } else { // Valid current start and end blocks
245
                        // Compare new block to old one
246
                        BlockCompare compare = BlockCompare.get(
247
                                newStart.getOffset(), newEnd.getOffset(),
248
                                currentBlockStart.getOffset(), currentBlockEnd.getOffset());
249
                        if (compare.invalidX()) { // newStart > newEnd
250
                            changeStart = null; // No change
251
                            changeEnd = null;
252
                        } else if (compare.equal()) { // Same blocks
253
                            changeStart = null; // No firing
254
                            changeEnd = null;
255
                        } else if (compare.equalStart()) {
256
                            if (compare.containsStrict()) {
257
                                changeStart = currentBlockEnd;
258
                                changeEnd = newEnd;
259
                            } else {
260
                                assert (compare.insideStrict());
261
                                changeStart = newEnd;
262
                                changeEnd = currentBlockEnd;
263
                            }
264
                        } else if (compare.equalEnd()) {
265
                            if (compare.containsStrict()) {
266
                                changeStart = newStart;
267
                                changeEnd = currentBlockStart;
268
                            } else {
269
                                assert (compare.insideStrict());
270
                                changeStart = currentBlockStart;
271
                                changeEnd = newStart;
272
                            }
273
                        } else {
274
                            changeStart = (compare.lowerStart()) ? newStart : currentBlockStart;
275
                            changeEnd = (compare.lowerEnd()) ? currentBlockEnd : newEnd;
276
                        }
277
                    }
278
279
                } else { // newBlock is null => selection removed
220
                } else { // newBlock is null => selection removed
280
                    newStart = null;
221
                    selectionsBag.clear();
281
                    newEnd = null;
282
                    changeStart = currentBlockStart;
283
                    changeEnd = currentBlockEnd;
284
                }
285
286
                if (LOG.isLoggable(Level.FINE)) {
287
                    LOG.fine("Caret selection block changed from [" //NOI18N
288
                            + positionToString(currentBlockStart) + ", " + positionToString(currentBlockEnd) + "] to [" //NOI18N
289
                            + positionToString(newStart) + ", " + positionToString(newEnd) + "]" //NOI18N
290
                            + ", layer=" + s2s(this) + '\n'); //NOI18N
291
                }
292
                currentBlockStart = newStart;
293
                currentBlockEnd = newEnd;
294
            }
295
296
            if (changeStart != null) {
297
                if (fire) {
298
                    fireHighlightsChange(changeStart.getOffset(), changeEnd.getOffset());
299
                }
222
                }
300
            }
223
            }
301
        } finally {
224
        } finally {
Lines 356-401 Link Here
356
    private static String s2s(Object o) {
279
    private static String s2s(Object o) {
357
        return o == null ? "null" : o.getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(o)); //NOI18N
280
        return o == null ? "null" : o.getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(o)); //NOI18N
358
    }
281
    }
359
360
    public static final class SimpleHighlightsSequence implements HighlightsSequence {
361
        
362
        private int startOffset;
363
        private int endOffset;
364
        private AttributeSet attribs;
365
        
366
        private boolean end = false;
367
        
368
        public SimpleHighlightsSequence(int startOffset, int endOffset, AttributeSet attribs) {
369
            this.startOffset = startOffset;
370
            this.endOffset = endOffset;
371
            this.attribs = attribs;
372
        }
373
374
        @Override
375
        public boolean moveNext() {
376
            if (!end) {
377
                end = true;
378
                return true;
379
            } else {
380
                return false;
381
            }
382
        }
383
384
        @Override
385
        public int getStartOffset() {
386
            return startOffset;
387
        }
388
389
        @Override
390
        public int getEndOffset() {
391
            return endOffset;
392
        }
393
394
        @Override
395
        public AttributeSet getAttributes() {
396
            return attribs;
397
        }
398
    } // End of SimpleHighlightsSequence
399
    
282
    
400
    public static final class CaretRowHighlighting extends CaretBasedBlockHighlighting {
283
    public static final class CaretRowHighlighting extends CaretBasedBlockHighlighting {
401
        
284
        
Lines 405-431 Link Here
405
            super(component, FontColorNames.CARET_ROW_COLORING, true, false);
288
            super(component, FontColorNames.CARET_ROW_COLORING, true, false);
406
        }
289
        }
407
        
290
        
408
        protected Position[] getCurrentBlockPositions(Document document) {
291
        @Override
292
        protected PositionsBag getCurrentBlockPositions(Document document) {
409
            Caret caret = caret();
293
            Caret caret = caret();
294
            PositionsBag selections = null;
410
            if (document != null && caret != null) {
295
            if (document != null && caret != null) {
411
                int caretOffset = caret.getDot();
296
                selections = new PositionsBag(document);
412
                try {
297
                if(caret instanceof EditorCaret) {
413
                    int startOffset = DocUtils.getRowStart(document, caretOffset, 0);
298
                    EditorCaret editorCaret = (EditorCaret) caret;
414
                    int endOffset = DocUtils.getRowEnd(document, caretOffset);
299
                    List<CaretInfo> carets = editorCaret.getCarets();
300
                    for (CaretInfo c : carets) {
301
                        int caretOffset = c.getDot();
302
                        try {
303
                            int startOffset = DocUtils.getRowStart(document, caretOffset, 0);
304
                            int endOffset = DocUtils.getRowEnd(document, caretOffset);
305
                            // include the new-line character or the end of the document
306
                            endOffset++;
307
                            Position startPos = document.createPosition(startOffset);
308
                            Position endPos = document.createPosition(endOffset);
309
                            selections.addHighlight(startPos, endPos, getAttribs());
310
                        } catch(BadLocationException e) {
311
                            LOG.log(Level.WARNING, e.getMessage(), e);
312
                        }
313
                    }
314
                } else {
315
                    int caretOffset = caret.getDot();
316
                    try {
317
                        int startOffset = DocUtils.getRowStart(document, caretOffset, 0);
318
                        int endOffset = DocUtils.getRowEnd(document, caretOffset);
415
319
416
                    // include the new-line character or the end of the document
320
                        // include the new-line character or the end of the document
417
                    endOffset++;
321
                        endOffset++;
418
322
                        Position startPos = document.createPosition(startOffset);
419
                    return new Position [] {
323
                        Position endPos = document.createPosition(endOffset);
420
                        document.createPosition(startOffset),
324
                        selections.addHighlight(startPos, endPos, getAttribs());
421
                        document.createPosition(endOffset),
325
                    } catch (BadLocationException e) {
422
                    };
326
                        LOG.log(Level.WARNING, e.getMessage(), e);
423
                } catch (BadLocationException e) {
327
                    }
424
                    LOG.log(Level.WARNING, e.getMessage(), e);
425
                }
328
                }
426
            }
329
            }
427
330
            return selections;
428
            return null;
429
        }
331
        }
430
    } // End of CaretRowHighlighting class
332
    } // End of CaretRowHighlighting class
431
    
333
    
Lines 445-487 Link Here
445
        }
347
        }
446
    
348
    
447
        @Override
349
        @Override
448
        protected Position[] getCurrentBlockPositions(Document document) {
350
        protected PositionsBag getCurrentBlockPositions(Document document) {
449
            Caret caret = caret();
351
            Caret caret = caret();
352
            PositionsBag selections = null;
450
            if (document != null && caret != null) {
353
            if (document != null && caret != null) {
451
                int caretOffset = caret.getDot();
354
                selections = new PositionsBag(document);
452
                int markOffset = caret.getMark();
355
                if(caret instanceof EditorCaret) {
356
                    EditorCaret editorCaret = (EditorCaret) caret;
357
                    for (CaretInfo caretInfo : editorCaret.getCarets()) {
358
                        int caretOffset = caretInfo.getDot();
359
                        int markOffset = caretInfo.getMark();
453
360
454
                if (caretOffset != markOffset) {
361
                        if (caretOffset != markOffset) {
455
                    try {
362
                            try {
456
                        return new Position [] {
363
                                Position start = document.createPosition(Math.min(caretOffset, markOffset));
457
                            document.createPosition(Math.min(caretOffset, markOffset)),
364
                                Position end = document.createPosition(Math.max(caretOffset, markOffset));
458
                            document.createPosition(Math.max(caretOffset, markOffset)),
365
                                selections.addHighlight(start, end, getAttribs());
459
                        };
366
                            } catch (BadLocationException e) {
460
                    } catch (BadLocationException e) {
367
                                LOG.log(Level.WARNING, e.getMessage(), e);
461
                        LOG.log(Level.WARNING, e.getMessage(), e);
368
                            }
369
                        }
370
                    }
371
                } else {
372
                    int caretOffset = caret.getDot();
373
                    int markOffset = caret.getMark();
374
375
                    if (caretOffset != markOffset) {
376
                        try {
377
                            Position start = document.createPosition(Math.min(caretOffset, markOffset));
378
                            Position end = document.createPosition(Math.max(caretOffset, markOffset));
379
                            selections.addHighlight(start, end, getAttribs());
380
                        } catch (BadLocationException e) {
381
                            LOG.log(Level.WARNING, e.getMessage(), e);
382
                        }
462
                    }
383
                    }
463
                }
384
                }
464
            }
385
            }
465
            
386
            
466
            return null;
387
            return selections;
467
        }
388
        }
468
389
469
        @Override
390
//        @Override
470
        public HighlightsSequence getHighlights(int startOffset, int endOffset) {
391
//        public HighlightsSequence getHighlights(int startOffset, int endOffset) {
471
            if (!RectangularSelectionUtils.isRectangularSelection(component())) { // regular selection
392
//            if (!RectangularSelectionUtils.isRectangularSelection(component())) { // regular selection
472
                return super.getHighlights(startOffset, endOffset);
393
//                return super.getHighlights(startOffset, endOffset);
473
            } else { // rectangular selection
394
//            } else { // rectangular selection
474
                return (rectangularSelectionBag != null)
395
//                return (rectangularSelectionBag != null)
475
                        ? (rectangularSelectionBag.getHighlights(startOffset, endOffset))
396
//                        ? (rectangularSelectionBag.getHighlights(startOffset, endOffset))
476
                        : HighlightsSequence.EMPTY;
397
//                        : HighlightsSequence.EMPTY;
477
            }
398
//            }
478
        }
399
//        }
479
400
480
        @Override
401
        @Override
481
        public void propertyChange(PropertyChangeEvent evt) {
402
        public void propertyChange(PropertyChangeEvent evt) {
482
            super.propertyChange(evt);
403
            super.propertyChange(evt);
483
            if (RectangularSelectionUtils.getRectangularSelectionProperty().equals(evt.getPropertyName())) {
404
            if (RectangularSelectionUtils.getRectangularSelectionProperty().equals(evt.getPropertyName())) {
484
                fireHighlightsChange(0, component().getDocument().getLength());
405
//                fireHighlightsChange(0, component().getDocument().getLength());
485
            }
406
            }
486
        }
407
        }
487
408
Lines 508-514 Link Here
508
                    }
429
                    }
509
                    // Fire change at once
430
                    // Fire change at once
510
                    if (hlChangeStartOffset != -1) {
431
                    if (hlChangeStartOffset != -1) {
511
                        fireHighlightsChange(hlChangeStartOffset, hlChangeEndOffset);
432
//                        fireHighlightsChange(hlChangeStartOffset, hlChangeEndOffset);
512
                        hlChangeStartOffset = -1;
433
                        hlChangeStartOffset = -1;
513
                    }
434
                    }
514
                }
435
                }
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/CaretOverwriteModeHighlighting.java (+351 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.editor.lib2.highlighting;
43
44
import java.beans.PropertyChangeEvent;
45
import java.beans.PropertyChangeListener;
46
import java.util.List;
47
import java.util.logging.Level;
48
import java.util.logging.Logger;
49
import javax.swing.text.AttributeSet;
50
import javax.swing.text.Caret;
51
import javax.swing.text.Document;
52
import javax.swing.text.JTextComponent;
53
import javax.swing.text.StyleConstants;
54
import org.netbeans.api.editor.caret.CaretInfo;
55
import org.netbeans.api.editor.caret.EditorCaret;
56
import org.netbeans.api.editor.caret.EditorCaretEvent;
57
import org.netbeans.api.editor.caret.EditorCaretListener;
58
import org.netbeans.api.editor.settings.AttributesUtilities;
59
import org.netbeans.lib.editor.util.ListenerList;
60
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
61
import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent;
62
import org.netbeans.spi.editor.highlighting.HighlightsChangeListener;
63
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
64
import org.netbeans.spi.editor.highlighting.ReleasableHighlightsContainer;
65
import org.netbeans.spi.editor.highlighting.ShiftHighlightsSequence;
66
import org.openide.util.WeakListeners;
67
68
/**
69
 * Used by EditorCaret to color a single character under the block caret(s) with inverse colors in
70
 * overwrite mode when the caret blinking timer ticks.
71
 *
72
 * @author Miloslav Metelka
73
 */
74
public final class CaretOverwriteModeHighlighting implements ReleasableHighlightsContainer, PropertyChangeListener, EditorCaretListener {
75
76
    public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.lib2.highlighting.CaretOverwriteModeHighlighting"; //NOI18N
77
78
    // -J-Dorg.netbeans.modules.editor.lib2.highlighting.CaretOverwriteModeHighlighting.level=FINE
79
    private static final Logger LOG = Logger.getLogger(CaretOverwriteModeHighlighting.class.getName());
80
    
81
    private final JTextComponent component;
82
    
83
    private boolean inited;
84
    
85
    private boolean visible;
86
87
    private EditorCaret editorCaret;
88
89
    private EditorCaretListener weakEditorCaretListener;
90
    
91
    private ListenerList<HighlightsChangeListener> listenerList = new ListenerList<>();
92
    
93
    private AttributeSet coloringAttrs;
94
    
95
    private CharSequence docText;
96
    
97
    private List<CaretInfo> sortedCarets;
98
    
99
    /** Creates a new instance of CaretSelectionLayer */
100
    public CaretOverwriteModeHighlighting(JTextComponent component) {
101
        this.component = component;
102
        component.putClientProperty(CaretOverwriteModeHighlighting.class, this);
103
    }
104
    
105
    public void setVisible(boolean visible) {
106
        if (visible != this.visible) {
107
            this.visible = visible;
108
            if (editorCaret != null) {
109
                List<CaretInfo> sortedCaretsL;
110
                if (visible) {
111
                    sortedCaretsL = editorCaret.getSortedCarets();
112
                    synchronized (this) {
113
                        sortedCarets = sortedCaretsL;
114
                    }
115
                } else {
116
                    synchronized (this) {
117
                        sortedCaretsL = sortedCarets;
118
                        sortedCarets = null;
119
                    }
120
                }
121
                if (sortedCaretsL != null) {
122
                    int changeStartOffset = sortedCaretsL.get(0).getDot();
123
                    int changeEndOffset = sortedCaretsL.get(sortedCaretsL.size() - 1).getDot() + 1;
124
                    fireHighlightsChange(changeStartOffset, changeEndOffset);
125
                }
126
            }
127
        }
128
    }
129
    
130
    private void init() {
131
        component.addPropertyChangeListener(WeakListeners.propertyChange(this, component));
132
        updateActiveCaret();
133
        updateColoring();
134
    }
135
    
136
    // ------------------------------------------------
137
    // AbstractHighlightsContainer implementation
138
    // ------------------------------------------------
139
    
140
    @Override
141
    public HighlightsSequence getHighlights(int startOffset, int endOffset) {
142
        if (!inited) {
143
            inited = true;
144
            init();
145
        }
146
        HighlightsSequence hs;
147
        boolean visibleL;
148
        List<CaretInfo> sortedCaretsL;
149
        synchronized (this) {
150
            visibleL = visible;
151
            sortedCaretsL = sortedCarets;
152
        }
153
        if (editorCaret != null && visibleL) {
154
            hs = new HS(sortedCaretsL, startOffset, endOffset);
155
            if (LOG.isLoggable(Level.FINE)) {
156
                LOG.fine("CaretOverwriteModeHighlighting.getHighlights() <" + startOffset + "," + endOffset + ">\n");
157
            }
158
        } else {
159
            hs = HighlightsSequence.EMPTY;
160
        }
161
        return hs;
162
    }
163
164
    @Override
165
    public void addHighlightsChangeListener(HighlightsChangeListener listener) {
166
        listenerList.add(listener);
167
    }
168
169
    @Override
170
    public void removeHighlightsChangeListener(HighlightsChangeListener listener) {
171
        listenerList.remove(listener);
172
    }
173
    
174
    private void fireHighlightsChange(HighlightsChangeEvent evt) {
175
        for (HighlightsChangeListener listener : listenerList.getListeners()) {
176
            listener.highlightChanged(evt);
177
        }
178
    }
179
180
    private void fireHighlightsChange(int startOffset, int endOffset) {
181
        fireHighlightsChange(new HighlightsChangeEvent(this, startOffset, endOffset));
182
    }
183
184
    // ------------------------------------------------
185
    // PropertyChangeListener implementation
186
    // ------------------------------------------------
187
    
188
    @Override
189
    public void propertyChange(PropertyChangeEvent evt) {
190
        String propName = evt.getPropertyName();
191
        if (propName == null || "caret".equals(propName)) { //NOI18N
192
            updateActiveCaret();
193
        } else if ("document".equals(propName)) {
194
            updateDocText();
195
        } else if ("caretColor".equals(propName) || "background".equals(propName)) {
196
            updateColoring();
197
        }
198
    }
199
    
200
    @Override
201
    public void caretChanged(EditorCaretEvent evt) {
202
        if (visible) {
203
            fireHighlightsChange(evt.getAffectedStartOffset(), evt.getAffectedEndOffset());
204
            List<CaretInfo> sortedCaretsL;
205
            sortedCaretsL = editorCaret.getSortedCarets();
206
            synchronized (this) {
207
                sortedCarets = sortedCaretsL;
208
            }
209
        }
210
    }
211
212
    @Override
213
    public void released() {
214
        component.removePropertyChangeListener(this);
215
    }
216
    
217
    private void updateActiveCaret() {
218
        if (visible) {
219
            setVisible(false);
220
        }
221
        if (editorCaret != null) {
222
            editorCaret.removeEditorCaretListener(weakEditorCaretListener);
223
            weakEditorCaretListener = null;
224
            editorCaret = null;
225
            docText = null;
226
        }
227
228
        Caret caret = component.getCaret();
229
        if (caret instanceof EditorCaret) { // Only work for editor caret
230
            editorCaret = (EditorCaret) caret;
231
        }
232
233
        if (editorCaret != null) {
234
            updateDocText();
235
            weakEditorCaretListener = WeakListeners.create(EditorCaretListener.class, this, editorCaret);
236
            editorCaret.addEditorCaretListener(weakEditorCaretListener);
237
        }
238
    }
239
240
    private void updateColoring() {
241
        coloringAttrs = AttributesUtilities.createImmutable(
242
                StyleConstants.Background, component.getCaretColor(),
243
                StyleConstants.Foreground, component.getBackground());
244
    }
245
    
246
    private void updateDocText() {
247
        JTextComponent c = component;
248
        CharSequence text = null;
249
        if (c != null) {
250
            Document doc = c.getDocument();
251
            if (doc != null) {
252
                text = DocumentUtilities.getText(doc);
253
            }
254
        }
255
        docText = text;
256
    }
257
    
258
    private final class HS implements ShiftHighlightsSequence {
259
        
260
        private final List<CaretInfo> sortedCarets;
261
        
262
        private final int startOffset;
263
        
264
        private final int endOffset;
265
266
        private int caretOffset = -1;
267
        
268
        private int caretOffsetEndShift;
269
270
        private int caretIndex;
271
        
272
        HS(List<CaretInfo> sortedCarets, int startOffset, int endOffset) {
273
            this.sortedCarets = sortedCarets;
274
            this.startOffset = startOffset;
275
            this.endOffset = endOffset;
276
        }
277
278
        @Override
279
        public boolean moveNext() {
280
            boolean ret = false;
281
            if (caretOffset == -1) { // Return first highlight
282
                while (caretOffset < startOffset && caretIndex < sortedCarets.size()) {
283
                    CaretInfo caret = sortedCarets.get(caretIndex++);
284
                    caretOffset = caret.getDot();
285
                }
286
                if (caretOffset != -1) {
287
                    ret = true;
288
                }
289
            } else {
290
                while (caretIndex < sortedCarets.size()) {
291
                    CaretInfo caret = sortedCarets.get(caretIndex++);
292
                    int offset = caret.getDot();
293
                    // Check for case if sorted carets would not be truly sorted or there would be duplicates
294
                    if (offset > caretOffset) {
295
                        caretOffset = offset;
296
                        if (LOG.isLoggable(Level.FINE)) {
297
                            LOG.fine("  CaretOverwriteModeHighlighting.Highlight <" + caretOffset + "," + (caretOffset + 1) + ">\n");
298
                        }
299
                        return true;
300
                    }
301
                    if (offset >= endOffset) {
302
                        return false;
303
                    }
304
                }
305
            }
306
            if (ret) {
307
                caretOffsetEndShift = 0;
308
                CharSequence text = docText;
309
                if (text != null) {
310
                    char ch = text.charAt(caretOffset);
311
                    if (ch == '\t' || ch == '\n') {
312
                        caretOffsetEndShift = 1;
313
                    }
314
                }
315
                if (LOG.isLoggable(Level.FINE)) {
316
                    LOG.fine("  CaretOverwriteModeHighlighting.Highlight <" + caretOffset + "(sh=0)," +
317
                            (caretOffset + 1) + "(sh=" + caretOffsetEndShift + ")>\n");
318
                }
319
                return true;
320
            }
321
            return false;
322
        }
323
324
        @Override
325
        public int getStartOffset() {
326
            return caretOffset;
327
        }
328
329
        @Override
330
        public int getEndOffset() {
331
            return caretOffset + 1;
332
        }
333
334
        @Override
335
        public AttributeSet getAttributes() {
336
            return coloringAttrs;
337
        }
338
339
        @Override
340
        public int getStartShift() {
341
            return 0;
342
        }
343
344
        @Override
345
        public int getEndShift() {
346
            return caretOffsetEndShift;
347
        }
348
        
349
        
350
    }
351
}
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/CompoundAttributes.java (-2 / +2 lines)
Lines 50-58 Link Here
50
/**
50
/**
51
 * Multiple highlights for a particular view contains an array of highlights
51
 * Multiple highlights for a particular view contains an array of highlights
52
 * and a starting offset to which the (ending) offsets in HighlightItem(s) are related.
52
 * and a starting offset to which the (ending) offsets in HighlightItem(s) are related.
53
 * <br/>
53
 * <br>
54
 * The view should assign this object to its internal 'attributes' variable that holds the AttributeSet.
54
 * The view should assign this object to its internal 'attributes' variable that holds the AttributeSet.
55
 * <br/>
55
 * <br>
56
 * The AttributeSet implementation returns attributes of the first section of the compound attributes.
56
 * The AttributeSet implementation returns attributes of the first section of the compound attributes.
57
 *
57
 *
58
 * @author Miloslav Metelka
58
 * @author Miloslav Metelka
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/DirectMergeContainer.java (-103 / +236 lines)
Lines 57-70 Link Here
57
import org.netbeans.spi.editor.highlighting.HighlightsContainer;
57
import org.netbeans.spi.editor.highlighting.HighlightsContainer;
58
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
58
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
59
import org.netbeans.spi.editor.highlighting.ReleasableHighlightsContainer;
59
import org.netbeans.spi.editor.highlighting.ReleasableHighlightsContainer;
60
import org.netbeans.spi.editor.highlighting.ShiftHighlightsSequence;
60
import org.openide.util.WeakListeners;
61
import org.openide.util.WeakListeners;
61
62
62
/**
63
/**
63
 * Compound highlights-layer container that does non-cached direct merging
64
 * Compound highlights-layer container that does non-cached direct merging
64
 * of individual layers' highlights.
65
 * of individual layers' highlights.
65
 * <br/>
66
 * <br>
66
 * It's somewhat similar to a view building process in view hierarchy which also maintains
67
 * It's somewhat similar to a view building process in view hierarchy which also maintains
67
 * next-change-offset where a change in the particular layer occurs and needs to be processed.
68
 * next-change-offset where a change in the particular layer occurs and needs to be processed.
69
 * <br>
70
 * {@link ShiftHighlightsSequence} are supported and the highlights sequences returned by the container
71
 * are always instances of this interface.
68
 *
72
 *
69
 * @author Miloslav Metelka
73
 * @author Miloslav Metelka
70
 */
74
 */
Lines 124-132 Link Here
124
        layerEvent = event;
128
        layerEvent = event;
125
        try {
129
        try {
126
            if (!listeners.isEmpty()) {
130
            if (!listeners.isEmpty()) {
127
                event = new HighlightsChangeEvent(this, event.getStartOffset(), event.getEndOffset());
131
                HighlightsChangeEvent thisEvt = new HighlightsChangeEvent(this, event.getStartOffset(), event.getEndOffset());
128
                for (HighlightsChangeListener l : listeners) {
132
                for (HighlightsChangeListener l : listeners) {
129
                    l.highlightChanged(event);
133
                    l.highlightChanged(thisEvt);
130
                }
134
                }
131
            }
135
            }
132
            synchronized (activeHlSeqs) {
136
            synchronized (activeHlSeqs) {
Lines 146-152 Link Here
146
    /**
150
    /**
147
     * Get event from a contained layer which caused highlight change
151
     * Get event from a contained layer which caused highlight change
148
     * (mainly for debugging purposes).
152
     * (mainly for debugging purposes).
149
     * <br/>
153
     * <br>
150
     * The information is only available during firing to change listeners registered by
154
     * The information is only available during firing to change listeners registered by
151
     * {@link #addHighlightsChangeListener(org.netbeans.spi.editor.highlighting.HighlightsChangeListener)}.
155
     * {@link #addHighlightsChangeListener(org.netbeans.spi.editor.highlighting.HighlightsChangeListener)}.
152
     * 
156
     * 
Lines 177-183 Link Here
177
        }
181
        }
178
    }
182
    }
179
    
183
    
180
    static final class HlSequence implements HighlightsSequenceEx {
184
    static final class HlSequence implements ShiftHighlightsSequence {
181
185
182
        /**
186
        /**
183
         * Wrappers around layers used to compute merged highlights.
187
         * Wrappers around layers used to compute merged highlights.
Lines 186-197 Link Here
186
        
190
        
187
        private int topWrapperIndex;
191
        private int topWrapperIndex;
188
        
192
        
189
        private final int endOffset;
193
        final int endOffset;
190
        
194
        
191
        int mergedHighlightStartOffset;
195
        int mergedHighlightStartOffset;
192
        
196
        
197
        int mergedHighlightStartShift;
198
        
193
        int mergedHighlightEndOffset;
199
        int mergedHighlightEndOffset;
194
        
200
        
201
        int mergedHighlightEndShift;
202
        
195
        AttributeSet mergedAttrs;
203
        AttributeSet mergedAttrs;
196
        
204
        
197
        volatile boolean finished; // Either no more highlights or layers were changed;
205
        volatile boolean finished; // Either no more highlights or layers were changed;
Lines 205-241 Link Here
205
            for (int i = 0; i < layers.length; i++) {
213
            for (int i = 0; i < layers.length; i++) {
206
                HighlightsContainer container = layers[i];
214
                HighlightsContainer container = layers[i];
207
                HighlightsSequence hlSequence = container.getHighlights(startOffset, endOffset);
215
                HighlightsSequence hlSequence = container.getHighlights(startOffset, endOffset);
208
                Wrapper wrapper = new Wrapper(container, hlSequence, startOffset);
216
                Wrapper wrapper = new Wrapper(container, hlSequence, endOffset);
209
                if (!wrapper.isFinished()) { // For no-highlight wrapper do not include it at all in the array
217
                if (wrapper.init(startOffset)) { // For no-highlight wrapper do not include it at all in the array
210
                    wrappers[topWrapperIndex++] = wrapper;
218
                    wrappers[topWrapperIndex++] = wrapper;
211
                }
219
                }
212
            }
220
            }
213
            topWrapperIndex--;
221
            topWrapperIndex--;
214
            updateMergeVars(-1, startOffset); // Update all layers to fetch correct values
222
            updateMergeVars(-1, startOffset, 0); // Update all layers to fetch correct values
215
        }
223
        }
216
        
224
        
217
        @Override
225
        @Override
218
        public boolean moveNext() {
226
        public boolean moveNext() {
219
            if (finished || topWrapperIndex < 0) {
227
            if (finished) {
220
                return false;
228
                return false;
221
            }
229
            }
222
            Wrapper topWrapper;
230
            Wrapper topWrapper;
223
            int lastHighlightEndOffset = mergedHighlightEndOffset;
231
            int nextHighlightStartOffset = mergedHighlightEndOffset;
224
            while ((topWrapper = nextMerge(lastHighlightEndOffset)) != null) {
232
            int nextHighlightStartShift = mergedHighlightEndShift;
225
                int nextChangeOffset = topWrapper.mNextChangeOffset;
233
            while ((topWrapper = nextMerge(nextHighlightStartOffset, nextHighlightStartShift)) != null) {
226
                if (nextChangeOffset <= lastHighlightEndOffset) { // No advance in change offset => Finished
234
                int nextChangeOffset = topWrapper.mergedNextChangeOffset;
227
                    finished = true;
235
                int nextChangeShift = topWrapper.mergedNextChangeShift;
228
                    return false;
229
                }
230
                AttributeSet attrs = topWrapper.mAttrs;
236
                AttributeSet attrs = topWrapper.mAttrs;
231
                if (attrs != null) {
237
                if (attrs != null) { // Do not return regions with empty attrs (they are not highlights)
232
                    mergedHighlightStartOffset = lastHighlightEndOffset;
238
                    mergedHighlightStartOffset = nextHighlightStartOffset;
239
                    mergedHighlightStartShift = nextHighlightStartShift;
233
                    mergedHighlightEndOffset = nextChangeOffset;
240
                    mergedHighlightEndOffset = nextChangeOffset;
241
                    mergedHighlightEndShift = nextChangeShift;
234
                    mergedAttrs = attrs;
242
                    mergedAttrs = attrs;
235
                    return true;
243
                    return true;
236
                }
244
                }
237
                lastHighlightEndOffset = nextChangeOffset;
245
                nextHighlightStartOffset = nextChangeOffset;
246
                nextHighlightStartShift = nextChangeShift;
238
            }
247
            }
248
            finished = true;
239
            return false;
249
            return false;
240
        }
250
        }
241
251
Lines 245-264 Link Here
245
        }
255
        }
246
256
247
        @Override
257
        @Override
258
        public int getStartShift() {
259
            return mergedHighlightStartShift;
260
        }
261
        
262
        @Override
248
        public int getEndOffset() {
263
        public int getEndOffset() {
249
            return mergedHighlightEndOffset;
264
            return mergedHighlightEndOffset;
250
        }
265
        }
251
266
252
        @Override
267
        @Override
268
        public int getEndShift() {
269
            return mergedHighlightEndShift;
270
        }
271
        
272
        @Override
253
        public AttributeSet getAttributes() {
273
        public AttributeSet getAttributes() {
254
            return mergedAttrs;
274
            return mergedAttrs;
255
        }
275
        }
256
276
257
        @Override
258
        public boolean isStale() {
259
            return finished;
260
        }
261
        
262
        void notifyLayersChanged() { // Notify that layers were changed => stop iteration
277
        void notifyLayersChanged() { // Notify that layers were changed => stop iteration
263
            finished = true;
278
            finished = true;
264
        }
279
        }
Lines 267-325 Link Here
267
         * Do merge above the given offset.
282
         * Do merge above the given offset.
268
         *
283
         *
269
         * @param offset end of last merged highlight.
284
         * @param offset end of last merged highlight.
285
         * @param shift end shift of last merged highlight (accompanying the offset).
270
         * @return top wrapper containing info about the performed merge or null
286
         * @return top wrapper containing info about the performed merge or null
271
         *  if there is zero wrappers.
287
         *  if there is zero wrappers.
272
         */
288
         */
273
        Wrapper nextMerge(int offset) {
289
        Wrapper nextMerge(int offset, int shift) {
274
            int i = topWrapperIndex;
290
            int i = topWrapperIndex;
275
            for (; i >= 0 && wrappers[i].mNextChangeOffset <= offset; i--) { }
291
            for (; i >= 0 && wrappers[i].isMergedNextChangeBelowOrAt(offset, shift); i--) { }
276
            // i contains first layer which has mNextChangeOffset > offset
292
            // i contains first layer which has mergedNextChangeOffset > offset (or same offset but lower shift)
277
            return updateMergeVars(i, offset);
293
            return updateMergeVars(i, offset, shift);
278
        }
294
        }
279
        
295
        
280
        /**
296
        /**
281
         * Update merged vars of wrappers at (startIndex+1) and above.
297
         * Update merged vars of wrappers at (startIndex+1) and above.
282
         *
298
         *
283
         * @param startIndex index of first wrapper which has mNextChangeOffset &lt; offset.
299
         * @param startIndex index of first wrapper which has mergedNextChangeOffset above given offset (and shift)
284
         *  All wrappers above it will have their mNextChangeOffset and mAttrs updated.
300
         *  or -1 if all wrappers need to be updated.
285
         * @param offset.
301
         *  All wrappers above this index will have their mergedNextChangeOffset and mAttrs updated.
286
         * @return mNextChangeOffset of the top wrapper.
302
         * @param offset current offset at which to update.
303
         * @param shift current shift "within" the char at offset.
304
         * @return top wrapper (wrapper at topWrapperIndex).
287
         */
305
         */
288
        Wrapper updateMergeVars(int startIndex, int offset) {
306
        Wrapper updateMergeVars(int startIndex, int offset, int shift) {
289
            Wrapper wrapper = null;
307
            Wrapper wrapper = null;
290
            int nextChangeOffset;
308
            int nextChangeOffset;
309
            int nextChangeShift;
291
            AttributeSet lastAttrs;
310
            AttributeSet lastAttrs;
292
            if (startIndex < 0) { // No valid layers
311
            if (startIndex < 0) { // No valid layers
293
                nextChangeOffset = endOffset;
312
                nextChangeOffset = endOffset;
313
                nextChangeShift = 0;
294
                lastAttrs = null;
314
                lastAttrs = null;
295
            } else {
315
            } else {
296
                wrapper = wrappers[startIndex];
316
                wrapper = wrappers[startIndex];
297
                nextChangeOffset = wrapper.mNextChangeOffset;
317
                nextChangeOffset = wrapper.mergedNextChangeOffset;
318
                nextChangeShift = wrapper.mergedNextChangeShift;
298
                lastAttrs = wrapper.mAttrs;
319
                lastAttrs = wrapper.mAttrs;
299
            }
320
            }
300
            startIndex++; // Move to first wrapper that needs to be updated
321
            // Start with first wrapper that needs to be updated
301
            for (; startIndex <= topWrapperIndex; startIndex++) {
322
            wrapperIteration:
302
                wrapper = wrappers[startIndex];
323
            for (int i = startIndex + 1; i <= topWrapperIndex; i++) {
303
                if (wrapper.nextChangeOffset <= offset) {
324
                wrapper = wrappers[i];
304
                    if (wrapper.updateCurrentState(offset)) { // Requires next highlight fetch
325
                if (wrapper.isNextChangeBelowOrAt(offset, shift)) {
305
                        if (!wrapper.fetchNextHighlight(offset)) { // Finished all highlights in sequence
326
                    while (wrapper.updateCurrentState(offset, shift)) { // Check if next highlight fetch is necessary
306
                            removeWrapper(startIndex); // Remove this wrapper
327
                        if (!wrapper.fetchNextHighlight()) { // Finished all highlights in sequence
328
                            removeWrapper(i); // Remove this wrapper (does topWrapperIndex--)
307
                            // Ensure that the wrapper returned from method is correct after removeWrapper()
329
                            // Ensure that the wrapper returned from method is correct after removeWrapper()
308
                            // topWrapperIndex already decreased by removeWrapper()
330
                            // topWrapperIndex already decreased by removeWrapper()
309
                            startIndex--; // Compensate for addition in for(;;)
331
                            i--; // Compensate wrapper removal in for(;;)
310
                            if (startIndex == topWrapperIndex) { // Would be no more iterations
332
                            if (i == topWrapperIndex) {
311
                                // Previous wrapper or null
333
                                // Since "wrapper" variable should return wrapper at current topWrapperIndex
312
                                wrapper = (startIndex >= 0) ? wrappers[startIndex] : null;
334
                                // that in this particular case was just removed
335
                                // then assign current top wrapper explicitly.
336
                                wrapper = (i >= 0) ? wrappers[i] : null;
337
                                break wrapperIteration;
313
                            }
338
                            }
314
                            continue; // Use next wrapper (now at same index i)
339
                            continue wrapperIteration;
315
                        }
340
                        }
316
                        wrapper.updateCurrentState(offset); // Update state to just fetched highlight
317
                    }
341
                    }
318
                }
342
                }
319
                if (wrapper.nextChangeOffset < nextChangeOffset) {
343
                if (wrapper.isNextChangeBelow(nextChangeOffset, nextChangeShift)) {
320
                    nextChangeOffset = wrapper.nextChangeOffset;
344
                    nextChangeOffset = wrapper.nextChangeOffset;
345
                    nextChangeShift = wrapper.nextChangeShift;
321
                }
346
                }
322
                wrapper.mNextChangeOffset = nextChangeOffset;
347
                wrapper.mergedNextChangeOffset = nextChangeOffset;
348
                wrapper.mergedNextChangeShift = nextChangeShift;
323
                lastAttrs = (lastAttrs != null)
349
                lastAttrs = (lastAttrs != null)
324
                        ? ((wrapper.currentAttrs != null)
350
                        ? ((wrapper.currentAttrs != null)
325
                            ? AttributesUtilities.createComposite(wrapper.currentAttrs, lastAttrs) // first prior second
351
                            ? AttributesUtilities.createComposite(wrapper.currentAttrs, lastAttrs) // first prior second
Lines 360-373 Link Here
360
    static final class Wrapper {
386
    static final class Wrapper {
361
387
362
        /**
388
        /**
363
         * Layer over which hlSequence is constructed (for debugging purposes).
389
         * Layer over which layerSequence is constructed (for debugging purposes).
364
         */
390
         */
365
        final HighlightsContainer layer;
391
        final HighlightsContainer layer;
366
        
392
        
367
        /**
393
        /**
368
         * Highlights sequence for layer corresponding to this wrapper.
394
         * Highlights sequence for layer corresponding to this wrapper.
369
         */
395
         */
370
        final HighlightsSequence hlSequence;
396
        final HighlightsSequence layerSequence;
397
        
398
        /**
399
         * Highlights sequence supporting coloring of characters inside tabs or newlines.
400
         */
401
        final ShiftHighlightsSequence shiftLayerSequence;
402
        
403
        /**
404
         * End offset of the region on which upper hlSequence operates so the highlights
405
         * returned by layerSequence should adhere to this limit too.
406
         */
407
        final int endOffset;
371
        
408
        
372
        /**
409
        /**
373
         * Start offset of the last fetched highlight.
410
         * Start offset of the last fetched highlight.
Lines 375-397 Link Here
375
        int hlStartOffset;
412
        int hlStartOffset;
376
        
413
        
377
        /**
414
        /**
415
         * Possible shift accompanying hlStartOffset or zero for non-ShiftHighlightsSequence.
416
         */
417
        int hlStartShift;
418
        
419
        /**
378
         * End offset of the last fetched highlight.
420
         * End offset of the last fetched highlight.
379
         */
421
         */
380
        int hlEndOffset;
422
        int hlEndOffset;
381
        
423
        
382
        /**
424
        /**
425
         * Possible shift accompanying hlEndOffset or zero for non-ShiftHighlightsSequence.
426
         */
427
        int hlEndShift;
428
429
        /**
383
         * Attributes of the last fetched highlight.
430
         * Attributes of the last fetched highlight.
384
         */
431
         */
385
        AttributeSet hlAttrs;
432
        AttributeSet hlAttrs;
386
        
433
        
387
        /**
434
        /**
388
         * Offset where a change in highlighting for the current layer will occur.
435
         * Offset where a change in highlighting for the current layer will occur.
389
         * If an offset is below hlStartOffset then the value is hlStartOffset.
436
         * If currently processed offset is below current highlight (fetched into hlStartOffset and hlEndOffset)
390
         * Otherwise it will be hlEndOffset.
437
         * then the value is set to hlStartOffset.
438
         * For an offset inside current highlight the value will be set to hlEndOffset.
439
         * Offset above hlEndOffset will trigger a next highlight fetching.
391
         */
440
         */
392
        int nextChangeOffset;
441
        int nextChangeOffset;
393
        
442
        
394
        /**
443
        /**
444
         * If shiftHLSequence != null then (similarly to nextChangeOffset)
445
         * this is set either to start shift of the next highlight
446
         * or (if the offset and its shift are inside current highlight) then
447
         * this variable is set to end shift of the current highlight
448
         * or a next highlight will be fetched (if current offset and shift are above the highlight).
449
         */
450
        int nextChangeShift;
451
        
452
        /**
395
         * Attributes for an offset: when before hlStartOffset it's null.
453
         * Attributes for an offset: when before hlStartOffset it's null.
396
         * Otherwise it's hlAttrs.
454
         * Otherwise it's hlAttrs.
397
         */
455
         */
Lines 401-407 Link Here
401
         * Merged next change offset: minimum of nextChangeOffset from all
459
         * Merged next change offset: minimum of nextChangeOffset from all
402
         * wrappers below this one in the wrappers array.
460
         * wrappers below this one in the wrappers array.
403
         */
461
         */
404
        int mNextChangeOffset;
462
        int mergedNextChangeOffset;
463
        
464
        /**
465
         * Merged next change shift - possible shift accompanying mergedNextChangeOffset or zero.
466
         */
467
        int mergedNextChangeShift;
405
        
468
        
406
        /**
469
        /**
407
         * Merged attributes: merge of currentAttrs from all
470
         * Merged attributes: merge of currentAttrs from all
Lines 412-445 Link Here
412
        private int emptyHighlightCount;
475
        private int emptyHighlightCount;
413
        
476
        
414
        
477
        
415
        public Wrapper(HighlightsContainer layer, HighlightsSequence hlSequence, int startOffset) {
478
        public Wrapper(HighlightsContainer layer, HighlightsSequence hlSequence, int endOffset) {
416
            this.layer = layer;
479
            this.layer = layer;
417
            this.hlSequence = hlSequence;
480
            this.layerSequence = hlSequence;
418
            fetchNextHighlight(startOffset);
481
            this.shiftLayerSequence = (hlSequence instanceof ShiftHighlightsSequence) ? (ShiftHighlightsSequence) hlSequence : null;
419
            updateCurrentState(startOffset);
482
            this.endOffset = endOffset;
420
            this.mNextChangeOffset = startOffset; // Will cause recomputation
421
        }
483
        }
422
        
484
        
423
        boolean isFinished() { // Whether no more highlights from 
485
        boolean init(int startOffset) {
424
            return (hlStartOffset == Integer.MAX_VALUE);
486
            do {
487
                if (!fetchNextHighlight()) {
488
                    return false;
489
                }
490
            } while (hlEndOffset <= startOffset); // Exclude any possible highlights ending below startOffset
491
            updateCurrentState(startOffset, 0);
492
            return true;
425
        }
493
        }
426
        
494
        
427
        /**
495
        /**
496
         * Whether next change offset and shift of this wrapper are below the given parameters.
497
         *
498
         * @param offset current offset.
499
         * @param shift current shift (accompanying the current offset).
500
         * @return true if next change offset and shift of this wrapper are below the given parameters
501
         *  or false otherwise.
502
         */
503
        boolean isNextChangeBelow(int offset, int shift) {
504
            return nextChangeOffset < offset || (nextChangeOffset == offset && nextChangeShift < shift);
505
        }
506
507
        /**
508
         * Whether next change offset and shift of this wrapper are below the given parameters
509
         * or right at them.
510
         *
511
         * @param offset current offset.
512
         * @param shift current shift (accompanying the current offset).
513
         * @return true if next change offset and shift of this wrapper are below or right at the given parameters
514
         *  or false otherwise.
515
         */
516
        boolean isNextChangeBelowOrAt(int offset, int shift) {
517
            return nextChangeOffset < offset || (nextChangeOffset == offset && nextChangeShift <= shift);
518
        }
519
520
        /**
521
         * Whether merged next change offset and shift of this wrapper are below the given parameters
522
         * or right at them.
523
         *
524
         * @param offset current offset.
525
         * @param shift current shift (accompanying the current offset).
526
         * @return true if next change offset and shift of this wrapper are below or right at the given parameters
527
         *  or false otherwise.
528
         */
529
        boolean isMergedNextChangeBelowOrAt(int offset, int shift) {
530
            return mergedNextChangeOffset < offset || (mergedNextChangeOffset == offset && mergedNextChangeShift <= shift);
531
        }
532
533
        /**
428
         * Update currentAttrs and nextChangeOffset according to given offset.
534
         * Update currentAttrs and nextChangeOffset according to given offset.
429
         * @param offset offset to which to update
535
         * @param offset offset to which to update
536
         * @param shift shift inside tab or newline character on the given offset.
430
         * @return true if the offset is >= hlEndOffset and so fetchNextHighlight() is necessary.
537
         * @return true if the offset is >= hlEndOffset and so fetchNextHighlight() is necessary.
431
         */
538
         */
432
        boolean updateCurrentState(int offset) {
539
        boolean updateCurrentState(int offset, int shift) {
433
            if (offset < hlStartOffset) { // before hl start
540
            if (offset < hlStartOffset) { // offset before current hl start
434
                currentAttrs = null;
541
                currentAttrs = null;
435
                nextChangeOffset = hlStartOffset;
542
                nextChangeOffset = hlStartOffset;
543
                nextChangeShift = hlStartShift;
436
                return false;
544
                return false;
437
            } else if (offset < hlEndOffset) { // inside hl (assuming call after fetchNextHighlight())
545
            } else if (offset == hlStartOffset) { // inside hl (assuming call after fetchNextHighlight())
546
                if (shift < hlStartShift) {
547
                    currentAttrs = null;
548
                    nextChangeOffset = hlStartOffset;
549
                    nextChangeShift = hlStartShift;
550
                    return false;
551
                    
552
                } else { // Above (or at) highlight's start
553
                    if (offset < hlEndOffset || (offset == hlEndOffset && shift < hlEndShift)) {
554
                        currentAttrs = hlAttrs;
555
                        nextChangeOffset = hlEndOffset;
556
                        nextChangeShift = hlEndShift;
557
                        return false;
558
                    } else {
559
                        return true; // Fetch next highlight
560
                    }
561
                } // else: fetch next highlight
562
            } else if (offset < hlEndOffset || (offset == hlEndOffset && shift < hlEndShift)) {
438
                currentAttrs = hlAttrs;
563
                currentAttrs = hlAttrs;
439
                nextChangeOffset = hlEndOffset;
564
                nextChangeOffset = hlEndOffset;
565
                nextChangeShift = hlEndShift;
440
                return false;
566
                return false;
441
            } // else: offset >= hlEndOffset
567
            } else { // Above hlEndOffset (or hlEndShift) => fetch next highlight
442
            return true;
568
                return true; // Fetch next highlight
569
            }
443
        }
570
        }
444
        
571
        
445
        /**
572
        /**
Lines 447-502 Link Here
447
         * @param offset
574
         * @param offset
448
         * @return true if highlight fetched successfully or false if there are no more highlights.
575
         * @return true if highlight fetched successfully or false if there are no more highlights.
449
         */
576
         */
450
        boolean fetchNextHighlight(int offset) {
577
        boolean fetchNextHighlight() {
451
            assert (hlStartOffset != Integer.MAX_VALUE);
578
            while (layerSequence.moveNext()) { // Loop to allow for skip over empty highlights
452
            do {
579
                hlStartOffset = layerSequence.getStartOffset();
453
                if (hlSequence.moveNext()) {
580
                if (hlStartOffset < hlEndOffset) { // Invalid layer: next highlight overlaps previous one
454
                    hlStartOffset = hlSequence.getStartOffset();
581
                    // To prevent infinite loops finish this HL
455
                    if (hlStartOffset < hlEndOffset) { // Invalid layer: next highlight overlaps previous one
582
                    if (LOG.isLoggable(Level.FINE)) {
583
                        LOG.fine("Disabled an invalid highlighting layer: hlStartOffset=" + hlStartOffset + // NOI18N
584
                            " < previous hlEndOffset=" + hlEndOffset + " for layer=" + layer); // NOI18N
585
                    }
586
                    return false;
587
                }
588
                hlEndOffset = layerSequence.getEndOffset();
589
                if (hlEndOffset <= hlStartOffset) {
590
                    if (hlEndOffset < hlStartOffset) { // Invalid highlight: end offset before start offset
456
                        // To prevent infinite loops finish this HL
591
                        // To prevent infinite loops finish this HL
457
                        if (LOG.isLoggable(Level.FINE)) {
592
                        if (LOG.isLoggable(Level.FINE)) {
458
                            LOG.fine("Disabled an invalid highlighting layer: hlStartOffset=" + hlStartOffset + // NOI18N
593
                            LOG.fine("Disabled an invalid highlighting layer: hlStartOffset=" + hlStartOffset + // NOI18N
459
                                " < previous hlEndOffset=" + hlEndOffset + " for layer=" + layer); // NOI18N
594
                                " > hlEndOffset=" + hlEndOffset + " for layer=" + layer); // NOI18N
460
                        }
595
                        }
461
                        hlStartOffset = hlEndOffset = Integer.MAX_VALUE;
462
                        return false;
596
                        return false;
463
                    }
597
                    }
464
                    hlEndOffset = hlSequence.getEndOffset();
598
                    emptyHighlightCount++;
465
                    if (hlEndOffset <= hlStartOffset) {
599
                    if (emptyHighlightCount >= MAX_EMPTY_HIGHLIGHT_COUNT) {
466
                        if (hlEndOffset < hlStartOffset) { // Invalid highlight: end offset before start offset
600
                        if (LOG.isLoggable(Level.FINE)) {
467
                            // To prevent infinite loops finish this HL
601
                            LOG.fine("Disabled an invalid highlighting layer: too many empty highlights=" + emptyHighlightCount); // NOI18N
468
                            if (LOG.isLoggable(Level.FINE)) {
469
                                LOG.fine("Disabled an invalid highlighting layer: hlStartOffset=" + hlStartOffset + // NOI18N
470
                                    " > hlEndOffset=" + hlEndOffset + " for layer=" + layer); // NOI18N
471
                            }
472
                            hlStartOffset = hlEndOffset = Integer.MAX_VALUE;
473
                            return false;
474
                        }
602
                        }
475
                        emptyHighlightCount++;
603
                        return false;
476
                        if (emptyHighlightCount >= MAX_EMPTY_HIGHLIGHT_COUNT) {
477
                            if (LOG.isLoggable(Level.FINE)) {
478
                                LOG.fine("Disabled an invalid highlighting layer: too many empty highlights=" + emptyHighlightCount); // NOI18N
479
                            }
480
                            hlStartOffset = hlEndOffset = Integer.MAX_VALUE;
481
                            return false;
482
                        }
483
                    }
604
                    }
484
                    hlAttrs = hlSequence.getAttributes();
605
                    continue; // Fetch next highlight
485
                    if (LOG.isLoggable(Level.FINER)) {
606
                }
486
                        LOG.fine("Fetched highlight: <" + hlStartOffset + // NOI18N
607
                if (hlEndOffset > endOffset) {
487
                                "," + hlEndOffset + "> for layer=" + layer + '\n'); // NOI18N
608
                    if (hlStartOffset >= endOffset) {
609
                        return false;
488
                    }
610
                    }
489
                } else {
611
                    hlEndOffset = endOffset; // Limit the highlight to endOffset - it should still remain non-empty
490
                    hlStartOffset = hlEndOffset = Integer.MAX_VALUE; // Signal that sequence is finished
491
                    return false;
492
                }
612
                }
493
            } while (hlEndOffset <= offset);
613
                if (shiftLayerSequence != null) {
494
            return true; // Valid highlight fetched
614
                    hlStartShift = shiftLayerSequence.getStartShift();
615
                    hlEndShift = shiftLayerSequence.getEndShift();
616
                    // Do not perform extra checking of validity (non-overlapping with previous highlight
617
                    //  and validity of shifts since it should not be crucial
618
                    //  for proper functioning of updateCurrentState() method.
619
                } // else hlStartShift and hlEndShift are always zero in the wrapper
620
                hlAttrs = layerSequence.getAttributes();
621
                if (LOG.isLoggable(Level.FINER)) {
622
                    LOG.fine("Fetched highlight: <" + hlStartOffset + // NOI18N
623
                            "," + hlEndOffset + "> for layer=" + layer + '\n'); // NOI18N
624
                }
625
                return true; // Valid highlight fetched
626
            }
627
            return false; // No more highlights
495
        }
628
        }
496
629
497
        @Override
630
        @Override
498
        public String toString() {
631
        public String toString() {
499
            return  "M[" + mNextChangeOffset + ",A=" + mAttrs + // NOI18N
632
            return  "M[" + mergedNextChangeOffset + ",A=" + mAttrs + // NOI18N
500
                    "]  Next[" + nextChangeOffset + ",A=" + currentAttrs + // NOI18N
633
                    "]  Next[" + nextChangeOffset + ",A=" + currentAttrs + // NOI18N
501
                    "]  HL:<" + hlStartOffset + "," + hlEndOffset + ">,A=" + hlAttrs; // NOI18N
634
                    "]  HL:<" + hlStartOffset + "," + hlEndOffset + ">,A=" + hlAttrs; // NOI18N
502
        }
635
        }
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/Factory.java (+7 lines)
Lines 114-119 Link Here
114
            true, // fixed size
114
            true, // fixed size
115
            new WhitespaceHighlighting(context.getComponent()))
115
            new WhitespaceHighlighting(context.getComponent()))
116
        );
116
        );
117
        
118
        layers.add(HighlightsLayer.create(
119
            CaretOverwriteModeHighlighting.LAYER_TYPE_ID,
120
            ZOrder.TOP_RACK.forPosition(100),
121
            true, // fixed size
122
            new CaretOverwriteModeHighlighting(context.getComponent()))
123
        );
117
124
118
        return layers.toArray(new HighlightsLayer [layers.size()]);
125
        return layers.toArray(new HighlightsLayer [layers.size()]);
119
    }
126
    }
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightingManager.java (-2 / +2 lines)
Lines 81-90 Link Here
81
81
82
/**
82
/**
83
 * Highlighting manager maintains all the highlighting layers instances.
83
 * Highlighting manager maintains all the highlighting layers instances.
84
 * <br/>
84
 * <br>
85
 * It divides them into two groups according to their z-order and fixedSize attributes.
85
 * It divides them into two groups according to their z-order and fixedSize attributes.
86
 * Top layers that have fixedSize set to true are one group. The rest is the other group.
86
 * Top layers that have fixedSize set to true are one group. The rest is the other group.
87
 * <br/>
87
 * <br>
88
 * View hierarchy only rebuilds views if the second group of layers (bottom ones) changes.
88
 * View hierarchy only rebuilds views if the second group of layers (bottom ones) changes.
89
 * If the top group changes the view hierarchy only triggers repaint of affected part.
89
 * If the top group changes the view hierarchy only triggers repaint of affected part.
90
 *
90
 *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java (-3 / +3 lines)
Lines 56-62 Link Here
56
    
56
    
57
    /**
57
    /**
58
     * List of highlight items. First highlights item starts at startOffset.
58
     * List of highlight items. First highlights item starts at startOffset.
59
     * <br/>
59
     * <br>
60
     * GapList is used due to its copyElements() method.
60
     * GapList is used due to its copyElements() method.
61
     */
61
     */
62
    private HighlightItem[] highlightItems;
62
    private HighlightItem[] highlightItems;
Lines 128-134 Link Here
128
     * Create attribute set covering {@link #startOffset()} till maxEndOffset
128
     * Create attribute set covering {@link #startOffset()} till maxEndOffset
129
     * or lower offset if font would differ for the particular attribute set
129
     * or lower offset if font would differ for the particular attribute set
130
     * of an item.
130
     * of an item.
131
     * <br/>
131
     * <br>
132
     * The list must cover cutEndOffset otherwise the behavior is undefined.
132
     * The list must cover cutEndOffset otherwise the behavior is undefined.
133
     *
133
     *
134
     * @param defaultFont default font to which the attributes in highlight items
134
     * @param defaultFont default font to which the attributes in highlight items
Lines 251-257 Link Here
251
    
251
    
252
    /**
252
    /**
253
     * Create attribute set covering single character at {@link #startOffset()}.
253
     * Create attribute set covering single character at {@link #startOffset()}.
254
     * <br/>
254
     * <br>
255
     * The list must cover {@link #startOffset()} otherwise the behavior is undefined.
255
     * The list must cover {@link #startOffset()} otherwise the behavior is undefined.
256
     *
256
     *
257
     * @return attribute set.
257
     * @return attribute set.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/ReadOnlyFilesHighlighting.java (-1 / +41 lines)
Lines 114-120 Link Here
114
                if (LOG.isLoggable(Level.FINE)) {
114
                if (LOG.isLoggable(Level.FINE)) {
115
                    LOG.fine("Highlighting file " + file + " in <" + startOffset + ", " + endOffset + ">"); //NOI18N
115
                    LOG.fine("Highlighting file " + file + " in <" + startOffset + ", " + endOffset + ">"); //NOI18N
116
                }
116
                }
117
                return new CaretBasedBlockHighlighting.SimpleHighlightsSequence(
117
                return new SimpleHighlightsSequence(
118
                    Math.max(0, startOffset),
118
                    Math.max(0, startOffset),
119
                    Math.min(document.getLength(), endOffset),
119
                    Math.min(document.getLength(), endOffset),
120
                    attribs);
120
                    attribs);
Lines 215-218 Link Here
215
        }
215
        }
216
        return null;
216
        return null;
217
    }
217
    }
218
    
219
    private static final class SimpleHighlightsSequence implements HighlightsSequence {
220
        
221
        private int startOffset;
222
        private int endOffset;
223
        private AttributeSet attribs;
224
        
225
        private boolean end = false;
226
        
227
        public SimpleHighlightsSequence(int startOffset, int endOffset, AttributeSet attribs) {
228
            this.startOffset = startOffset;
229
            this.endOffset = endOffset;
230
            this.attribs = attribs;
231
        }
232
233
        @Override
234
        public boolean moveNext() {
235
            if (!end) {
236
                end = true;
237
                return true;
238
            } else {
239
                return false;
240
            }
241
        }
242
243
        @Override
244
        public int getStartOffset() {
245
            return startOffset;
246
        }
247
248
        @Override
249
        public int getEndOffset() {
250
            return endOffset;
251
        }
252
253
        @Override
254
        public AttributeSet getAttributes() {
255
            return attribs;
256
        }
257
    } // End of SimpleHighlightsSequence
218
}
258
}
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/SyntaxHighlighting.java (-1 / +1 lines)
Lines 79-85 Link Here
79
79
80
/**
80
/**
81
 * The syntax coloring layer.
81
 * The syntax coloring layer.
82
 * <br/>
82
 * <br>
83
 * It excludes newline chars from any colorings so that if e.g. a whitespace highlighting is set
83
 * It excludes newline chars from any colorings so that if e.g. a whitespace highlighting is set
84
 * the rest of line after newline is not colored.
84
 * the rest of line after newline is not colored.
85
 * 
85
 * 
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/VisualMark.java (-2 / +2 lines)
Lines 44-50 Link Here
44
/**
44
/**
45
 * Visual mark encapsulates y-coordinate offset together with an object
45
 * Visual mark encapsulates y-coordinate offset together with an object
46
 * that provides an offset (assumed that it's tracked as a SWing position).
46
 * that provides an offset (assumed that it's tracked as a SWing position).
47
 * <br/>
47
 * <br>
48
 * The y-coordinate is tracked as a raw value that must first be preprocessed
48
 * The y-coordinate is tracked as a raw value that must first be preprocessed
49
 * by {@link VisualMarkVector}.
49
 * by {@link VisualMarkVector}.
50
 *
50
 *
Lines 62-68 Link Here
62
    
62
    
63
    /**
63
    /**
64
     * Get offset of this visual mark.
64
     * Get offset of this visual mark.
65
     * <br/>
65
     * <br>
66
     * It's assumed that the offset is tracked as a Swing position.
66
     * It's assumed that the offset is tracked as a Swing position.
67
     *
67
     *
68
     * @return &gt;=0 offset of this mark.
68
     * @return &gt;=0 offset of this mark.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/WhitespaceHighlighting.java (-1 / +1 lines)
Lines 60-66 Link Here
60
60
61
/**
61
/**
62
 * Highlighting of indentation and trailing whitespace.
62
 * Highlighting of indentation and trailing whitespace.
63
 * <br/>
63
 * <br>
64
 * If there's no non-WS text on the line the whitespace is treated as trailing.
64
 * If there's no non-WS text on the line the whitespace is treated as trailing.
65
 *
65
 *
66
 * @author Miloslav Metelka
66
 * @author Miloslav Metelka
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/search/TextStorageSet.java (-1 / +1 lines)
Lines 47-53 Link Here
47
47
48
/**
48
/**
49
 * Set of character sequences allowing to compare a part of character sequence.
49
 * Set of character sequences allowing to compare a part of character sequence.
50
 * <br/>
50
 * <br>
51
 * Note: Character sequences stored must be comparable by their equals() method.
51
 * Note: Character sequences stored must be comparable by their equals() method.
52
 *
52
 *
53
 * @author Miloslav Metelka
53
 * @author Miloslav Metelka
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/AttributedCharSequence.java (-2 / +2 lines)
Lines 49-58 Link Here
49
49
50
/**
50
/**
51
 * Char sequence with additional attributes.
51
 * Char sequence with additional attributes.
52
 * <br/>
52
 * <br>
53
 * Method operate with javax.swing.text.AttributeSet which they transfer
53
 * Method operate with javax.swing.text.AttributeSet which they transfer
54
 * to corresponding TextAttribute constants.
54
 * to corresponding TextAttribute constants.
55
 * <br/>
55
 * <br>
56
 * After creation the series of addTextRun() is called followed by setText()
56
 * After creation the series of addTextRun() is called followed by setText()
57
 * which completes modification and makes the object ready for clients.
57
 * which completes modification and makes the object ready for clients.
58
 *
58
 *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentView.java (-4 / +4 lines)
Lines 72-78 Link Here
72
72
73
/**
73
/**
74
 * View representing the whole document.
74
 * View representing the whole document.
75
 * <br/>
75
 * <br>
76
 * It consists of individual paragraph views that typically map to line elements
76
 * It consists of individual paragraph views that typically map to line elements
77
 * but e.g. code folding may cause multiple line elements correspond to one line view.
77
 * but e.g. code folding may cause multiple line elements correspond to one line view.
78
 * 
78
 * 
Lines 165-171 Link Here
165
    
165
    
166
    /**
166
    /**
167
     * Start offset of the document view after last processed modification.
167
     * Start offset of the document view after last processed modification.
168
     * <br/>
168
     * <br>
169
     * This is used during updating of the document view by document modification.
169
     * This is used during updating of the document view by document modification.
170
     * For undo of removal the position of first paragraph may restore inside the inserted area
170
     * For undo of removal the position of first paragraph may restore inside the inserted area
171
     * and holding integer offset allows to know the original start offset.
171
     * and holding integer offset allows to know the original start offset.
Lines 174-180 Link Here
174
174
175
    /**
175
    /**
176
     * Start offset of the document view.
176
     * Start offset of the document view.
177
     * <br/>
177
     * <br>
178
     * This is used during updating of the document view by document modification.
178
     * This is used during updating of the document view by document modification.
179
     * For undo of removal the position of last paragraph may restore inside the inserted area
179
     * For undo of removal the position of last paragraph may restore inside the inserted area
180
     * and holding integer offset allows to know the original end offset of the document view.
180
     * and holding integer offset allows to know the original end offset of the document view.
Lines 472-478 Link Here
472
    /**
472
    /**
473
     * Get current allocation of the document view by using size from last call
473
     * Get current allocation of the document view by using size from last call
474
     * to {@link #setSize(float, float)}.
474
     * to {@link #setSize(float, float)}.
475
     * <br/>
475
     * <br>
476
     * Returned instance may not be mutated (use getAllocationMutable()).
476
     * Returned instance may not be mutated (use getAllocationMutable()).
477
     *
477
     *
478
     * @return current allocation of document view.
478
     * @return current allocation of document view.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewChildren.java (-4 / +4 lines)
Lines 63-73 Link Here
63
63
64
/**
64
/**
65
 * Class that manages children of {@link EditorBoxView}.
65
 * Class that manages children of {@link EditorBoxView}.
66
 * <br/>
66
 * <br>
67
 * The class can manage offsets of children in case {@link #rawEndOffsetManaged()}
67
 * The class can manage offsets of children in case {@link #rawEndOffsetManaged()}
68
 * returns true. In such case each child must properly implement {@link EditorView#getRawEndOffset()}
68
 * returns true. In such case each child must properly implement {@link EditorView#getRawEndOffset()}
69
 * and the maintained raw offsets are relative to corresponding box view's getStartOffset().
69
 * and the maintained raw offsets are relative to corresponding box view's getStartOffset().
70
 * <br/>
70
 * <br>
71
 * Generally children of {@link #ParagraphView} manage their raw end offsets
71
 * Generally children of {@link #ParagraphView} manage their raw end offsets
72
 * while children of {@link #DocumentView} do not manage them (they use Position objects
72
 * while children of {@link #DocumentView} do not manage them (they use Position objects
73
 * to manage its start).
73
 * to manage its start).
Lines 125-131 Link Here
125
    
125
    
126
    /**
126
    /**
127
     * Replace paragraph views inside DocumentView.
127
     * Replace paragraph views inside DocumentView.
128
     * <br/>
128
     * <br>
129
     * In case both removeCount == 0 and addedViews is empty this method does not need to be called.
129
     * In case both removeCount == 0 and addedViews is empty this method does not need to be called.
130
     *
130
     *
131
     * @param docView
131
     * @param docView
Lines 242-248 Link Here
242
    /**
242
    /**
243
     * Get view index of first view that "contains" the offset (starts with it or it's inside)
243
     * Get view index of first view that "contains" the offset (starts with it or it's inside)
244
     * by examining child views' absolute start offsets.
244
     * by examining child views' absolute start offsets.
245
     * <br/>
245
     * <br>
246
     * This is suitable for document view where start offsets of paragraph views
246
     * This is suitable for document view where start offsets of paragraph views
247
     * are maintained as positions.
247
     * are maintained as positions.
248
     * 
248
     * 
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java (-3 / +3 lines)
Lines 197-203 Link Here
197
    
197
    
198
    /**
198
    /**
199
     * Whether the "children" is currently reflecting the document state.
199
     * Whether the "children" is currently reflecting the document state.
200
     * <br/>
200
     * <br>
201
     * The children may hold a semi-valid view hierarchy which may still be partly used
201
     * The children may hold a semi-valid view hierarchy which may still be partly used
202
     * to resolve queries in some cases.
202
     * to resolve queries in some cases.
203
     */
203
     */
Lines 322-328 Link Here
322
     * Constant retrieved from preferences settings that allows the user to increase/decrease
322
     * Constant retrieved from preferences settings that allows the user to increase/decrease
323
     * paragraph view's height (which may help for problematic fonts that do not report its height
323
     * paragraph view's height (which may help for problematic fonts that do not report its height
324
     * correctly for them to fit the line).
324
     * correctly for them to fit the line).
325
     * <br/>
325
     * <br>
326
     * By default it's 1.0. All the ascents, descent and leadings of all fonts
326
     * By default it's 1.0. All the ascents, descent and leadings of all fonts
327
     * are multiplied by the constant.
327
     * are multiplied by the constant.
328
     */
328
     */
Lines 1221-1227 Link Here
1221
    /**
1221
    /**
1222
     * Get displayed portion of the component (either viewport.getViewRect())
1222
     * Get displayed portion of the component (either viewport.getViewRect())
1223
     * or (if viewport is missing) size of the component.
1223
     * or (if viewport is missing) size of the component.
1224
     * <br/>
1224
     * <br>
1225
     * Note: The value may be obsolete during paint - clipping bounds may already
1225
     * Note: The value may be obsolete during paint - clipping bounds may already
1226
     *  incorporate a just performed scroll while visibleRect does not yet.
1226
     *  incorporate a just performed scroll while visibleRect does not yet.
1227
     *
1227
     *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/EditorTabExpander.java (-1 / +1 lines)
Lines 51-57 Link Here
51
51
52
/**
52
/**
53
 * Tab expander of the editor.
53
 * Tab expander of the editor.
54
 * <br/>
54
 * <br>
55
 * It aligns visually (not by number of characters) since it's a desired behavior
55
 * It aligns visually (not by number of characters) since it's a desired behavior
56
 * for asian languages characters that visually occupy two regular characters and they should
56
 * for asian languages characters that visually occupy two regular characters and they should
57
 * be treated in that way in terms of tab aligning.
57
 * be treated in that way in terms of tab aligning.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/EditorView.java (-4 / +4 lines)
Lines 60-75 Link Here
60
60
61
/**
61
/**
62
 * Base class for views in editor view hierarchy.
62
 * Base class for views in editor view hierarchy.
63
 * <br/>
63
 * <br>
64
 * In general there are three types of views:<ul>
64
 * In general there are three types of views:<ul>
65
 * <li>Document view</li>
65
 * <li>Document view</li>
66
 * <li>Paragraph views</li>
66
 * <li>Paragraph views</li>
67
 * <li>Children of paragraph views which include highlights view, newline view and others.</li>
67
 * <li>Children of paragraph views which include highlights view, newline view and others.</li>
68
 * </ul>
68
 * </ul>
69
 * <br/>
69
 * <br>
70
 * Paragraph views have their start offset based over a swing text position. Their end offset
70
 * Paragraph views have their start offset based over a swing text position. Their end offset
71
 * is based on last child's end offset.
71
 * is based on last child's end offset.
72
 * <br/>
72
 * <br>
73
 * Children of paragraph views have their start offset based over a relative distance
73
 * Children of paragraph views have their start offset based over a relative distance
74
 * to their parent's paragraph view's start offset. Therefore their start offset does not mutate
74
 * to their parent's paragraph view's start offset. Therefore their start offset does not mutate
75
 * upon modification unless the whole paragraph's start offset mutates.
75
 * upon modification unless the whole paragraph's start offset mutates.
Lines 97-103 Link Here
97
    /**
97
    /**
98
     * Get raw end offset of the view which may transform to real end offset
98
     * Get raw end offset of the view which may transform to real end offset
99
     * when post-processed by parent view.
99
     * when post-processed by parent view.
100
     * <br/>
100
     * <br>
101
     * <b>Note:</b> Typical clients should NOT call this method (they should call
101
     * <b>Note:</b> Typical clients should NOT call this method (they should call
102
     * {@link #getEndOffset()} method instead).
102
     * {@link #getEndOffset()} method instead).
103
     *
103
     *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/EditorViewFactory.java (-13 / +13 lines)
Lines 55-68 Link Here
55
55
56
/**
56
/**
57
 * SPI class allowing to produce views.
57
 * SPI class allowing to produce views.
58
 * <br/>
58
 * <br>
59
 * There are two main factories: for folds and for highlights (the default one).
59
 * There are two main factories: for folds and for highlights (the default one).
60
 * The factories have a priority and factory with highest priority
60
 * The factories have a priority and factory with highest priority
61
 * will always "win" in terms that its view will surely be created as desired.
61
 * will always "win" in terms that its view will surely be created as desired.
62
 * Factories at lower levels will receive a limitOffset into createView()
62
 * Factories at lower levels will receive a limitOffset into createView()
63
 * being a start offset of the view produced by a higher level factory.
63
 * being a start offset of the view produced by a higher level factory.
64
 * The factory may decide whether it will create view with limiting offset or not.
64
 * The factory may decide whether it will create view with limiting offset or not.
65
 * <br/>
65
 * <br>
66
 * Factory generally operates in two modes:<ul>
66
 * Factory generally operates in two modes:<ul>
67
 * <li>Regular mode when the factory produces views</li>
67
 * <li>Regular mode when the factory produces views</li>
68
 * <li>Offset mode when the factory only returns bounds of the produced views
68
 * <li>Offset mode when the factory only returns bounds of the produced views
Lines 152-158 Link Here
152
    /**
152
    /**
153
     * Notify next offset area where views will be created in case endCreationOffset
153
     * Notify next offset area where views will be created in case endCreationOffset
154
     * in {@link #restart(int, int)} was exceeded.
154
     * in {@link #restart(int, int)} was exceeded.
155
     * <br/>
155
     * <br>
156
     * This method may be called multiple times if views building still does not match
156
     * This method may be called multiple times if views building still does not match
157
     * original views boundaries.
157
     * original views boundaries.
158
     *
158
     *
Lines 165-171 Link Here
165
165
166
    /**
166
    /**
167
     * Return starting offset of the next view to be produced by this view factory.
167
     * Return starting offset of the next view to be produced by this view factory.
168
     * <br/>
168
     * <br>
169
     * This method gets called after restarting of this view factory
169
     * This method gets called after restarting of this view factory
170
     * (with a <code>startOffset</code> parameter passed to {@link #restart(int)})
170
     * (with a <code>startOffset</code> parameter passed to {@link #restart(int)})
171
     * and also after any of the registered view factories created a view
171
     * and also after any of the registered view factories created a view
Lines 181-187 Link Here
181
     * Create a view at the given offset. The view factory must determine
181
     * Create a view at the given offset. The view factory must determine
182
     * the appropriate end offset of the produced view and set its length
182
     * the appropriate end offset of the produced view and set its length
183
     * returned by {@link EditorView#getLength()} appropriately.
183
     * returned by {@link EditorView#getLength()} appropriately.
184
     * <br/>
184
     * <br>
185
     * This method is only called if the factory is in view-producing mode
185
     * This method is only called if the factory is in view-producing mode
186
     * (its {@link #viewEndOffset(startOffset, limitOffset)} is not called).
186
     * (its {@link #viewEndOffset(startOffset, limitOffset)} is not called).
187
     *
187
     *
Lines 197-206 Link Here
197
     * @param origView original view located at the given position (it may have a different
197
     * @param origView original view located at the given position (it may have a different
198
     *  physical offset due to just performed modification but it corresponds to the same text
198
     *  physical offset due to just performed modification but it corresponds to the same text
199
     *  in the document). It may be null if there is no view to reuse.
199
     *  in the document). It may be null if there is no view to reuse.
200
     *  <br/>
200
     *  <br>
201
     *  The factory may not return the given instance but it may reuse an arbitrary information
201
     *  The factory may not return the given instance but it may reuse an arbitrary information
202
     *  from it.
202
     *  from it.
203
     *  <br/>
203
     *  <br>
204
     *  For example for text layout reuse the highlights view factory will first check if the view
204
     *  For example for text layout reuse the highlights view factory will first check if the view
205
     *  is non-null and matches views produced by it then it will check
205
     *  is non-null and matches views produced by it then it will check
206
     *  if the new view has same length as the original one and that the view attributes
206
     *  if the new view has same length as the original one and that the view attributes
Lines 222-228 Link Here
222
222
223
    /**
223
    /**
224
     * Return to-be-created view's end offset.
224
     * Return to-be-created view's end offset.
225
     * <br/>
225
     * <br>
226
     * This method is only called when createViews parameter
226
     * This method is only called when createViews parameter
227
     * in {@link #restart(int, int, boolean)} is false. In such mode no physical views
227
     * in {@link #restart(int, int, boolean)} is false. In such mode no physical views
228
     * are created and only view boundaries of potential views are being determined.
228
     * are created and only view boundaries of potential views are being determined.
Lines 240-246 Link Here
240
240
241
    /**
241
    /**
242
     * Finish this round of views creation.
242
     * Finish this round of views creation.
243
     * <br/>
243
     * <br>
244
     * {@link #restart(int) } may be called subsequently to init a new round
244
     * {@link #restart(int) } may be called subsequently to init a new round
245
     * of views creation.
245
     * of views creation.
246
     */
246
     */
Lines 263-269 Link Here
263
263
264
    /**
264
    /**
265
     * Schedule repaint request on the view hierarchy.
265
     * Schedule repaint request on the view hierarchy.
266
     * <br/>
266
     * <br>
267
     * Document must be read-locked prior calling this method.
267
     * Document must be read-locked prior calling this method.
268
     *
268
     *
269
     * @param startOffset
269
     * @param startOffset
Lines 277-288 Link Here
277
     *  Signal that this view factory is no longer able to produce
277
     *  Signal that this view factory is no longer able to produce
278
     *  valid views due to some serious changes that it processes
278
     *  valid views due to some serious changes that it processes
279
     *  (for example highlights change for HighlightsViewFactory).
279
     *  (for example highlights change for HighlightsViewFactory).
280
     *  <br/>
280
     *  <br>
281
     *  View creation may be stopped immediately by the caller and restarted to get
281
     *  View creation may be stopped immediately by the caller and restarted to get
282
     *  the correct views. However if it would fail periodically the caller may decide
282
     *  the correct views. However if it would fail periodically the caller may decide
283
     *  to continue the creation to have at least some views. In both cases
283
     *  to continue the creation to have at least some views. In both cases
284
     *  the view factory should be able to continue working normally.
284
     *  the view factory should be able to continue working normally.
285
     *  <br/>
285
     *  <br>
286
     *  This method can be called from any thread.
286
     *  This method can be called from any thread.
287
     */
287
     */
288
    protected final void notifyStaleCreation() {
288
    protected final void notifyStaleCreation() {
Lines 303-309 Link Here
303
    /**
303
    /**
304
     * Notification that this factory is no longer being used so it should
304
     * Notification that this factory is no longer being used so it should
305
     * release its resources - for example detach all listeners.
305
     * release its resources - for example detach all listeners.
306
     * <br/>
306
     * <br>
307
     * It's called upon document view receives setParent(null) which typically signals
307
     * It's called upon document view receives setParent(null) which typically signals
308
     * that a new document view will be created for the particular editor pane.
308
     * that a new document view will be created for the particular editor pane.
309
     */
309
     */
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/EditorViewFactoryChange.java (-1 / +1 lines)
Lines 43-49 Link Here
43
43
44
/**
44
/**
45
 * Offset range describing change in particular view factory.
45
 * Offset range describing change in particular view factory.
46
 * <br/>
46
 * <br>
47
 * Each factory may fire a list of changes of different types at once.
47
 * Each factory may fire a list of changes of different types at once.
48
 * 
48
 * 
49
 * @author Miloslav Metelka
49
 * @author Miloslav Metelka
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsView.java (-1 / +1 lines)
Lines 60-66 Link Here
60
60
61
/**
61
/**
62
 * View with highlights. This is the most used view.
62
 * View with highlights. This is the most used view.
63
 * <br/>
63
 * <br>
64
 * It can either have no highlights (attributes == null) or simple attributes
64
 * It can either have no highlights (attributes == null) or simple attributes
65
 * (attributes == non-
65
 * (attributes == non-
66
 *
66
 *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java (-2 / +2 lines)
Lines 67-73 Link Here
67
/**
67
/**
68
 * View factory returning highlights views. It is specific in that it always
68
 * View factory returning highlights views. It is specific in that it always
69
 * covers the whole document area by views even if there are no particular highlights
69
 * covers the whole document area by views even if there are no particular highlights
70
 * <br/>
70
 * <br>
71
 * Currently the factory coalesces highlights change requests from non-AWT thread.
71
 * Currently the factory coalesces highlights change requests from non-AWT thread.
72
 *
72
 *
73
 * @author Miloslav Metelka
73
 * @author Miloslav Metelka
Lines 94-100 Link Here
94
     * then the infrastructure will attempt to end current long view creation
94
     * then the infrastructure will attempt to end current long view creation
95
     * at a given nextOrigViewOffset parameter in order to save views creation and reuse
95
     * at a given nextOrigViewOffset parameter in order to save views creation and reuse
96
     * existing text layouts (and their slit text layouts for line wrapping).
96
     * existing text layouts (and their slit text layouts for line wrapping).
97
     * <br/>
97
     * <br>
98
     * The user would have to insert or remove LONG_VIEW_TOLERANCE of characters into long view
98
     * The user would have to insert or remove LONG_VIEW_TOLERANCE of characters into long view
99
     * in order to force the factory to not match to the given nextOrigViewOffset.
99
     * in order to force the factory to not match to the given nextOrigViewOffset.
100
     */
100
     */
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewUtils.java (-3 / +14 lines)
Lines 66-71 Link Here
66
import javax.swing.text.Position.Bias;
66
import javax.swing.text.Position.Bias;
67
import javax.swing.text.StyleConstants;
67
import javax.swing.text.StyleConstants;
68
import javax.swing.text.View;
68
import javax.swing.text.View;
69
import org.netbeans.api.editor.caret.CaretInfo;
70
import org.netbeans.api.editor.caret.EditorCaret;
69
import org.netbeans.api.editor.settings.EditorStyleConstants;
71
import org.netbeans.api.editor.settings.EditorStyleConstants;
70
import org.netbeans.lib.editor.util.CharSequenceUtilities;
72
import org.netbeans.lib.editor.util.CharSequenceUtilities;
71
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
73
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
Lines 73-79 Link Here
73
75
74
/**
76
/**
75
 * Utilities related to HighlightsView and TextLayout management.
77
 * Utilities related to HighlightsView and TextLayout management.
76
 * <br/>
78
 * <br>
77
 * Unfortunately the TextLayout based on AttributedCharacterIterator does not handle
79
 * Unfortunately the TextLayout based on AttributedCharacterIterator does not handle
78
 * correctly italic fonts (at least on Mac it renders background rectangle non-italicized).
80
 * correctly italic fonts (at least on Mac it renders background rectangle non-italicized).
79
 * Therefore child views with foreground that differs from text layout's "global" foreground
81
 * Therefore child views with foreground that differs from text layout's "global" foreground
Lines 159-165 Link Here
159
            return 0d;
161
            return 0d;
160
        }
162
        }
161
        Caret caret = textComponent.getCaret();
163
        Caret caret = textComponent.getCaret();
162
        Point magicCaretPoint = (caret != null) ? caret.getMagicCaretPosition() : null;
164
        Point magicCaretPoint = null;
165
        if(caret != null) {
166
            if(caret instanceof EditorCaret) {
167
                EditorCaret editorCaret = (EditorCaret) caret;
168
                CaretInfo info = editorCaret.getCaretAt(offset);
169
                magicCaretPoint = (info != null) ? info.getMagicCaretPosition() : null;
170
            } else {
171
                magicCaretPoint = caret.getMagicCaretPosition();
172
            }
173
        }
163
        double x;
174
        double x;
164
        if (magicCaretPoint == null) {
175
        if (magicCaretPoint == null) {
165
            Shape offsetBounds = view.modelToViewChecked(offset, alloc, bias);
176
            Shape offsetBounds = view.modelToViewChecked(offset, alloc, bias);
Lines 528-534 Link Here
528
    /**
539
    /**
529
     * Paint strike-through line for a font currently set to the graphics
540
     * Paint strike-through line for a font currently set to the graphics
530
     * with the color currently set to the graphics.
541
     * with the color currently set to the graphics.
531
     * <br/>
542
     * <br>
532
     * It's assumed that the clipping is set appropriately because the method
543
     * It's assumed that the clipping is set appropriately because the method
533
     * renders whole textLayoutAlloc with the strike-through.
544
     * renders whole textLayoutAlloc with the strike-through.
534
     *
545
     *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/LockedViewHierarchy.java (-10 / +10 lines)
Lines 44-53 Link Here
44
44
45
/**
45
/**
46
 * Locked view hierarchy as result of {@link ViewHierarchy#lock() }.
46
 * Locked view hierarchy as result of {@link ViewHierarchy#lock() }.
47
 * <br/>
47
 * <br>
48
 * Underlying document of the view hierarchy's text component must be
48
 * Underlying document of the view hierarchy's text component must be
49
 * read-locked to guarantee stability of offsets passed to methods of this class.
49
 * read-locked to guarantee stability of offsets passed to methods of this class.
50
 * <br/>
50
 * <br>
51
 * If editor view hierarchy is not installed into text component
51
 * If editor view hierarchy is not installed into text component
52
 * (text component's root view is not an instance of DocumentView)
52
 * (text component's root view is not an instance of DocumentView)
53
 * the methods return default values as described in their documentation.
53
 * the methods return default values as described in their documentation.
Lines 82-88 Link Here
82
82
83
    /**
83
    /**
84
     * Get text component that this view hierarchy is associated with.
84
     * Get text component that this view hierarchy is associated with.
85
     * <br/>
85
     * <br>
86
     * 
86
     * 
87
     * @return non-null text component.
87
     * @return non-null text component.
88
     */
88
     */
Lines 93-102 Link Here
93
    
93
    
94
    /**
94
    /**
95
     * Get y coordinate of a visual row that corresponds to given offset.
95
     * Get y coordinate of a visual row that corresponds to given offset.
96
     * <br/>
96
     * <br>
97
     * Underlying document of the view hierarchy's text component should be read-locked
97
     * Underlying document of the view hierarchy's text component should be read-locked
98
     * to guarantee stability of passed offset.
98
     * to guarantee stability of passed offset.
99
     * <br/>
99
     * <br>
100
     * If editor view hierarchy is not installed into text component this method
100
     * If editor view hierarchy is not installed into text component this method
101
     * delegates to {@link JTextComponent#modelToView(int) }.
101
     * delegates to {@link JTextComponent#modelToView(int) }.
102
     *
102
     *
Lines 197-205 Link Here
197
197
198
    /**
198
    /**
199
     * Get height of a visual row of text.
199
     * Get height of a visual row of text.
200
     * <br/>
200
     * <br>
201
     * For wrapped lines (containing multiple visual rows) this is height of a single visual row.
201
     * For wrapped lines (containing multiple visual rows) this is height of a single visual row.
202
     * <br/>
202
     * <br>
203
     * Current editor view hierarchy implementation uses uniform row height for all the rows.
203
     * Current editor view hierarchy implementation uses uniform row height for all the rows.
204
     * 
204
     * 
205
     * @return height of a visual row.
205
     * @return height of a visual row.
Lines 211-217 Link Here
211
    
211
    
212
    /**
212
    /**
213
     * Get width of a typical character of a default font used by view hierarchy.
213
     * Get width of a typical character of a default font used by view hierarchy.
214
     * <br/>
214
     * <br>
215
     * In case mixed fonts (non-monospaced) are used this gives a little value
215
     * In case mixed fonts (non-monospaced) are used this gives a little value
216
     * but certain tools such as rectangular selection may use this value.
216
     * but certain tools such as rectangular selection may use this value.
217
     */
217
     */
Lines 222-231 Link Here
222
    
222
    
223
    /**
223
    /**
224
     * Return true if the view hierarchy is actively managing its contained views.
224
     * Return true if the view hierarchy is actively managing its contained views.
225
     * <br/>
225
     * <br>
226
     * Infrastructure may turn the view hierarchy inactive in case there are many
226
     * Infrastructure may turn the view hierarchy inactive in case there are many
227
     * edits performed in the document (mainly during code reformatting).
227
     * edits performed in the document (mainly during code reformatting).
228
     * <br/>
228
     * <br>
229
     * Also the view hierarchy is not active when a document modification was performed
229
     * Also the view hierarchy is not active when a document modification was performed
230
     * but the view hierarchy did not update itself accordingly yet (its DocumentListener
230
     * but the view hierarchy did not update itself accordingly yet (its DocumentListener
231
     * was not called yet).
231
     * was not called yet).
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/OffsetRegion.java (-2 / +2 lines)
Lines 127-133 Link Here
127
127
128
    /**
128
    /**
129
     * Return union of this region with the given region.
129
     * Return union of this region with the given region.
130
     * <br/>
130
     * <br>
131
     * If the given region is empty then return "this" region.
131
     * If the given region is empty then return "this" region.
132
     * 
132
     * 
133
     * @param doc non-null document to which the offsets relate.
133
     * @param doc non-null document to which the offsets relate.
Lines 164-170 Link Here
164
    
164
    
165
    /**
165
    /**
166
     * Return union of this region with the given region.
166
     * Return union of this region with the given region.
167
     * <br/>
167
     * <br>
168
     * If the given region is empty then return "this" region.
168
     * If the given region is empty then return "this" region.
169
     * 
169
     * 
170
     * @param region region to union with.
170
     * @param region region to union with.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphView.java (-8 / +8 lines)
Lines 66-74 Link Here
66
/**
66
/**
67
 * View of a line typically spans a single textual line of a corresponding document
67
 * View of a line typically spans a single textual line of a corresponding document
68
 * but it may span several lines if it contains a fold view for collapsed code fold.
68
 * but it may span several lines if it contains a fold view for collapsed code fold.
69
 * <br/>
69
 * <br>
70
 * It is capable to do a word-wrapping.
70
 * It is capable to do a word-wrapping.
71
 * <br/>
71
 * <br>
72
 * It is not tight to any element (its element is null).
72
 * It is not tight to any element (its element is null).
73
 * Its contained views may span multiple lines (e.g. in case of code folding).
73
 * Its contained views may span multiple lines (e.g. in case of code folding).
74
 * 
74
 * 
Lines 82-96 Link Here
82
82
83
    /**
83
    /**
84
     * Whether children are non-null and contain up-to-date views.
84
     * Whether children are non-null and contain up-to-date views.
85
     * <br/>
85
     * <br>
86
     * If false then either children == null (children not computed yet)
86
     * If false then either children == null (children not computed yet)
87
     * or some (or all) child views are marked as invalid (they should be recomputed
87
     * or some (or all) child views are marked as invalid (they should be recomputed
88
     * since some highlight factories reported particular text span as changed).
88
     * since some highlight factories reported particular text span as changed).
89
     * <br/>
89
     * <br>
90
     * The local offset range of invalid children can be obtained (if children != null)
90
     * The local offset range of invalid children can be obtained (if children != null)
91
     * by children.getStartInvalidChildrenLocalOffset()
91
     * by children.getStartInvalidChildrenLocalOffset()
92
     * and children.getEndInvalidChildrenLocalOffset()).
92
     * and children.getEndInvalidChildrenLocalOffset()).
93
     * <br/>
93
     * <br>
94
     * When all children are dropped (children == null) then LAYOUT_VALID is cleared too.
94
     * When all children are dropped (children == null) then LAYOUT_VALID is cleared too.
95
     */
95
     */
96
    private static final int CHILDREN_VALID = 1;
96
    private static final int CHILDREN_VALID = 1;
Lines 98-108 Link Here
98
    /**
98
    /**
99
     * Whether layout information is valid for this paragraph view
99
     * Whether layout information is valid for this paragraph view
100
     * (note that layout may be valid even when some children were marked as invalid).
100
     * (note that layout may be valid even when some children were marked as invalid).
101
     * <br/>
101
     * <br>
102
     * Since span of child views is initialized upon views replace
102
     * Since span of child views is initialized upon views replace
103
     * the layout updating means checking whether the pView is too wide
103
     * the layout updating means checking whether the pView is too wide
104
     * and thus needs to compute wrap lines and building of those wrap lines.
104
     * and thus needs to compute wrap lines and building of those wrap lines.
105
     * <br/>
105
     * <br>
106
     * Whether particular operation (mainly model-to-view, view-to-model and painting operations)
106
     * Whether particular operation (mainly model-to-view, view-to-model and painting operations)
107
     * needs an up-to-date layout is upon decision of each operation
107
     * needs an up-to-date layout is upon decision of each operation
108
     * (done in DocumentViewChildren).
108
     * (done in DocumentViewChildren).
Lines 255-261 Link Here
255
    
255
    
256
    /**
256
    /**
257
     * Check whether layout must be updated.
257
     * Check whether layout must be updated.
258
     * <br/>
258
     * <br>
259
     * It should only be called when children != null.
259
     * It should only be called when children != null.
260
     *
260
     *
261
     * @return true if layout update was necessary or false otherwise.
261
     * @return true if layout update was necessary or false otherwise.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java (-1 / +1 lines)
Lines 373-379 Link Here
373
373
374
    /**
374
    /**
375
     * Layout pView's children according to line wrap setting.
375
     * Layout pView's children according to line wrap setting.
376
     * <br/>
376
     * <br>
377
     * This method should ONLY be called by pView which then re-checks children size
377
     * This method should ONLY be called by pView which then re-checks children size
378
     * and possibly updates itself appropriately.
378
     * and possibly updates itself appropriately.
379
     */
379
     */
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewDescriptor.java (-5 / +5 lines)
Lines 58-64 Link Here
58
    
58
    
59
    /**
59
    /**
60
     * Get start offset of the paragraph view represented by this descriptor.
60
     * Get start offset of the paragraph view represented by this descriptor.
61
     * <br/>
61
     * <br>
62
     * When a LockedViewHierarchy that provided this paragraph view descriptor
62
     * When a LockedViewHierarchy that provided this paragraph view descriptor
63
     * is unlocked then operation of this method is undefined.
63
     * is unlocked then operation of this method is undefined.
64
     *
64
     *
Lines 70-76 Link Here
70
70
71
    /**
71
    /**
72
     * Get textual length of the paragraph view represented by this descriptor.
72
     * Get textual length of the paragraph view represented by this descriptor.
73
     * <br/>
73
     * <br>
74
     * When a LockedViewHierarchy that provided this paragraph view descriptor
74
     * When a LockedViewHierarchy that provided this paragraph view descriptor
75
     * is unlocked then operation of this method is undefined.
75
     * is unlocked then operation of this method is undefined.
76
     *
76
     *
Lines 82-88 Link Here
82
    
82
    
83
    /**
83
    /**
84
     * Get visual allocation of the whole paragraph view (represented by this descriptor).
84
     * Get visual allocation of the whole paragraph view (represented by this descriptor).
85
     * <br/>
85
     * <br>
86
     * When a LockedViewHierarchy that provided this paragraph view descriptor
86
     * When a LockedViewHierarchy that provided this paragraph view descriptor
87
     * is unlocked then operation of this method is undefined.
87
     * is unlocked then operation of this method is undefined.
88
     *
88
     *
Lines 95-105 Link Here
95
    /**
95
    /**
96
     * Get ascent (useful for text rendering using a particular font)
96
     * Get ascent (useful for text rendering using a particular font)
97
     * of the paragraph view represented by this descriptor.
97
     * of the paragraph view represented by this descriptor.
98
     * <br/>
98
     * <br>
99
     * This method is useful when a tool (such as a side bar performing rendering of a line
99
     * This method is useful when a tool (such as a side bar performing rendering of a line
100
     * number) wants to render a text that should vertically match the text
100
     * number) wants to render a text that should vertically match the text
101
     * rendered by the paragraph view.
101
     * rendered by the paragraph view.
102
     * <br/>
102
     * <br>
103
     * When a LockedViewHierarchy that provided this paragraph view descriptor
103
     * When a LockedViewHierarchy that provided this paragraph view descriptor
104
     * is unlocked then operation of this method is undefined.
104
     * is unlocked then operation of this method is undefined.
105
     *
105
     *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/TabView.java (-3 / +3 lines)
Lines 58-70 Link Here
58
58
59
/**
59
/**
60
 * View of (possibly multiple) '\t' characters.
60
 * View of (possibly multiple) '\t' characters.
61
 * <br/>
61
 * <br>
62
 * It needs to be measured specially - it needs to get visually aligned to multiples
62
 * It needs to be measured specially - it needs to get visually aligned to multiples
63
 * of TAB_SIZE char width.
63
 * of TAB_SIZE char width.
64
 * <br/>
64
 * <br>
65
 * Note that the view does not hold a last tab expander itself by itself so if tab expander
65
 * Note that the view does not hold a last tab expander itself by itself so if tab expander
66
 * changes the view does not call a preference change.
66
 * changes the view does not call a preference change.
67
 * <br/>
67
 * <br>
68
 * Due to line wrap the view cannot base its tab-stop calculations upon alloc.getX().
68
 * Due to line wrap the view cannot base its tab-stop calculations upon alloc.getX().
69
 *
69
 *
70
 *
70
 *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/TextLayoutBreakInfo.java (-1 / +1 lines)
Lines 45-51 Link Here
45
45
46
/**
46
/**
47
 * Info about last successful breaking of the view.
47
 * Info about last successful breaking of the view.
48
 * <br/>
48
 * <br>
49
 * Since TextLayout creation is expensive this should speed up breaking of the views
49
 * Since TextLayout creation is expensive this should speed up breaking of the views
50
 * (used in line wrap) considerably.
50
 * (used in line wrap) considerably.
51
 *
51
 *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/TextLayoutCache.java (-1 / +1 lines)
Lines 54-60 Link Here
54
/**
54
/**
55
 * Cache containing paragraph-view references of lines where text layouts
55
 * Cache containing paragraph-view references of lines where text layouts
56
 * are actively held or where children are actively maintained.
56
 * are actively held or where children are actively maintained.
57
 * <br/>
57
 * <br>
58
 * This class is not multi-thread safe.
58
 * This class is not multi-thread safe.
59
 * 
59
 * 
60
 * @author Miloslav Metelka
60
 * @author Miloslav Metelka
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/TextLayoutUtils.java (-1 / +1 lines)
Lines 136-142 Link Here
136
    
136
    
137
    /**
137
    /**
138
     * Get real allocation (possibly not rectangular) of a part of layout.
138
     * Get real allocation (possibly not rectangular) of a part of layout.
139
     * <br/>
139
     * <br>
140
     * It's used when rendering the text layout for filling background highlights of the view.
140
     * It's used when rendering the text layout for filling background highlights of the view.
141
     *
141
     *
142
     * @param length Total number of characters for which the allocation is computed.
142
     * @param length Total number of characters for which the allocation is computed.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewBuilder.java (-3 / +3 lines)
Lines 117-123 Link Here
117
    
117
    
118
    /**
118
    /**
119
     * Start offset of a before-modOffset local view that may be reused.
119
     * Start offset of a before-modOffset local view that may be reused.
120
     * <br/>
120
     * <br>
121
     * If set to Integer.MAX_VALUE then no more before-modOffset reusing possible
121
     * If set to Integer.MAX_VALUE then no more before-modOffset reusing possible
122
     * or bmReusePView has no children.
122
     * or bmReusePView has no children.
123
     */
123
     */
Lines 136-142 Link Here
136
    /**
136
    /**
137
     * Start offset of an above-modOffset local view that may be reused
137
     * Start offset of an above-modOffset local view that may be reused
138
     * (or if no local views exist inside 
138
     * (or if no local views exist inside 
139
     * <br/>
139
     * <br>
140
     * If set to Integer.MAX_VALUE then no more before-modOffset reusing possible
140
     * If set to Integer.MAX_VALUE then no more before-modOffset reusing possible
141
     * or bmReusePView has no children.
141
     * or bmReusePView has no children.
142
     */
142
     */
Lines 186-192 Link Here
186
186
187
    /**
187
    /**
188
     * Actual local views replace inside currently served paragraph view.
188
     * Actual local views replace inside currently served paragraph view.
189
     * <br/>
189
     * <br>
190
     * It may be equal to firstReplace when replacing inside firstly updated paragraph view.
190
     * It may be equal to firstReplace when replacing inside firstly updated paragraph view.
191
     */
191
     */
192
    private ViewReplace<ParagraphView,EditorView> localReplace;
192
    private ViewReplace<ParagraphView,EditorView> localReplace;
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewChildren.java (-3 / +3 lines)
Lines 52-61 Link Here
52
52
53
/**
53
/**
54
 * Class that manages children of either DocumentView or ParagraphView.
54
 * Class that manages children of either DocumentView or ParagraphView.
55
 * <br/>
55
 * <br>
56
 * For document view the class manages visual spans (end visual offsets).
56
 * For document view the class manages visual spans (end visual offsets).
57
 * For paragraphs the class manages end offsets of children as well as their end visual offsets.
57
 * For paragraphs the class manages end offsets of children as well as their end visual offsets.
58
 * <br/>
58
 * <br>
59
 * Generally children of {@link #ParagraphView} manage their raw end offsets
59
 * Generally children of {@link #ParagraphView} manage their raw end offsets
60
 * while children of {@link #DocumentView} do not manage them (they use Position objects
60
 * while children of {@link #DocumentView} do not manage them (they use Position objects
61
 * to manage its start).
61
 * to manage its start).
Lines 95-101 Link Here
95
    /**
95
    /**
96
     * Get view index of first view that "contains" the given offset (starts with it or it's inside)
96
     * Get view index of first view that "contains" the given offset (starts with it or it's inside)
97
     * by examining child views' raw end offsets.
97
     * by examining child views' raw end offsets.
98
     * <br/>
98
     * <br>
99
     * This is suitable for paragraph view which manages its views' raw end offsets.
99
     * This is suitable for paragraph view which manages its views' raw end offsets.
100
     * 
100
     * 
101
     * @param offset offset to search for.
101
     * @param offset offset to search for.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewGapStorage.java (-2 / +2 lines)
Lines 69-75 Link Here
69
69
70
    /**
70
    /**
71
     * Start of the visual gap in child views along their major axis.
71
     * Start of the visual gap in child views along their major axis.
72
     * <br/>
72
     * <br>
73
     * Place it above end of all views initially.
73
     * Place it above end of all views initially.
74
     */
74
     */
75
    double visualGapStart; // 8-super + 8 = 16 bytes
75
    double visualGapStart; // 8-super + 8 = 16 bytes
Lines 86-92 Link Here
86
    /**
86
    /**
87
     * Start of the offset gap used for managing end offsets of HighlightsView views.
87
     * Start of the offset gap used for managing end offsets of HighlightsView views.
88
     * It is not used for paragraph views.
88
     * It is not used for paragraph views.
89
     * <br/>
89
     * <br>
90
     * Place it above end of all views initially.
90
     * Place it above end of all views initially.
91
     */
91
     */
92
    int offsetGapStart; // 28 + 4 = 32 bytes
92
    int offsetGapStart; // 28 + 4 = 32 bytes
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewHierarchy.java (-6 / +6 lines)
Lines 42-50 Link Here
42
42
43
/**
43
/**
44
 * View hierarchy associated with a particular text component (for its whole lifetime).
44
 * View hierarchy associated with a particular text component (for its whole lifetime).
45
 * <br/>
45
 * <br>
46
 * View hierarchy needs to be locked before doing most operations - see {@link #lock() }.
46
 * View hierarchy needs to be locked before doing most operations - see {@link #lock() }.
47
 * <br/>
47
 * <br>
48
 * If editor view hierarchy is currently not installed into particular text component
48
 * If editor view hierarchy is currently not installed into particular text component
49
 * (text component's root view is not an instance of DocumentView)
49
 * (text component's root view is not an instance of DocumentView)
50
 * the methods (in LockedViewHierarchy) return default values as described in their documentation.
50
 * the methods (in LockedViewHierarchy) return default values as described in their documentation.
Lines 76-82 Link Here
76
    
76
    
77
    /**
77
    /**
78
     * Get text component that this view hierarchy is associated with.
78
     * Get text component that this view hierarchy is associated with.
79
     * <br/>
79
     * <br>
80
     * 
80
     * 
81
     * @return non-null text component.
81
     * @return non-null text component.
82
     */
82
     */
Lines 86-95 Link Here
86
    
86
    
87
    /**
87
    /**
88
     * Lock view hierarchy in order to perform operations described in {@link LockedViewHierarchy }.
88
     * Lock view hierarchy in order to perform operations described in {@link LockedViewHierarchy }.
89
     * <br/>
89
     * <br>
90
     * Underlying document of the view hierarchy's text component must be read-locked
90
     * Underlying document of the view hierarchy's text component must be read-locked
91
     * to guarantee stability of offsets passed to methods of LockedViewHierarchy.
91
     * to guarantee stability of offsets passed to methods of LockedViewHierarchy.
92
     * <br/>
92
     * <br>
93
     * Code example:<code>
93
     * Code example:<code>
94
     * // Possible textComponent.getDocument() read-locking
94
     * // Possible textComponent.getDocument() read-locking
95
     * LockedViewHierarchy lvh = ViewHierarchy.get(textComponent).lock();
95
     * LockedViewHierarchy lvh = ViewHierarchy.get(textComponent).lock();
Lines 109-115 Link Here
109
    
109
    
110
    /**
110
    /**
111
     * Add listener for view hierarchy changes.
111
     * Add listener for view hierarchy changes.
112
     * <br/>
112
     * <br>
113
     * Listener will be notified on a locked view hierarchy.
113
     * Listener will be notified on a locked view hierarchy.
114
     *
114
     *
115
     * @param l non-null listener.
115
     * @param l non-null listener.
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewHierarchyEvent.java (-15 / +15 lines)
Lines 42-48 Link Here
42
42
43
/**
43
/**
44
 * View hierarchy event describing view rebuilding or view re-measurement change in view hierarchy.
44
 * View hierarchy event describing view rebuilding or view re-measurement change in view hierarchy.
45
 * <br/>
45
 * <br>
46
 * Change may be related to several events:<ul>
46
 * Change may be related to several events:<ul>
47
 * <li> Document modification produces immediate updates to view hierarchy.
47
 * <li> Document modification produces immediate updates to view hierarchy.
48
 * </li>
48
 * </li>
Lines 62-71 Link Here
62
 * {@link #changeEndOffset()}) and whether the change produces any changes for y-coordinate related
62
 * {@link #changeEndOffset()}) and whether the change produces any changes for y-coordinate related
63
 * components (such as error stripe and various side bars). Note that y-coordinate related clients
63
 * components (such as error stripe and various side bars). Note that y-coordinate related clients
64
 * may completely ignore the change start/end offsets and only take care of y-related change information.
64
 * may completely ignore the change start/end offsets and only take care of y-related change information.
65
 * <br/>
65
 * <br>
66
 * If change produces y-coordinate changes the changed visual area corresponds to
66
 * If change produces y-coordinate changes the changed visual area corresponds to
67
 * &lt;{@link #startY()},{@link #endY()}&gt;.
67
 * &lt;{@link #startY()},{@link #endY()}&gt;.
68
 * <br/>
68
 * <br>
69
 * The change may cause rest of the document to move visually down/up which is reflected in {@link #deltaY()}
69
 * The change may cause rest of the document to move visually down/up which is reflected in {@link #deltaY()}
70
 * giving amount of pixels the area starting at {@link #endY()} moves down (negative value means moving up).
70
 * giving amount of pixels the area starting at {@link #endY()} moves down (negative value means moving up).
71
 * </p>
71
 * </p>
Lines 73-79 Link Here
73
 * <p>
73
 * <p>
74
 * Note that when this event is notified the listeners must make no direct queries to view hierarchy.
74
 * Note that when this event is notified the listeners must make no direct queries to view hierarchy.
75
 * They should only mark what has changed and needs to be recomputed and ask later.
75
 * They should only mark what has changed and needs to be recomputed and ask later.
76
 * <br/>
76
 * <br>
77
 * View hierarchy events are fired rather frequently so the code in listeners should be efficient.
77
 * View hierarchy events are fired rather frequently so the code in listeners should be efficient.
78
 * </p>
78
 * </p>
79
 * 
79
 * 
Lines 108-119 Link Here
108
    
108
    
109
    /**
109
    /**
110
     * Start offset of a visual change in view hierarchy.
110
     * Start offset of a visual change in view hierarchy.
111
     * <br/>
111
     * <br>
112
     * All model-to-view translations between {@link #changeStartOffset()} till {@link #changeEndOffset()}
112
     * All model-to-view translations between {@link #changeStartOffset()} till {@link #changeEndOffset()}
113
     * might shift or change. They could change in x-coordinate and possibly also in y-coordinate in case
113
     * might shift or change. They could change in x-coordinate and possibly also in y-coordinate in case
114
     * they lay between {@link #startY()} and {@link #endY()}. Those below {@link #endY()} have y-coordinate
114
     * they lay between {@link #startY()} and {@link #endY()}. Those below {@link #endY()} have y-coordinate
115
     * shifted down by {@link #deltaY()} (may be negative for shifting up).
115
     * shifted down by {@link #deltaY()} (may be negative for shifting up).
116
     * <br/>
116
     * <br>
117
     * Offset corresponds to a state after possible document modification
117
     * Offset corresponds to a state after possible document modification
118
     * (returned by {@link #documentEvent()}.
118
     * (returned by {@link #documentEvent()}.
119
     *
119
     *
Lines 125-136 Link Here
125
    
125
    
126
    /**
126
    /**
127
     * End offset of a visual change in view hierarchy.
127
     * End offset of a visual change in view hierarchy.
128
     * <br/>
128
     * <br>
129
     * All model-to-view translations between {@link #changeStartOffset()} till {@link #changeEndOffset()}
129
     * All model-to-view translations between {@link #changeStartOffset()} till {@link #changeEndOffset()}
130
     * might shift or change. They could change in x-coordinate and possibly also in y-coordinate in case
130
     * might shift or change. They could change in x-coordinate and possibly also in y-coordinate in case
131
     * they lay between {@link #startY()} and {@link #endY()}. Those below {@link #endY()} have y-coordinate
131
     * they lay between {@link #startY()} and {@link #endY()}. Those below {@link #endY()} have y-coordinate
132
     * shifted down by {@link #deltaY()} (may be negative for shifting up).
132
     * shifted down by {@link #deltaY()} (may be negative for shifting up).
133
     * <br/>
133
     * <br>
134
     * Offset corresponds to a state after possible document modification
134
     * Offset corresponds to a state after possible document modification
135
     * (returned by {@link #documentEvent()}.
135
     * (returned by {@link #documentEvent()}.
136
     *
136
     *
Lines 142-152 Link Here
142
142
143
    /**
143
    /**
144
     * Whether this change affects y-coordinate clients or not.
144
     * Whether this change affects y-coordinate clients or not.
145
     * <br/>
145
     * <br>
146
     * Return true if there was at least one paragraph view which changed its visual height.
146
     * Return true if there was at least one paragraph view which changed its visual height.
147
     * Such views are be included inside {@link #startY()} and {@link #endY()} interval
147
     * Such views are be included inside {@link #startY()} and {@link #endY()} interval
148
     * and a corresponding {@link #deltaY()} is provided.
148
     * and a corresponding {@link #deltaY()} is provided.
149
     * <br/>
149
     * <br>
150
     * If the paragraph views retain their original offset boundaries upon rebuild
150
     * If the paragraph views retain their original offset boundaries upon rebuild
151
     * (and their precise span is not computed) they are not reported
151
     * (and their precise span is not computed) they are not reported
152
     * as being y-changed.
152
     * as being y-changed.
Lines 159-168 Link Here
159
159
160
    /**
160
    /**
161
     * Where the y-coordinate change begins.
161
     * Where the y-coordinate change begins.
162
     * <br/>
162
     * <br>
163
     * Previously computed model-to-view values that have their y-coordinate
163
     * Previously computed model-to-view values that have their y-coordinate
164
     * within {@link #startY()} and {@link #endY()} interval may be affected.
164
     * within {@link #startY()} and {@link #endY()} interval may be affected.
165
     * <br/>
165
     * <br>
166
     * Measurements below {@link #endY()} should shift
166
     * Measurements below {@link #endY()} should shift
167
     * its y-coordinate down by {@link #deltaY()} (may be negative for shifting up).
167
     * its y-coordinate down by {@link #deltaY()} (may be negative for shifting up).
168
     *
168
     *
Lines 174-183 Link Here
174
    
174
    
175
    /**
175
    /**
176
     * Where the y-coordinate change ends.
176
     * Where the y-coordinate change ends.
177
     * <br/>
177
     * <br>
178
     * Previously computed model-to-view values that have their y-coordinate
178
     * Previously computed model-to-view values that have their y-coordinate
179
     * within {@link #startY()} and {@link #endY()} interval may be affected.
179
     * within {@link #startY()} and {@link #endY()} interval may be affected.
180
     * <br/>
180
     * <br>
181
     * Measurements below {@link #endY()} should shift
181
     * Measurements below {@link #endY()} should shift
182
     * its y-coordinate down by {@link #deltaY()} (may be negative for shifting up).
182
     * its y-coordinate down by {@link #deltaY()} (may be negative for shifting up).
183
     *
183
     *
Lines 189-195 Link Here
189
189
190
    /**
190
    /**
191
     * Shift of area starting at {@link #endY()} down (or up if negative).
191
     * Shift of area starting at {@link #endY()} down (or up if negative).
192
     * <br/>
192
     * <br>
193
     * Measurements below {@link #endY()} should shift
193
     * Measurements below {@link #endY()} should shift
194
     * its y-coordinate down by {@link #deltaY()} (may be negative for shifting up).
194
     * its y-coordinate down by {@link #deltaY()} (may be negative for shifting up).
195
     *
195
     *
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewHierarchyImpl.java (-14 / +14 lines)
Lines 68-76 Link Here
68
    
68
    
69
    /**
69
    /**
70
     * Logger for core operations of the view hierarchy - resolving modelToView() and viewToModel() etc.
70
     * Logger for core operations of the view hierarchy - resolving modelToView() and viewToModel() etc.
71
     * <br/>
71
     * <br>
72
     * FINE logs basic info about view hierarchy operations performed.
72
     * FINE logs basic info about view hierarchy operations performed.
73
     * <br/>
73
     * <br>
74
     * FINER also reports query during document's modification (when the view hierarchy cannot respond
74
     * FINER also reports query during document's modification (when the view hierarchy cannot respond
75
     *  appropriately to queries since it would give incorrect results).
75
     *  appropriately to queries since it would give incorrect results).
76
     */
76
     */
Lines 79-89 Link Here
79
    /**
79
    /**
80
     * Logger tracking all view factory changes that cause either rebuild of the views
80
     * Logger tracking all view factory changes that cause either rebuild of the views
81
     * or offset repaints.
81
     * or offset repaints.
82
     * <br/>
82
     * <br>
83
     * FINE reports which factory reported a change and an offset range of that change.
83
     * FINE reports which factory reported a change and an offset range of that change.
84
     * <br/>
84
     * <br>
85
     * FINER reports additional detailed information about the change.
85
     * FINER reports additional detailed information about the change.
86
     * <br/>
86
     * <br>
87
     * FINEST reports stacktrace where a particular span change request originated.
87
     * FINEST reports stacktrace where a particular span change request originated.
88
     */
88
     */
89
    static final Logger CHANGE_LOG = Logger.getLogger("org.netbeans.editor.view.change"); // -J-Dorg.netbeans.editor.view.change.level=FINE
89
    static final Logger CHANGE_LOG = Logger.getLogger("org.netbeans.editor.view.change"); // -J-Dorg.netbeans.editor.view.change.level=FINE
Lines 100-145 Link Here
100
    
100
    
101
    /**
101
    /**
102
     * Logger for span change requests on the views and underlying text component.
102
     * Logger for span change requests on the views and underlying text component.
103
     * <br/>
103
     * <br>
104
     * FINE reports span change descriptions
104
     * FINE reports span change descriptions
105
     * <br/>
105
     * <br>
106
     * FINEST reports stacktrace where a particular span change request originated.
106
     * FINEST reports stacktrace where a particular span change request originated.
107
     */
107
     */
108
    static final Logger SPAN_LOG = Logger.getLogger("org.netbeans.editor.view.span"); // -J-Dorg.netbeans.editor.view.span.level=FINE
108
    static final Logger SPAN_LOG = Logger.getLogger("org.netbeans.editor.view.span"); // -J-Dorg.netbeans.editor.view.span.level=FINE
109
    
109
    
110
    /**
110
    /**
111
     * Logger for repaint requests of the underlying text component.
111
     * Logger for repaint requests of the underlying text component.
112
     * <br/>
112
     * <br>
113
     * FINE reports repaint request's coordinates
113
     * FINE reports repaint request's coordinates
114
     * <br/>
114
     * <br>
115
     * FINEST reports stacktrace where a particular repaint request originated.
115
     * FINEST reports stacktrace where a particular repaint request originated.
116
     */
116
     */
117
    static final Logger REPAINT_LOG = Logger.getLogger("org.netbeans.editor.view.repaint"); // -J-Dorg.netbeans.editor.view.repaint.level=FINE
117
    static final Logger REPAINT_LOG = Logger.getLogger("org.netbeans.editor.view.repaint"); // -J-Dorg.netbeans.editor.view.repaint.level=FINE
118
    
118
    
119
    /**
119
    /**
120
     * Logger for extra consistency checks inside view hierarchy (may slow down processing).
120
     * Logger for extra consistency checks inside view hierarchy (may slow down processing).
121
     * <br/>
121
     * <br>
122
     */
122
     */
123
    static final Logger CHECK_LOG = Logger.getLogger("org.netbeans.editor.view.check"); // -J-Dorg.netbeans.editor.view.check.level=FINE
123
    static final Logger CHECK_LOG = Logger.getLogger("org.netbeans.editor.view.check"); // -J-Dorg.netbeans.editor.view.check.level=FINE
124
    
124
    
125
    /**
125
    /**
126
     * Logger related to any settings being used in view hierarchy.
126
     * Logger related to any settings being used in view hierarchy.
127
     * <br/>
127
     * <br>
128
     */
128
     */
129
    static final Logger SETTINGS_LOG = Logger.getLogger("org.netbeans.editor.view.settings"); // -J-Dorg.netbeans.editor.view.settings.level=FINE
129
    static final Logger SETTINGS_LOG = Logger.getLogger("org.netbeans.editor.view.settings"); // -J-Dorg.netbeans.editor.view.settings.level=FINE
130
    
130
    
131
    /**
131
    /**
132
     * Logger related to view hierarchy events generation.
132
     * Logger related to view hierarchy events generation.
133
     * <br/>
133
     * <br>
134
     */
134
     */
135
    static final Logger EVENT_LOG = Logger.getLogger("org.netbeans.editor.view.event"); // -J-Dorg.netbeans.editor.view.event.level=FINE
135
    static final Logger EVENT_LOG = Logger.getLogger("org.netbeans.editor.view.event"); // -J-Dorg.netbeans.editor.view.event.level=FINE
136
    
136
    
137
    /**
137
    /**
138
     * Logger for tracking view hierarchy locking.
138
     * Logger for tracking view hierarchy locking.
139
     * <br/>
139
     * <br>
140
     * FINER stores the stack of the lock thread of view hierarchy in lockStack variable
140
     * FINER stores the stack of the lock thread of view hierarchy in lockStack variable
141
     * (it should help to find missing unlock).
141
     * (it should help to find missing unlock).
142
     * <br/>
142
     * <br>
143
     * FINEST in addition it dumps thread dump of each locker of a view hierarchy. 
143
     * FINEST in addition it dumps thread dump of each locker of a view hierarchy. 
144
     */
144
     */
145
    // -J-Dorg.netbeans.modules.editor.lib2.view.ViewHierarchyImpl.level=FINER
145
    // -J-Dorg.netbeans.modules.editor.lib2.view.ViewHierarchyImpl.level=FINER
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewHierarchyListener.java (-3 / +3 lines)
Lines 50-61 Link Here
50
50
51
    /**
51
    /**
52
     * Notification about visual change that occurred in view hierarchy.
52
     * Notification about visual change that occurred in view hierarchy.
53
     * <br/>
53
     * <br>
54
     * Notification may come in response to document modification but also in response
54
     * Notification may come in response to document modification but also in response
55
     * to a model&lt;-&gt;view query to view hierarchy (due to fact that view hierarchy is computed lazily).
55
     * to a model&lt;-&gt;view query to view hierarchy (due to fact that view hierarchy is computed lazily).
56
     * <br/>
56
     * <br>
57
     * Notification may come from any thread.
57
     * Notification may come from any thread.
58
     * <br/>
58
     * <br>
59
     * When this event is notified the listeners must make no
59
     * When this event is notified the listeners must make no
60
     * queries to view hierarchy synchronously (they should only mark what has changed and
60
     * queries to view hierarchy synchronously (they should only mark what has changed and
61
     * ask later).
61
     * ask later).
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewPaintHighlights.java (-3 / +3 lines)
Lines 50-61 Link Here
50
50
51
/**
51
/**
52
 * Special highlights sequence used for painting of individual views.
52
 * Special highlights sequence used for painting of individual views.
53
 * <br/>
53
 * <br>
54
 * It merges together highlights contained in views (as attributes) together
54
 * It merges together highlights contained in views (as attributes) together
55
 * with extra painting highlights (from highlighting layers that do not change metrics).
55
 * with extra painting highlights (from highlighting layers that do not change metrics).
56
 * <br/>
56
 * <br>
57
 * It "covers" even non-highlighted areas by returning null from {@link #getAttributes()}.
57
 * It "covers" even non-highlighted areas by returning null from {@link #getAttributes()}.
58
 * <br/>
58
 * <br>
59
 * The instance can only be used by a single thread.
59
 * The instance can only be used by a single thread.
60
 *
60
 *
61
 * @author mmetelka
61
 * @author mmetelka
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewRenderContext.java (-4 / +4 lines)
Lines 47-53 Link Here
47
47
48
/**
48
/**
49
 * Additional info related to view rendering.
49
 * Additional info related to view rendering.
50
 * <br/>
50
 * <br>
51
 * Provided by {@link EditorView.Parent }.
51
 * Provided by {@link EditorView.Parent }.
52
 *
52
 *
53
 * @author Miloslav Metelka
53
 * @author Miloslav Metelka
Lines 72-78 Link Here
72
72
73
    /**
73
    /**
74
     * Get special highlighting sequence that is a merge of attributes of the
74
     * Get special highlighting sequence that is a merge of attributes of the
75
     * view with top painting highlights. <br/> It's only allowed to call this
75
     * view with top painting highlights. <br> It's only allowed to call this
76
     * method (and use the returned value) during view's paint() methods
76
     * method (and use the returned value) during view's paint() methods
77
     * execution otherwise it throws an IllegalStateException.
77
     * execution otherwise it throws an IllegalStateException.
78
     *
78
     *
Lines 88-94 Link Here
88
88
89
    /**
89
    /**
90
     * Get row height of an single row of views being rendered by a paragraph view.
90
     * Get row height of an single row of views being rendered by a paragraph view.
91
     * <br/>
91
     * <br>
92
     * For views that only render text the views should return this height
92
     * For views that only render text the views should return this height
93
     * as their vertical span since the user may forcibly decrease row height
93
     * as their vertical span since the user may forcibly decrease row height
94
     * (see DocumentViewOp.rowHeightCorrection).
94
     * (see DocumentViewOp.rowHeightCorrection).
Lines 113-119 Link Here
113
    /**
113
    /**
114
     * Get font for text rendering that incorporates a possible text zoom
114
     * Get font for text rendering that incorporates a possible text zoom
115
     * (Alt+MouseWheel function).
115
     * (Alt+MouseWheel function).
116
     * <br/>
116
     * <br>
117
     * Ideally all the fonts rendered by views should be "translated" by this method.
117
     * Ideally all the fonts rendered by views should be "translated" by this method.
118
     */
118
     */
119
    public Font getRenderFont(Font font) {
119
    public Font getRenderFont(Font font) {
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfo.java (-1 / +1 lines)
Lines 59-65 Link Here
59
59
60
/**
60
/**
61
 * Information about line wrapping that may be attached to {@link ParagraphViewChildren}.
61
 * Information about line wrapping that may be attached to {@link ParagraphViewChildren}.
62
 * <br/>
62
 * <br>
63
 * Wrapping uses constant wrap line height - children height from {@link ParagraphViewChildren}.
63
 * Wrapping uses constant wrap line height - children height from {@link ParagraphViewChildren}.
64
 * 
64
 * 
65
 * @author Miloslav Metelka
65
 * @author Miloslav Metelka
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapLine.java (-2 / +2 lines)
Lines 68-74 Link Here
68
68
69
    /**
69
    /**
70
     * Index of a first view located at this line.
70
     * Index of a first view located at this line.
71
     * <br/>
71
     * <br>
72
     * Logically if there's a non-null startPart then it comes from view
72
     * Logically if there's a non-null startPart then it comes from view
73
     * at (firstViewIndex - 1).
73
     * at (firstViewIndex - 1).
74
     */
74
     */
Lines 76-82 Link Here
76
76
77
    /**
77
    /**
78
     * Index that follows last view located at this line.
78
     * Index that follows last view located at this line.
79
     * <br/>
79
     * <br>
80
     * It should be >= firstViewIndex.
80
     * It should be >= firstViewIndex.
81
     */
81
     */
82
    int endViewIndex;
82
    int endViewIndex;
(-)a/editor.lib2/src/org/netbeans/spi/editor/AbstractEditorAction.java (-100 / +100 lines)
Lines 74-111 Link Here
74
/**
74
/**
75
 * Base class for editor actions that should be used together with
75
 * Base class for editor actions that should be used together with
76
 * {@link EditorActionRegistration} annotation.
76
 * {@link EditorActionRegistration} annotation.
77
 * <br/>
77
 * <br>
78
 * It may be constructed and used in two ways: direct construction or construction
78
 * It may be constructed and used in two ways: direct construction or construction
79
 * upon invocation by a wrapper action:
79
 * upon invocation by a wrapper action:
80
 * <ul>
80
 * <ul>
81
 *   <li> Direct construction - action is created directly when an editor kit
81
 *   <li> Direct construction - action is created directly when an editor kit
82
 *        gets constructed (its <code>kit.getActions()</code> gets used).
82
 *        gets constructed (its <code>kit.getActions()</code> gets used).
83
 *        <br/>
83
 *        <br>
84
 *        Advantages: Action controls all its behavior and properties (including enabled status)
84
 *        Advantages: Action controls all its behavior and properties (including enabled status)
85
 *        since the begining.
85
 *        since the begining.
86
 *        <br/>
86
 *        <br>
87
 *        Disadvantages: Action's class is loaded by classloader at editor kit's construction.
87
 *        Disadvantages: Action's class is loaded by classloader at editor kit's construction.
88
 *        <br/>
88
 *        <br>
89
 *        Example of registration:
89
 *        Example of registration:
90
 *        <br/>
90
 *        <br>
91
 * <code>
91
 * <code>
92
 * public static final class MyAction extends AbstractEditorAction {<br/>
92
 * public static final class MyAction extends AbstractEditorAction {<br>
93
 *<br/>
93
 *<br>
94
 *     &#64;EditorActionRegistration(name = "my-action")<br/>
94
 *     &#64;EditorActionRegistration(name = "my-action")<br>
95
 *     public static MyAction create(Map&lt;String,?&gt; attrs) {<br/>
95
 *     public static MyAction create(Map&lt;String,?&gt; attrs) {<br>
96
 *         return new MyAction(attrs);<br/>
96
 *         return new MyAction(attrs);<br>
97
 *     }<br/>
97
 *     }<br>
98
 * <br/>
98
 * <br>
99
 *     private MyAction(Map&lt;String,?&gt; attrs) {<br/>
99
 *     private MyAction(Map&lt;String,?&gt; attrs) {<br>
100
 *         super(attrs);<br/>
100
 *         super(attrs);<br>
101
 *         ...<br/>
101
 *         ...<br>
102
 *     }<br/>
102
 *     }<br>
103
 * <br/>
103
 * <br>
104
 *     protected void actionPerformed(ActionEvent evt, JTextComponent component) {<br/>
104
 *     protected void actionPerformed(ActionEvent evt, JTextComponent component) {<br>
105
 *         ...<br/>
105
 *         ...<br>
106
 *     }<br/>
106
 *     }<br>
107
 * <br/>
107
 * <br>
108
 * }<br/>
108
 * }<br>
109
 * </code>
109
 * </code>
110
 *   </li>
110
 *   </li>
111
 * 
111
 * 
Lines 115-147 Link Here
115
 *        (upon {@link Action#actionPerformed(java.awt.event.ActionEvent)} call).
115
 *        (upon {@link Action#actionPerformed(java.awt.event.ActionEvent)} call).
116
 *        Existing properties of the wrapper action (including <code>Action.NAME</code> property)
116
 *        Existing properties of the wrapper action (including <code>Action.NAME</code> property)
117
 *        get transferred into delegate action.
117
 *        get transferred into delegate action.
118
 *        <br/>
118
 *        <br>
119
 *        Advantages: Action's class is only loaded upon action's execution.
119
 *        Advantages: Action's class is only loaded upon action's execution.
120
 *        <br/>
120
 *        <br>
121
 *        Disadvantages: Only a limited set of action's properties gets populated
121
 *        Disadvantages: Only a limited set of action's properties gets populated
122
 *        (those defined by {@link EditorActionRegistration}).
122
 *        (those defined by {@link EditorActionRegistration}).
123
 *        <br/>
123
 *        <br>
124
 *        Example of registration:
124
 *        Example of registration:
125
 *        <br/>
125
 *        <br>
126
 * <code>
126
 * <code>
127
 * &#64;EditorActionRegistration(name = "my-action")<br/>
127
 * &#64;EditorActionRegistration(name = "my-action")<br>
128
 * public static final class MyAction extends AbstractEditorAction {<br/>
128
 * public static final class MyAction extends AbstractEditorAction {<br>
129
 *<br/>
129
 *<br>
130
 *     public MyAction() {<br/>
130
 *     public MyAction() {<br>
131
 *         // Here the properties are not yet set.<br/>
131
 *         // Here the properties are not yet set.<br>
132
 *     }<br/>
132
 *     }<br>
133
 * <br/>
133
 * <br>
134
 *     &#64;Override<br/>
134
 *     &#64;Override<br>
135
 *     protected void valuesUpdated() {<br/>
135
 *     protected void valuesUpdated() {<br>
136
 *         // Here the wrapper action has transferred all its properties into this action<br/>
136
 *         // Here the wrapper action has transferred all its properties into this action<br>
137
 *         // so properties like Action.NAME etc. are now populated.<br/>
137
 *         // so properties like Action.NAME etc. are now populated.<br>
138
 *     }<br/>
138
 *     }<br>
139
 * <br/>
139
 * <br>
140
 *     protected void actionPerformed(ActionEvent evt, JTextComponent component) {<br/>
140
 *     protected void actionPerformed(ActionEvent evt, JTextComponent component) {<br>
141
 *         ...<br/>
141
 *         ...<br>
142
 *     }<br/>
142
 *     }<br>
143
 * <br/>
143
 * <br>
144
 * }<br/>
144
 * }<br>
145
 * </code>
145
 * </code>
146
 *   </li>
146
 *   </li>
147
 * </ul>
147
 * </ul>
Lines 155-177 Link Here
155
    
155
    
156
    /**
156
    /**
157
     * Key of {@link String} property containing a localized display name of the action.
157
     * Key of {@link String} property containing a localized display name of the action.
158
     * <br/>
158
     * <br>
159
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
159
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
160
     */
160
     */
161
    public static final String DISPLAY_NAME_KEY = "displayName"; // (named in sync with AlwaysEnabledAction) NOI18N
161
    public static final String DISPLAY_NAME_KEY = "displayName"; // (named in sync with AlwaysEnabledAction) NOI18N
162
162
163
    /**
163
    /**
164
     * Key of {@link String} property containing a localized text to be displayed in a main menu for this action.
164
     * Key of {@link String} property containing a localized text to be displayed in a main menu for this action.
165
     * <br/>
165
     * <br>
166
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
166
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
167
     */
167
     */
168
    public static final String MENU_TEXT_KEY = "menuText"; // (named in sync with AlwaysEnabledAction) NOI18N
168
    public static final String MENU_TEXT_KEY = "menuText"; // (named in sync with AlwaysEnabledAction) NOI18N
169
169
170
    /**
170
    /**
171
     * Key of {@link String} property containing a localized text to be displayed in a popup menu for this action.
171
     * Key of {@link String} property containing a localized text to be displayed in a popup menu for this action.
172
     * <br/>
172
     * <br>
173
     * If this property is not set then {@link #MENU_TEXT_KEY} is attempted.
173
     * If this property is not set then {@link #MENU_TEXT_KEY} is attempted.
174
     * <br/>
174
     * <br>
175
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
175
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
176
     */
176
     */
177
    public static final String POPUP_TEXT_KEY = "popupText"; // (named in sync with AlwaysEnabledAction) NOI18N
177
    public static final String POPUP_TEXT_KEY = "popupText"; // (named in sync with AlwaysEnabledAction) NOI18N
Lines 184-190 Link Here
184
    /**
184
    /**
185
     * Key of {@link Boolean} property which determines whether icon of this action should be
185
     * Key of {@link Boolean} property which determines whether icon of this action should be
186
     * displayed in menu (false or unset) or not (true).
186
     * displayed in menu (false or unset) or not (true).
187
     * <br/>
187
     * <br>
188
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
188
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
189
     * @since 1.74
189
     * @since 1.74
190
     */
190
     */
Lines 193-199 Link Here
193
    /**
193
    /**
194
     * Key of {@link Boolean} property which determines if this action should be
194
     * Key of {@link Boolean} property which determines if this action should be
195
     * displayed in key binding customizer (false or unset) or not (true).
195
     * displayed in key binding customizer (false or unset) or not (true).
196
     * <br/>
196
     * <br>
197
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
197
     * It may be passed to {@link #getValue(java.lang.String) } to obtain the property value.
198
     * @since 1.74
198
     * @since 1.74
199
     */
199
     */
Lines 202-208 Link Here
202
    /**
202
    /**
203
     * Key of property containing a <code>List &lt; List &lt; {@link KeyStroke} &gt; &gt;</code>
203
     * Key of property containing a <code>List &lt; List &lt; {@link KeyStroke} &gt; &gt;</code>
204
     * listing all multi-key bindings by which the action may be invoked.
204
     * listing all multi-key bindings by which the action may be invoked.
205
     * <br/>
205
     * <br>
206
     * There may be multiple multi-key bindings to invoke a single action e.g. a code completion
206
     * There may be multiple multi-key bindings to invoke a single action e.g. a code completion
207
     * may be invoked by Ctrl+SPACE and also Ctrl+'\'
207
     * may be invoked by Ctrl+SPACE and also Ctrl+'\'
208
     * (in fact each of these bindings could also consist of multiple keystrokes).
208
     * (in fact each of these bindings could also consist of multiple keystrokes).
Lines 219-229 Link Here
219
    /**
219
    /**
220
     * Key of {@link String} property containing a mime type for which this action
220
     * Key of {@link String} property containing a mime type for which this action
221
     * is registered.
221
     * is registered.
222
     * <br/>
222
     * <br>
223
     * Note: action's mime-type is not necessarily the same like <code>EditorKit.getContentType()</code>
223
     * Note: action's mime-type is not necessarily the same like <code>EditorKit.getContentType()</code>
224
     * for which the action was created because the kit may inherit some actions
224
     * for which the action was created because the kit may inherit some actions
225
     * from a global mime-type "".
225
     * from a global mime-type "".
226
     * <br/>
226
     * <br>
227
     * Value of this property is checked at action's initialization
227
     * Value of this property is checked at action's initialization
228
     * (it needs to be passed as part of 'attrs' parameter to constructor).
228
     * (it needs to be passed as part of 'attrs' parameter to constructor).
229
     * Subsequent modifications of this property should be avoided and they will likely not affect its behavior.
229
     * Subsequent modifications of this property should be avoided and they will likely not affect its behavior.
Lines 238-244 Link Here
238
    /**
238
    /**
239
     * Key of {@link String} property containing a name of a boolean key in preferences in which this action changes settings
239
     * Key of {@link String} property containing a name of a boolean key in preferences in which this action changes settings
240
     * (according to {@link #PREFERENCES_NODE_KEY} property).
240
     * (according to {@link #PREFERENCES_NODE_KEY} property).
241
     * <br/>
241
     * <br>
242
     * Once this property is set then it's expected that {@link #PREFERENCES_NODE_KEY} is also set
242
     * Once this property is set then it's expected that {@link #PREFERENCES_NODE_KEY} is also set
243
     * to a valid value and checkbox menu presenter will be used automatically.
243
     * to a valid value and checkbox menu presenter will be used automatically.
244
     */
244
     */
Lines 253-259 Link Here
253
     * Key of {@link Boolean} property determining whether this is just a wrapper action
253
     * Key of {@link Boolean} property determining whether this is just a wrapper action
254
     * that is being used until the action needs to be executed. Then the target action
254
     * that is being used until the action needs to be executed. Then the target action
255
     * gets created and run.
255
     * gets created and run.
256
     * <br/>
256
     * <br>
257
     * Value of this property is checked at action's initialization
257
     * Value of this property is checked at action's initialization
258
     * (it needs to be passed as part of 'attrs' parameter to constructor).
258
     * (it needs to be passed as part of 'attrs' parameter to constructor).
259
     * Subsequent modifications of this property should be avoided and they will likely not affect its behavior.
259
     * Subsequent modifications of this property should be avoided and they will likely not affect its behavior.
Lines 290-316 Link Here
290
     * Constructor that takes a map of attributes that are typically obtained
290
     * Constructor that takes a map of attributes that are typically obtained
291
     * from an xml layer when an action's creation method is annotated with
291
     * from an xml layer when an action's creation method is annotated with
292
     * <code>@EditorActionRegistration</code>.
292
     * <code>@EditorActionRegistration</code>.
293
     * <br/>
293
     * <br>
294
     * Example:
294
     * Example:
295
     * <br/>
295
     * <br>
296
     * <code>
296
     * <code>
297
     * public static final class MyAction extends AbstractEditorAction {<br/>
297
     * public static final class MyAction extends AbstractEditorAction {<br>
298
     *<br/>
298
     *<br>
299
     *     &#64;EditorActionRegistration(name = "my-action")<br/>
299
     *     &#64;EditorActionRegistration(name = "my-action")<br>
300
     *     public static MyAction create(Map&lt;String,?&gt; attrs) {<br/>
300
     *     public static MyAction create(Map&lt;String,?&gt; attrs) {<br>
301
     *         return new MyAction(attrs);<br/>
301
     *         return new MyAction(attrs);<br>
302
     *     }<br/>
302
     *     }<br>
303
     * <br/>
303
     * <br>
304
     *     private MyAction(Map&lt;String,?&gt; attrs) {<br/>
304
     *     private MyAction(Map&lt;String,?&gt; attrs) {<br>
305
     *         super(attrs);<br/>
305
     *         super(attrs);<br>
306
     *         ...<br/>
306
     *         ...<br>
307
     *     }<br/>
307
     *     }<br>
308
     * <br/>
308
     * <br>
309
     *     protected void actionPerformed(ActionEvent evt, JTextComponent component) {<br/>
309
     *     protected void actionPerformed(ActionEvent evt, JTextComponent component) {<br>
310
     *         ...<br/>
310
     *         ...<br>
311
     *     }<br/>
311
     *     }<br>
312
     * <br/>
312
     * <br>
313
     * }<br/>
313
     * }<br>
314
     * </code>
314
     * </code>
315
     *
315
     *
316
     * @param attrs non-null attributes that hold action's properties.
316
     * @param attrs non-null attributes that hold action's properties.
Lines 330-357 Link Here
330
     * Constructor typically used when action is constructed lazily
330
     * Constructor typically used when action is constructed lazily
331
     * upon its performing (the action is always enabled and its properties
331
     * upon its performing (the action is always enabled and its properties
332
     * are declared in xml layer by annotation processor for <code>@EditorActionRegistration</code>).
332
     * are declared in xml layer by annotation processor for <code>@EditorActionRegistration</code>).
333
     * <br/>
333
     * <br>
334
     * Example:
334
     * Example:
335
     * <br/>
335
     * <br>
336
     * <code>
336
     * <code>
337
     * &#64;EditorActionRegistration(name = "my-action")<br/>
337
     * &#64;EditorActionRegistration(name = "my-action")<br>
338
     * public static final class MyAction extends AbstractEditorAction {<br/>
338
     * public static final class MyAction extends AbstractEditorAction {<br>
339
     *<br/>
339
     *<br>
340
     *     public MyAction() {<br/>
340
     *     public MyAction() {<br>
341
     *         // Here the properties are not yet set.<br/>
341
     *         // Here the properties are not yet set.<br>
342
     *     }<br/>
342
     *     }<br>
343
     * <br/>
343
     * <br>
344
     *     &#64;Override<br/>
344
     *     &#64;Override<br>
345
     *     protected void valuesUpdated() {<br/>
345
     *     protected void valuesUpdated() {<br>
346
     *         // Here the wrapper action has transferred all its properties into this action<br/>
346
     *         // Here the wrapper action has transferred all its properties into this action<br>
347
     *         // so properties like Action.NAME etc. are now populated.<br/>
347
     *         // so properties like Action.NAME etc. are now populated.<br>
348
     *     }<br/>
348
     *     }<br>
349
     * <br/>
349
     * <br>
350
     *     protected void actionPerformed(ActionEvent evt, JTextComponent component) {<br/>
350
     *     protected void actionPerformed(ActionEvent evt, JTextComponent component) {<br>
351
     *         ...<br/>
351
     *         ...<br>
352
     *     }<br/>
352
     *     }<br>
353
     * <br/>
353
     * <br>
354
     * }<br/>
354
     * }<br>
355
     * </code>
355
     * </code>
356
     */
356
     */
357
    protected AbstractEditorAction() {
357
    protected AbstractEditorAction() {
Lines 388-394 Link Here
388
388
389
    /**
389
    /**
390
     * Reset caret's magic position.
390
     * Reset caret's magic position.
391
     * <br/>
391
     * <br>
392
     * Magic caret position is useful when going through empty lines with Down/Up arrow
392
     * Magic caret position is useful when going through empty lines with Down/Up arrow
393
     * then the caret returns on original horizontal column when a particular line has sufficient
393
     * then the caret returns on original horizontal column when a particular line has sufficient
394
     * number of characters.
394
     * number of characters.
Lines 401-411 Link Here
401
401
402
    /**
402
    /**
403
     * Get presenter of this action in main menu.
403
     * Get presenter of this action in main menu.
404
     * <br/>
404
     * <br>
405
     * Default implementation uses {@link #MENU_TEXT_KEY} for menu item's text
405
     * Default implementation uses {@link #MENU_TEXT_KEY} for menu item's text
406
     * and the presenter is placed in the menu according to rules
406
     * and the presenter is placed in the menu according to rules
407
     * given in the corresponding {@link EditorActionRegistration}.
407
     * given in the corresponding {@link EditorActionRegistration}.
408
     * <br/>
408
     * <br>
409
     * Moreover the default presenter is sensitive to currently active text component
409
     * Moreover the default presenter is sensitive to currently active text component
410
     * and if the active editor kit has that action redefined it uses the active action's
410
     * and if the active editor kit has that action redefined it uses the active action's
411
     * properties for this presenter.
411
     * properties for this presenter.
Lines 420-426 Link Here
420
420
421
    /**
421
    /**
422
     * Get presenter of this action in popup menu.
422
     * Get presenter of this action in popup menu.
423
     * <br/>
423
     * <br>
424
     * Default implementation uses {@link #POPUP_TEXT_KEY} for popup menu item's text
424
     * Default implementation uses {@link #POPUP_TEXT_KEY} for popup menu item's text
425
     * and the presenter is placed in the popup menu according to rules
425
     * and the presenter is placed in the popup menu according to rules
426
     * given in the corresponding {@link EditorActionRegistration}.
426
     * given in the corresponding {@link EditorActionRegistration}.
Lines 563-574 Link Here
563
    /**
563
    /**
564
     * This method is called when a value for the given property
564
     * This method is called when a value for the given property
565
     * was not yet populated.
565
     * was not yet populated.
566
     * <br/>
566
     * <br>
567
     * This method is only called once for the given property. Even if this method
567
     * This method is only called once for the given property. Even if this method
568
     * returns null for the given property the infrastructure remembers the
568
     * returns null for the given property the infrastructure remembers the
569
     * returned value and no longer queries this method (the property can still
569
     * returned value and no longer queries this method (the property can still
570
     * be modified by {@link #putValue(java.lang.String, java.lang.Object) }.)
570
     * be modified by {@link #putValue(java.lang.String, java.lang.Object) }.)
571
     * <br/>
571
     * <br>
572
     * Calling of this method and remembering of the returned value does not trigger
572
     * Calling of this method and remembering of the returned value does not trigger
573
     * {@link #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) }.
573
     * {@link #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) }.
574
     *
574
     *
(-)a/editor.lib2/src/org/netbeans/spi/editor/caret/CaretMoveHandler.java (+74 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.spi.editor.caret;
43
44
import org.netbeans.api.annotations.common.NonNull;
45
import org.netbeans.api.editor.caret.CaretMoveContext;
46
47
/**
48
 * Handle possible moving of individual carets to new positions or change their selections.
49
 *
50
 * @author Miloslav Metelka
51
 * @since 2.6
52
 */
53
public interface CaretMoveHandler {
54
    
55
    /**
56
     * Possibly move one or more carets to new position or change their selections
57
     * by using methods in the given context.
58
     * <br>
59
     * The method will be called with a document lock acquired.
60
     * <br>
61
     * The method is allowed to make document mutations in case the caller
62
     * of {@link org.netbeans.api.editor.caret.EditorCaret#moveCarets(CaretMoveHandler) }
63
     * acquired document write-lock.
64
     * <br>
65
     * To prevent deadlocks the method should not acquire any additional locks.
66
     * <br>
67
     * The method is not allowed to call methods of EditorCaret that mutate its state
68
     * and do nested calls to <code>EditorCaret.moveCarets()</code>.
69
     *
70
     * @param context non-null context containing the manipulation methods.
71
     */
72
    void moveCarets(@NonNull CaretMoveContext context);
73
    
74
}
(-)a/editor.lib2/src/org/netbeans/spi/editor/caret/package.html (+82 lines)
Line 0 Link Here
1
<!DOCTYPE html>
2
<!--
3
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
5
Copyright 2016 Oracle and/or its affiliates. All rights reserved.
6
7
Oracle and Java are registered trademarks of Oracle and/or its affiliates.
8
Other names may be trademarks of their respective owners.
9
10
The contents of this file are subject to the terms of either the GNU
11
General Public License Version 2 only ("GPL") or the Common
12
Development and Distribution License("CDDL") (collectively, the
13
"License"). You may not use this file except in compliance with the
14
License. You can obtain a copy of the License at
15
http://www.netbeans.org/cddl-gplv2.html
16
or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
17
specific language governing permissions and limitations under the
18
License.  When distributing the software, include this License Header
19
Notice in each file and include the License file at
20
nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
21
particular file as subject to the "Classpath" exception as provided
22
by Oracle in the GPL Version 2 section of the License file that
23
accompanied this code. If applicable, add the following below the
24
License Header, with the fields enclosed by brackets [] replaced by
25
your own identifying information:
26
"Portions Copyrighted [year] [name of copyright owner]"
27
28
If you wish your version of this file to be governed by only the CDDL
29
or only the GPL Version 2, indicate your decision by adding
30
"[Contributor] elects to include this software in this distribution
31
under the [CDDL or GPL Version 2] license." If you do not indicate a
32
single choice of license, a recipient has the option to distribute
33
your version of this file under either the CDDL, the GPL Version 2 or
34
to extend the choice of license to its licensees as provided above.
35
However, if you add GPL Version 2 code and therefore, elected the GPL
36
Version 2 license, then the option applies only if the new code is
37
made subject to such option by the copyright holder.
38
39
Contributor(s):
40
41
Portions Copyrighted 2016 Sun Microsystems, Inc.
42
-->
43
<html>
44
  <head>
45
    <title>org.netbeans.spi.editor.caret</title>
46
  </head>
47
  <body>
48
49
  <p>
50
  The Editor Caret SPI contains interface(s) to be implemented by clients
51
  in order to manipulate the editor carets.
52
  </p>
53
  
54
  <h3>Key parts of the SPI</h3>
55
  
56
  <p>
57
  <code><a href="@org-netbeans-modules-editor-lib2@/org/netbeans/spi/editor/caret/CaretMoveHandler.html">CaretMoveHandler</a></code>
58
  allows to implement a transaction that will move individual carets or change their selections.
59
  <br>
60
  The following code shows how all carets are moved to the
61
  end of the word they are currently on.  
62
  </p>
63
  <pre>
64
  <code>
65
    editorCaret.moveCarets((CaretMoveContext context) -> {
66
        for (CaretInfo ci : context.getOriginalCarets()) {
67
            Position pos = target.getDocument().createPosition(Utilities.getWordEnd(target, ci.getDot()));
68
            context.setDot(ci, pos);
69
        }
70
    });
71
  </code>
72
  </pre>
73
74
  <h3><a name="compatibilty">Backwards compatibility</a></h3>
75
  
76
  <h3><a name="usecases">Use cases</a></h3>
77
  <p>
78
  Use cases are shown in javadoc documentation of particular methods.
79
  </p>
80
  
81
  </body>
82
</html>
(-)a/editor.lib2/src/org/netbeans/spi/editor/codegen/CodeGenerator.java (-1 / +1 lines)
Lines 73-79 Link Here
73
    public void invoke();
73
    public void invoke();
74
    
74
    
75
    /**
75
    /**
76
     * Factory creating code generators.<br/> The factory instances are looked up
76
     * Factory creating code generators.<br> The factory instances are looked up
77
     * by the {@link org.netbeans.api.editor.mimelookup.MimeLookup} so they
77
     * by the {@link org.netbeans.api.editor.mimelookup.MimeLookup} so they
78
     * should be registered in an xml-layer in
78
     * should be registered in an xml-layer in
79
     * <i>Editors/&lt;mime-type&gt;/CodeGenerators</i> directory.
79
     * <i>Editors/&lt;mime-type&gt;/CodeGenerators</i> directory.
(-)a/editor.lib2/src/org/netbeans/spi/editor/codegen/CodeGeneratorContextProvider.java (-1 / +1 lines)
Lines 48-54 Link Here
48
/**
48
/**
49
 * Serves for adding an additonal content to the context which is passed
49
 * Serves for adding an additonal content to the context which is passed
50
 * as a parameter to the {@link CodeGenerator.Factory#create(org.openide.util.Lookup)}
50
 * as a parameter to the {@link CodeGenerator.Factory#create(org.openide.util.Lookup)}
51
 * method.<br/> Instances of this interface are looked up by the
51
 * method.<br> Instances of this interface are looked up by the
52
 * {@link org.netbeans.api.editor.mimelookup.MimeLookup} so they should be
52
 * {@link org.netbeans.api.editor.mimelookup.MimeLookup} so they should be
53
 * registered in an xml-layer in
53
 * registered in an xml-layer in
54
 * <i>Editors/&lt;mime-type&gt;/CodeGeneratorContextProviders</i> directory.
54
 * <i>Editors/&lt;mime-type&gt;/CodeGeneratorContextProviders</i> directory.
(-)a/editor.lib2/src/org/netbeans/spi/editor/document/OnSaveTask.java (-7 / +7 lines)
Lines 68-74 Link Here
68
     * Perform the given runnable under a lock specific for this task.
68
     * Perform the given runnable under a lock specific for this task.
69
     * The runnable will include a call to {@link #performTask() } but it may
69
     * The runnable will include a call to {@link #performTask() } but it may
70
     * also call other tasks if there are multiple ones.
70
     * also call other tasks if there are multiple ones.
71
     * <br/>
71
     * <br>
72
     * For multiple task factories registered their {@link #runLocked(java.lang.Runnable) }
72
     * For multiple task factories registered their {@link #runLocked(java.lang.Runnable) }
73
     * methods will be called subsequently (according to their registration order) in a nested way.
73
     * methods will be called subsequently (according to their registration order) in a nested way.
74
     *
74
     *
Lines 87-94 Link Here
87
87
88
    /**
88
    /**
89
     * Factory for creation of on-save task.
89
     * Factory for creation of on-save task.
90
     * It should be registered in MimeLookup by using mime registration e.g.<br/>
90
     * It should be registered in MimeLookup by using mime registration e.g.<br>
91
     * <code>@MimeRegistration(mimeType="", service=OnSaveTask.Factory.class, position=300)</code><br/>
91
     * <code>@MimeRegistration(mimeType="", service=OnSaveTask.Factory.class, position=300)</code><br>
92
     * Optional 'position' parameter may be used to force an order of tasks.
92
     * Optional 'position' parameter may be used to force an order of tasks.
93
     * Currently there are two default global factories:
93
     * Currently there are two default global factories:
94
     * <ul>
94
     * <ul>
Lines 139-148 Link Here
139
139
140
        /**
140
        /**
141
         * Task may add a custom undoable edit related to its operation by using this method.
141
         * Task may add a custom undoable edit related to its operation by using this method.
142
         * <br/>
142
         * <br>
143
         * When undo would be performed after the save then this edit would be undone
143
         * When undo would be performed after the save then this edit would be undone
144
         * (together with any possible modification changes performed by the task on the underlying document).
144
         * (together with any possible modification changes performed by the task on the underlying document).
145
         * <br/>
145
         * <br>
146
         * Note: this method should only be called during {@link OnSaveTask#performTask() }.
146
         * Note: this method should only be called during {@link OnSaveTask#performTask() }.
147
         * 
147
         * 
148
         * @param edit a custom undoable edit provided by the task.
148
         * @param edit a custom undoable edit provided by the task.
Lines 159-167 Link Here
159
        /**
159
        /**
160
         * Get a root element with zero or more child elements each designating a modified region
160
         * Get a root element with zero or more child elements each designating a modified region
161
         * of a document.
161
         * of a document.
162
         * <br/>
162
         * <br>
163
         * Tasks may use this information to work on modified document parts only.
163
         * Tasks may use this information to work on modified document parts only.
164
         * <br/>
164
         * <br>
165
         * Note: unlike in some other root element implementations here the child elements
165
         * Note: unlike in some other root element implementations here the child elements
166
         * do not fully cover the root element's offset space.
166
         * do not fully cover the root element's offset space.
167
         *
167
         *
(-)a/editor.lib2/src/org/netbeans/spi/editor/highlighting/ShiftHighlightsSequence.java (+76 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.spi.editor.highlighting;
43
44
/**
45
 * Highlights sequence that supports shifts in addition to regular offsets.
46
 * This allows to color individual spaces within a tab character
47
 * or to color extra virtual characters beyond a newline character.
48
 *
49
 * @author Miloslav Metelka
50
 * @since 2.5.0
51
 */
52
public interface ShiftHighlightsSequence extends HighlightsSequence {
53
54
    /**
55
     * Get extra shift "within" a particular character (either tab or newline)
56
     * while {@link #getStartOffset()} points to the tab or newline character.
57
     * <br>
58
     * To highlight second and third space of a tab character at offset == 123
59
     * the {@link #getStartOffset() } == {@link #getEndOffset() } == 123
60
     * and {@link #getStartShift() } == 1 and {@link #getEndShift() } == 3.
61
     *
62
     * @return &gt;=0 start shift.
63
     * @see #getStartOffset() 
64
     */
65
    int getStartShift();
66
    
67
    /**
68
     * Get end shift of a highlight "within" a particular character (either tab or newline)
69
     * while {@link #getEndOffset()} points to the tab or newline character.
70
     *
71
     * @return &gt;=0 end shift.
72
     * @see #getStartShift() 
73
     */
74
    int getEndShift();
75
76
}
(-)a/editor.lib2/src/org/netbeans/spi/editor/typinghooks/TypedBreakInterceptor.java (-1 / +1 lines)
Lines 97-103 Link Here
97
 *   additional rules (eg. correctly replacing selected text, handling insert vs override
97
 *   additional rules (eg. correctly replacing selected text, handling insert vs override
98
 *   modes of the caret, etc). The first interceptor that modifies the insertion text
98
 *   modes of the caret, etc). The first interceptor that modifies the insertion text
99
 *   will win and no other interceptor's <code>insert</code> method will be called.
99
 *   will win and no other interceptor's <code>insert</code> method will be called.
100
 *   <br/>
100
 *   <br>
101
 *   The interceptors are allowed to insert more than just a line break, but the text
101
 *   The interceptors are allowed to insert more than just a line break, but the text
102
 *   they insert has to contain at least one line break. They can also request the
102
 *   they insert has to contain at least one line break. They can also request the
103
 *   inserted text to be reindented. Please see {@link MutableContext#setText(java.lang.String, int, int, int...)}
103
 *   inserted text to be reindented. Please see {@link MutableContext#setText(java.lang.String, int, int, int...)}
(-)a/editor.lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/DirectMergeContainerTest.java (-3 / +9 lines)
Lines 83-93 Link Here
83
    @Test
83
    @Test
84
    public void testRandomMerges() throws Exception {
84
    public void testRandomMerges() throws Exception {
85
        RandomTestContainer container = HighlightsMergeTesting.createContainer();
85
        RandomTestContainer container = HighlightsMergeTesting.createContainer();
86
        container.setName("testRandomMerges");
86
        RandomTestContainer.Round round = HighlightsMergeTesting.addRound(container);
87
        RandomTestContainer.Round round = HighlightsMergeTesting.addRound(container);
87
        round.setOpCount(100);
88
        round.setOpCount(100);
88
        container.setLogOp(false);
89
//        container.setLogOp(true);
89
        container.run(1303832573413L);
90
//        HighlightsMergeTesting.setLogChecks(true);
90
        container.run(0L);
91
        container.runInit(1303832573413L);
92
        container.runOps(1);
93
        container.runOps(1);
94
        container.runOps(0); // Run till end
95
96
//        container.run(0L);
91
    }
97
    }
92
98
93
}
99
}
(-)a/editor.lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsMergeTesting.java (-17 / +41 lines)
Lines 82-87 Link Here
82
82
83
    private static final int DOCUMENT_LENGTH = 1000; // Fixed document length
83
    private static final int DOCUMENT_LENGTH = 1000; // Fixed document length
84
    private static final int MAX_LAYER_HIGHLIGHT_COUNT = 20;
84
    private static final int MAX_LAYER_HIGHLIGHT_COUNT = 20;
85
    
86
    private static boolean logChecks;
87
    
88
    public static void setLogChecks(boolean logChecks) {
89
        HighlightsMergeTesting.logChecks = logChecks;
90
    }
85
91
86
    public static RandomTestContainer createContainer() {
92
    public static RandomTestContainer createContainer() {
87
        RandomTestContainer container = new RandomTestContainer();
93
        RandomTestContainer container = new RandomTestContainer();
Lines 147-167 Link Here
147
            Highlight highlight = new Highlight(offset0, offset1, (AttributeSet)highlights[i+2]);
153
            Highlight highlight = new Highlight(offset0, offset1, (AttributeSet)highlights[i+2]);
148
            highlightList.add(highlight);
154
            highlightList.add(highlight);
149
        }
155
        }
150
        // Possibly do logging
151
        if (context.isLogOp()) {
152
            StringBuilder sb = context.logOpBuilder();
153
            sb.append(" ADD_LAYER(").append(zIndex).append("): ");
154
            sb.append('\n');
155
            int digitCount = ArrayUtilities.digitCount(highlightList.size());
156
            for (int i = 0; i < highlightList.size(); i++) {
157
                Highlight hi = highlightList.get(i);
158
                ArrayUtilities.appendBracketedIndex(sb, i, digitCount);
159
                sb.append(hi);
160
                sb.append('\n');
161
            }
162
            context.logOp(sb);
163
        }
164
        
165
        HighlightsContainer[] layers = compoundHighlightsContainer.getLayers();
156
        HighlightsContainer[] layers = compoundHighlightsContainer.getLayers();
166
        HighlightsContainer[] newLayers = new HighlightsContainer[layers.length + 1];
157
        HighlightsContainer[] newLayers = new HighlightsContainer[layers.length + 1];
167
        zIndex = Math.min(zIndex, layers.length);
158
        zIndex = Math.min(zIndex, layers.length);
Lines 172-177 Link Here
172
            highlight.addTo(bag);
163
            highlight.addTo(bag);
173
        }
164
        }
174
        newLayers[zIndex] = bag;
165
        newLayers[zIndex] = bag;
166
        
167
        // Possibly do logging
168
        if (context.isLogOp()) {
169
            StringBuilder sb = context.logOpBuilder();
170
            sb.append(" ADD_LAYER(").append(zIndex).append("): ");
171
            sb.append('\n');
172
            int digitCount = ArrayUtilities.digitCount(highlightList.size());
173
            for (int i = 0; i < highlightList.size(); i++) {
174
                Highlight hi = highlightList.get(i);
175
                sb.append("Parameter highlight");
176
                ArrayUtilities.appendBracketedIndex(sb, i, digitCount);
177
                sb.append(hi);
178
                sb.append('\n');
179
            }
180
            HighlightsSequence hs = bag.getHighlights(0, Integer.MAX_VALUE);
181
            int i = 0;
182
            while (hs.moveNext()) {
183
                int startOffset = hs.getStartOffset();
184
                int endOffset = hs.getEndOffset();
185
                AttributeSet attrs = hs.getAttributes();
186
                sb.append("Bag highlight");
187
                ArrayUtilities.appendBracketedIndex(sb, i, digitCount); // May be actually more/less digits due to splitting/merging
188
                sb.append(new Highlight(startOffset, endOffset, attrs));
189
                sb.append('\n');
190
            }
191
            context.logOp(sb);
192
        }
175
        compoundHighlightsContainer.setLayers(doc, newLayers);
193
        compoundHighlightsContainer.setLayers(doc, newLayers);
176
        DirectMergeContainer directMergeContainer = directMergeContainer(context);
194
        DirectMergeContainer directMergeContainer = directMergeContainer(context);
177
        directMergeContainer = new DirectMergeContainer(newLayers);
195
        directMergeContainer = new DirectMergeContainer(newLayers);
Lines 210-215 Link Here
210
        int startOffset = 0;
228
        int startOffset = 0;
211
        int endOffset = 0;
229
        int endOffset = 0;
212
        AttributeSet attrs = null;
230
        AttributeSet attrs = null;
231
        int i = 0;
213
        while (expectedSeq.moveNext()) {
232
        while (expectedSeq.moveNext()) {
214
            startOffset = expectedSeq.getStartOffset();
233
            startOffset = expectedSeq.getStartOffset();
215
            endOffset = expectedSeq.getEndOffset();
234
            endOffset = expectedSeq.getEndOffset();
Lines 226-234 Link Here
226
            assert (attrs.equals(testAttrs)) : "attrs=" + attrs + " != testAttrs=" + testAttrs
245
            assert (attrs.equals(testAttrs)) : "attrs=" + attrs + " != testAttrs=" + testAttrs
227
                    + ", startOffset=" + startOffset + ", endOffset=" + endOffset + " seq: " + testSeq;
246
                    + ", startOffset=" + startOffset + ", endOffset=" + endOffset + " seq: " + testSeq;
228
            if (logChecks) {
247
            if (logChecks) {
229
                StringBuilder sb = context.logOpBuilder().append("Passed: ").append(new Highlight(startOffset, endOffset, attrs));
248
                StringBuilder sb = context.logOpBuilder();
249
                sb.append("DMContainer passed highlight");
250
                ArrayUtilities.appendBracketedIndex(sb, i, 1); // Unknown digit count
251
                sb.append(new Highlight(startOffset, endOffset, attrs));
252
                sb.append('\n');
230
                context.logOp(sb);
253
                context.logOp(sb);
231
            }
254
            }
255
            i++;
232
        }
256
        }
233
    }
257
    }
234
258
Lines 286-292 Link Here
286
310
287
        @Override
311
        @Override
288
        protected void check(Context context) throws Exception {
312
        protected void check(Context context) throws Exception {
289
            checkMerge(context, false);
313
            checkMerge(context, HighlightsMergeTesting.logChecks);
290
        }
314
        }
291
        
315
        
292
    }
316
    }
(-)a/editor.search/nbproject/project.xml (-8 / +8 lines)
Lines 117-130 Link Here
117
                    </run-dependency>
117
                    </run-dependency>
118
                </dependency>
118
                </dependency>
119
                <dependency>
119
                <dependency>
120
                    <code-name-base>org.openide.util.ui</code-name-base>
121
                    <build-prerequisite/>
122
                    <compile-dependency/>
123
                    <run-dependency>
124
                        <specification-version>9.3</specification-version>
125
                    </run-dependency>
126
                </dependency>
127
                <dependency>
128
                    <code-name-base>org.openide.util</code-name-base>
120
                    <code-name-base>org.openide.util</code-name-base>
129
                    <build-prerequisite/>
121
                    <build-prerequisite/>
130
                    <compile-dependency/>
122
                    <compile-dependency/>
Lines 140-145 Link Here
140
                        <specification-version>8.12</specification-version>
132
                        <specification-version>8.12</specification-version>
141
                    </run-dependency>
133
                    </run-dependency>
142
                </dependency>
134
                </dependency>
135
                <dependency>
136
                    <code-name-base>org.openide.util.ui</code-name-base>
137
                    <build-prerequisite/>
138
                    <compile-dependency/>
139
                    <run-dependency>
140
                        <specification-version>9.3</specification-version>
141
                    </run-dependency>
142
                </dependency>
143
            </module-dependencies>
143
            </module-dependencies>
144
            <test-dependencies>
144
            <test-dependencies>
145
                <test-type>
145
                <test-type>
(-)a/editor.search/src/org/netbeans/modules/editor/search/Bundle.properties (+2 lines)
Lines 6-13 Link Here
6
TOOLTIP_CloseIncrementalSearchSidebar=Close Incremental Search Sidebar (Esc)
6
TOOLTIP_CloseIncrementalSearchSidebar=Close Incremental Search Sidebar (Esc)
7
CTL_Find=&Find: 
7
CTL_Find=&Find: 
8
TOOLTIP_IncrementalSearchText=Search Text (ENTER - Find Next, Shift+ENTER - Find Previous)
8
TOOLTIP_IncrementalSearchText=Search Text (ENTER - Find Next, Shift+ENTER - Find Previous)
9
TOOLTIP_SelectAllText=Place a selection around all occurrences in the editor
9
TOOLTIP_ReplaceText=Replace Text (ENTER - Replace, Shift+ENTER - Replace All)
10
TOOLTIP_ReplaceText=Replace Text (ENTER - Replace, Shift+ENTER - Replace All)
10
CTL_FindNext=Next
11
CTL_FindNext=Next
12
CTL_SelectAll=Select
11
CTL_FindPrevious=Previous
13
CTL_FindPrevious=Previous
12
TT_MatchCase=Match case (Alt + C)
14
TT_MatchCase=Match case (Alt + C)
13
TT_WholeWords=Whole word (Alt + O)
15
TT_WholeWords=Whole word (Alt + O)
(-)a/editor.search/src/org/netbeans/modules/editor/search/EditorFindSupport.java (-2 / +23 lines)
Lines 76-86 Link Here
76
import org.netbeans.api.editor.settings.FontColorNames;
76
import org.netbeans.api.editor.settings.FontColorNames;
77
import org.netbeans.api.editor.settings.SimpleValueNames;
77
import org.netbeans.api.editor.settings.SimpleValueNames;
78
import org.netbeans.api.editor.NavigationHistory;
78
import org.netbeans.api.editor.NavigationHistory;
79
import org.netbeans.api.editor.caret.EditorCaret;
79
import org.netbeans.modules.editor.lib2.ComponentUtils;
80
import org.netbeans.modules.editor.lib2.ComponentUtils;
80
import org.netbeans.modules.editor.lib2.DocUtils;
81
import org.netbeans.modules.editor.lib2.DocUtils;
81
import org.netbeans.modules.editor.lib2.highlighting.BlockHighlighting;
82
import org.netbeans.modules.editor.lib2.highlighting.BlockHighlighting;
82
import org.netbeans.modules.editor.lib2.highlighting.Factory;
83
import org.netbeans.modules.editor.lib2.highlighting.Factory;
83
import org.netbeans.modules.editor.search.DocumentFinder.FindReplaceResult;
84
import org.netbeans.modules.editor.search.DocumentFinder.FindReplaceResult;
85
import org.openide.util.Exceptions;
84
import org.openide.util.NbBundle;
86
import org.openide.util.NbBundle;
85
87
86
/**
88
/**
Lines 131-136 Link Here
131
    public static final String FIND_BLOCK_SEARCH = "find-block-search"; //NOI18N
133
    public static final String FIND_BLOCK_SEARCH = "find-block-search"; //NOI18N
132
    public static final String FIND_BLOCK_SEARCH_START = "find-block-search-start"; //NOI18N
134
    public static final String FIND_BLOCK_SEARCH_START = "find-block-search-start"; //NOI18N
133
    public static final String FIND_BLOCK_SEARCH_END = "find-block-search-end"; //NOI18N
135
    public static final String FIND_BLOCK_SEARCH_END = "find-block-search-end"; //NOI18N
136
    public static final String ADD_MULTICARET = "add-multi-caret"; //NOI18N
134
137
135
    private static final String FOUND_LOCALE = "find-found"; // NOI18N
138
    private static final String FOUND_LOCALE = "find-found"; // NOI18N
136
    private static final String NOT_FOUND_LOCALE = "find-not-found"; // NOI18N
139
    private static final String NOT_FOUND_LOCALE = "find-not-found"; // NOI18N
Lines 203-208 Link Here
203
        props.put(FIND_REG_EXP, Boolean.FALSE);
206
        props.put(FIND_REG_EXP, Boolean.FALSE);
204
        props.put(FIND_HISTORY, Integer.valueOf(30));
207
        props.put(FIND_HISTORY, Integer.valueOf(30));
205
        props.put(FIND_PRESERVE_CASE, Boolean.FALSE);
208
        props.put(FIND_PRESERVE_CASE, Boolean.FALSE);
209
        props.put(ADD_MULTICARET, Boolean.FALSE);
206
210
207
        return props;
211
        return props;
208
    }
212
    }
Lines 440-445 Link Here
440
        }
444
        }
441
        return back;
445
        return back;
442
    }
446
    }
447
    
448
    private void addCaretSelectText(JTextComponent c, int start, int end, boolean back) {
449
        Caret eCaret = c.getCaret();
450
        ensureVisible(c, start, end);
451
        if (eCaret instanceof EditorCaret) {
452
            EditorCaret caret = (EditorCaret) eCaret;
453
            try {
454
                caret.addCaret(c.getDocument().createPosition(end), c.getDocument().createPosition(start));
455
            } catch (BadLocationException ex) {
456
                Exceptions.printStackTrace(ex);
457
            }
458
        }
459
    }
443
460
444
    private void selectText(JTextComponent c, int start, int end, boolean back){
461
    private void selectText(JTextComponent c, int start, int end, boolean back){
445
        Caret caret = c.getCaret();
462
        Caret caret = c.getCaret();
Lines 585-591 Link Here
585
                    blk = result.getFoundPositions();
602
                    blk = result.getFoundPositions();
586
                }
603
                }
587
                if (blk != null) {
604
                if (blk != null) {
588
                    selectText(c, blk[0], blk[1], back);
605
                    if (Boolean.TRUE.equals(props.get(EditorFindSupport.ADD_MULTICARET))) {
606
                        addCaretSelectText(c, blk[0], blk[1], back);
607
                    } else {
608
                        selectText(c, blk[0], blk[1], back);
609
                    }
589
                    String msg = NbBundle.getMessage(EditorFindSupport.class, FOUND_LOCALE, findWhat, DocUtils.debugPosition(c.getDocument(), Integer.valueOf(blk[0])));
610
                    String msg = NbBundle.getMessage(EditorFindSupport.class, FOUND_LOCALE, findWhat, DocUtils.debugPosition(c.getDocument(), Integer.valueOf(blk[0])));
590
//                    String msg = exp + NbBundle.getMessage(EditorFindSupport.class, FOUND_LOCALE)
611
//                    String msg = exp + NbBundle.getMessage(EditorFindSupport.class, FOUND_LOCALE)
591
//                                 + ' ' + DocUtils.debugPosition(c.getDocument(), blk[0]);
612
//                                 + ' ' + DocUtils.debugPosition(c.getDocument(), blk[0]);
Lines 609-615 Link Here
609
                                                    EditorFindSupport.class, NOT_FOUND_LOCALE, findWhat), IMPORTANCE_FIND_OR_REPLACE);
630
                                                    EditorFindSupport.class, NOT_FOUND_LOCALE, findWhat), IMPORTANCE_FIND_OR_REPLACE);
610
                    // issue 14189 - selection was not removed
631
                    // issue 14189 - selection was not removed
611
                    c.getCaret().setDot(c.getCaret().getDot());
632
                    c.getCaret().setDot(c.getCaret().getDot());
612
                }
633
                    }
613
            } catch (BadLocationException e) {
634
            } catch (BadLocationException e) {
614
                LOG.log(Level.WARNING, e.getMessage(), e);
635
                LOG.log(Level.WARNING, e.getMessage(), e);
615
            }
636
            }
(-)a/editor.search/src/org/netbeans/modules/editor/search/SearchBar.java (-4 / +69 lines)
Lines 65-70 Link Here
65
import org.netbeans.api.editor.mimelookup.MimeLookup;
65
import org.netbeans.api.editor.mimelookup.MimeLookup;
66
import org.netbeans.api.editor.mimelookup.MimePath;
66
import org.netbeans.api.editor.mimelookup.MimePath;
67
import org.netbeans.api.editor.settings.SimpleValueNames;
67
import org.netbeans.api.editor.settings.SimpleValueNames;
68
import org.netbeans.api.editor.caret.EditorCaret;
68
import org.netbeans.api.search.ReplacePattern;
69
import org.netbeans.api.search.ReplacePattern;
69
import org.netbeans.api.search.SearchHistory;
70
import org.netbeans.api.search.SearchHistory;
70
import org.netbeans.api.search.SearchPattern;
71
import org.netbeans.api.search.SearchPattern;
Lines 79-84 Link Here
79
import org.openide.filesystems.FileUtil;
80
import org.openide.filesystems.FileUtil;
80
import org.openide.util.Exceptions;
81
import org.openide.util.Exceptions;
81
import org.openide.util.NbBundle;
82
import org.openide.util.NbBundle;
83
import org.openide.util.Pair;
82
import org.openide.util.WeakListeners;
84
import org.openide.util.WeakListeners;
83
85
84
/**
86
/**
Lines 109-114 Link Here
109
    private boolean hadFocusOnIncSearchTextField = false;
111
    private boolean hadFocusOnIncSearchTextField = false;
110
    private final JButton findNextButton;
112
    private final JButton findNextButton;
111
    private final JButton findPreviousButton;
113
    private final JButton findPreviousButton;
114
    private final JButton selectAllButton;
112
    private final JToggleButton matchCase;
115
    private final JToggleButton matchCase;
113
    private final JToggleButton wholeWords;
116
    private final JToggleButton wholeWords;
114
    private final JToggleButton regexp;
117
    private final JToggleButton regexp;
Lines 164-169 Link Here
164
        incSearchTextField.getDocument().addDocumentListener(incSearchTextFieldListener);
167
        incSearchTextField.getDocument().addDocumentListener(incSearchTextFieldListener);
165
        addEnterKeystrokeFindNextTo(incSearchTextField);
168
        addEnterKeystrokeFindNextTo(incSearchTextField);
166
        addShiftEnterKeystrokeFindPreviousTo(incSearchTextField);
169
        addShiftEnterKeystrokeFindPreviousTo(incSearchTextField);
170
        addAltEnterKeystrokeSelect(incSearchTextField);
167
        if (getCurrentKeyMapProfile().startsWith("Emacs")) { // NOI18N
171
        if (getCurrentKeyMapProfile().startsWith("Emacs")) { // NOI18N
168
            emacsProfileFix(incSearchTextField);
172
            emacsProfileFix(incSearchTextField);
169
        }
173
        }
Lines 205-210 Link Here
205
        });
209
        });
206
        add(findNextButton);
210
        add(findNextButton);
207
        
211
        
212
        selectAllButton = SearchButton.createButton("org/netbeans/modules/editor/search/resources/select_all.png", "CTL_SelectAll"); // NOI18N
213
        selectAllButton.setToolTipText(NbBundle.getMessage(SearchBar.class, "TOOLTIP_SelectAllText")); //NOI18N
214
        selectAllButton.addActionListener(new ActionListener() {
215
216
            @Override
217
            public void actionPerformed(ActionEvent e) {
218
                selectAll();
219
            }
220
        });
221
        add(selectAllButton);
222
        
208
        final JToolBar.Separator rightSeparator = new JToolBar.Separator();
223
        final JToolBar.Separator rightSeparator = new JToolBar.Separator();
209
        rightSeparator.setOrientation(SwingConstants.VERTICAL);
224
        rightSeparator.setOrientation(SwingConstants.VERTICAL);
210
        add(rightSeparator);
225
        add(rightSeparator);
Lines 434-440 Link Here
434
            @Override
449
            @Override
435
            public void keyPressed(KeyEvent e) {
450
            public void keyPressed(KeyEvent e) {
436
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
451
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
437
                    looseFocus();
452
                    if(looseFocus()) {
453
                        e.consume();
454
                    };
438
                    ReplaceBar replaceBarInstance = ReplaceBar.getInstance(SearchBar.this);
455
                    ReplaceBar replaceBarInstance = ReplaceBar.getInstance(SearchBar.this);
439
                    if (replaceBarInstance.isVisible()) {
456
                    if (replaceBarInstance.isVisible()) {
440
                        replaceBarInstance.looseFocus();
457
                        replaceBarInstance.looseFocus();
Lines 540-545 Link Here
540
        return pcl;
557
        return pcl;
541
    }
558
    }
542
559
560
     private void addAltEnterKeystrokeSelect(JTextComponent incSearchTextField) {
561
        incSearchTextField.getInputMap().put(KeyStroke.getKeyStroke(
562
                KeyEvent.VK_ENTER, InputEvent.ALT_MASK, true),
563
                "select-all"); // NOI18N
564
        incSearchTextField.getActionMap().put("select-all", // NOI18N
565
                new AbstractAction() {
566
567
            @Override
568
            public void actionPerformed(ActionEvent e) {
569
                selectAll();
570
            }
571
        });
572
    }
573
     
543
    private void addShiftEnterKeystrokeFindPreviousTo(JTextComponent incSearchTextField) {
574
    private void addShiftEnterKeystrokeFindPreviousTo(JTextComponent incSearchTextField) {
544
        incSearchTextField.getInputMap().put(KeyStroke.getKeyStroke(
575
        incSearchTextField.getInputMap().put(KeyStroke.getKeyStroke(
545
                KeyEvent.VK_ENTER, InputEvent.SHIFT_MASK, true),
576
                KeyEvent.VK_ENTER, InputEvent.SHIFT_MASK, true),
Lines 811-820 Link Here
811
        incSearchTextField.getDocument().addDocumentListener(incSearchTextFieldListener);
842
        incSearchTextField.getDocument().addDocumentListener(incSearchTextFieldListener);
812
    }
843
    }
813
844
814
    public void looseFocus() {
845
    public boolean looseFocus() {
815
        hadFocusOnIncSearchTextField = false;
846
        hadFocusOnIncSearchTextField = false;
816
        if (!isVisible()) {
847
        if (!isVisible()) {
817
            return;
848
            return false;
818
        }
849
        }
819
        EditorFindSupport.getInstance().setBlockSearchHighlight(0, 0);
850
        EditorFindSupport.getInstance().setBlockSearchHighlight(0, 0);
820
        EditorFindSupport.getInstance().incSearchReset();
851
        EditorFindSupport.getInstance().incSearchReset();
Lines 839-844 Link Here
839
                searchProps.saveToPrefs();
870
                searchProps.saveToPrefs();
840
            }
871
            }
841
        });
872
        });
873
        return true;
842
    }
874
    }
843
875
844
    private void incrementalSearch() {
876
    private void incrementalSearch() {
Lines 891-896 Link Here
891
    void findPrevious() {
923
    void findPrevious() {
892
        find(false);
924
        find(false);
893
    }
925
    }
926
    
927
    void selectAll() {
928
        EditorFindSupport findSupport = EditorFindSupport.getInstance();
929
        JTextComponent textComponent = getActualTextComponent();
930
        Document doc = textComponent.getDocument();
931
        Caret caret = textComponent.getCaret();
932
        if(caret instanceof EditorCaret) {
933
            EditorCaret editorCaret = (EditorCaret) caret;
934
            try {
935
                int[] blocks = findSupport.getBlocks(new int[]{-1, -1}, doc, 0, doc.getLength());
936
                if(blocks[0] >= 0 && blocks.length % 2 == 0) {
937
                    List<Position> newCarets = new ArrayList<>(editorCaret.getCarets().size() << 1);
938
                    for (int i = 0; i < blocks.length; i += 2) {
939
                        int start = blocks[i];
940
                        int end = blocks[i+1];
941
                        if(start == -1 || end == -1) {
942
                            break;
943
                        }
944
                        Position startPos = doc.createPosition(start);
945
                        Position endPos = doc.createPosition(end);
946
                        newCarets.add(endPos);
947
                        newCarets.add(startPos);
948
                    }
949
                    
950
                    editorCaret.replaceCarets(newCarets);
951
                    
952
                    textComponent.requestFocusInWindow();
953
                }
954
            } catch (BadLocationException ex) {
955
                Exceptions.printStackTrace(ex);
956
            }
957
        }
958
    }
894
959
895
    public int getNumOfMatches() {
960
    public int getNumOfMatches() {
896
        return numOfMatches;
961
        return numOfMatches;
Lines 1221-1224 Link Here
1221
        String lfID = laf.getID();
1286
        String lfID = laf.getID();
1222
        return lf.equals(lfID);
1287
        return lf.equals(lfID);
1223
    }
1288
    }
1224
}
1289
}
(-)a/editor.search/src/org/netbeans/modules/editor/search/actions/FindSelectionAction.java (-59 / +62 lines)
Lines 1-5 Link Here
1
package org.netbeans.modules.editor.search.actions;
2
3
/*
1
/*
4
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5
 *
3
 *
Lines 42-74 Link Here
42
 * Portions Copyrighted 2013 Sun Microsystems, Inc.
40
 * Portions Copyrighted 2013 Sun Microsystems, Inc.
43
 */
41
 */
44
42
43
package org.netbeans.modules.editor.search.actions;
45
44
46
import java.awt.event.ActionEvent;
45
import java.awt.event.ActionEvent;
47
import java.util.HashMap;
46
import java.util.HashMap;
48
import java.util.Map;
47
import java.util.ArrayList;
48
import java.util.List;
49
import java.util.logging.Level;
49
import java.util.logging.Level;
50
import java.util.logging.Logger;
50
import java.util.logging.Logger;
51
import javax.swing.text.BadLocationException;
51
import javax.swing.text.BadLocationException;
52
import javax.swing.text.Caret;
52
import javax.swing.text.Caret;
53
import javax.swing.text.Document;
53
import javax.swing.text.JTextComponent;
54
import javax.swing.text.JTextComponent;
55
import javax.swing.text.Position;
54
import org.netbeans.api.editor.EditorActionRegistration;
56
import org.netbeans.api.editor.EditorActionRegistration;
57
import org.netbeans.api.editor.caret.EditorCaret;
55
import org.netbeans.editor.BaseDocument;
58
import org.netbeans.editor.BaseDocument;
56
import org.netbeans.editor.BaseKit;
57
import org.netbeans.editor.EditorUI;
59
import org.netbeans.editor.EditorUI;
58
import org.netbeans.editor.Utilities;
60
import org.netbeans.editor.Utilities;
59
import org.netbeans.modules.editor.search.EditorFindSupport;
61
import org.netbeans.modules.editor.search.EditorFindSupport;
60
import org.netbeans.spi.editor.AbstractEditorAction;
62
import org.netbeans.spi.editor.AbstractEditorAction;
63
import org.openide.util.Exceptions;
64
import org.openide.util.Pair;
61
65
62
/** Finds either selection or if there's no selection it finds
66
/**
63
 * the word where the cursor is standing.
67
 * Select
64
 */
68
 */
65
// NOI18N
69
// NOI18N
66
@EditorActionRegistration(name = BaseKit.findSelectionAction, iconResource = "org/netbeans/modules/editor/search/resources/find_selection.png") // NOI18N
70
@EditorActionRegistration(
67
public class FindSelectionAction extends AbstractEditorAction {
71
        name = "addCaretSelectAll")
68
    static final long serialVersionUID = -5601618936504699565L;
72
public class AddCaretSelectAllAction extends AbstractEditorAction {
69
    private static final Logger LOGGER = Logger.getLogger(FindSelectionAction.class.getName());
70
73
71
    public FindSelectionAction() {
74
    private static final Logger LOGGER = Logger.getLogger(AddCaretSelectAllAction.class.getName());
75
76
    public AddCaretSelectAllAction() {
72
        super();
77
        super();
73
    }
78
    }
74
79
Lines 76-138 Link Here
76
    @SuppressWarnings("unchecked")
81
    @SuppressWarnings("unchecked")
77
    public void actionPerformed(ActionEvent evt, JTextComponent target) {
82
    public void actionPerformed(ActionEvent evt, JTextComponent target) {
78
        if (target != null) {
83
        if (target != null) {
79
            EditorFindSupport findSupport = EditorFindSupport.getInstance();
80
            Caret caret = target.getCaret();
84
            Caret caret = target.getCaret();
81
            int dotPos = caret.getDot();
85
            if (!Utilities.isSelectionShowing(caret)) {
82
            HashMap<String, Object> props = new HashMap<>(findSupport.createDefaultFindProperties());
83
            String searchWord = null;
84
            boolean revert = false;
85
            Boolean originalValue = null;
86
            Map<String, Object> revertMap = (Map<String, Object>) props.get(EditorFindSupport.REVERT_MAP);
87
            Boolean revertValue = revertMap != null ? (Boolean) revertMap.get(EditorFindSupport.FIND_WHOLE_WORDS) : null;
88
            if (Utilities.isSelectionShowing(caret)) {
89
                // valid selection
90
                searchWord = target.getSelectedText();
91
                originalValue = (Boolean) props.put(EditorFindSupport.FIND_WHOLE_WORDS, Boolean.FALSE);
92
                if (Boolean.FALSE.equals(revertValue)) {
93
                    revertMap.remove(EditorFindSupport.FIND_WHOLE_WORDS);
94
                } else {
95
                    revert = !Boolean.FALSE.equals(originalValue);
96
                }
97
            } else {
98
                // no selection, get current word
99
                try {
86
                try {
100
                    searchWord = Utilities.getIdentifier((BaseDocument) target.getDocument(), dotPos);
87
                    int[] identifierBlock = Utilities.getIdentifierBlock((BaseDocument) target.getDocument(), caret.getDot());
101
                    originalValue = (Boolean) props.put(EditorFindSupport.FIND_WHOLE_WORDS, Boolean.TRUE);
88
                    if (identifierBlock != null) {
102
                    if (Boolean.TRUE.equals(revertValue)) {
89
                        caret.setDot(identifierBlock[0]);
103
                        revertMap.remove(EditorFindSupport.FIND_WHOLE_WORDS);
90
                        caret.moveDot(identifierBlock[1]);
104
                    } else {
105
                        revert = !Boolean.TRUE.equals(originalValue);
106
                    }
91
                    }
107
                } catch (BadLocationException e) {
92
                } catch (BadLocationException e) {
108
                    LOGGER.log(Level.WARNING, null, e);
93
                    LOGGER.log(Level.WARNING, null, e);
109
                }
94
                }
110
            }
95
            }
111
            if (searchWord != null) {
96
112
                int n = searchWord.indexOf('\n');
97
            EditorFindSupport findSupport = EditorFindSupport.getInstance();
113
                if (n >= 0) {
98
            HashMap<String, Object> props = new HashMap<>(findSupport.createDefaultFindProperties());
114
                    searchWord = searchWord.substring(0, n);
99
            String searchWord = target.getSelectedText();
115
                }
100
            int n = searchWord.indexOf('\n');
116
                props.put(EditorFindSupport.FIND_WHAT, searchWord);
101
            if (n >= 0) {
117
                if (revert) {
102
                searchWord = searchWord.substring(0, n);
118
                    revertMap = new HashMap<>();
103
            }
119
                    revertMap.put(EditorFindSupport.FIND_WHOLE_WORDS, originalValue != null ? originalValue : Boolean.FALSE);
104
            props.put(EditorFindSupport.FIND_WHAT, searchWord);
120
                    props.put(EditorFindSupport.REVERT_MAP, revertMap);
105
            Document doc = target.getDocument();
121
                }
106
            EditorUI eui = org.netbeans.editor.Utilities.getEditorUI(target);
122
                props.put(EditorFindSupport.FIND_BLOCK_SEARCH, Boolean.FALSE);
107
            if (eui.getComponent().getClientProperty("AsTextField") == null) { //NOI18N
123
                props.put(EditorFindSupport.FIND_BLOCK_SEARCH_START, null);
108
                findSupport.setFocusedTextComponent(eui.getComponent());
124
                props.put(EditorFindSupport.FIND_BLOCK_SEARCH_END, null);
109
            }
125
                EditorUI eui = org.netbeans.editor.Utilities.getEditorUI(target);
110
            findSupport.putFindProperties(props);
126
                if (eui.getComponent().getClientProperty("AsTextField") == null) {
111
            findSupport.find(null, false);
127
                    //NOI18N
112
128
                    findSupport.setFocusedTextComponent(eui.getComponent());
113
            if (caret instanceof EditorCaret) {
129
                }
114
                EditorCaret editorCaret = (EditorCaret) caret;
130
                findSupport.putFindProperties(props);
115
                try {
131
                if (findSupport.find(null, false)) {
116
                    int[] blocks = findSupport.getBlocks(new int[]{-1, -1}, doc, 0, doc.getLength());
132
                    findSupport.addToHistory(new EditorFindSupport.SPW((String) props.get(EditorFindSupport.FIND_WHAT), (Boolean) props.get(EditorFindSupport.FIND_WHOLE_WORDS), (Boolean) props.get(EditorFindSupport.FIND_MATCH_CASE), (Boolean) props.get(EditorFindSupport.FIND_REG_EXP)));
117
                    if (blocks[0] >= 0 && blocks.length % 2 == 0) {
118
                        List<Position> newCarets = new ArrayList<>();
119
120
                        for (int i = 0; i < blocks.length; i += 2) {
121
                            int start = blocks[i];
122
                            int end = blocks[i + 1];
123
                            if (start == -1 || end == -1) {
124
                                break;
125
                            }
126
                            Position startPos = doc.createPosition(start);
127
                            Position endPos = doc.createPosition(end);
128
                            newCarets.add(endPos);
129
                            newCarets.add(startPos);
130
                        }
131
132
                        editorCaret.replaceCarets(newCarets);
133
                    }
134
                } catch (BadLocationException ex) {
135
                    Exceptions.printStackTrace(ex);
133
                }
136
                }
134
            }
137
            }
138
135
        }
139
        }
136
    }
140
    }
137
138
}
141
}
(-)a/editor.search/src/org/netbeans/modules/editor/search/actions/FindSelectionAction.java (-54 / +23 lines)
Lines 1-5 Link Here
1
package org.netbeans.modules.editor.search.actions;
2
3
/*
1
/*
4
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5
 *
3
 *
Lines 42-51 Link Here
42
 * Portions Copyrighted 2013 Sun Microsystems, Inc.
40
 * Portions Copyrighted 2013 Sun Microsystems, Inc.
43
 */
41
 */
44
42
43
package org.netbeans.modules.editor.search.actions;
45
44
46
import java.awt.event.ActionEvent;
45
import java.awt.event.ActionEvent;
47
import java.util.HashMap;
46
import java.util.HashMap;
48
import java.util.Map;
49
import java.util.logging.Level;
47
import java.util.logging.Level;
50
import java.util.logging.Logger;
48
import java.util.logging.Logger;
51
import javax.swing.text.BadLocationException;
49
import javax.swing.text.BadLocationException;
Lines 53-74 Link Here
53
import javax.swing.text.JTextComponent;
51
import javax.swing.text.JTextComponent;
54
import org.netbeans.api.editor.EditorActionRegistration;
52
import org.netbeans.api.editor.EditorActionRegistration;
55
import org.netbeans.editor.BaseDocument;
53
import org.netbeans.editor.BaseDocument;
56
import org.netbeans.editor.BaseKit;
57
import org.netbeans.editor.EditorUI;
54
import org.netbeans.editor.EditorUI;
58
import org.netbeans.editor.Utilities;
55
import org.netbeans.editor.Utilities;
59
import org.netbeans.modules.editor.search.EditorFindSupport;
56
import org.netbeans.modules.editor.search.EditorFindSupport;
60
import org.netbeans.spi.editor.AbstractEditorAction;
57
import org.netbeans.spi.editor.AbstractEditorAction;
61
58
62
/** Finds either selection or if there's no selection it finds
59
/** 
63
 * the word where the cursor is standing.
60
 * Select a whole word if there is not selection on word otherwise it adds a new caret for selected text
64
 */
61
 */
65
// NOI18N
62
// NOI18N
66
@EditorActionRegistration(name = BaseKit.findSelectionAction, iconResource = "org/netbeans/modules/editor/search/resources/find_selection.png") // NOI18N
63
@EditorActionRegistration(
67
public class FindSelectionAction extends AbstractEditorAction {
64
        name = "addCaretSelectNext")
68
    static final long serialVersionUID = -5601618936504699565L;
65
public class AddCaretSelectNextAction extends AbstractEditorAction {
69
    private static final Logger LOGGER = Logger.getLogger(FindSelectionAction.class.getName());
66
    private static final Logger LOGGER = Logger.getLogger(AddCaretSelectNextAction.class.getName());
70
67
71
    public FindSelectionAction() {
68
    public AddCaretSelectNextAction() {
72
        super();
69
        super();
73
    }
70
    }
74
71
Lines 76-135 Link Here
76
    @SuppressWarnings("unchecked")
73
    @SuppressWarnings("unchecked")
77
    public void actionPerformed(ActionEvent evt, JTextComponent target) {
74
    public void actionPerformed(ActionEvent evt, JTextComponent target) {
78
        if (target != null) {
75
        if (target != null) {
79
            EditorFindSupport findSupport = EditorFindSupport.getInstance();
80
            Caret caret = target.getCaret();
76
            Caret caret = target.getCaret();
81
            int dotPos = caret.getDot();
82
            HashMap<String, Object> props = new HashMap<>(findSupport.createDefaultFindProperties());
83
            String searchWord = null;
84
            boolean revert = false;
85
            Boolean originalValue = null;
86
            Map<String, Object> revertMap = (Map<String, Object>) props.get(EditorFindSupport.REVERT_MAP);
87
            Boolean revertValue = revertMap != null ? (Boolean) revertMap.get(EditorFindSupport.FIND_WHOLE_WORDS) : null;
88
            if (Utilities.isSelectionShowing(caret)) {
77
            if (Utilities.isSelectionShowing(caret)) {
89
                // valid selection
78
                EditorFindSupport findSupport = EditorFindSupport.getInstance();
90
                searchWord = target.getSelectedText();
79
                HashMap<String, Object> props = new HashMap<>(findSupport.createDefaultFindProperties());
91
                originalValue = (Boolean) props.put(EditorFindSupport.FIND_WHOLE_WORDS, Boolean.FALSE);
80
                String searchWord = target.getSelectedText();
92
                if (Boolean.FALSE.equals(revertValue)) {
93
                    revertMap.remove(EditorFindSupport.FIND_WHOLE_WORDS);
94
                } else {
95
                    revert = !Boolean.FALSE.equals(originalValue);
96
                }
97
            } else {
98
                // no selection, get current word
99
                try {
100
                    searchWord = Utilities.getIdentifier((BaseDocument) target.getDocument(), dotPos);
101
                    originalValue = (Boolean) props.put(EditorFindSupport.FIND_WHOLE_WORDS, Boolean.TRUE);
102
                    if (Boolean.TRUE.equals(revertValue)) {
103
                        revertMap.remove(EditorFindSupport.FIND_WHOLE_WORDS);
104
                    } else {
105
                        revert = !Boolean.TRUE.equals(originalValue);
106
                    }
107
                } catch (BadLocationException e) {
108
                    LOGGER.log(Level.WARNING, null, e);
109
                }
110
            }
111
            if (searchWord != null) {
112
                int n = searchWord.indexOf('\n');
81
                int n = searchWord.indexOf('\n');
113
                if (n >= 0) {
82
                if (n >= 0) {
114
                    searchWord = searchWord.substring(0, n);
83
                    searchWord = searchWord.substring(0, n);
115
                }
84
                }
116
                props.put(EditorFindSupport.FIND_WHAT, searchWord);
85
                props.put(EditorFindSupport.FIND_WHAT, searchWord);
117
                if (revert) {
86
                props.put(EditorFindSupport.ADD_MULTICARET, Boolean.TRUE);
118
                    revertMap = new HashMap<>();
119
                    revertMap.put(EditorFindSupport.FIND_WHOLE_WORDS, originalValue != null ? originalValue : Boolean.FALSE);
120
                    props.put(EditorFindSupport.REVERT_MAP, revertMap);
121
                }
122
                props.put(EditorFindSupport.FIND_BLOCK_SEARCH, Boolean.FALSE);
123
                props.put(EditorFindSupport.FIND_BLOCK_SEARCH_START, null);
124
                props.put(EditorFindSupport.FIND_BLOCK_SEARCH_END, null);
125
                EditorUI eui = org.netbeans.editor.Utilities.getEditorUI(target);
87
                EditorUI eui = org.netbeans.editor.Utilities.getEditorUI(target);
126
                if (eui.getComponent().getClientProperty("AsTextField") == null) {
88
                if (eui.getComponent().getClientProperty("AsTextField") == null) { //NOI18N
127
                    //NOI18N
128
                    findSupport.setFocusedTextComponent(eui.getComponent());
89
                    findSupport.setFocusedTextComponent(eui.getComponent());
129
                }
90
                }
130
                findSupport.putFindProperties(props);
91
                findSupport.putFindProperties(props);
131
                if (findSupport.find(null, false)) {
92
                findSupport.find(null, false);
132
                    findSupport.addToHistory(new EditorFindSupport.SPW((String) props.get(EditorFindSupport.FIND_WHAT), (Boolean) props.get(EditorFindSupport.FIND_WHOLE_WORDS), (Boolean) props.get(EditorFindSupport.FIND_MATCH_CASE), (Boolean) props.get(EditorFindSupport.FIND_REG_EXP)));
93
            } else {
94
                try {
95
                    int[] identifierBlock = Utilities.getIdentifierBlock((BaseDocument) target.getDocument(), caret.getDot());
96
                    if (identifierBlock != null) {
97
                        caret.setDot(identifierBlock[0]);
98
                        caret.moveDot(identifierBlock[1]);
99
                    }
100
                } catch (BadLocationException e) {
101
                    LOGGER.log(Level.WARNING, null, e);
133
                }
102
                }
134
            }
103
            }
135
        }
104
        }
(-)a/editor.search/src/org/netbeans/modules/editor/search/actions/Bundle.properties (-1 / +3 lines)
Lines 5-8 Link Here
5
find-next=Find Next Occurrence
5
find-next=Find Next Occurrence
6
find-previous=Find Previous Occurrence
6
find-previous=Find Previous Occurrence
7
find-selection=Find Selection
7
find-selection=Find Selection
8
toggle-highlight-search=Toggle Highlight Search
8
toggle-highlight-search=Toggle Highlight Search
9
addCaretSelectNext=Add Selection For Next Occurrence
10
addCaretSelectAll=Select All Occurrences
(-)a/editor.util/apichanges.xml (+13 lines)
Lines 106-111 Link Here
106
<!-- ACTUAL CHANGES BEGIN HERE: -->
106
<!-- ACTUAL CHANGES BEGIN HERE: -->
107
107
108
  <changes>
108
  <changes>
109
      <change id="GapListAddAll">
110
          <summary>Added methods to append elements to a GapList</summary>
111
          <version major="1" minor="64"/>
112
          <date day="22" month="2" year="2016"/>
113
          <author login="mmetelka"/>
114
          <compatibility addition="yes" deletion="no" modification="no" binary="compatible" semantic="compatible"/>
115
          <description>
116
              Added supplementary methods to GapList to accompany the existing
117
              methods to add elements.
118
          </description>
119
          <class name="GapList" package="org.netbeans.lib.editor.util"/>
120
          <issue number="257888"/>
121
      </change>
109
      <change id="EventPropertyStorage.test">
122
      <change id="EventPropertyStorage.test">
110
          <summary>Added test for property storage support on events</summary>
123
          <summary>Added test for property storage support on events</summary>
111
          <version major="1" minor="60"/>
124
          <version major="1" minor="60"/>
(-)a/editor.util/manifest.mf (-1 / +1 lines)
Lines 2-5 Link Here
2
OpenIDE-Module: org.netbeans.modules.editor.util/1
2
OpenIDE-Module: org.netbeans.modules.editor.util/1
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/lib/editor/util/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/lib/editor/util/Bundle.properties
4
AutoUpdate-Show-In-Client: false
4
AutoUpdate-Show-In-Client: false
5
OpenIDE-Module-Specification-Version: 1.63
5
OpenIDE-Module-Specification-Version: 1.64
(-)a/editor.util/src/org/netbeans/lib/editor/util/GapList.java (-3 / +79 lines)
Lines 122-127 Link Here
122
        this.gapLength = elementData.length - size;
122
        this.gapLength = elementData.length - size;
123
    }
123
    }
124
    
124
    
125
    private GapList(E[] data, int gapStart, int gapLength) {
126
        this.elementData = data;
127
        this.gapStart = gapStart;
128
        this.gapLength = gapLength;
129
    }
130
    
125
    /**
131
    /**
126
     * Trims the capacity of this <tt>GapList</tt> instance to be the
132
     * Trims the capacity of this <tt>GapList</tt> instance to be the
127
     * list's current size.  An application can use this operation to minimize
133
     * list's current size.  An application can use this operation to minimize
Lines 314-320 Link Here
314
            throw new InternalError();
320
            throw new InternalError();
315
        }
321
        }
316
    }
322
    }
317
323
    
324
    /**
325
     * Create shallow copy of this gap list.
326
     * @return copy of this gap list with zero extra capacity.
327
     * @since 1.63
328
     */
329
    public GapList<E> copy() {
330
        int size = size();
331
        E[] data = allocateElementsArray(size);
332
        copyAllData(data);
333
        return new GapList<E>(data, size, 0);
334
    }
335
    
318
    /**
336
    /**
319
     * @deprecated use {@link #copyElements(int, int, Object[], int)} which performs the same operation
337
     * @deprecated use {@link #copyElements(int, int, Object[], int)} which performs the same operation
320
     */
338
     */
Lines 556-561 Link Here
556
    }
574
    }
557
    
575
    
558
    /**
576
    /**
577
     * Appends elements from the specified collection to the end of
578
     * this list, in the order that they are returned by the
579
     * specified collection's iterator.  The behavior of this operation is
580
     * undefined if the specified Collection is modified while the operation
581
     * is in progress.  (This implies that the behavior of this call is
582
     * undefined if the specified Collection is this list, and this
583
     * list is nonempty.)
584
     *
585
     * @param c collection containing the elements to be inserted into this list.
586
     * @param off offset in the collection pointing to first element to copy.
587
     * @param len number of elements to copy from the collection.
588
     * @return <tt>true</tt> if this list changed as a result of the call.
589
     * @throws    IndexOutOfBoundsException if index out of range <tt>(index
590
     *		  &lt; 0 || index &gt; size())</tt>.
591
     * @throws    NullPointerException if the specified Collection is null.
592
     * @since 1.64
593
     */
594
    public boolean addAll(Collection<? extends E> c, int off, int len) {
595
        return addArray(size(), c.toArray(), off, len);
596
    }
597
598
    /**
559
     * Inserts all of the elements in the specified Collection into this
599
     * Inserts all of the elements in the specified Collection into this
560
     * list, starting at the specified position.  Shifts the element
600
     * list, starting at the specified position.  Shifts the element
561
     * currently at that position (if any) and any subsequent elements to
601
     * currently at that position (if any) and any subsequent elements to
Lines 575-586 Link Here
575
        return addArray(index, c.toArray());
615
        return addArray(index, c.toArray());
576
    }
616
    }
577
617
578
    /*
618
    /**
619
     * Inserts elements in the specified Collection into this
620
     * list, starting at the specified position.  Shifts the element
621
     * currently at that position (if any) and any subsequent elements to
622
     * the right (increases their indices).  The new elements will appear
623
     * in the list in the order that they are returned by the
624
     * specified Collection's iterator.
625
     *
626
     * @param index index at which to insert first element
627
     *		    from the specified collection.
628
     * @param c collection containing the elements to be inserted into this list.
629
     * @param off offset in the collection pointing to first element to copy.
630
     * @param len number of elements to copy from the collection.
631
     * @return <tt>true</tt> if this list changed as a result of the call.
632
     * @throws    IndexOutOfBoundsException if index out of range <tt>(index
633
     *		  &lt; 0 || index &gt; size())</tt>.
634
     * @throws    NullPointerException if the specified Collection is null.
635
     * @since 1.64
636
     */
637
    public boolean addAll(int index, Collection<? extends E> c, int off, int len) {
638
        return addArray(index, c.toArray(), off, len);
639
    }
640
641
    /**
642
     * Inserts all elements of the given array at the end of this list.
643
     *
644
     * @param elements array of elements to insert.
645
     * @return <tt>true</tt> if this list changed as a result of the call.
646
     * @since 1.64
647
     */
648
    public boolean addArray(Object[] elements) {
649
        return addArray(size(), elements, 0, elements.length);
650
    }
651
652
    /**
579
     * Inserts all elements from the given array into this list, starting
653
     * Inserts all elements from the given array into this list, starting
580
     * at the given index.
654
     * at the given index.
581
     *
655
     *
582
     * @param index index at which to insert first element from the array.
656
     * @param index index at which to insert first element from the array.
583
     * @param elements array of elements to insert.
657
     * @param elements array of elements to insert.
658
     * @return <tt>true</tt> if this list changed as a result of the call.
584
     */
659
     */
585
    public boolean addArray(int index, Object[] elements) {
660
    public boolean addArray(int index, Object[] elements) {
586
        return addArray(index, elements, 0, elements.length);
661
        return addArray(index, elements, 0, elements.length);
Lines 594-599 Link Here
594
     * @param elements array of elements from which to insert elements.
669
     * @param elements array of elements from which to insert elements.
595
     * @param off offset in the elements pointing to first element to copy.
670
     * @param off offset in the elements pointing to first element to copy.
596
     * @param len number of elements to copy from the elements array.
671
     * @param len number of elements to copy from the elements array.
672
     * @return <tt>true</tt> if this list changed as a result of the call.
597
     */
673
     */
598
    public boolean addArray(int index, Object[] elements, int off, int len) {
674
    public boolean addArray(int index, Object[] elements, int off, int len) {
599
        int size = size();
675
        int size = size();
Lines 646-652 Link Here
646
        
722
        
647
        return oldValue;
723
        return oldValue;
648
    }
724
    }
649
725
    
650
    /**
726
    /**
651
     * Removes elements at the given index.
727
     * Removes elements at the given index.
652
     *
728
     *
(-)a/editor.util/test/unit/src/org/netbeans/lib/editor/util/GapListRandomTest.java (-10 / +73 lines)
Lines 45-50 Link Here
45
package org.netbeans.lib.editor.util;
45
package org.netbeans.lib.editor.util;
46
46
47
import java.util.ArrayList;
47
import java.util.ArrayList;
48
import java.util.Arrays;
48
import java.util.Random;
49
import java.util.Random;
49
import junit.framework.TestCase;
50
import junit.framework.TestCase;
50
51
Lines 65-70 Link Here
65
    private static final int REMOVE_RANGE_RATIO_1 = 10;
66
    private static final int REMOVE_RANGE_RATIO_1 = 10;
66
    private static final int CLEAR_RATIO_1 = 5;
67
    private static final int CLEAR_RATIO_1 = 5;
67
    private static final int SET_RATIO_1 = 50;
68
    private static final int SET_RATIO_1 = 50;
69
    private static final int COPY_RATIO_1 = 50;
68
    
70
    
69
    private static final int OP_COUNT_2 = 10000;
71
    private static final int OP_COUNT_2 = 10000;
70
    private static final int ADD_RATIO_2 = 50;
72
    private static final int ADD_RATIO_2 = 50;
Lines 74-79 Link Here
74
    private static final int REMOVE_RANGE_RATIO_2 = 10;
76
    private static final int REMOVE_RANGE_RATIO_2 = 10;
75
    private static final int CLEAR_RATIO_2 = 3;
77
    private static final int CLEAR_RATIO_2 = 3;
76
    private static final int SET_RATIO_2 = 50;
78
    private static final int SET_RATIO_2 = 50;
79
    private static final int COPY_RATIO_2 = 50;
77
    
80
    
78
    private ArrayList<Object> al;
81
    private ArrayList<Object> al;
79
    
82
    
Lines 99-112 Link Here
99
        
102
        
100
        
103
        
101
        testRound(random, OP_COUNT_1, ADD_RATIO_1, ADD_ALL_RATIO_1, ADD_ALL_MAX_COUNT_1,
104
        testRound(random, OP_COUNT_1, ADD_RATIO_1, ADD_ALL_RATIO_1, ADD_ALL_MAX_COUNT_1,
102
            REMOVE_RATIO_1, REMOVE_RANGE_RATIO_1, CLEAR_RATIO_1, SET_RATIO_1);
105
            REMOVE_RATIO_1, REMOVE_RANGE_RATIO_1, CLEAR_RATIO_1, SET_RATIO_1, COPY_RATIO_1);
103
        testRound(random, OP_COUNT_2, ADD_RATIO_2, ADD_ALL_RATIO_2, ADD_ALL_MAX_COUNT_2,
106
        testRound(random, OP_COUNT_2, ADD_RATIO_2, ADD_ALL_RATIO_2, ADD_ALL_MAX_COUNT_2,
104
            REMOVE_RATIO_2, REMOVE_RANGE_RATIO_2, CLEAR_RATIO_2, SET_RATIO_2);
107
            REMOVE_RATIO_2, REMOVE_RANGE_RATIO_2, CLEAR_RATIO_2, SET_RATIO_2, COPY_RATIO_2);
105
    }
108
    }
106
    
109
    
107
    private void testRound(Random random, int opCount,
110
    private void testRound(Random random, int opCount,
108
    int addRatio, int addAllRatio, int addAllMaxCount,
111
    int addRatio, int addAllRatio, int addAllMaxCount,
109
    int removeRatio, int removeRangeRatio, int clearRatio, int setRatio) {
112
    int removeRatio, int removeRangeRatio, int clearRatio, int setRatio, int copyRatio) {
110
        
113
        
111
        int ratioSum = addRatio + addAllRatio + removeRatio + removeRangeRatio
114
        int ratioSum = addRatio + addAllRatio + removeRatio + removeRangeRatio
112
            + clearRatio + setRatio;
115
            + clearRatio + setRatio;
Lines 125-142 Link Here
125
128
126
            } else if ((r -= addAllRatio) < 0) {
129
            } else if ((r -= addAllRatio) < 0) {
127
                int count = (int)(random.nextDouble() * addAllMaxCount);
130
                int count = (int)(random.nextDouble() * addAllMaxCount);
131
                int index = (int)(random.nextDouble() * (al.size() + 1));
132
                int off = (int)(random.nextDouble() * count);
133
                int len = (int)(random.nextDouble() * (count + 1 - off));
128
                ArrayList<Object> l = new ArrayList<Object>();
134
                ArrayList<Object> l = new ArrayList<Object>();
129
                for (int i = count; i > 0; i--) {
135
                for (int i = count; i > 0; i--) {
130
                    l.add(new Object());
136
                    l.add(new Object());
131
                }
137
                }
138
                int methodType = (int)(random.nextDouble() * 5);
139
                switch (methodType) {
140
                    case 0: // addAll(Collection<? extends E> c)
141
                        al.addAll(l);
142
                        if (debug) {
143
                            debugOp(op, "addAll(index, collection)"); // NOI18N
144
                        }
145
                        gl.addAll(l);
146
                        break;
147
                    case 1: // addAll(Collection<? extends E> c, int off, int len)
148
                        al.addAll(l.subList(off, off + len));
149
                        if (debug) {
150
                            debugOp(op, "addAll(collection, off, len)"); // NOI18N
151
                        }
152
                        gl.addAll(l, off, len);
153
                        break;
154
                    case 2: // addAll(int index, Collection<? extends E> c)
155
                        al.addAll(index, l);
156
                        if (debug) {
157
                            debugOp(op, "addAll(index, collection)"); // NOI18N
158
                        }
159
                        gl.addAll(index, l);
160
                        break;
161
                    case 3: // addAll(int index, Collection<? extends E> c, int off, int len)
162
                        al.addAll(index, l.subList(off, off + len));
163
                        if (debug) {
164
                            debugOp(op, "addAll(index, collection, off, len)"); // NOI18N
165
                        }
166
                        gl.addAll(index, l, off, len);
167
                        break;
168
                    case 4: // addArray(Object[] elements)
169
                        al.addAll(l);
170
                        if (debug) {
171
                            debugOp(op, "addArray(array)"); // NOI18N
172
                        }
173
                        gl.addArray(l.toArray());
174
                        break;
175
                        
176
                    default:
177
                        throw new AssertionError();
178
                }
132
179
133
                Object o = new Object();
134
                int index = (int)(al.size() * random.nextDouble());
135
                al.addAll(index, l);
136
                if (debug) {
137
                    debugOp(op, "addAll() at index=" + index); // NOI18N
138
                }
139
                gl.addAll(index, l);
140
180
141
            } else if ((r -= removeRatio) < 0) {
181
            } else if ((r -= removeRatio) < 0) {
142
                if (al.size() > 0) { // is anything to remove
182
                if (al.size() > 0) { // is anything to remove
Lines 178-183 Link Here
178
                    }
218
                    }
179
                    gl.set(index, o);
219
                    gl.set(index, o);
180
                }
220
                }
221
            } else if ((r -= copyRatio) < 0) {
222
                int methodType = (int)(random.nextDouble() * 3);
223
                int alSize = al.size();
224
                int off = (int)(random.nextDouble() * alSize);
225
                int len = (int)(random.nextDouble() * (alSize + 1 - off));
226
                switch (methodType) {
227
                    case 0:
228
                        GapList copyList = gl.copy();
229
                        assertEquals("Lists differ", al, copyList);
230
                        break;
231
                    case 1:
232
                        ArrayList<Object> targetList = new ArrayList<Object>();
233
                        gl.copyElements(off, len, targetList);
234
                        assertEquals("Lists differ", al.subList(off, off + len), targetList);
235
                        break;
236
                    case 2:
237
                        Object[] targetArray = new Object[len];
238
                        gl.copyElements(off, len, targetArray, 0);
239
                        assertEquals("Lists differ", al.subList(off, off + len), Arrays.asList(targetArray));
240
                        break;
241
                    default:
242
                        throw new AssertionError();
243
                }
181
            }
244
            }
182
245
183
            checkConsistency();
246
            checkConsistency();
(-)a/editor.util/test/unit/src/org/netbeans/lib/editor/util/random/RandomTestContainer.java (-5 / +6 lines)
Lines 400-406 Link Here
400
                    }
400
                    }
401
                    context.incrementOpCount();
401
                    context.incrementOpCount();
402
                }
402
                }
403
                container.dumpString(container.name() + " finished successfully."); // NOI18N
403
                container.dumpString(container.name() + " finished successfully.\n"); // NOI18N
404
                ok = true;
404
                ok = true;
405
            } finally {
405
            } finally {
406
                if (!ok) {
406
                if (!ok) {
Lines 486-492 Link Here
486
        
486
        
487
        private StringBuilder logOpBuilder = new StringBuilder(256);
487
        private StringBuilder logOpBuilder = new StringBuilder(256);
488
488
489
        private int maxOpsLogged = 10;
489
        private int maxOpsLogged = 4; // By default log only last 4 operations
490
490
491
        private final Map<Object,Object> properties;
491
        private final Map<Object,Object> properties;
492
492
Lines 600-610 Link Here
600
                logOpBuilder.setLength(0);
600
                logOpBuilder.setLength(0);
601
                addOpLog(opLog);
601
                addOpLog(opLog);
602
            }
602
            }
603
            if (!opSuccess) { // Dump the logs
603
            if (true) { // Dump the op logs always (not only after failure) - might want to examine something
604
                StringBuilder sb = new StringBuilder(1024);
604
                StringBuilder sb = new StringBuilder(1024);
605
                while (!opLogs.isEmpty()) {
605
                for (String opLog : opLogs) {
606
                    sb.append(opLogs.removeFirst());
606
                    sb.append(opLog);
607
                }
607
                }
608
                opLogs.clear();
608
                container.dumpString(sb.toString());
609
                container.dumpString(sb.toString());
609
            }
610
            }
610
        }
611
        }
(-)a/editor/src/org/netbeans/modules/editor/resources/NetBeans-keybindings.xml (+9 lines)
Lines 129-143 Link Here
129
    <bind actionName="paste-from-clipboard" key="D-V"/>
129
    <bind actionName="paste-from-clipboard" key="D-V"/>
130
    <bind actionName="paste-from-clipboard" key="S-INSERT"/>
130
    <bind actionName="paste-from-clipboard" key="S-INSERT"/>
131
    <bind actionName="clipboard-history" key="DS-D"/>
131
    <bind actionName="clipboard-history" key="DS-D"/>
132
    <bind actionName="addCaretSelectNext" key="D-D"/>
133
    <bind actionName="addCaretSelectAll" key="O-F3"/>
132
    <bind actionName="redo" key="D-Y"/>
134
    <bind actionName="redo" key="D-Y"/>
133
    <bind actionName="redo" key="DS-Z"/>
135
    <bind actionName="redo" key="DS-Z"/>
134
    <bind actionName="remove-line" key="D-E"/>
136
    <bind actionName="remove-line" key="D-E"/>
135
    <!-- <bind actionName="remove-line-begin" key="D-U"/> -->
137
    <!-- <bind actionName="remove-line-begin" key="D-U"/> -->
136
    <bind actionName="remove-tab" key="S-TAB"/>
138
    <bind actionName="remove-tab" key="S-TAB"/>
139
<!--
137
    <bind actionName="move-selection-else-line-up" key="OS-UP"/>
140
    <bind actionName="move-selection-else-line-up" key="OS-UP"/>
138
    <bind actionName="move-selection-else-line-up" key="OS-KP_UP"/>
141
    <bind actionName="move-selection-else-line-up" key="OS-KP_UP"/>
139
    <bind actionName="move-selection-else-line-down" key="OS-DOWN"/>
142
    <bind actionName="move-selection-else-line-down" key="OS-DOWN"/>
140
    <bind actionName="move-selection-else-line-down" key="OS-KP_DOWN"/>
143
    <bind actionName="move-selection-else-line-down" key="OS-KP_DOWN"/>
144
    -->
145
    <bind actionName="add-selection-else-caret-up" key="OS-UP"/>
146
    <bind actionName="add-selection-else-caret-up" key="OS-KP_UP"/>
147
    <bind actionName="add-selection-else-caret-down" key="OS-DOWN"/>
148
    <bind actionName="add-selection-else-caret-down" key="OS-KP_DOWN"/>
149
    <bind actionName="remove-last-caret" key="OS-U"/>
141
    <bind actionName="move-code-element-up" key="OS-PAGE_UP"/>
150
    <bind actionName="move-code-element-up" key="OS-PAGE_UP"/>
142
    <bind actionName="move-code-element-down" key="OS-PAGE_DOWN"/>
151
    <bind actionName="move-code-element-down" key="OS-PAGE_DOWN"/>
143
    <bind actionName="copy-selection-else-line-up" key="DS-UP"/>
152
    <bind actionName="copy-selection-else-line-up" key="DS-UP"/>
(-)a/html.editor/src/org/netbeans/modules/html/editor/APIAccessor.java (+63 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.html.editor;
43
44
import org.netbeans.modules.html.editor.api.HtmlKit;
45
46
/**
47
 *
48
 * @author Ralph Ruijs
49
 */
50
public abstract class APIAccessor {
51
    public static APIAccessor DEFAULT;
52
53
    static {
54
        Class c = HtmlKit.class;
55
        try {
56
            Class.forName(c.getName(), true, c.getClassLoader());
57
        } catch (Exception ex) {
58
            ex.printStackTrace();
59
        }
60
    }
61
62
    public abstract void setContentType(HtmlKit kit, String contentType);
63
}
(-)a/html.editor/src/org/netbeans/modules/html/editor/HtmlTransferHandler.java (-621 / +123 lines)
Lines 41-699 Link Here
41
 */
41
 */
42
package org.netbeans.modules.html.editor;
42
package org.netbeans.modules.html.editor;
43
43
44
import java.awt.datatransfer.Clipboard;
44
import java.awt.datatransfer.DataFlavor;
45
import java.awt.datatransfer.DataFlavor;
45
import java.awt.datatransfer.Transferable;
46
import java.awt.datatransfer.Transferable;
46
import java.awt.datatransfer.UnsupportedFlavorException;
47
import java.awt.event.InputEvent;
47
import java.awt.im.InputContext;
48
import javax.swing.Icon;
48
import java.io.ByteArrayInputStream;
49
import java.io.IOException;
50
import java.io.InputStream;
51
import java.io.Reader;
52
import java.io.StringReader;
53
import java.io.StringWriter;
54
import javax.swing.JComponent;
49
import javax.swing.JComponent;
55
import javax.swing.JEditorPane;
50
import javax.swing.JEditorPane;
56
import javax.swing.JPasswordField;
57
import javax.swing.TransferHandler;
51
import javax.swing.TransferHandler;
58
import javax.swing.plaf.UIResource;
52
import javax.swing.plaf.UIResource;
59
import javax.swing.text.BadLocationException;
60
import javax.swing.text.Document;
61
import javax.swing.text.EditorKit;
53
import javax.swing.text.EditorKit;
62
import javax.swing.text.JTextComponent;
54
import javax.swing.text.JTextComponent;
63
import javax.swing.text.Position;
55
import org.netbeans.modules.html.editor.api.HtmlKit;
64
56
65
/* !!!!!!!!!!!!!!!!!!!!!
57
/* !!!!!!!!!!!!!!!!!!!!!
66
 *
58
 *
67
 * classes bellow were taken from BasicTextUI and rewritten in the place marked
59
 * fix for issue #43309
68
 * with [REWRITE_PLACE]. This needs to be done to fix the issue #43309
69
 *
60
 *
70
 * !!!!!!!!!!!!!!!!!!!!!
61
 * !!!!!!!!!!!!!!!!!!!!!
71
 */
62
 */
72
public class HtmlTransferHandler extends TransferHandler implements UIResource {
63
public class HtmlTransferHandler extends TransferHandler implements UIResource {
73
    private JTextComponent exportComp;
64
    
74
    private boolean shouldRemove;
65
    public static void install(JTextComponent c) {
75
    private int p0;
66
        TransferHandler origHandler = c.getTransferHandler();
76
    private int p1;
67
        if (!(origHandler instanceof HtmlTransferHandler)) {
68
            c.setTransferHandler(new HtmlTransferHandler(c.getTransferHandler()));
69
        }
70
    }
71
    
72
    private final TransferHandler delegate;
77
73
78
    /**
74
    public HtmlTransferHandler(TransferHandler delegate) {
79
     * Try to find a flavor that can be used to import a Transferable. The
75
        this.delegate = delegate;
80
     * set of usable flavors are tried in the following order:
76
    }
81
     * <ol>
77
    
82
     * <li>First, an attempt is made to find a flavor matching the content
78
    TransferHandler getDelegate() {
83
     * type of the EditorKit for the component.
79
        return delegate;
84
     * <li>Second, an attempt to find a text/plain flavor is made.
80
    }
85
     * <li>Third, an attempt to find a flavor representing a String
81
    
86
     * reference in the same VM is made.
82
    @Override
87
     * <li>Lastly, DataFlavor.stringFlavor is searched for.
83
    public boolean canImport(TransferSupport support) {
88
     * </ol>
84
        if(support.getComponent() instanceof JEditorPane &&
89
     */
85
                ((JEditorPane) support.getComponent()).getEditorKit() instanceof HtmlKit) {
90
    protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) {
86
            HtmlKit kit = (HtmlKit) ((JEditorPane) support.getComponent()).getEditorKit();
91
        DataFlavor plainFlavor = null;
87
            APIAccessor.DEFAULT.setContentType(kit, "text/plain");
92
        DataFlavor refFlavor = null;
88
            try {
93
        DataFlavor stringFlavor = null;
89
                return delegate.canImport(support);
94
        if (c instanceof JEditorPane) {
90
            } finally {
95
            for (int i = 0; i < flavors.length; i++) {
91
                APIAccessor.DEFAULT.setContentType(kit, HtmlKit.HTML_MIME_TYPE);
96
                String mime = flavors[i].getMimeType();
97
                if (mime.startsWith(((JEditorPane) c).getEditorKit().getContentType())) {
98
                    //return flavors[i]; [REWRITE_PLACE]
99
                } else if (plainFlavor == null && mime.startsWith("text/plain")) {
100
                    //NOI18N
101
                    plainFlavor = flavors[i];
102
                } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref") && flavors[i].getRepresentationClass() == java.lang.String.class) {
103
                    refFlavor = flavors[i];
104
                } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
105
                    stringFlavor = flavors[i];
106
                }
107
            }
92
            }
108
            if (plainFlavor != null) {
93
        } else {
109
                return plainFlavor;
94
            return delegate.canImport(support);
110
            } else if (refFlavor != null) {
95
        }
111
                return refFlavor;
96
    }
112
            } else if (stringFlavor != null) {
97
    
113
                return stringFlavor;
98
    @Override
99
    public boolean importData(TransferSupport support) {
100
        if(support.getComponent() instanceof JEditorPane &&
101
                ((JEditorPane) support.getComponent()).getEditorKit() instanceof HtmlKit) {
102
            HtmlKit kit = (HtmlKit) ((JEditorPane) support.getComponent()).getEditorKit();
103
            APIAccessor.DEFAULT.setContentType(kit, "text/plain");
104
            try {
105
                return delegate.importData(support);
106
            } finally {
107
                APIAccessor.DEFAULT.setContentType(kit, HtmlKit.HTML_MIME_TYPE);
114
            }
108
            }
115
            return null;
109
        } else {
110
            return delegate.importData(support);
116
        }
111
        }
117
        for (int i = 0; i < flavors.length; i++) {
112
    }
118
            String mime = flavors[i].getMimeType();
113
119
            if (mime.startsWith("text/plain")) {
114
    @Override
120
                //NOI18N
115
    public boolean canImport(JComponent comp, DataFlavor[] t) {
121
                return flavors[i];
116
        if(comp instanceof JEditorPane &&
122
            } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref") && flavors[i].getRepresentationClass() == java.lang.String.class) {
117
                ((JEditorPane) comp).getEditorKit() instanceof HtmlKit) {
123
                refFlavor = flavors[i];
118
            HtmlKit kit = (HtmlKit) ((JEditorPane) comp).getEditorKit();
124
            } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
119
            APIAccessor.DEFAULT.setContentType(kit, "text/plain");
125
                stringFlavor = flavors[i];
120
            try {
121
                return delegate.canImport(comp, t);
122
            } finally {
123
                APIAccessor.DEFAULT.setContentType(kit, HtmlKit.HTML_MIME_TYPE);
126
            }
124
            }
125
        } else {
126
            return delegate.canImport(comp, t);
127
        }
127
        }
128
        if (refFlavor != null) {
128
    }
129
            return refFlavor;
129
130
        } else if (stringFlavor != null) {
130
    @Override
131
            return stringFlavor;
131
    public boolean importData(JComponent comp, Transferable t) {
132
        if(comp instanceof JEditorPane &&
133
                ((JEditorPane) comp).getEditorKit() instanceof HtmlKit) {
134
            HtmlKit kit = (HtmlKit) ((JEditorPane) comp).getEditorKit();
135
            APIAccessor.DEFAULT.setContentType(kit, "text/plain");
136
            try {
137
                return delegate.importData(comp, t);
138
            } finally {
139
                APIAccessor.DEFAULT.setContentType(kit, HtmlKit.HTML_MIME_TYPE);
140
            }
141
        } else {
142
            return delegate.importData(comp, t);
143
        }
144
    }
145
    
146
    @Override
147
    protected Transferable createTransferable(JComponent c) {
148
        try {
149
            java.lang.reflect.Method method = delegate.getClass().getDeclaredMethod(
150
                    "createTransferable", // NOI18N
151
                    new Class[]{javax.swing.JComponent.class});
152
            method.setAccessible(true);
153
154
            return (Transferable) method.invoke(delegate, new Object[]{c});
155
        } catch (NoSuchMethodException ex) {
156
            ex.printStackTrace();
157
        } catch (IllegalAccessException ex) {
158
            ex.printStackTrace();
159
        } catch (java.lang.reflect.InvocationTargetException ex) {
160
            ex.printStackTrace();
132
        }
161
        }
133
        return null;
162
        return null;
134
    }
163
    }
135
164
136
    /**
165
    @Override
137
     * Import the given stream data into the text component.
166
    public void exportAsDrag(JComponent comp, InputEvent e, int action) {
138
     */
167
        delegate.exportAsDrag(comp, e, action);
139
    protected void handleReaderImport(Reader in, JTextComponent c, boolean useRead) throws BadLocationException, IOException {
168
    }
140
        if (useRead) {
169
141
            int startPosition = c.getSelectionStart();
170
    @Override
142
            int endPosition = c.getSelectionEnd();
171
    protected void exportDone(JComponent source, Transferable data, int action) {
143
            int length = endPosition - startPosition;
172
        try {
144
            EditorKit kit = c.getUI().getEditorKit(c);
173
            java.lang.reflect.Method method = delegate.getClass().getDeclaredMethod(
145
            Document doc = c.getDocument();
174
                    "exportDone", // NOI18N
146
            if (length > 0) {
175
                    new Class[]{javax.swing.JComponent.class, Transferable.class, int.class});
147
                doc.remove(startPosition, length);
176
            method.setAccessible(true);
148
            }
177
            method.invoke(delegate, new Object[]{source, data, new Integer(action)});
149
            kit.read(in, doc, startPosition);
178
        } catch (NoSuchMethodException ex) {
150
        } else {
179
            ex.printStackTrace();
151
            char[] buff = new char[1024];
180
        } catch (IllegalAccessException ex) {
152
            int nch;
181
            ex.printStackTrace();
153
            boolean lastWasCR = false;
182
        } catch (java.lang.reflect.InvocationTargetException ex) {
154
            int last;
183
            ex.printStackTrace();
155
            StringBuffer sbuff = null;
156
            // Read in a block at a time, mapping \r\n to \n, as well as single
157
            // \r to \n.
158
            while ((nch = in.read(buff, 0, buff.length)) != -1) {
159
                if (sbuff == null) {
160
                    sbuff = new StringBuffer(nch);
161
                }
162
                last = 0;
163
                for (int counter = 0; counter < nch; counter++) {
164
                    switch (buff[counter]) {
165
                        case '\r':
166
                            if (lastWasCR) {
167
                                if (counter == 0) {
168
                                    sbuff.append('\n');
169
                                } else {
170
                                    buff[counter - 1] = '\n';
171
                                }
172
                            } else {
173
                                lastWasCR = true;
174
                            }
175
                            break;
176
                        case '\n':
177
                            if (lastWasCR) {
178
                                if (counter > (last + 1)) {
179
                                    sbuff.append(buff, last, counter - last - 1);
180
                                }
181
                                // else nothing to do, can skip \r, next write will
182
                                // write \n
183
                                lastWasCR = false;
184
                                last = counter;
185
                            }
186
                            break;
187
                        default:
188
                            if (lastWasCR) {
189
                                if (counter == 0) {
190
                                    sbuff.append('\n');
191
                                } else {
192
                                    buff[counter - 1] = '\n';
193
                                }
194
                                lastWasCR = false;
195
                            }
196
                            break;
197
                    }
198
                }
199
                if (last < nch) {
200
                    if (lastWasCR) {
201
                        if (last < (nch - 1)) {
202
                            sbuff.append(buff, last, nch - last - 1);
203
                        }
204
                    } else {
205
                        sbuff.append(buff, last, nch - last);
206
                    }
207
                }
208
            }
209
            if (lastWasCR) {
210
                sbuff.append('\n');
211
            }
212
            c.replaceSelection(sbuff != null ? sbuff.toString() : ""); //NOI18N
213
        }
184
        }
214
    }
185
    }
215
186
216
    // --- TransferHandler methods ------------------------------------
187
    @Override
217
    /**
188
    public void exportToClipboard(JComponent c, Clipboard clip, int action) throws IllegalStateException {
218
     * This is the type of transfer actions supported by the source. Some
189
        delegate.exportToClipboard(c, clip, action);
219
     * models are not mutable, so a transfer operation of COPY only should
190
    }
220
     * be advertised in that case.
191
221
     *
222
     * @param c The component holding the data to be transfered. This
223
     * argument is provided to enable sharing of TransferHandlers by
224
     * multiple components.
225
     * @return This is implemented to return NONE if the component is a
226
     * JPasswordField since exporting data via user gestures is not allowed.
227
     * If the text component is editable, COPY_OR_MOVE is returned,
228
     * otherwise just COPY is allowed.
229
     */
230
    @Override
192
    @Override
231
    public int getSourceActions(JComponent c) {
193
    public int getSourceActions(JComponent c) {
232
        int actions = NONE;
194
        return delegate.getSourceActions(c);
233
        if (!(c instanceof JPasswordField)) {
234
            if (((JTextComponent) c).isEditable()) {
235
                actions = COPY_OR_MOVE;
236
            } else {
237
                actions = COPY;
238
            }
239
        }
240
        return actions;
241
    }
195
    }
242
196
243
    /**
244
     * Create a Transferable to use as the source for a data transfer.
245
     *
246
     * @param comp The component holding the data to be transfered. This
247
     * argument is provided to enable sharing of TransferHandlers by
248
     * multiple components.
249
     * @return The representation of the data to be transfered.
250
     *
251
     */
252
    @Override
197
    @Override
253
    protected Transferable createTransferable(JComponent comp) {
198
    public Icon getVisualRepresentation(Transferable t) {
254
        exportComp = (JTextComponent) comp;
199
        return delegate.getVisualRepresentation(t);
255
        shouldRemove = true;
256
        p0 = exportComp.getSelectionStart();
257
        p1 = exportComp.getSelectionEnd();
258
        return (p0 != p1) ? (new HtmlTransferable(exportComp, p0, p1)) : null;
259
    }
200
    }
260
261
    /**
262
     * This method is called after data has been exported. This method
263
     * should remove the data that was transfered if the action was MOVE.
264
     *
265
     * @param source The component that was the source of the data.
266
     * @param data The data that was transferred or possibly null if the
267
     * action is <code>NONE</code>.
268
     * @param action The actual action that was performed.
269
     */
270
    @Override
271
    protected void exportDone(JComponent source, Transferable data, int action) {
272
        // only remove the text if shouldRemove has not been set to
273
        // false by importData and only if the action is a move
274
        if (shouldRemove && action == MOVE) {
275
            HtmlTransferable t = (HtmlTransferable) data;
276
            t.removeText();
277
        }
278
        exportComp = null;
279
    }
280
281
    /**
282
     * This method causes a transfer to a component from a clipboard or a
283
     * DND drop operation. The Transferable represents the data to be
284
     * imported into the component.
285
     *
286
     * @param comp The component to receive the transfer. This argument is
287
     * provided to enable sharing of TransferHandlers by multiple
288
     * components.
289
     * @param t The data to import
290
     * @return true if the data was inserted into the component, false
291
     * otherwise.
292
     */
293
    @Override
294
    public boolean importData(JComponent comp, Transferable t) {
295
        JTextComponent c = (JTextComponent) comp;
296
        // if we are importing to the same component that we exported from
297
        // then don't actually do anything if the drop location is inside
298
        // the drag location and set shouldRemove to false so that exportDone
299
        // knows not to remove any data
300
        if (c == exportComp && c.getCaretPosition() >= p0 && c.getCaretPosition() <= p1) {
301
            shouldRemove = false;
302
            return true;
303
        }
304
        boolean imported = false;
305
        DataFlavor importFlavor = getImportFlavor(t.getTransferDataFlavors(), c);
306
        if (importFlavor != null) {
307
            try {
308
                boolean useRead = false;
309
                if (comp instanceof JEditorPane) {
310
                    JEditorPane ep = (JEditorPane) comp;
311
                    if (!ep.getContentType().startsWith("text/plain") && importFlavor.getMimeType().startsWith(ep.getContentType())) {
312
                        useRead = true;
313
                    }
314
                }
315
                InputContext ic = c.getInputContext();
316
                if (ic != null) {
317
                    ic.endComposition();
318
                }
319
                Reader r = importFlavor.getReaderForText(t);
320
                handleReaderImport(r, c, useRead);
321
                imported = true;
322
            } catch (UnsupportedFlavorException | BadLocationException | IOException ufe) {
323
                //just ignore
324
            }
325
        }
326
        return imported;
327
    }
328
329
    /**
330
     * This method indicates if a component would accept an import of the
331
     * given set of data flavors prior to actually attempting to import it.
332
     *
333
     * @param comp The component to receive the transfer. This argument is
334
     * provided to enable sharing of TransferHandlers by multiple
335
     * components.
336
     * @param flavors The data formats available
337
     * @return true if the data can be inserted into the component, false
338
     * otherwise.
339
     */
340
    @Override
341
    public boolean canImport(JComponent comp, DataFlavor[] flavors) {
342
        JTextComponent c = (JTextComponent) comp;
343
        if (!(c.isEditable() && c.isEnabled())) {
344
            return false;
345
        }
346
        return getImportFlavor(flavors, c) != null;
347
    }
348
349
    /**
350
     * A possible implementation of the Transferable interface for text
351
     * components. For a JEditorPane with a rich set of EditorKit
352
     * implementations, conversions could be made giving a wider set of
353
     * formats. This is implemented to offer up only the active content type
354
     * and text/plain (if that is not the active format) since that can be
355
     * extracted from other formats.
356
     */
357
    static class HtmlTransferable extends BasicTransferable {
358
359
        HtmlTransferable(JTextComponent c, int start, int end) {
360
            super(null, null);
361
            this.c = c;
362
            Document doc = c.getDocument();
363
            try {
364
                p0 = doc.createPosition(start);
365
                p1 = doc.createPosition(end);
366
                plainData = c.getSelectedText();
367
                if (c instanceof JEditorPane) {
368
                    JEditorPane ep = (JEditorPane) c;
369
                    mimeType = ep.getContentType();
370
                    if (mimeType.startsWith("text/plain")) {
371
                        //NOI18N
372
                        return;
373
                    }
374
                    StringWriter sw = new StringWriter(p1.getOffset() - p0.getOffset());
375
                    ep.getEditorKit().write(sw, doc, p0.getOffset(), p1.getOffset() - p0.getOffset());
376
                    if (mimeType.startsWith("text/html")) {
377
                        //NOI18N
378
                        htmlData = sw.toString();
379
                    } else {
380
                        richText = sw.toString();
381
                    }
382
                }
383
            } catch (BadLocationException | IOException ble) {
384
            }
385
        }
386
387
        void removeText() {
388
            if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) {
389
                try {
390
                    Document doc = c.getDocument();
391
                    doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset());
392
                } catch (BadLocationException e) {
393
                }
394
            }
395
        }
396
397
        // ---- EditorKit other than plain or HTML text -----------------------
398
        /**
399
         * If the EditorKit is not for text/plain or text/html, that format
400
         * is supported through the "richer flavors" part of
401
         * BasicTransferable.
402
         */
403
        @Override
404
        protected DataFlavor[] getRicherFlavors() {
405
            if (richText == null) {
406
                return null;
407
            }
408
            try {
409
                DataFlavor[] flavors = new DataFlavor[3];
410
                flavors[0] = new DataFlavor(mimeType + ";class=java.lang.String"); //NOI18N
411
                flavors[1] = new DataFlavor(mimeType + ";class=java.io.Reader"); //NOI18N
412
                flavors[2] = new DataFlavor(mimeType + ";class=java.io.InputStream;charset=unicode"); //NOI18N
413
                return flavors;
414
            } catch (ClassNotFoundException cle) {
415
                // fall through to unsupported (should not happen)
416
            }
417
            return null;
418
        }
419
420
        /**
421
         * The only richer format supported is the file list flavor
422
         */
423
        @Override
424
        protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException {
425
            if (richText == null) {
426
                return null;
427
            }
428
            if (String.class.equals(flavor.getRepresentationClass())) {
429
                return richText;
430
            } else if (Reader.class.equals(flavor.getRepresentationClass())) {
431
                return new StringReader(richText);
432
            } else if (InputStream.class.equals(flavor.getRepresentationClass())) {
433
                return new ByteArrayInputStream(richText.getBytes());
434
            }
435
            throw new UnsupportedFlavorException(flavor);
436
        }
437
        Position p0;
438
        Position p1;
439
        String mimeType;
440
        String richText;
441
        JTextComponent c;
442
    }
443
    
444
    private static class BasicTransferable implements Transferable, UIResource {
445
446
        protected String plainData;
447
        protected String htmlData;
448
        private static DataFlavor[] htmlFlavors;
449
        private static DataFlavor[] stringFlavors;
450
        private static DataFlavor[] plainFlavors;
451
452
        static {
453
            try {
454
                htmlFlavors = new DataFlavor[3];
455
                htmlFlavors[0] = new DataFlavor("text/html;class=java.lang.String"); //NOI18N
456
                htmlFlavors[1] = new DataFlavor("text/html;class=java.io.Reader"); //NOI18N
457
                htmlFlavors[2] = new DataFlavor("text/html;charset=unicode;class=java.io.InputStream"); //NOI18N
458
459
                plainFlavors = new DataFlavor[3];
460
                plainFlavors[0] = new DataFlavor("text/plain;class=java.lang.String"); //NOI18N
461
                plainFlavors[1] = new DataFlavor("text/plain;class=java.io.Reader"); //NOI18N
462
                plainFlavors[2] = new DataFlavor("text/plain;charset=unicode;class=java.io.InputStream"); //NOI18N
463
464
                stringFlavors = new DataFlavor[2];
465
                stringFlavors[0] = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=java.lang.String"); //NOI18N
466
                stringFlavors[1] = DataFlavor.stringFlavor;
467
468
            } catch (ClassNotFoundException cle) {
469
                System.err.println("error initializing javax.swing.plaf.basic.BasicTranserable"); ////NOI18N
470
            }
471
        }
472
473
        public BasicTransferable(String plainData, String htmlData) {
474
            this.plainData = plainData;
475
            this.htmlData = htmlData;
476
        }
477
478
        /**
479
         * Returns an array of DataFlavor objects indicating the flavors the
480
         * data can be provided in. The array should be ordered according to
481
         * preference for providing the data (from most richly descriptive to
482
         * least descriptive).
483
         *
484
         * @return an array of data flavors in which this data can be
485
         * transferred
486
         */
487
        @Override
488
        public DataFlavor[] getTransferDataFlavors() {
489
            DataFlavor[] richerFlavors = getRicherFlavors();
490
            int nRicher = (richerFlavors != null) ? richerFlavors.length : 0;
491
            int nHTML = (isHTMLSupported()) ? htmlFlavors.length : 0;
492
            int nPlain = (isPlainSupported()) ? plainFlavors.length : 0;
493
            int nString = (isPlainSupported()) ? stringFlavors.length : 0;
494
            int nFlavors = nRicher + nHTML + nPlain + nString;
495
            DataFlavor[] flavors = new DataFlavor[nFlavors];
496
497
            // fill in the array
498
            int nDone = 0;
499
            if (nRicher > 0) {
500
                System.arraycopy(richerFlavors, 0, flavors, nDone, nRicher);
501
                nDone += nRicher;
502
            }
503
            if (nHTML > 0) {
504
                System.arraycopy(htmlFlavors, 0, flavors, nDone, nHTML);
505
                nDone += nHTML;
506
            }
507
            if (nPlain > 0) {
508
                System.arraycopy(plainFlavors, 0, flavors, nDone, nPlain);
509
                nDone += nPlain;
510
            }
511
            if (nString > 0) {
512
                System.arraycopy(stringFlavors, 0, flavors, nDone, nString);
513
                nDone += nString;
514
            }
515
            return flavors;
516
        }
517
518
        /**
519
         * Returns whether or not the specified data flavor is supported for
520
         * this object.
521
         *
522
         * @param flavor the requested flavor for the data
523
         * @return boolean indicating whether or not the data flavor is
524
         * supported
525
         */
526
        @Override
527
        public boolean isDataFlavorSupported(DataFlavor flavor) {
528
            DataFlavor[] flavors = getTransferDataFlavors();
529
            for (int i = 0; i < flavors.length; i++) {
530
                if (flavors[i].equals(flavor)) {
531
                    return true;
532
                }
533
            }
534
            return false;
535
        }
536
537
        /**
538
         * Returns an object which represents the data to be transferred. The
539
         * class of the object returned is defined by the representation class
540
         * of the flavor.
541
         *
542
         * @param flavor the requested flavor for the data
543
         * @see DataFlavor#getRepresentationClass
544
         * @exception IOException if the data is no longer available in the
545
         * requested flavor.
546
         * @exception UnsupportedFlavorException if the requested data flavor is
547
         * not supported.
548
         */
549
        @Override
550
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
551
            DataFlavor[] richerFlavors = getRicherFlavors();
552
            if (isRicherFlavor(flavor)) {
553
                return getRicherData(flavor);
554
            } else if (isHTMLFlavor(flavor)) {
555
                String data = getHTMLData();
556
                data = (data == null) ? "" : data; //NOI18N
557
                if (String.class.equals(flavor.getRepresentationClass())) {
558
                    return data;
559
                } else if (Reader.class.equals(flavor.getRepresentationClass())) {
560
                    return new StringReader(data);
561
                } else if (InputStream.class.equals(flavor.getRepresentationClass())) {
562
                    return new ByteArrayInputStream(data.getBytes());
563
                }
564
                // fall through to unsupported
565
            } else if (isPlainFlavor(flavor)) {
566
                String data = getPlainData();
567
                data = (data == null) ? "" : data;
568
                if (String.class.equals(flavor.getRepresentationClass())) {
569
                    return data;
570
                } else if (Reader.class.equals(flavor.getRepresentationClass())) {
571
                    return new StringReader(data);
572
                } else if (InputStream.class.equals(flavor.getRepresentationClass())) {
573
                    return new ByteArrayInputStream(data.getBytes());
574
                }
575
                // fall through to unsupported
576
577
            } else if (isStringFlavor(flavor)) {
578
                String data = getPlainData();
579
                data = (data == null) ? "" : data; //NOI18N
580
                return data;
581
            }
582
            throw new UnsupportedFlavorException(flavor);
583
        }
584
585
        // --- richer subclass flavors ----------------------------------------------
586
        protected boolean isRicherFlavor(DataFlavor flavor) {
587
            DataFlavor[] richerFlavors = getRicherFlavors();
588
            int nFlavors = (richerFlavors != null) ? richerFlavors.length : 0;
589
            for (int i = 0; i < nFlavors; i++) {
590
                if (richerFlavors[i].equals(flavor)) {
591
                    return true;
592
                }
593
            }
594
            return false;
595
        }
596
597
        /**
598
         * Some subclasses will have flavors that are more descriptive than HTML
599
         * or plain text. If this method returns a non-null value, it will be
600
         * placed at the start of the array of supported flavors.
601
         */
602
        protected DataFlavor[] getRicherFlavors() {
603
            return null;
604
        }
605
606
        protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException {
607
            return null;
608
        }
609
610
        // --- html flavors ----------------------------------------------------------
611
        /**
612
         * Returns whether or not the specified data flavor is an HTML flavor
613
         * that is supported.
614
         *
615
         * @param flavor the requested flavor for the data
616
         * @return boolean indicating whether or not the data flavor is
617
         * supported
618
         */
619
        protected boolean isHTMLFlavor(DataFlavor flavor) {
620
            DataFlavor[] flavors = htmlFlavors;
621
            for (int i = 0; i < flavors.length; i++) {
622
                if (flavors[i].equals(flavor)) {
623
                    return true;
624
                }
625
            }
626
            return false;
627
        }
628
629
        /**
630
         * Should the HTML flavors be offered? If so, the method getHTMLData
631
         * should be implemented to provide something reasonable.
632
         */
633
        protected boolean isHTMLSupported() {
634
            return htmlData != null;
635
        }
636
637
        /**
638
         * Fetch the data in a text/html format
639
         */
640
        protected String getHTMLData() {
641
            return htmlData;
642
        }
643
644
        // --- plain text flavors ----------------------------------------------------
645
        /**
646
         * Returns whether or not the specified data flavor is an plain flavor
647
         * that is supported.
648
         *
649
         * @param flavor the requested flavor for the data
650
         * @return boolean indicating whether or not the data flavor is
651
         * supported
652
         */
653
        protected boolean isPlainFlavor(DataFlavor flavor) {
654
            DataFlavor[] flavors = plainFlavors;
655
            for (int i = 0; i < flavors.length; i++) {
656
                if (flavors[i].equals(flavor)) {
657
                    return true;
658
                }
659
            }
660
            return false;
661
        }
662
663
        /**
664
         * Should the plain text flavors be offered? If so, the method
665
         * getPlainData should be implemented to provide something reasonable.
666
         */
667
        protected boolean isPlainSupported() {
668
            return plainData != null;
669
        }
670
671
        /**
672
         * Fetch the data in a text/plain format.
673
         */
674
        protected String getPlainData() {
675
            return plainData;
676
        }
677
678
        // --- string flavorss --------------------------------------------------------
679
        /**
680
         * Returns whether or not the specified data flavor is a String flavor
681
         * that is supported.
682
         *
683
         * @param flavor the requested flavor for the data
684
         * @return boolean indicating whether or not the data flavor is
685
         * supported
686
         */
687
        protected boolean isStringFlavor(DataFlavor flavor) {
688
            DataFlavor[] flavors = stringFlavors;
689
            for (int i = 0; i < flavors.length; i++) {
690
                if (flavors[i].equals(flavor)) {
691
                    return true;
692
                }
693
            }
694
            return false;
695
        }
696
    }
697
    // END of fix of issue #43309
698
    
699
}
201
}
(-)a/html.editor/src/org/netbeans/modules/html/editor/api/AccessorImpl.java (+58 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.html.editor.api;
43
44
import org.netbeans.modules.html.editor.APIAccessor;
45
46
/**
47
 *
48
 * @author Ralph Ruijs
49
 */
50
class AccessorImpl extends APIAccessor {
51
    
52
    AccessorImpl() {}
53
54
    @Override
55
    public void setContentType(HtmlKit kit, String contentType) {
56
        kit.setContentType(contentType);
57
    }
58
}
(-)a/html.editor/src/org/netbeans/modules/html/editor/api/HtmlKit.java (-2 / +13 lines)
Lines 49-54 Link Here
49
import org.netbeans.editor.ext.ExtKit;
49
import org.netbeans.editor.ext.ExtKit;
50
import org.netbeans.modules.csl.api.CslActions;
50
import org.netbeans.modules.csl.api.CslActions;
51
import org.netbeans.modules.editor.NbEditorKit;
51
import org.netbeans.modules.editor.NbEditorKit;
52
import org.netbeans.modules.html.editor.APIAccessor;
52
53
53
/**
54
/**
54
 * Editor kit implementation for HTML content type
55
 * Editor kit implementation for HTML content type
Lines 57-62 Link Here
57
 *
58
 *
58
 */
59
 */
59
public class HtmlKit extends NbEditorKit implements org.openide.util.HelpCtx.Provider {
60
public class HtmlKit extends NbEditorKit implements org.openide.util.HelpCtx.Provider {
61
    
62
    static {
63
        APIAccessor.DEFAULT = new AccessorImpl();
64
    }
60
65
61
    public @Override
66
    public @Override
62
    org.openide.util.HelpCtx getHelpCtx() {
67
    org.openide.util.HelpCtx getHelpCtx() {
Lines 65-70 Link Here
65
    static final long serialVersionUID = -1381945567613910297L;
70
    static final long serialVersionUID = -1381945567613910297L;
66
    
71
    
67
    public static final String HTML_MIME_TYPE = "text/html"; // NOI18N
72
    public static final String HTML_MIME_TYPE = "text/html"; // NOI18N
73
    private String contentType;
68
74
69
    public HtmlKit() {
75
    public HtmlKit() {
70
        this(HTML_MIME_TYPE);
76
        this(HTML_MIME_TYPE);
Lines 72-82 Link Here
72
78
73
    public HtmlKit(String mimeType) {
79
    public HtmlKit(String mimeType) {
74
        super();
80
        super();
81
        contentType = HTML_MIME_TYPE;
75
    }
82
    }
76
83
77
    @Override
84
    @Override
78
    public String getContentType() {
85
    public String getContentType() {
79
        return HTML_MIME_TYPE;
86
        return contentType;
87
    }
88
    
89
    void setContentType(String contentType) {
90
        this.contentType = contentType;
80
    }
91
    }
81
92
82
    @Override
93
    @Override
Lines 90-96 Link Here
90
    @Override
101
    @Override
91
    public void install(javax.swing.JEditorPane c) {
102
    public void install(javax.swing.JEditorPane c) {
92
        super.install(c);
103
        super.install(c);
93
        c.setTransferHandler(new HtmlTransferHandler());
104
        HtmlTransferHandler.install(c);
94
    }
105
    }
95
106
96
    @Override
107
    @Override
(-)a/java.editor/src/org/netbeans/modules/java/editor/imports/ClipboardHandler.java (-8 / +13 lines)
Lines 97-102 Link Here
97
import com.sun.source.tree.ClassTree;
97
import com.sun.source.tree.ClassTree;
98
import com.sun.source.tree.ImportTree;
98
import com.sun.source.tree.ImportTree;
99
import com.sun.source.tree.MethodTree;
99
import com.sun.source.tree.MethodTree;
100
import java.awt.Component;
100
import java.util.Set;
101
import java.util.Set;
101
import org.netbeans.api.editor.EditorActionRegistration;
102
import org.netbeans.api.editor.EditorActionRegistration;
102
import org.netbeans.api.editor.EditorActionRegistrations;
103
import org.netbeans.api.editor.EditorActionRegistrations;
Lines 550-561 Link Here
550
        }
551
        }
551
552
552
        @Override
553
        @Override
553
        public boolean importData(TransferSupport support) {
554
        public boolean importData(JComponent comp, Transferable t) {
554
            return delegate.importData(support);
555
            return delegate.importData(comp, t);
555
        }
556
        }
556
557
557
        @Override
558
        @Override
558
        public boolean importData(JComponent comp, Transferable t) {
559
        public boolean importData(TransferSupport support) {
560
            Transferable t = support.getTransferable();
561
            Component comp = support.getComponent();
559
            if (t.isDataFlavorSupported(IMPORT_FLAVOR) && comp instanceof JTextComponent && !insideToken((JTextComponent)comp, JavaTokenId.STRING_LITERAL, JavaTokenId.BLOCK_COMMENT, JavaTokenId.JAVADOC_COMMENT, JavaTokenId.LINE_COMMENT)) {
562
            if (t.isDataFlavorSupported(IMPORT_FLAVOR) && comp instanceof JTextComponent && !insideToken((JTextComponent)comp, JavaTokenId.STRING_LITERAL, JavaTokenId.BLOCK_COMMENT, JavaTokenId.JAVADOC_COMMENT, JavaTokenId.LINE_COMMENT)) {
560
                boolean result = false;
563
                boolean result = false;
561
564
Lines 563-569 Link Here
563
                    final JTextComponent tc = (JTextComponent) comp;
566
                    final JTextComponent tc = (JTextComponent) comp;
564
                    final int caret = tc.getSelectionStart();
567
                    final int caret = tc.getSelectionStart();
565
568
566
                    if (result = delegatedImportData(comp, t)) {
569
                    if (result = delegatedImportData(support)) {
567
                        final ImportsWrapper imports = (ImportsWrapper) t.getTransferData(IMPORT_FLAVOR);
570
                        final ImportsWrapper imports = (ImportsWrapper) t.getTransferData(IMPORT_FLAVOR);
568
                        final FileObject file = NbEditorUtilities.getFileObject(tc.getDocument());
571
                        final FileObject file = NbEditorUtilities.getFileObject(tc.getDocument());
569
                        final Document doc = tc.getDocument();
572
                        final Document doc = tc.getDocument();
Lines 610-620 Link Here
610
                return result;
613
                return result;
611
            }
614
            }
612
615
613
            return delegatedImportData(comp, t);
616
            return delegatedImportData(support);
614
        }
617
        }
615
        
618
        
616
        private boolean delegatedImportData(final JComponent comp, final Transferable t) {
619
        private boolean delegatedImportData(final TransferSupport support) {
617
            if (comp instanceof JTextComponent && !t.isDataFlavorSupported(COPY_FROM_STRING_FLAVOR) && insideToken((JTextComponent) comp, JavaTokenId.STRING_LITERAL)) {
620
            JComponent comp = (JComponent) support.getComponent();
621
            if (comp instanceof JTextComponent && !support.isDataFlavorSupported(COPY_FROM_STRING_FLAVOR) && insideToken((JTextComponent) comp, JavaTokenId.STRING_LITERAL)) {
622
                final Transferable t = support.getTransferable();
618
                return delegate.importData(comp, new Transferable() {
623
                return delegate.importData(comp, new Transferable() {
619
                    @Override
624
                    @Override
620
                    public DataFlavor[] getTransferDataFlavors() {
625
                    public DataFlavor[] getTransferDataFlavors() {
Lines 654-660 Link Here
654
                    }
659
                    }
655
                });
660
                });
656
            }
661
            }
657
            return delegate.importData(comp, t);
662
            return delegate.importData(support);
658
        }
663
        }
659
        
664
        
660
        private boolean insideToken(final JTextComponent jtc, final JavaTokenId first, final JavaTokenId... rest) {
665
        private boolean insideToken(final JTextComponent jtc, final JavaTokenId first, final JavaTokenId... rest) {
(-)a/openide.text/src/org/openide/text/QuietEditorPane.java (-4 / +16 lines)
Lines 373-381 Link Here
373
373
374
        @Override
374
        @Override
375
        public boolean importData(JComponent comp, Transferable t) {
375
        public boolean importData(JComponent comp, Transferable t) {
376
            return delegator.importData(comp, t);
377
        }
378
379
        @Override
380
        public boolean importData(TransferSupport t) {
376
            try {
381
            try {
377
                if (t.isDataFlavorSupported(ActiveEditorDrop.FLAVOR)){
382
                if (t.isDataFlavorSupported(ActiveEditorDrop.FLAVOR)){
378
                    Object obj = t.getTransferData(ActiveEditorDrop.FLAVOR);
383
                    Object obj = t.getTransferable().getTransferData(ActiveEditorDrop.FLAVOR);
384
                    final JComponent comp = (JComponent) t.getComponent();
379
                    if (obj instanceof ActiveEditorDrop && comp instanceof JTextComponent){
385
                    if (obj instanceof ActiveEditorDrop && comp instanceof JTextComponent){
380
                        boolean success = false;
386
                        boolean success = false;
381
                        try {
387
                        try {
Lines 390-396 Link Here
390
            } catch (Exception exc){
396
            } catch (Exception exc){
391
                exc.printStackTrace();
397
                exc.printStackTrace();
392
            }
398
            }
393
            return delegator.importData(comp, t);
399
            return delegator.importData(t);
394
        }
400
        }
395
401
396
        private void requestFocus(JComponent comp) {
402
        private void requestFocus(JComponent comp) {
Lines 412-426 Link Here
412
                comp.requestFocus();
418
                comp.requestFocus();
413
            }
419
            }
414
        }
420
        }
421
422
        @Override
423
        public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
424
            return delegator.canImport(comp, transferFlavors);
425
        }
415
        
426
        
416
        @Override
427
        @Override
417
        public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
428
        public boolean canImport(TransferSupport support) {
429
            DataFlavor[] transferFlavors = support.getDataFlavors();
418
            for (int i=0; i<transferFlavors.length; i++){
430
            for (int i=0; i<transferFlavors.length; i++){
419
                if (transferFlavors[i] == ActiveEditorDrop.FLAVOR){
431
                if (transferFlavors[i] == ActiveEditorDrop.FLAVOR){
420
                    return true;
432
                    return true;
421
                }
433
                }
422
            }
434
            }
423
            return delegator.canImport(comp, transferFlavors);
435
            return delegator.canImport(support);
424
        }
436
        }
425
437
426
        @Override
438
        @Override
(-)a/refactoring.api/src/org/netbeans/modules/refactoring/api/resources/layer.xml (-2 / +3 lines)
Lines 75-85 Link Here
75
     </file>
75
     </file>
76
   </folder> <!-- Window -->
76
   </folder> <!-- Window -->
77
  </folder>
77
  </folder>
78
    <folder name="Shortcuts">
78
  <!-- Removed shortcut as it conflicts with Remove Last Caret. -->
79
<!--    <folder name="Shortcuts">
79
        <file name="OS-U.shadow">
80
        <file name="OS-U.shadow">
80
            <attr name="originalFile" stringvalue="Actions/Window/org-netbeans-modules-refactoring-spi-impl-FindUsagesOpenAction.instance"/>
81
            <attr name="originalFile" stringvalue="Actions/Window/org-netbeans-modules-refactoring-spi-impl-FindUsagesOpenAction.instance"/>
81
        </file>
82
        </file>
82
    </folder>
83
    </folder>-->
83
84
84
     <folder name="Windows2">
85
     <folder name="Windows2">
85
        <folder name="Modes">
86
        <folder name="Modes">

Return to bug 257893