~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to core/output2/src/org/netbeans/core/output2/ui/AbstractOutputPane.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 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
 
 
42
package org.netbeans.core.output2.ui;
 
43
 
 
44
import java.awt.Rectangle;
 
45
import javax.swing.plaf.TextUI;
 
46
 
 
47
import javax.swing.*;
 
48
import javax.swing.event.ChangeEvent;
 
49
import javax.swing.event.ChangeListener;
 
50
import javax.swing.event.DocumentEvent;
 
51
import javax.swing.event.DocumentListener;
 
52
import javax.swing.text.*;
 
53
import java.awt.*;
 
54
import java.awt.event.*;
 
55
import org.netbeans.core.output2.OutputDocument;
 
56
import org.openide.util.Exceptions;
 
57
 
 
58
/**
 
59
 * A scroll pane containing an editor pane, with special handling of the caret
 
60
 * and scrollbar - until a keyboard or mouse event, after a call to setDocument(),
 
61
 * the caret and scrollbar are locked to the last line of the document.  This avoids
 
62
 * "jumping" scrollbars as the position of the caret (and thus the scrollbar) get updated
 
63
 * to reposition them at the bottom of the document on every document change.
 
64
 *
 
65
 * @author  Tim Boudreau
 
66
 */
 
67
public abstract class AbstractOutputPane extends JScrollPane implements DocumentListener, MouseListener, MouseMotionListener, KeyListener, ChangeListener, MouseWheelListener, Runnable {
 
68
    private boolean locked = true;
 
69
    
 
70
    private int fontHeight = -1;
 
71
    private int fontWidth = -1;
 
72
    protected JEditorPane textView;
 
73
    int lastCaretLine = 0;
 
74
    boolean hadSelection = false;
 
75
    boolean recentlyReset = false;
 
76
 
 
77
    public AbstractOutputPane() {
 
78
        textView = createTextView();
 
79
        init();
 
80
    }
 
81
 
 
82
    //#114290
 
83
    public void doUpdateCaret() {
 
84
        Caret car = textView.getCaret();
 
85
        if (car instanceof DefaultCaret) {
 
86
            ((DefaultCaret)car).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
 
87
        }
 
88
    }
 
89
 
 
90
    public void dontUpdateCaret() {
 
91
        Caret car = textView.getCaret();
 
92
        if (car instanceof DefaultCaret) {
 
93
            ((DefaultCaret)car).setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
 
94
        }
 
95
    }
 
96
 
 
97
    @Override
 
98
    public void requestFocus() {
 
99
        textView.requestFocus();
 
100
    }
 
101
    
 
102
    @Override
 
103
    public boolean requestFocusInWindow() {
 
104
        return textView.requestFocusInWindow();
 
105
    }
 
106
    
 
107
    protected abstract JEditorPane createTextView();
 
108
 
 
109
    protected void documentChanged() {
 
110
        lastLength = -1;
 
111
        if (pendingCaretLine != -1) {
 
112
            if (!sendCaretToLine (pendingCaretLine, pendingCaretSelect)) {
 
113
                ensureCaretPosition();
 
114
            }
 
115
        } else {
 
116
            ensureCaretPosition();
 
117
        }
 
118
        if (recentlyReset && isShowing()) {
 
119
            recentlyReset = false;
 
120
        }
 
121
        if (locked) {
 
122
            setMouseLine(-1);
 
123
        }
 
124
        if (isWrapped()) {
 
125
            //Saves having OutputEditorKit have to do its own listening
 
126
            getViewport().revalidate();
 
127
            getViewport().repaint();
 
128
        }
 
129
    }
 
130
    
 
131
    public abstract boolean isWrapped();
 
132
    public abstract void setWrapped (boolean val);
 
133
 
 
134
    public boolean hasSelection() {
 
135
        return textView.getSelectionStart() != textView.getSelectionEnd();
 
136
    }
 
137
 
 
138
    boolean isScrollLocked() {
 
139
        return locked;
 
140
    }
 
141
 
 
142
    /**
 
143
     * Ensure that the document is scrolled all the way to the bottom (unless
 
144
     * some user event like scrolling or placing the caret has unlocked it).
 
145
     * <p>
 
146
     * Note that this method is always called on the event queue, since 
 
147
     * OutputDocument only fires changes on the event queue.
 
148
     */
 
149
    public final void ensureCaretPosition() {
 
150
        if (locked) {           
 
151
            //Make sure the scrollbar is updated *after* the document change
 
152
            //has been processed and the scrollbar model's maximum updated
 
153
            if (!enqueued) {
 
154
                SwingUtilities.invokeLater(this);
 
155
                enqueued = true;
 
156
            }
 
157
        }
 
158
    }
 
159
    
 
160
    /** True when invokeLater has already been called on this instance */
 
161
    private boolean enqueued = false;
 
162
    /**
 
163
     * Scrolls the pane to the bottom, invokeLatered to ensure all state has
 
164
     * been updated, so the bottom really *is* the bottom.
 
165
     */
 
166
    public void run() {
 
167
        enqueued = false;
 
168
        getVerticalScrollBar().setValue(getVerticalScrollBar().getModel().getMaximum());
 
169
        getHorizontalScrollBar().setValue(getHorizontalScrollBar().getModel().getMinimum());
 
170
    }
 
171
 
 
172
    public int getSelectionStart() {
 
173
        return textView.getSelectionStart();
 
174
    }
 
175
    
 
176
    public int getSelectionEnd() {
 
177
        return textView.getSelectionEnd();
 
178
    }
 
179
 
 
180
    public void setSelection (int start, int end) {
 
181
        int rstart = Math.min (start, end);
 
182
        int rend = Math.max (start, end);
 
183
        if (rstart == rend) {
 
184
            getCaret().setDot(rstart);
 
185
        } else {
 
186
            textView.setSelectionStart(rstart);
 
187
            textView.setSelectionEnd(rend);
 
188
        }
 
189
    }
 
190
 
 
191
    public void selectAll() {
 
192
        unlockScroll();
 
193
        getCaret().setVisible(true);
 
194
        textView.setSelectionStart(0);
 
195
        textView.setSelectionEnd(getLength());
 
196
    }
 
197
 
 
198
    public boolean isAllSelected() {
 
199
        return textView.getSelectionStart() == 0 && textView.getSelectionEnd() == getLength();
 
200
    }
 
201
 
 
202
    protected void init() {
 
203
        setViewportView(textView);
 
204
        textView.setEditable(false);
 
205
 
 
206
        textView.addMouseListener(this);
 
207
        textView.addMouseWheelListener(this);
 
208
        textView.addMouseMotionListener(this);
 
209
        textView.addKeyListener(this);
 
210
        //#107354
 
211
        OCaret oc = new OCaret();
 
212
        oc.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
 
213
        textView.setCaret (oc);
 
214
        
 
215
        getCaret().setVisible(true);
 
216
        getCaret().setBlinkRate(0);
 
217
        getCaret().setSelectionVisible(true);
 
218
        
 
219
        getVerticalScrollBar().getModel().addChangeListener(this);
 
220
        getVerticalScrollBar().addMouseMotionListener(this);
 
221
        
 
222
        getViewport().addMouseListener(this);
 
223
        getVerticalScrollBar().addMouseListener(this);
 
224
        setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
 
225
        setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
 
226
        addMouseListener(this);
 
227
 
 
228
        getCaret().addChangeListener(this);
 
229
        Integer i = (Integer) UIManager.get("customFontSize"); //NOI18N
 
230
        int size;
 
231
        if (i != null) {
 
232
            size = i.intValue();
 
233
        } else {
 
234
            Font f = (Font) UIManager.get("controlFont");
 
235
            size = f != null ? f.getSize() : 11;
 
236
        }
 
237
        textView.setFont (new Font ("Monospaced", Font.PLAIN, size)); //NOI18N
 
238
        setBorder (BorderFactory.createEmptyBorder());
 
239
        setViewportBorder (BorderFactory.createEmptyBorder());
 
240
        
 
241
        Color c = UIManager.getColor("nb.output.selectionBackground");
 
242
        if (c != null) {
 
243
            textView.setSelectionColor(c);
 
244
        }
 
245
    }
 
246
 
 
247
    public final Document getDocument() {
 
248
        return textView.getDocument();
 
249
    }
 
250
    
 
251
    /**
 
252
     * This method is here for use *only* by unit tests.
 
253
     */
 
254
    public final JTextComponent getTextView() {
 
255
        return textView;
 
256
    }
 
257
 
 
258
    public final void copy() {
 
259
        if (getCaret().getDot() != getCaret().getMark()) {
 
260
            textView.copy();
 
261
        } else {
 
262
            Toolkit.getDefaultToolkit().beep();
 
263
        }
 
264
    }
 
265
 
 
266
    protected void setDocument (Document doc) {
 
267
        if (hasSelection()) {
 
268
            hasSelectionChanged(false);
 
269
        }
 
270
        hadSelection = false;
 
271
        lastCaretLine = 0;
 
272
        lastLength = -1;
 
273
        Document old = textView.getDocument();
 
274
        old.removeDocumentListener(this);
 
275
        if (doc != null) {
 
276
            textView.setDocument(doc);
 
277
            doc.addDocumentListener(this);
 
278
            lockScroll();
 
279
            recentlyReset = true;
 
280
            pendingCaretLine = -1;
 
281
        } else {
 
282
            textView.setDocument (new PlainDocument());
 
283
            textView.setEditorKit(new DefaultEditorKit());
 
284
        }
 
285
    }
 
286
    
 
287
    protected void setEditorKit(EditorKit kit) {
 
288
        Document doc = textView.getDocument();
 
289
        
 
290
        textView.setEditorKit(kit);
 
291
        textView.setDocument(doc);
 
292
        updateKeyBindings();
 
293
        getCaret().setVisible(true);
 
294
        getCaret().setBlinkRate(0);
 
295
    }
 
296
    
 
297
    /**
 
298
     * Setting the editor kit will clear the action map/key map connection
 
299
     * to the TopComponent, so we reset it here.
 
300
     */
 
301
    protected final void updateKeyBindings() {
 
302
        Keymap keymap = textView.getKeymap();
 
303
        keymap.removeKeyStrokeBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
 
304
    }
 
305
    
 
306
    protected EditorKit getEditorKit() {
 
307
        return textView.getEditorKit();
 
308
    }
 
309
    
 
310
    public final int getLineCount() {
 
311
        return textView.getDocument().getDefaultRootElement().getElementCount();
 
312
    }
 
313
 
 
314
    private int lastLength = -1;
 
315
    public final int getLength() {
 
316
        if (lastLength == -1) {
 
317
            lastLength = textView.getDocument().getLength();
 
318
        }
 
319
        return lastLength;
 
320
    }
 
321
    
 
322
    /**
 
323
     * If we are sending the caret to a hyperlinked line, but it is < 3 lines
 
324
     * from the bottom, we will hold the line number in this field until there
 
325
     * are enough lines that it will be semi-centered.
 
326
     */
 
327
    private int pendingCaretLine = -1;
 
328
    private boolean pendingCaretSelect = false;
 
329
    private boolean inSendCaretToLine = false;
 
330
    
 
331
    public final boolean sendCaretToLine(int idx, boolean select) {
 
332
        int count = getLineCount();
 
333
        if (count - idx < 3) {
 
334
            pendingCaretLine = idx;
 
335
            pendingCaretSelect = select;
 
336
            return false;
 
337
        } else {
 
338
            inSendCaretToLine = true;
 
339
            pendingCaretLine = -1;
 
340
            unlockScroll();
 
341
            getCaret().setVisible(true);
 
342
            getCaret().setSelectionVisible(true);
 
343
            Element el = textView.getDocument().getDefaultRootElement().getElement(Math.min(idx, getLineCount() - 1));
 
344
            int position = el.getStartOffset();
 
345
            if (select) {
 
346
                getCaret().setDot (el.getEndOffset()-1);
 
347
                getCaret().moveDot (position);
 
348
                getCaret().setSelectionVisible(true);
 
349
                textView.repaint();
 
350
            } else {
 
351
                getCaret().setDot(position);
 
352
            }
 
353
            if (idx + 3 < getLineCount()) {
 
354
                try {
 
355
                    Rectangle r = textView.modelToView(textView.getDocument().getDefaultRootElement().getElement(idx + 3).getStartOffset());
 
356
                    if (r != null) { //Will be null if maximized - no parent, no coordinate space
 
357
                        textView.scrollRectToVisible(r);
 
358
                    }
 
359
                } catch (BadLocationException ble) {
 
360
                    Exceptions.printStackTrace(ble);
 
361
                }
 
362
            }
 
363
            inSendCaretToLine = false;
 
364
            return true;
 
365
        }
 
366
    }
 
367
 
 
368
 
 
369
    public final void lockScroll() {
 
370
        if (!locked) {
 
371
            locked = true;
 
372
        }
 
373
    }
 
374
    
 
375
    public final void unlockScroll() {
 
376
        if (locked) {
 
377
            locked = false;
 
378
        }
 
379
    }
 
380
 
 
381
    protected abstract void caretEnteredLine (int line);
 
382
    
 
383
    protected abstract void lineClicked (int line, Point p);
 
384
    
 
385
    protected abstract void postPopupMenu (Point p, Component src);
 
386
    
 
387
    public final int getCaretLine() {
 
388
        int result = -1;
 
389
        int charPos = getCaret().getDot();
 
390
        if (charPos > 0) {
 
391
            result = textView.getDocument().getDefaultRootElement().getElementIndex(charPos);
 
392
        }
 
393
        return result;
 
394
    }
 
395
 
 
396
    public final int getCaretPos() {
 
397
        return getCaret().getDot();
 
398
    }
 
399
 
 
400
    @Override
 
401
    public final void paint (Graphics g) {
 
402
        if (fontHeight == -1) {
 
403
            fontHeight = g.getFontMetrics(textView.getFont()).getHeight();
 
404
            fontWidth = g.getFontMetrics(textView.getFont()).charWidth('m'); //NOI18N
 
405
        }
 
406
        super.paint(g);
 
407
    }
 
408
 
 
409
//***********************Listener implementations*****************************
 
410
 
 
411
    public void stateChanged(ChangeEvent e) {
 
412
        if (e.getSource() instanceof JViewport) {
 
413
            if (locked) {
 
414
                ensureCaretPosition();
 
415
            }
 
416
        } else if (e.getSource() == getVerticalScrollBar().getModel()) {
 
417
            if (!locked) { //XXX check if doc is still being written?
 
418
                BoundedRangeModel mdl = getVerticalScrollBar().getModel();
 
419
                if (mdl.getValue() + mdl.getExtent() == mdl.getMaximum()) {
 
420
                    lockScroll();
 
421
                }
 
422
            }
 
423
        } else {
 
424
            if (!locked) {
 
425
                maybeSendCaretEnteredLine();
 
426
            }
 
427
            boolean hasSelection = textView.getSelectionStart() != textView.getSelectionEnd();
 
428
            if (hasSelection != hadSelection) {
 
429
                hadSelection = hasSelection;
 
430
                hasSelectionChanged (hasSelection);
 
431
            }
 
432
        }
 
433
    }
 
434
 
 
435
    private boolean caretLineChanged() {
 
436
        int line = getCaretLine();
 
437
        boolean result = line != lastCaretLine;
 
438
        lastCaretLine = line;
 
439
        return result;
 
440
    }
 
441
 
 
442
    private void maybeSendCaretEnteredLine() {
 
443
        if (EventQueue.getCurrentEvent() instanceof MouseEvent) {
 
444
            //User may have clicked a hyperlink, in which case, we'll test
 
445
            //it and see if it's really in the text of the hyperlink - so
 
446
            //don't do anything here
 
447
            return;
 
448
        }
 
449
        //Don't message the controller if we're programmatically setting
 
450
        //the selection, or if the caret moved because output was written - 
 
451
        //it can cause the controller to send events to OutputListeners which
 
452
        //should only happen for user events
 
453
        if ((!locked && caretLineChanged()) && !inSendCaretToLine) {
 
454
            int line = getCaretLine();
 
455
            boolean sel = textView.getSelectionStart() != textView.getSelectionEnd();
 
456
            if (line != -1 && !sel) {
 
457
                caretEnteredLine(getCaretLine());
 
458
            }
 
459
            if (sel != hadSelection) {
 
460
                hadSelection = sel;
 
461
                hasSelectionChanged (sel);
 
462
            }
 
463
        }
 
464
    }
 
465
 
 
466
 
 
467
    private void hasSelectionChanged(boolean sel) {
 
468
        ((AbstractOutputTab) getParent()).hasSelectionChanged(sel);
 
469
    }
 
470
 
 
471
    public final void changedUpdate(DocumentEvent e) {
 
472
        //Ensure it is consumed
 
473
        e.getLength();
 
474
        documentChanged();
 
475
        if (e.getOffset() >= getCaretPos() && (locked || !(e instanceof OutputDocument.DO))) {
 
476
            //#119985 only move caret when not in editable section
 
477
            OutputDocument doc = (OutputDocument)e.getDocument();
 
478
            if (! (e instanceof OutputDocument.DO) && getCaretPos() >= doc.getOutputLength()) {
 
479
                return ;
 
480
            }
 
481
            
 
482
            getCaret().setDot(e.getOffset() + e.getLength());
 
483
        }
 
484
    }
 
485
 
 
486
    public final void insertUpdate(DocumentEvent e) {
 
487
        //Ensure it is consumed
 
488
        e.getLength();
 
489
        documentChanged();
 
490
        if (e.getOffset() >= getCaretPos() && (locked || !(e instanceof OutputDocument.DO))) {
 
491
            //#119985 only move caret when not in editable section
 
492
            OutputDocument doc = (OutputDocument)e.getDocument();
 
493
            if (! (e instanceof OutputDocument.DO) && getCaretPos() >= doc.getOutputLength()) {
 
494
                return ;
 
495
            }
 
496
            
 
497
            getCaret().setDot(e.getOffset() + e.getLength());
 
498
        }
 
499
    }
 
500
 
 
501
    public final void removeUpdate(DocumentEvent e) {
 
502
        //Ensure it is consumed
 
503
        e.getLength();
 
504
        documentChanged();
 
505
    }
 
506
 
 
507
    public void mouseClicked(MouseEvent e) {
 
508
    }
 
509
 
 
510
    public void mouseEntered(MouseEvent e) {
 
511
    }
 
512
 
 
513
    public void mouseExited(MouseEvent e) {
 
514
        setMouseLine (-1);
 
515
    }
 
516
 
 
517
    private int mouseLine = -1;
 
518
    public void setMouseLine (int line, Point p) {
 
519
        if (mouseLine != line) {
 
520
            mouseLine = line;
 
521
        }
 
522
    }
 
523
    
 
524
    public final void setMouseLine (int line) {
 
525
        setMouseLine (line, null);
 
526
    }
 
527
 
 
528
 
 
529
    public void mouseMoved(MouseEvent e) {
 
530
        Point p = e.getPoint();
 
531
        int pos = textView.viewToModel(p);
 
532
        if (pos < getLength()) {
 
533
            int line = getDocument().getDefaultRootElement().getElementIndex(pos);
 
534
            int lineStart = getDocument().getDefaultRootElement().getElement(line).getStartOffset();
 
535
            int lineLength = getDocument().getDefaultRootElement().getElement(line).getEndOffset() -
 
536
                    lineStart;
 
537
 
 
538
            try {
 
539
                Rectangle r = textView.modelToView(lineStart + lineLength -1);
 
540
                int maxX = r.x + r.width;
 
541
                boolean inLine = p.x <= maxX;
 
542
                if (isWrapped()) {
 
543
                    Rectangle ra = textView.modelToView(lineStart);
 
544
                    if (ra.y <= r.y) {
 
545
                        if (p.y < r.y) {
 
546
                            inLine = true;
 
547
                        }
 
548
                    }
 
549
                }
 
550
                
 
551
                if (inLine) {
 
552
                    setMouseLine (line, p);
 
553
                } else {
 
554
                    setMouseLine(-1);
 
555
                }
 
556
            } catch (BadLocationException ble) {
 
557
                setMouseLine(-1);
 
558
            }
 
559
        }
 
560
    }
 
561
 
 
562
    public void mouseDragged(MouseEvent e) {
 
563
        if (e.getSource() == getVerticalScrollBar()) {
 
564
            int y = e.getY();
 
565
            if (y > getVerticalScrollBar().getHeight()) {
 
566
                lockScroll();
 
567
            }
 
568
        }
 
569
    }
 
570
    
 
571
    public void mousePressed(MouseEvent e) {
 
572
        if (locked && !e.isPopupTrigger()) {
 
573
            Element el = getDocument().getDefaultRootElement().getElement(getLineCount()-1);
 
574
            getCaret().setDot(el.getStartOffset());
 
575
            unlockScroll();
 
576
            //We should now set the caret position so the caret doesn't
 
577
            //seem to ignore the first click
 
578
            if (e.getSource() == textView) {
 
579
                getCaret().setDot (textView.viewToModel(e.getPoint()));
 
580
            }
 
581
        }
 
582
        if (e.isPopupTrigger()) {
 
583
            //Convert immediately to our component space - if the 
 
584
            //text view scrolls before the component is opened, popup can
 
585
            //appear above the top of the screen
 
586
            Point p = SwingUtilities.convertPoint((Component) e.getSource(), 
 
587
                e.getPoint(), this);
 
588
            
 
589
            postPopupMenu (p, this);
 
590
        }
 
591
    }
 
592
 
 
593
    public final void mouseReleased(MouseEvent e) {
 
594
        if (e.getSource() == textView && SwingUtilities.isLeftMouseButton(e)) {
 
595
            int pos = textView.viewToModel(e.getPoint());
 
596
            if (pos != -1) {
 
597
                int line = textView.getDocument().getDefaultRootElement().getElementIndex(pos);
 
598
                if (line >= 0) {
 
599
                    lineClicked(line, e.getPoint());
 
600
                    e.consume(); //do NOT allow this window's caret to steal the focus from editor window
 
601
                }
 
602
            }
 
603
        }
 
604
        if (e.isPopupTrigger()) {
 
605
            Point p = SwingUtilities.convertPoint((Component) e.getSource(), 
 
606
            //Convert immediately to our component space - if the 
 
607
            //text view scrolls before the component is opened, popup can
 
608
            //appear above the top of the screen
 
609
                e.getPoint(), this);
 
610
            
 
611
            postPopupMenu (p, this);
 
612
        }
 
613
    }
 
614
    
 
615
    public void keyPressed(KeyEvent keyEvent) {
 
616
        if (keyEvent.getKeyCode() == KeyEvent.VK_END) {
 
617
            lockScroll();
 
618
        } else {
 
619
            unlockScroll();
 
620
        }
 
621
    }
 
622
 
 
623
    public void keyReleased(KeyEvent keyEvent) {
 
624
    }
 
625
 
 
626
    public void keyTyped(KeyEvent keyEvent) {
 
627
    }
 
628
 
 
629
    public final void mouseWheelMoved(MouseWheelEvent e) {
 
630
        BoundedRangeModel sbmodel = getVerticalScrollBar().getModel();
 
631
        int max = sbmodel.getMaximum();
 
632
        int range = sbmodel.getExtent();
 
633
 
 
634
        int currPosition = sbmodel.getValue();
 
635
        if (e.getSource() == textView) {
 
636
            int newPosition = Math.max (0, Math.min (sbmodel.getMaximum(),
 
637
                currPosition + (e.getUnitsToScroll() * textView.getFontMetrics(textView.getFont()).getHeight())));
 
638
            // height is a magic constant because of #57532
 
639
            sbmodel.setValue (newPosition);
 
640
            if (newPosition + range >= max) {
 
641
                lockScroll();
 
642
                return;
 
643
            }
 
644
        }
 
645
        unlockScroll();
 
646
    }
 
647
 
 
648
    Caret getCaret() {
 
649
        return textView.getCaret();
 
650
    }
 
651
    
 
652
    private class OCaret extends DefaultCaret {
 
653
        @Override
 
654
        public void setSelectionVisible(boolean val) {
 
655
            super.setSelectionVisible(true);
 
656
            super.setBlinkRate(0);
 
657
        }
 
658
        @Override
 
659
        public boolean isSelectionVisible() {
 
660
            return true;
 
661
        }
 
662
        @Override
 
663
        public void setBlinkRate(int rate) {
 
664
            super.setBlinkRate(0);
 
665
        }
 
666
 
 
667
        @Override
 
668
        public boolean isVisible() { return true; }
 
669
        
 
670
        @Override
 
671
        public void paint(Graphics g) {
 
672
            JTextComponent component = textView;
 
673
            if(isVisible() && y >= 0) {
 
674
                try {
 
675
                    TextUI mapper = component.getUI();
 
676
                    Rectangle r = mapper.modelToView(component, getDot(), Position.Bias.Forward);
 
677
 
 
678
                    if ((r == null) || ((r.width == 0) && (r.height == 0))) {
 
679
                        return;
 
680
                    }
 
681
                    if (width > 0 && height > 0 &&
 
682
                                    !this._contains(r.x, r.y, r.width, r.height)) {
 
683
                        // We seem to have gotten out of sync and no longer
 
684
                        // contain the right location, adjust accordingly.
 
685
                        Rectangle clip = g.getClipBounds();
 
686
 
 
687
                        if (clip != null && !clip.contains(this)) {
 
688
                            // Clip doesn't contain the old location, force it
 
689
                            // to be repainted lest we leave a caret around.
 
690
                            repaint();
 
691
                        }
 
692
 //                       System.err.println("WRONG! Caret dot m2v = " + r + " but my bounds are " + x + "," + y + "," + width + "," + height);
 
693
                        
 
694
                        // This will potentially cause a repaint of something
 
695
                        // we're already repainting, but without changing the
 
696
                        // semantics of damage we can't really get around this.
 
697
                        damage(r);
 
698
                    }
 
699
                    g.setColor(component.getCaretColor());
 
700
                    g.drawLine(r.x, r.y, r.x, r.y + r.height - 1);
 
701
                    g.drawLine(r.x+1, r.y, r.x+1, r.y + r.height - 1);
 
702
 
 
703
                } catch (BadLocationException e) {
 
704
                    // can't render I guess
 
705
//                    System.err.println("Can't render cursor");
 
706
                }
 
707
            }
 
708
        }    
 
709
        
 
710
        private boolean _contains(int X, int Y, int W, int H) {
 
711
            int w = this.width;
 
712
            int h = this.height;
 
713
            if ((w | h | W | H) < 0) {
 
714
                // At least one of the dimensions is negative...
 
715
                return false;
 
716
            }
 
717
            // Note: if any dimension is zero, tests below must return false...
 
718
            int x = this.x;
 
719
            int y = this.y;
 
720
            if (X < x || Y < y) {
 
721
                return false;
 
722
            }
 
723
            if (W > 0) {
 
724
                w += x;
 
725
                W += X;
 
726
                if (W <= X) {
 
727
                    // X+W overflowed or W was zero, return false if...
 
728
                    // either original w or W was zero or
 
729
                    // x+w did not overflow or
 
730
                    // the overflowed x+w is smaller than the overflowed X+W
 
731
                    if (w >= x || W > w) {
 
732
                        return false;
 
733
                    }
 
734
                } else {
 
735
                    // X+W did not overflow and W was not zero, return false if...
 
736
                    // original w was zero or
 
737
                    // x+w did not overflow and x+w is smaller than X+W
 
738
                    if (w >= x && W > w) {
 
739
                        //This is the bug in DefaultCaret - returns false here
 
740
                        return true;
 
741
                    }
 
742
                }
 
743
            }
 
744
            else if ((x + w) < X) {
 
745
                return false;
 
746
            }
 
747
            if (H > 0) {
 
748
                h += y;
 
749
                H += Y;
 
750
                if (H <= Y) {
 
751
                    if (h >= y || H > h) return false;
 
752
                } else {
 
753
                    if (h >= y && H > h) return false;
 
754
                }
 
755
            }
 
756
            else if ((y + h) < Y) {
 
757
                return false;
 
758
            }
 
759
            return true;
 
760
        }        
 
761
 
 
762
        @Override
 
763
        public void mouseReleased(MouseEvent e) {
 
764
            if( !e.isConsumed() ) {
 
765
                super.mouseReleased(e);
 
766
            }
 
767
        }
 
768
    }
 
769
}