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

(-)a/spi.debugger.ui/apichanges.xml (+18 lines)
Lines 131-136 Link Here
131
        <issue number="138717"/>
131
        <issue number="138717"/>
132
    </change>
132
    </change>
133
133
134
    <change id="method-chooser">
135
        <api name="DebuggerCoreSPI"/>
136
        <summary><code>MethodChooser</code> added.</summary>
137
        <version major="2" minor="22"/>
138
        <date day="28" month="1" year="2010"/>
139
        <author login="dprusa"/>
140
        <compatibility binary="compatible" source="compatible" modification="yes" semantic="compatible"/>
141
        <description>
142
            <p>
143
                <code>MethodChooser</code> class added. It is a support for Step Into action
144
                    implementations. Providing a simple graphical interface, it allows the user
145
                    to select in a source file a method call the debugger should step into.
146
                    It has been originally implemented in the jpda debugger module, now it can be
147
                    reused by other debuggers.
148
            </p>
149
        </description>
150
        <issue number="171213"/>
151
    </change>
134
152
135
</changes>
153
</changes>
136
154
(-)a/spi.debugger.ui/manifest.mf (-1 / +1 lines)
Lines 2-7 Link Here
2
OpenIDE-Module: org.netbeans.spi.debugger.ui/1
2
OpenIDE-Module: org.netbeans.spi.debugger.ui/1
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/debugger/ui/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/debugger/ui/Bundle.properties
4
OpenIDE-Module-Layer: org/netbeans/modules/debugger/resources/mf-layer.xml
4
OpenIDE-Module-Layer: org/netbeans/modules/debugger/resources/mf-layer.xml
5
OpenIDE-Module-Specification-Version: 2.21
5
OpenIDE-Module-Specification-Version: 2.22
6
OpenIDE-Module-Provides: org.netbeans.spi.debugger.ui
6
OpenIDE-Module-Provides: org.netbeans.spi.debugger.ui
7
OpenIDE-Module-Install: org/netbeans/modules/debugger/ui/DebuggerModule.class
7
OpenIDE-Module-Install: org/netbeans/modules/debugger/ui/DebuggerModule.class
(-)a/spi.debugger.ui/nbproject/project.xml (+9 lines)
Lines 71-76 Link Here
71
                    <run-dependency>
71
                    <run-dependency>
72
                        <release-version>1</release-version>
72
                        <release-version>1</release-version>
73
                        <specification-version>1.11</specification-version>
73
                        <specification-version>1.11</specification-version>
74
                    </run-dependency>
75
                </dependency>
76
                <dependency>
77
                    <code-name-base>org.netbeans.modules.editor.settings</code-name-base>
78
                    <build-prerequisite/>
79
                    <compile-dependency/>
80
                    <run-dependency>
81
                        <release-version>1</release-version>
82
                        <specification-version>1.19</specification-version>
74
                    </run-dependency>
83
                    </run-dependency>
75
                </dependency>
84
                </dependency>
76
                <dependency>
85
                <dependency>
(-)a/spi.debugger.ui/nbproject/project.xml (+9 lines)
Lines 71-76 Link Here
71
                    <run-dependency>
71
                    <run-dependency>
72
                        <release-version>1</release-version>
72
                        <release-version>1</release-version>
73
                        <specification-version>1.11</specification-version>
73
                        <specification-version>1.11</specification-version>
74
                    </run-dependency>
75
                </dependency>
76
                <dependency>
77
                    <code-name-base>org.netbeans.modules.editor.settings</code-name-base>
78
                    <build-prerequisite/>
79
                    <compile-dependency/>
80
                    <run-dependency>
81
                        <release-version>1</release-version>
82
                        <specification-version>1.19</specification-version>
74
                    </run-dependency>
83
                    </run-dependency>
75
                </dependency>
84
                </dependency>
76
                <dependency>
85
                <dependency>
(-)d402432719a9 (+738 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * Contributor(s):
25
 *
26
 * The Original Software is NetBeans. The Initial Developer of the Original
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
 * Microsystems, Inc. All Rights Reserved.
29
 *
30
 * If you wish your version of this file to be governed by only the CDDL
31
 * or only the GPL Version 2, indicate your decision by adding
32
 * "[Contributor] elects to include this software in this distribution
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
34
 * single choice of license, a recipient has the option to distribute
35
 * your version of this file under either the CDDL, the GPL Version 2 or
36
 * to extend the choice of license to its licensees as provided above.
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
38
 * Version 2 license, then the option applies only if the new code is
39
 * made subject to such option by the copyright holder.
40
 */
41
package org.netbeans.spi.debugger.ui;
42
43
import java.awt.Color;
44
import java.awt.Cursor;
45
import java.awt.event.FocusEvent;
46
import java.awt.event.FocusListener;
47
import java.awt.event.KeyEvent;
48
import java.awt.event.KeyListener;
49
import java.awt.event.MouseEvent;
50
import java.awt.event.MouseListener;
51
import java.awt.event.MouseMotionListener;
52
import java.lang.reflect.InvocationTargetException;
53
import java.net.MalformedURLException;
54
import java.net.URL;
55
import java.util.ArrayList;
56
import java.util.List;
57
import javax.swing.JEditorPane;
58
import javax.swing.KeyStroke;
59
import javax.swing.SwingUtilities;
60
import javax.swing.text.AttributeSet;
61
import javax.swing.text.BadLocationException;
62
import javax.swing.text.Caret;
63
import javax.swing.text.Document;
64
import javax.swing.text.JTextComponent;
65
import javax.swing.text.StyleConstants;
66
67
import org.netbeans.editor.BaseDocument;
68
import org.netbeans.editor.BaseCaret;
69
import org.netbeans.editor.Utilities;
70
import org.netbeans.spi.editor.highlighting.HighlightAttributeValue;
71
import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
72
import org.netbeans.api.editor.settings.AttributesUtilities;
73
import org.netbeans.api.editor.settings.EditorStyleConstants;
74
75
import org.netbeans.spi.editor.highlighting.HighlightsLayer;
76
import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
77
import org.netbeans.spi.editor.highlighting.ZOrder;
78
import org.openide.cookies.EditorCookie;
79
import org.openide.filesystems.FileObject;
80
import org.openide.filesystems.URLMapper;
81
import org.openide.loaders.DataObject;
82
import org.openide.loaders.DataObjectNotFoundException;
83
import org.openide.util.Exceptions;
84
import org.openide.util.NbBundle;
85
86
/**
87
 * Support for Step Into action implementations. It allows the user to select
88
 * directly in a source file a method call the debugger should step into.
89
 * A simple graphical interface is provided. The user can navigate among available
90
 * method calls. The navigation can be done using a keyboard as well as a mouse.
91
 *
92
 * <p>The method chooser is initialized by an url (pointing to a source file), an array of
93
 * {@link Segment} elements (each of them corresponds typically to a method call name
94
 * in the source file) and an index of the segment element which is displayed
95
 * as the default selection.
96
 *
97
 * <p>Optionally, two sets of (additional) shortcuts that confirm, resp. cancel the selection
98
 * mode can be specified.
99
 * It is also possible to pass a text, which should be shown at the editor pane's
100
 * status line after the selection mode has been activated. This text serves as a hint
101
 * to the user how to make the method call selection.
102
 *
103
 * <p>Method chooser does not use any special highlighting for the background of the
104
 * area where the selection takes place. If it is required it can be done by attaching
105
 * instances of {@link Annotation} to the proper source file's lines. These annotation should
106
 * be added before calling {@link #showUI} and removed after calling {@link #releaseUI}.
107
 *
108
 * <p>To display the method chooser's ui correctly, it is required to register
109
 * {@link HighlightsLayerFactory} created by {@link #createHighlihgtsLayerFactory}
110
 * in an xml layer. An example follows.
111
 *
112
 * <pre class="examplecode">
113
    &lt;folder name=&quot;Editors&quot;&gt;
114
        &lt;folder name=&quot;text&quot;&gt;
115
            &lt;folder name=&quot;x-java&quot;&gt;
116
                &lt;file name=&quot;org.netbeans.spi.editor.highlighting.HighlightsLayerFactory.instance&quot;&gt;
117
                    &lt;attr name=&quot;instanceCreate&quot; methodvalue=&quot;org.netbeans.spi.debugger.ui.MethodChooser.createHighlihgtsLayerFactory&quot;/&gt;
118
                &lt;/file&gt;
119
            &lt;/folder&gt;
120
        &lt;/folder&gt;
121
    &lt;/folder&gt;</pre>
122
 * <code>"x-java"</code> should be replaced by the targeted mime type.
123
 *
124
 * @author Daniel Prusa
125
 * @since 2.22
126
 */
127
public class MethodChooser {
128
129
    private static AttributeSet defaultHyperlinkHighlight;
130
131
    private String url;
132
    private Segment[] segments;
133
    private int selectedIndex = -1;
134
    private String hintText;
135
    private KeyStroke[] stopEvents;
136
    private KeyStroke[] confirmEvents;
137
138
    private AttributeSet attribsLeft = null;
139
    private AttributeSet attribsRight = null;
140
    private AttributeSet attribsMiddle = null;
141
    private AttributeSet attribsAll = null;
142
143
    private AttributeSet attribsArea = null;
144
    private AttributeSet attribsMethod = null;
145
    private AttributeSet attribsHyperlink = null;
146
147
    private Cursor handCursor;
148
    private Cursor arrowCursor;
149
    private Cursor originalCursor;
150
151
    private CentralListener mainListener;
152
    private Document doc;
153
    private JEditorPane editorPane;
154
    private List<ReleaseListener> releaseListeners = new ArrayList<ReleaseListener>();
155
156
    private int startLine;
157
    private int endLine;
158
    private int mousedIndex = -1;
159
    private boolean isInSelectMode = false;
160
161
    /**
162
     * Creates an instance of {@link MethodChooser}.
163
     *
164
     * @param url Url of the source file.
165
     * @param segments Array of segments where each of the segments represents one method
166
     *      call. The user traverses the calls in the order given by the array.
167
     * @param initialIndex Index of a call that should be preselected when the method chooser
168
     *      is shown.
169
     */
170
    public MethodChooser(String url, Segment[] segments, int initialIndex) {
171
        this(url, segments, initialIndex, null, new KeyStroke[0], new KeyStroke[0]);
172
    }
173
174
    /**
175
     * Creates an instance of {@link MethodChooser}. Supports optional parameters.
176
     *
177
     * @param url Url of the source file.
178
     * @param segments Array of segments where each of the segments represents one method
179
     *      call. The user traverses the calls in the order given by the array.
180
     * @param initialIndex Index of a call that should be preselected when the method chooser
181
     *      is shown.
182
     * @param hintText Text which is displayed in the editor pane's status line. Serves as a hint
183
     *      informing briefly the user how to make a selection.
184
     * @param stopEvents Custom key strokes which should stop the selection mode.
185
     *      For example, it is possible to pass a {@link KeyStroke} corresponding to
186
     *      the shortcut of Step Over action. Then, whenever the shorcut is pressed, the selection
187
     *      mode is cancelled. The generated {@link KeyEvent} is not consumed thus can be
188
     *      handled and invokes Step Over action.
189
     *      Note that a method chooser can be always cancelled by Esc or by clicking outside the
190
     *      visualized area in the source editor.
191
     * @param confirmEvents Custom key strokes which confirm the current selection.
192
     *      By default, a selection can be confirmed by Enter or Space Bar. It is possible
193
     *      to extend this set of confirmation keys.
194
     */
195
    public MethodChooser(String url, Segment[] segments, int initialIndex, String hintText,
196
            KeyStroke[] stopEvents, KeyStroke[] confirmEvents) {
197
        this.url = url;
198
        this.segments = segments;
199
        this.selectedIndex = initialIndex;
200
        this.hintText = hintText;
201
        if (stopEvents == null) {
202
            stopEvents = new KeyStroke[0];
203
        }
204
        if (confirmEvents == null) {
205
            confirmEvents = new KeyStroke[0];
206
        }
207
        this.stopEvents = stopEvents;
208
        this.confirmEvents = confirmEvents;
209
    }
210
211
    /**
212
     * Sets up and displays the method selection mode.
213
     *
214
     * @return <code>true</code> if a {@link JEditorPane} has been found and the selection mode
215
     *          has been properly displayed
216
     */
217
    public boolean showUI() {
218
        findEditorPane();
219
        if (editorPane == null) {
220
            return false; // cannot do anything without editor
221
        }
222
        doc = editorPane.getDocument();
223
        // compute start line and end line
224
        int minOffs = Integer.MAX_VALUE;
225
        int maxOffs = 0;
226
        for (int x = 0; x < segments.length; x++) {
227
            minOffs = Math.min(segments[x].getStartOffset(), minOffs);
228
            maxOffs = Math.max(segments[x].getEndOffset(), maxOffs);
229
        }
230
        try {
231
            startLine = Utilities.getLineOffset((BaseDocument)doc, minOffs) + 1;
232
            endLine = Utilities.getLineOffset((BaseDocument)doc, maxOffs) + 1;
233
        } catch (BadLocationException e) {
234
        }
235
        // continue by showing method selection ui
236
        mainListener = new CentralListener();
237
        editorPane.putClientProperty(MethodChooser.class, this);
238
        editorPane.addKeyListener(mainListener);
239
        editorPane.addMouseListener(mainListener);
240
        editorPane.addMouseMotionListener(mainListener);
241
        editorPane.addFocusListener(mainListener);
242
        originalCursor = editorPane.getCursor();
243
        handCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
244
        arrowCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
245
        editorPane.setCursor(arrowCursor);
246
        Caret caret = editorPane.getCaret();
247
        if (caret instanceof BaseCaret) {
248
            ((BaseCaret)caret).setVisible(false);
249
        }
250
        requestRepaint();
251
        if (hintText != null && hintText.trim().length() > 0) {
252
            Utilities.setStatusText(editorPane, " " + hintText);
253
        }
254
        isInSelectMode = true;
255
        return true;
256
    }
257
258
    /**
259
     * Ends the method selection mode, clears all used ui elements. Notifies each registered
260
     * {@link ReleaseListener}.
261
     *
262
     * @param performAction <code>true</code> indicates that the current selection should
263
     *          be used to perform an action, <code>false</code> means that the selection mode
264
     *          has beencancelled
265
     */
266
    public synchronized void releaseUI(boolean performAction) {
267
        if (!isInSelectMode) {
268
            return; // do nothing
269
        }
270
        getHighlightsBag(doc).clear();
271
        editorPane.removeKeyListener(mainListener);
272
        editorPane.removeMouseListener(mainListener);
273
        editorPane.removeMouseMotionListener(mainListener);
274
        editorPane.removeFocusListener(mainListener);
275
        editorPane.putClientProperty(MethodChooser.class, null);
276
        editorPane.setCursor(originalCursor);
277
        Caret caret = editorPane.getCaret();
278
        if (caret instanceof BaseCaret) {
279
            ((BaseCaret)caret).setVisible(true);
280
        }
281
282
        if (hintText != null && hintText.trim().length() > 0) {
283
            Utilities.clearStatusText(editorPane);
284
        }
285
        isInSelectMode = false;
286
        for (ReleaseListener listener : releaseListeners) {
287
            listener.released(performAction);
288
        }
289
    }
290
291
    /**
292
     * Can be used to check whether the selection mode is activated.
293
     *
294
     * @return <code>true</code> if the method selection mode is currently displayed
295
     */
296
    public boolean isUIActive() {
297
        return isInSelectMode;
298
    }
299
300
    /**
301
     * Returns index of {@link Segment} that is currently selected. If the method
302
     * chooser has been released, it corresponds to the final selection made by the user.
303
     *
304
     * @return index of currently selected method
305
     */
306
    public int getSelectedIndex() {
307
        return selectedIndex;
308
    }
309
310
    /**
311
     * Registers {@link ReleaseListener}. The listener is notified when the selection
312
     * mode finishes. This occurs whenever the user comfirms (or cancels) the current
313
     * selection. It also occrus when {@link #releaseUI} is called.
314
     *
315
     * @param listener an instance of {@link ReleaseListener} to be registered
316
     */
317
    public synchronized void addReleaseListener(ReleaseListener listener) {
318
        releaseListeners.add(listener);
319
    }
320
321
    /**
322
     * Unregisters {@link ReleaseListener}.
323
     *
324
     * @param listener an instance of {@link ReleaseListener} to be unregistered
325
     */
326
    public synchronized void removeReleaseListener(ReleaseListener listener) {
327
        releaseListeners.remove(listener);
328
    }
329
330
    /**
331
     * This method should be referenced in xml layer files. To display the method
332
     * chooser ui correctly, it is required to register an instance of
333
     * {@link HighlightsLayerFactory} using the following pattern.
334
     * <pre class="examplecode">
335
    &lt;folder name=&quot;Editors&quot;&gt;
336
        &lt;folder name=&quot;text&quot;&gt;
337
            &lt;folder name=&quot;x-java&quot;&gt;
338
                &lt;file name=&quot;org.netbeans.spi.editor.highlighting.HighlightsLayerFactory.instance&quot;&gt;
339
                    &lt;attr name=&quot;instanceCreate&quot; methodvalue=&quot;org.netbeans.spi.debugger.ui.MethodChooser.createHighlihgtsLayerFactory&quot;/&gt;
340
                &lt;/file&gt;
341
            &lt;/folder&gt;
342
        &lt;/folder&gt;
343
    &lt;/folder&gt;</pre>
344
     * <code>"x-java"</code> should be replaced by the targeted mime type
345
     *
346
     * @return highligts layer factory that handles method chooser ui visualization
347
     */
348
    public static HighlightsLayerFactory createHighlihgtsLayerFactory() {
349
        return new MethodChooserHighlightsLayerFactory();
350
    }
351
352
    static OffsetsBag getHighlightsBag(Document doc) {
353
        OffsetsBag bag = (OffsetsBag) doc.getProperty(MethodChooser.class);
354
        if (bag == null) {
355
            doc.putProperty(MethodChooser.class, bag = new OffsetsBag(doc, true));
356
        }
357
        return bag;
358
    }
359
360
    private void findEditorPane() {
361
        editorPane = null;
362
        FileObject file;
363
        try {
364
            file = URLMapper.findFileObject(new URL(url));
365
        } catch (MalformedURLException e) {
366
            return;
367
        }
368
        if (file == null) {
369
            return;
370
        }
371
        DataObject dobj = null;
372
        try {
373
            dobj = DataObject.find(file);
374
        } catch (DataObjectNotFoundException ex) {
375
        }
376
        if (dobj == null) {
377
            return;
378
        }
379
        final EditorCookie ec = (EditorCookie) dobj.getCookie(EditorCookie.class);
380
        try {
381
            SwingUtilities.invokeAndWait(new Runnable() {
382
                public void run() {
383
                    JEditorPane[] openedPanes = ec.getOpenedPanes();
384
                    if (openedPanes != null) {
385
                        editorPane = openedPanes[0];
386
                    }
387
                }
388
            });
389
        } catch (InterruptedException ex) {
390
            Exceptions.printStackTrace(ex);
391
        } catch (InvocationTargetException ex) {
392
            Exceptions.printStackTrace(ex);
393
        }
394
    }
395
396
    private void requestRepaint() {
397
        if (attribsLeft == null) {
398
            Color foreground = editorPane.getForeground();
399
400
            attribsLeft = createAttribs(EditorStyleConstants.LeftBorderLineColor, foreground, EditorStyleConstants.TopBorderLineColor, foreground, EditorStyleConstants.BottomBorderLineColor, foreground);
401
            attribsRight = createAttribs(EditorStyleConstants.RightBorderLineColor, foreground, EditorStyleConstants.TopBorderLineColor, foreground, EditorStyleConstants.BottomBorderLineColor, foreground);
402
            attribsMiddle = createAttribs(EditorStyleConstants.TopBorderLineColor, foreground, EditorStyleConstants.BottomBorderLineColor, foreground);
403
            attribsAll = createAttribs(EditorStyleConstants.LeftBorderLineColor, foreground, EditorStyleConstants.RightBorderLineColor, foreground, EditorStyleConstants.TopBorderLineColor, foreground, EditorStyleConstants.BottomBorderLineColor, foreground);
404
405
            attribsHyperlink = getHyperlinkHighlight();
406
407
            attribsMethod = createAttribs(StyleConstants.Foreground, foreground,
408
                    StyleConstants.Bold, Boolean.TRUE);
409
410
            attribsArea = createAttribs(
411
                    StyleConstants.Foreground, foreground,
412
                    StyleConstants.Italic, Boolean.FALSE,
413
                    StyleConstants.Bold, Boolean.FALSE);
414
        }
415
416
        OffsetsBag newBag = new OffsetsBag(doc, true);
417
        int start = segments[0].getStartOffset();
418
        int end = segments[segments.length - 1].getEndOffset();
419
        newBag.addHighlight(start, end, attribsArea);
420
421
        for (int i = 0; i < segments.length; i++) {
422
            int startOffset = segments[i].getStartOffset();
423
            int endOffset = segments[i].getEndOffset();
424
            newBag.addHighlight(startOffset, endOffset, attribsMethod);
425
            if (selectedIndex == i) {
426
                int size = endOffset - startOffset;
427
                if (size == 1) {
428
                    newBag.addHighlight(startOffset, endOffset, attribsAll);
429
                } else if (size > 1) {
430
                    newBag.addHighlight(startOffset, startOffset + 1, attribsLeft);
431
                    newBag.addHighlight(endOffset - 1, endOffset, attribsRight);
432
                    if (size > 2) {
433
                        newBag.addHighlight(startOffset + 1, endOffset - 1, attribsMiddle);
434
                    }
435
                }
436
            }
437
            if (mousedIndex == i) {
438
                AttributeSet attr = AttributesUtilities.createComposite(
439
                    attribsHyperlink,
440
                    AttributesUtilities.createImmutable(EditorStyleConstants.Tooltip, new TooltipResolver())
441
                );
442
                newBag.addHighlight(startOffset, endOffset, attr);
443
            }
444
        }
445
446
        OffsetsBag bag = getHighlightsBag(doc);
447
        bag.setHighlights(newBag);
448
    }
449
450
    private AttributeSet createAttribs(Object... keyValuePairs) {
451
        List<Object> list = new ArrayList<Object>();
452
        for (int i = keyValuePairs.length / 2 - 1; i >= 0; i--) {
453
            Object attrKey = keyValuePairs[2 * i];
454
            Object attrValue = keyValuePairs[2 * i + 1];
455
456
            if (attrKey != null && attrValue != null) {
457
                list.add(attrKey);
458
                list.add(attrValue);
459
            }
460
        }
461
        return AttributesUtilities.createImmutable(list.toArray());
462
    }
463
464
    private AttributeSet getHyperlinkHighlight() {
465
        synchronized(this) {
466
            if (defaultHyperlinkHighlight == null) {
467
                defaultHyperlinkHighlight = AttributesUtilities.createImmutable(
468
                        StyleConstants.Foreground, Color.BLUE, StyleConstants.Underline, Color.BLUE);
469
            }
470
        }
471
        return defaultHyperlinkHighlight;
472
    }
473
474
    // **************************************************************************
475
    // public inner classes
476
    // **************************************************************************
477
478
    /**
479
     * Represents an interval of offsets in a document. Used to pass entry points
480
     * of method calls among which the user selects the desired one to step into.
481
     */
482
    public static class Segment {
483
        int startOffset;
484
        int endOffset;
485
486
        /**
487
         * Creates a new instance of {@link Segment}.
488
         *
489
         * @param startOffset segment start offset (inclusive)
490
         * @param endOffset segment end offset (exclusive)
491
         */
492
        public Segment(int startOffset, int endOffset) {
493
            this.startOffset = startOffset;
494
            this.endOffset = endOffset;
495
        }
496
497
        /**
498
         * Returns the start offset.
499
         *
500
         * @return the start offset
501
         */
502
        public int getStartOffset() {
503
            return startOffset;
504
        }
505
506
        /**
507
         * Returns the endt offset.
508
         *
509
         * @return the end offset
510
         */
511
        public int getEndOffset() {
512
            return endOffset;
513
        }
514
515
    }
516
517
    /**
518
     * An instance of {@link ReleaseListener} can be registered using {@link MethodChooser#addReleaseListener}.
519
     * It is notified when the selection mode finishes (e.g. if the user confirms the current selection).
520
     * The selection mode finishes whenever {@link #releaseUI} is called.
521
     */
522
    public interface ReleaseListener {
523
524
        /**
525
         * Called on the method selection mode finish.
526
         *
527
         * @param performAction <code>true</code> means that the current selection has been confirmed
528
         *          and the proper action (Step Into) is expected to be performed, <code>false</code>
529
         *          means that the method chooser has been cancelled
530
         */
531
        public void released(boolean performAction);
532
533
    }
534
535
    // **************************************************************************
536
    // private inner classes
537
    // **************************************************************************
538
539
    private class CentralListener implements KeyListener, MouseListener, MouseMotionListener, FocusListener {
540
541
        // **************************************************************************
542
        // KeyListener implementation
543
        // **************************************************************************
544
545
        @Override
546
        public void keyTyped(KeyEvent e) {
547
            e.consume();
548
        }
549
550
        @Override
551
        public void keyPressed(KeyEvent e) {
552
            int code = e.getKeyCode();
553
            boolean consumeEvent = true;
554
            switch (code) {
555
                case KeyEvent.VK_ENTER:
556
                case KeyEvent.VK_SPACE:
557
                    // selection confirmed
558
                    releaseUI(true);
559
                    break;
560
                case KeyEvent.VK_ESCAPE:
561
                    // action canceled
562
                    releaseUI(false);
563
                    break;
564
                case KeyEvent.VK_RIGHT:
565
                case KeyEvent.VK_DOWN:
566
                case KeyEvent.VK_TAB:
567
                    selectedIndex++;
568
                    if (selectedIndex == segments.length) {
569
                        selectedIndex = 0;
570
                    }
571
                    requestRepaint();
572
                    break;
573
                case KeyEvent.VK_LEFT:
574
                case KeyEvent.VK_UP:
575
                    selectedIndex--;
576
                    if (selectedIndex < 0) {
577
                        selectedIndex = segments.length - 1;
578
                    }
579
                    requestRepaint();
580
                    break;
581
                case KeyEvent.VK_HOME:
582
                    selectedIndex = 0;
583
                    requestRepaint();
584
                    break;
585
                case KeyEvent.VK_END:
586
                    selectedIndex = segments.length - 1;
587
                    requestRepaint();
588
                    break;
589
                default:
590
                    int mods = e.getModifiersEx();
591
                    for (int x = 0; x < stopEvents.length; x++) {
592
                        if (stopEvents[x].getKeyCode() == code &&
593
                                (stopEvents[x].getModifiers() & mods) == stopEvents[x].getModifiers()) {
594
                            releaseUI(false);
595
                            consumeEvent = false;
596
                            break;
597
                        }
598
                    }
599
                    for (int x = 0; x < confirmEvents.length; x++) {
600
                        if (confirmEvents[x].getKeyCode() == code &&
601
                                (confirmEvents[x].getModifiers() & mods) == confirmEvents[x].getModifiers()) {
602
                            releaseUI(true);
603
                            break;
604
                        }
605
                    }
606
            }
607
            if (consumeEvent) {
608
                e.consume();
609
            }
610
        }
611
612
        @Override
613
        public void keyReleased(KeyEvent e) {
614
            e.consume();
615
        }
616
617
        // **************************************************************************
618
        // MouseListener and MouseMotionListener implementation
619
        // **************************************************************************
620
621
        @Override
622
        public void mouseClicked(MouseEvent e) {
623
            if (e.isPopupTrigger()) {
624
                return;
625
            }
626
            e.consume();
627
            int position = editorPane.viewToModel(e.getPoint());
628
            if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) {
629
                if (position < 0) {
630
                    return;
631
                }
632
                if (mousedIndex != -1) {
633
                    selectedIndex = mousedIndex;
634
                    releaseUI(true);
635
                    return;
636
                }
637
            }
638
            try {
639
                int line = Utilities.getLineOffset((BaseDocument) doc, position) + 1;
640
                if (line < startLine || line > endLine) {
641
                    releaseUI(false);
642
                    return;
643
                }
644
            } catch (BadLocationException ex) {
645
            }
646
        }
647
648
        @Override
649
        public void mouseMoved(MouseEvent e) {
650
            e.consume();
651
            int position = editorPane.viewToModel(e.getPoint());
652
            int newIndex = -1;
653
            if (position >= 0) {
654
                for (int x = 0; x < segments.length; x++) {
655
                    int start = segments[x].getStartOffset();
656
                    int end = segments[x].getEndOffset();
657
                    if (position >= start && position <= end) {
658
                        newIndex = x;
659
                        break;
660
                    }
661
                } // for
662
            } // if
663
            if (newIndex != mousedIndex) {
664
                if (newIndex == -1) {
665
                    editorPane.setCursor(arrowCursor);
666
                } else {
667
                    editorPane.setCursor(handCursor);
668
                }
669
                mousedIndex = newIndex;
670
                requestRepaint();
671
            }
672
        }
673
674
        @Override
675
        public void mouseReleased(MouseEvent e) {
676
            e.consume();
677
        }
678
679
        @Override
680
        public void mousePressed(MouseEvent e) {
681
            e.consume();
682
        }
683
684
        @Override
685
        public void mouseExited(MouseEvent e) {
686
            e.consume();
687
        }
688
689
        @Override
690
        public void mouseEntered(MouseEvent e) {
691
            e.consume();
692
        }
693
694
        @Override
695
        public void mouseDragged(MouseEvent e) {
696
            e.consume();
697
        }
698
699
        // **************************************************************************
700
        // FocusListener implementation
701
        // **************************************************************************
702
703
        @Override
704
        public void focusGained(FocusEvent e) {
705
            editorPane.getCaret().setVisible(false);
706
        }
707
708
        @Override
709
        public void focusLost(FocusEvent e) {
710
        }
711
712
    }
713
714
    static class MethodChooserHighlightsLayerFactory implements HighlightsLayerFactory {
715
716
        @Override
717
        public HighlightsLayer[] createLayers(Context context) {
718
            return new HighlightsLayer[] {
719
                HighlightsLayer.create(MethodChooser.class.getName(),
720
                        ZOrder.TOP_RACK, false, MethodChooser.getHighlightsBag(context.getDocument()))
721
            };
722
        }
723
724
    }
725
726
    private static final class TooltipResolver implements HighlightAttributeValue<String> {
727
728
        public TooltipResolver() {
729
        }
730
731
        @Override
732
        public String getValue(JTextComponent component, Document document, Object attributeKey, int startOffset, int endOffset) {
733
            return NbBundle.getMessage(MethodChooser.class, "MSG_Step_Into_Method");
734
        }
735
736
    }
737
738
}

Return to bug 171213