OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [javax/] [swing/] [text/] [DefaultCaret.java] - Blame information for rev 772

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 772 jeremybenn
/* DefaultCaret.java --
2
   Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
package javax.swing.text;
39
 
40
import java.awt.Graphics;
41
import java.awt.Point;
42
import java.awt.Rectangle;
43
import java.awt.event.ActionEvent;
44
import java.awt.event.ActionListener;
45
import java.awt.event.FocusEvent;
46
import java.awt.event.FocusListener;
47
import java.awt.event.MouseEvent;
48
import java.awt.event.MouseListener;
49
import java.awt.event.MouseMotionListener;
50
import java.beans.PropertyChangeEvent;
51
import java.beans.PropertyChangeListener;
52
import java.util.EventListener;
53
 
54
import javax.swing.JComponent;
55
import javax.swing.SwingUtilities;
56
import javax.swing.Timer;
57
import javax.swing.event.ChangeEvent;
58
import javax.swing.event.ChangeListener;
59
import javax.swing.event.DocumentEvent;
60
import javax.swing.event.DocumentListener;
61
import javax.swing.event.EventListenerList;
62
import javax.swing.text.Position.Bias;
63
 
64
/**
65
 * The default implementation of the {@link Caret} interface.
66
 *
67
 * @author original author unknown
68
 * @author Roman Kennke (roman@kennke.org)
69
 */
70
public class DefaultCaret extends Rectangle
71
  implements Caret, FocusListener, MouseListener, MouseMotionListener
72
{
73
 
74
  /** A text component in the current VM which currently has a
75
   * text selection or <code>null</code>.
76
   */
77
  static JTextComponent componentWithSelection;
78
 
79
  /** An implementation of NavigationFilter.FilterBypass which delegates
80
   * to the corresponding methods of the <code>DefaultCaret</code>.
81
   *
82
   * @author Robert Schuster (robertschuster@fsfe.org)
83
   */
84
  class Bypass extends NavigationFilter.FilterBypass
85
  {
86
 
87
    public Caret getCaret()
88
    {
89
      return DefaultCaret.this;
90
    }
91
 
92
    public void moveDot(int dot, Bias bias)
93
    {
94
      DefaultCaret.this.moveDotImpl(dot);
95
    }
96
 
97
    public void setDot(int dot, Bias bias)
98
    {
99
      DefaultCaret.this.setDotImpl(dot);
100
    }
101
 
102
  }
103
 
104
  /**
105
   * Controls the blinking of the caret.
106
   *
107
   * @author Roman Kennke (kennke@aicas.com)
108
   * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
109
   */
110
  private class BlinkTimerListener implements ActionListener
111
  {
112
    /**
113
     * Forces the next event to be ignored. The next event should be ignored
114
     * if we force the caret to appear. We do not know how long will it take
115
     * to fire the comming event; this may be near immediately. Better to leave
116
     * the caret visible one iteration longer.
117
     */
118
    boolean ignoreNextEvent;
119
 
120
    /**
121
     * Receives notification when the blink timer fires and updates the visible
122
     * state of the caret.
123
     *
124
     * @param event the action event
125
     */
126
    public void actionPerformed(ActionEvent event)
127
    {
128
      if (ignoreNextEvent)
129
        ignoreNextEvent = false;
130
      else
131
        {
132
          visible = !visible;
133
          repaint();
134
        }
135
    }
136
  }
137
 
138
  /**
139
   * Listens for changes in the text component's document and updates the
140
   * caret accordingly.
141
   *
142
   * @author Roman Kennke (kennke@aicas.com)
143
   */
144
  private class DocumentHandler implements DocumentListener
145
  {
146
    /**
147
     * Receives notification that some text attributes have changed. No action
148
     * is taken here.
149
     *
150
     * @param event the document event
151
     */
152
    public void changedUpdate(DocumentEvent event)
153
    {
154
      // Nothing to do here.
155
    }
156
 
157
    /**
158
     * Receives notification that some text has been inserted from the text
159
     * component. The caret is moved forward accordingly.
160
     *
161
     * @param event the document event
162
     */
163
    public void insertUpdate(DocumentEvent event)
164
    {
165
      if (policy == ALWAYS_UPDATE ||
166
          (SwingUtilities.isEventDispatchThread() &&
167
           policy == UPDATE_WHEN_ON_EDT))
168
        {
169
          int dot = getDot();
170
          setDot(dot + event.getLength());
171
        }
172
    }
173
 
174
    /**
175
     * Receives notification that some text has been removed into the text
176
     * component. The caret is moved backwards accordingly.
177
     *
178
     * @param event the document event
179
     */
180
    public void removeUpdate(DocumentEvent event)
181
    {
182
      if (policy == ALWAYS_UPDATE
183
          || (SwingUtilities.isEventDispatchThread()
184
              && policy == UPDATE_WHEN_ON_EDT))
185
        {
186
          int dot = getDot();
187
          setDot(dot - event.getLength());
188
        }
189
      else if (policy == NEVER_UPDATE
190
               || (! SwingUtilities.isEventDispatchThread()
191
                   && policy == UPDATE_WHEN_ON_EDT))
192
        {
193
          int docLength = event.getDocument().getLength();
194
          if (getDot() > docLength)
195
            setDot(docLength);
196
        }
197
    }
198
  }
199
 
200
  /**
201
   * Listens for property changes on the text document. This is used to add and
202
   * remove our document listener, if the document of the text component has
203
   * changed.
204
   *
205
   * @author Roman Kennke (kennke@aicas.com)
206
   */
207
  private class PropertyChangeHandler implements PropertyChangeListener
208
  {
209
 
210
    /**
211
     * Receives notification when a property has changed on the text component.
212
     * This adds/removes our document listener from the text component's
213
     * document when the document changes.
214
     *
215
     * @param e the property change event
216
     */
217
    public void propertyChange(PropertyChangeEvent e)
218
    {
219
      String name = e.getPropertyName();
220
 
221
      if (name.equals("document"))
222
        {
223
          Document oldDoc = (Document) e.getOldValue();
224
          if (oldDoc != null)
225
            oldDoc.removeDocumentListener(documentListener);
226
 
227
          Document newDoc = (Document) e.getNewValue();
228
          if (newDoc != null)
229
            newDoc.addDocumentListener(documentListener);
230
        }
231
      else if (name.equals("editable"))
232
        {
233
          active = (((Boolean) e.getNewValue()).booleanValue()
234
                   && textComponent.isEnabled());
235
        }
236
      else if (name.equals("enabled"))
237
        {
238
          active = (((Boolean) e.getNewValue()).booleanValue()
239
                   && textComponent.isEditable());
240
        }
241
 
242
    }
243
 
244
  }
245
 
246
  /** The serialization UID (compatible with JDK1.5). */
247
  private static final long serialVersionUID = 4325555698756477346L;
248
 
249
  /**
250
   * Indicates the Caret position should always be updated after Document
251
   * changes even if the updates are not performed on the Event Dispatching
252
   * thread.
253
   *
254
   * @since 1.5
255
   */
256
  public static final int ALWAYS_UPDATE = 2;
257
 
258
  /**
259
   * Indicates the Caret position should not be changed unless the Document
260
   * length becomes less than the Caret position, in which case the Caret
261
   * is moved to the end of the Document.
262
   *
263
   * @since 1.5
264
   */
265
  public static final int NEVER_UPDATE = 1;
266
 
267
  /**
268
   * Indicates the Caret position should be updated only if Document changes
269
   * are made on the Event Dispatcher thread.
270
   *
271
   * @since 1.5
272
   */
273
  public static final int UPDATE_WHEN_ON_EDT = 0;
274
 
275
  /** Keeps track of the current update policy **/
276
  int policy = UPDATE_WHEN_ON_EDT;
277
 
278
  /**
279
   * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}.
280
   */
281
  protected ChangeEvent changeEvent = new ChangeEvent(this);
282
 
283
  /**
284
   * Stores all registered event listeners.
285
   */
286
  protected EventListenerList listenerList = new EventListenerList();
287
 
288
  /**
289
   * Our document listener.
290
   */
291
  DocumentListener documentListener;
292
 
293
  /**
294
   * Our property listener.
295
   */
296
  PropertyChangeListener propertyChangeListener;
297
 
298
  /**
299
   * The text component in which this caret is installed.
300
   *
301
   * (Package private to avoid synthetic accessor method.)
302
   */
303
  JTextComponent textComponent;
304
 
305
  /**
306
   * Indicates if the selection should be visible or not.
307
   */
308
  private boolean selectionVisible = true;
309
 
310
  /**
311
   * The blink rate of this <code>Caret</code>.
312
   */
313
  private int blinkRate = 500;
314
 
315
  /**
316
   * The current dot position.
317
   */
318
  private int dot = 0;
319
 
320
  /**
321
   * The current mark position.
322
   */
323
  private int mark = 0;
324
 
325
  /**
326
   * The current visual caret position.
327
   */
328
  private Point magicCaretPosition = null;
329
 
330
  /**
331
   * Indicates if this <code>Caret</code> is currently visible or not. This is
332
   * package private to avoid an accessor method.
333
   */
334
  boolean visible = false;
335
 
336
  /** Indicates whether the text component where the caret is installed is
337
   * editable and enabled. If either of these properties is <code>false</code>
338
   * the caret is not drawn.
339
   */
340
  boolean active = true;
341
 
342
  /**
343
   * The current highlight entry.
344
   */
345
  private Object highlightEntry;
346
 
347
  private Timer blinkTimer;
348
 
349
  private BlinkTimerListener blinkListener;
350
 
351
  /**
352
   * A <code>NavigationFilter.FilterBypass</code> instance which
353
   * is provided to the a <code>NavigationFilter</code> to
354
   * unconditionally set or move the caret.
355
   */
356
  NavigationFilter.FilterBypass bypass;
357
 
358
  /**
359
   * Creates a new <code>DefaultCaret</code> instance.
360
   */
361
  public DefaultCaret()
362
  {
363
    // Nothing to do here.
364
  }
365
 
366
  /** Returns the caret's <code>NavigationFilter.FilterBypass</code> instance
367
   * and creates it if it does not yet exist.
368
   *
369
   * @return The caret's <code>NavigationFilter.FilterBypass</code> instance.
370
   */
371
  private NavigationFilter.FilterBypass getBypass()
372
  {
373
    return (bypass == null) ? bypass = new Bypass() : bypass;
374
  }
375
 
376
  /**
377
   * Sets the Caret update policy.
378
   *
379
   * @param policy the new policy.  Valid values are:
380
   * ALWAYS_UPDATE: always update the Caret position, even when Document
381
   * updates don't occur on the Event Dispatcher thread.
382
   * NEVER_UPDATE: don't update the Caret position unless the Document
383
   * length becomes less than the Caret position (then update the
384
   * Caret to the end of the Document).
385
   * UPDATE_WHEN_ON_EDT: update the Caret position when the
386
   * Document updates occur on the Event Dispatcher thread.  This is the
387
   * default.
388
   *
389
   * @since 1.5
390
   * @throws IllegalArgumentException if policy is not one of the above.
391
   */
392
  public void setUpdatePolicy (int policy)
393
  {
394
    if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE
395
        && policy != UPDATE_WHEN_ON_EDT)
396
      throw new
397
        IllegalArgumentException
398
        ("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT");
399
    this.policy = policy;
400
  }
401
 
402
  /**
403
   * Gets the caret update policy.
404
   *
405
   * @return the caret update policy.
406
   * @since 1.5
407
   */
408
  public int getUpdatePolicy ()
409
  {
410
    return policy;
411
  }
412
 
413
  /**
414
   * Moves the caret position when the mouse is dragged over the text
415
   * component, modifying the selectiony.
416
   *
417
   * <p>When the text component where the caret is installed is disabled,
418
   * the selection is not change but you can still scroll the text and
419
   * update the caret's location.</p>
420
   *
421
   * @param event the <code>MouseEvent</code> describing the drag operation
422
   */
423
  public void mouseDragged(MouseEvent event)
424
  {
425
    if (event.getButton() == MouseEvent.BUTTON1)
426
      {
427
        if (textComponent.isEnabled())
428
          moveCaret(event);
429
        else
430
          positionCaret(event);
431
      }
432
  }
433
 
434
  /**
435
   * Indicates a mouse movement over the text component. Does nothing here.
436
   *
437
   * @param event the <code>MouseEvent</code> describing the mouse operation
438
   */
439
  public void mouseMoved(MouseEvent event)
440
  {
441
    // Nothing to do here.
442
  }
443
 
444
  /**
445
   * When the click is received from Button 1 then the following actions
446
   * are performed here:
447
   *
448
   * <ul>
449
   * <li>If we receive a double click, the caret position (dot) is set
450
   *   to the position associated to the mouse click and the word at
451
   *   this location is selected. If there is no word at the pointer
452
   *   the gap is selected instead.</li>
453
   * <li>If we receive a triple click, the caret position (dot) is set
454
   *   to the position associated to the mouse click and the line at
455
   *   this location is selected.</li>
456
   * </ul>
457
   *
458
   * @param event the <code>MouseEvent</code> describing the click operation
459
   */
460
  public void mouseClicked(MouseEvent event)
461
  {
462
    // Do not modify selection if component is disabled.
463
    if (!textComponent.isEnabled())
464
      return;
465
 
466
    int count = event.getClickCount();
467
 
468
    if (event.getButton() == MouseEvent.BUTTON1 && count >= 2)
469
      {
470
        int newDot = getComponent().viewToModel(event.getPoint());
471
        JTextComponent t = getComponent();
472
 
473
        try
474
          {
475
            if (count == 3)
476
              {
477
                setDot(Utilities.getRowStart(t, newDot));
478
                moveDot( Utilities.getRowEnd(t, newDot));
479
              }
480
            else
481
              {
482
                int wordStart = Utilities.getWordStart(t, newDot);
483
 
484
                // When the mouse points at the offset of the first character
485
                // in a word Utilities().getPreviousWord will not return that
486
                // word but we want to select that. We have to use
487
                // Utilities.getWordStart() to get it.
488
                if (newDot == wordStart)
489
                  {
490
                    setDot(wordStart);
491
                    moveDot(Utilities.getWordEnd(t, wordStart));
492
                  }
493
                else
494
                  {
495
                    int nextWord = Utilities.getNextWord(t, newDot);
496
                    int previousWord = Utilities.getPreviousWord(t, newDot);
497
                    int previousWordEnd = Utilities.getWordEnd(t, previousWord);
498
 
499
                    // If the user clicked in the space between two words,
500
                    // then select the space.
501
                    if (newDot >= previousWordEnd && newDot <= nextWord)
502
                      {
503
                        setDot(previousWordEnd);
504
                        moveDot(nextWord);
505
                      }
506
                    // Otherwise select the word under the mouse pointer.
507
                    else
508
                      {
509
                        setDot(previousWord);
510
                        moveDot(previousWordEnd);
511
                      }
512
                  }
513
              }
514
          }
515
        catch(BadLocationException ble)
516
          {
517
            // TODO: Swallowing ok here?
518
          }
519
      }
520
 
521
  }
522
 
523
  /**
524
   * Indicates that the mouse has entered the text component. Nothing is done
525
   * here.
526
   *
527
   * @param event the <code>MouseEvent</code> describing the mouse operation
528
   */
529
  public void mouseEntered(MouseEvent event)
530
  {
531
    // Nothing to do here.
532
  }
533
 
534
  /**
535
   * Indicates that the mouse has exited the text component. Nothing is done
536
   * here.
537
   *
538
   * @param event the <code>MouseEvent</code> describing the mouse operation
539
   */
540
  public void mouseExited(MouseEvent event)
541
  {
542
    // Nothing to do here.
543
  }
544
 
545
  /**
546
   * If the button 1 is pressed, the caret position is updated to the
547
   * position of the mouse click and the text component requests the input
548
   * focus if it is enabled. If the SHIFT key is held down, the caret will
549
   * be moved, which might select the text between the old and new location.
550
   *
551
   * @param event the <code>MouseEvent</code> describing the press operation
552
   */
553
  public void mousePressed(MouseEvent event)
554
  {
555
 
556
    // The implementation assumes that consuming the event makes the AWT event
557
    // mechanism forget about this event instance and not transfer focus.
558
    // By observing how the RI reacts the following behavior has been
559
    // implemented (in regard to text components):
560
    // - a left-click moves the caret
561
    // - a left-click when shift is held down expands the selection
562
    // - a right-click or click with any additional mouse button
563
    //   on a text component is ignored
564
    // - a middle-click positions the caret and pastes the clipboard
565
    //   contents.
566
    // - a middle-click when shift is held down is ignored
567
 
568
    if (SwingUtilities.isLeftMouseButton(event))
569
      {
570
        // Handle the caret.
571
        if (event.isShiftDown() && getDot() != -1)
572
          {
573
            moveCaret(event);
574
          }
575
        else
576
          {
577
            positionCaret(event);
578
          }
579
 
580
        // Handle the focus.
581
        if (textComponent != null && textComponent.isEnabled()
582
            && textComponent.isRequestFocusEnabled())
583
          {
584
            textComponent.requestFocus();
585
          }
586
 
587
        // TODO: Handle double click for selecting words.
588
      }
589
    else if(event.getButton() == MouseEvent.BUTTON2)
590
      {
591
        // Special handling for X11-style pasting.
592
        if (! event.isShiftDown())
593
          {
594
            positionCaret(event);
595
            textComponent.paste();
596
          }
597
      }
598
  }
599
 
600
  /**
601
   * Indicates that a mouse button has been released on the text component.
602
   * Nothing is done here.
603
   *
604
   * @param event the <code>MouseEvent</code> describing the mouse operation
605
   */
606
  public void mouseReleased(MouseEvent event)
607
  {
608
    // Nothing to do here.
609
  }
610
 
611
  /**
612
   * Sets the caret to <code>visible</code> if the text component is editable.
613
   *
614
   * @param event the <code>FocusEvent</code>
615
   */
616
  public void focusGained(FocusEvent event)
617
  {
618
    if (textComponent.isEditable())
619
      {
620
        setVisible(true);
621
        updateTimerStatus();
622
      }
623
  }
624
 
625
  /**
626
   * Sets the caret to <code>invisible</code>.
627
   *
628
   * @param event the <code>FocusEvent</code>
629
   */
630
  public void focusLost(FocusEvent event)
631
  {
632
    if (textComponent.isEditable() && event.isTemporary() == false)
633
      {
634
        setVisible(false);
635
 
636
        // Stop the blinker, if running.
637
        if (blinkTimer != null && blinkTimer.isRunning())
638
          blinkTimer.stop();
639
      }
640
  }
641
 
642
  /**
643
   * Install (if not present) and start the timer, if the caret must blink. The
644
   * caret does not blink if it is invisible, or the component is disabled or
645
   * not editable.
646
   */
647
  private void updateTimerStatus()
648
  {
649
    if (textComponent.isEnabled() && textComponent.isEditable())
650
      {
651
        if (blinkTimer == null)
652
          initBlinkTimer();
653
        if (!blinkTimer.isRunning())
654
          blinkTimer.start();
655
      }
656
    else
657
      {
658
        if (blinkTimer != null)
659
          blinkTimer.stop();
660
      }
661
  }
662
 
663
  /**
664
   * Moves the caret to the position specified in the <code>MouseEvent</code>.
665
   * This will cause a selection if the dot and mark are different.
666
   *
667
   * @param event the <code>MouseEvent</code> from which to fetch the position
668
   */
669
  protected void moveCaret(MouseEvent event)
670
  {
671
    int newDot = getComponent().viewToModel(event.getPoint());
672
    moveDot(newDot);
673
  }
674
 
675
  /**
676
   * Repositions the caret to the position specified in the
677
   * <code>MouseEvent</code>.
678
   *
679
   * @param event the <code>MouseEvent</code> from which to fetch the position
680
   */
681
  protected void positionCaret(MouseEvent event)
682
  {
683
    int newDot = getComponent().viewToModel(event.getPoint());
684
    setDot(newDot);
685
  }
686
 
687
  /**
688
   * Deinstalls this <code>Caret</code> from the specified
689
   * <code>JTextComponent</code>. This removes any listeners that have been
690
   * registered by this <code>Caret</code>.
691
   *
692
   * @param c the text component from which to install this caret
693
   */
694
  public void deinstall(JTextComponent c)
695
  {
696
    textComponent.removeFocusListener(this);
697
    textComponent.removeMouseListener(this);
698
    textComponent.removeMouseMotionListener(this);
699
    textComponent.getDocument().removeDocumentListener(documentListener);
700
    documentListener = null;
701
    textComponent.removePropertyChangeListener(propertyChangeListener);
702
    propertyChangeListener = null;
703
    textComponent = null;
704
 
705
    // Deinstall blink timer if present.
706
    if (blinkTimer != null)
707
      blinkTimer.stop();
708
    blinkTimer = null;
709
  }
710
 
711
  /**
712
   * Installs this <code>Caret</code> on the specified
713
   * <code>JTextComponent</code>. This registers a couple of listeners
714
   * on the text component.
715
   *
716
   * @param c the text component on which to install this caret
717
   */
718
  public void install(JTextComponent c)
719
  {
720
    textComponent = c;
721
    textComponent.addFocusListener(this);
722
    textComponent.addMouseListener(this);
723
    textComponent.addMouseMotionListener(this);
724
    propertyChangeListener = new PropertyChangeHandler();
725
    textComponent.addPropertyChangeListener(propertyChangeListener);
726
    documentListener = new DocumentHandler();
727
 
728
    Document doc = textComponent.getDocument();
729
    if (doc != null)
730
      doc.addDocumentListener(documentListener);
731
 
732
    active = textComponent.isEditable() && textComponent.isEnabled();
733
 
734
    repaint();
735
  }
736
 
737
  /**
738
   * Sets the current visual position of this <code>Caret</code>.
739
   *
740
   * @param p the Point to use for the saved location. May be <code>null</code>
741
   *        to indicate that there is no visual location
742
   */
743
  public void setMagicCaretPosition(Point p)
744
  {
745
    magicCaretPosition = p;
746
  }
747
 
748
  /**
749
   * Returns the current visual position of this <code>Caret</code>.
750
   *
751
   * @return the current visual position of this <code>Caret</code>
752
   *
753
   * @see #setMagicCaretPosition
754
   */
755
  public Point getMagicCaretPosition()
756
  {
757
    return magicCaretPosition;
758
  }
759
 
760
  /**
761
   * Returns the current position of the <code>mark</code>. The
762
   * <code>mark</code> marks the location in the <code>Document</code> that
763
   * is the end of a selection. If there is no selection, the <code>mark</code>
764
   * is the same as the <code>dot</code>.
765
   *
766
   * @return the current position of the mark
767
   */
768
  public int getMark()
769
  {
770
    return mark;
771
  }
772
 
773
  private void clearHighlight()
774
  {
775
    Highlighter highlighter = textComponent.getHighlighter();
776
 
777
    if (highlighter == null)
778
      return;
779
 
780
    if (selectionVisible)
781
      {
782
    try
783
      {
784
        if (highlightEntry != null)
785
          highlighter.changeHighlight(highlightEntry, 0, 0);
786
 
787
        // Free the global variable which stores the text component with an active
788
        // selection.
789
        if (componentWithSelection == textComponent)
790
          componentWithSelection = null;
791
      }
792
    catch (BadLocationException e)
793
      {
794
        // This should never happen.
795
        throw new InternalError();
796
      }
797
      }
798
    else
799
      {
800
    if (highlightEntry != null)
801
      {
802
        highlighter.removeHighlight(highlightEntry);
803
        highlightEntry = null;
804
      }
805
      }
806
  }
807
 
808
  private void handleHighlight()
809
  {
810
    Highlighter highlighter = textComponent.getHighlighter();
811
 
812
    if (highlighter == null)
813
      return;
814
 
815
    int p0 = Math.min(dot, mark);
816
    int p1 = Math.max(dot, mark);
817
 
818
    if (selectionVisible)
819
      {
820
        try
821
          {
822
            if (highlightEntry == null)
823
              highlightEntry = highlighter.addHighlight(p0, p1, getSelectionPainter());
824
            else
825
              highlighter.changeHighlight(highlightEntry, p0, p1);
826
 
827
            // If another component currently has a text selection clear that selection
828
            // first.
829
            if (componentWithSelection != null)
830
              if (componentWithSelection != textComponent)
831
                {
832
                  Caret c = componentWithSelection.getCaret();
833
                  c.setDot(c.getDot());
834
                }
835
            componentWithSelection = textComponent;
836
 
837
          }
838
        catch (BadLocationException e)
839
          {
840
            // This should never happen.
841
            throw new InternalError();
842
          }
843
      }
844
    else
845
      {
846
        if (highlightEntry != null)
847
          {
848
            highlighter.removeHighlight(highlightEntry);
849
            highlightEntry = null;
850
          }
851
      }
852
  }
853
 
854
  /**
855
   * Sets the visiblity state of the selection.
856
   *
857
   * @param v <code>true</code> if the selection should be visible,
858
   *        <code>false</code> otherwise
859
   */
860
  public void setSelectionVisible(boolean v)
861
  {
862
    if (selectionVisible == v)
863
      return;
864
 
865
    selectionVisible = v;
866
    handleHighlight();
867
    repaint();
868
  }
869
 
870
  /**
871
   * Returns <code>true</code> if the selection is currently visible,
872
   * <code>false</code> otherwise.
873
   *
874
   * @return <code>true</code> if the selection is currently visible,
875
   *         <code>false</code> otherwise
876
   */
877
  public boolean isSelectionVisible()
878
  {
879
    return selectionVisible;
880
  }
881
 
882
  /**
883
   * Causes the <code>Caret</code> to repaint itself.
884
   */
885
  protected final void repaint()
886
  {
887
    getComponent().repaint(x, y, width, height);
888
  }
889
 
890
  /**
891
   * Paints this <code>Caret</code> using the specified <code>Graphics</code>
892
   * context.
893
   *
894
   * @param g the graphics context to use
895
   */
896
  public void paint(Graphics g)
897
  {
898
    JTextComponent comp = getComponent();
899
    if (comp == null)
900
      return;
901
 
902
    // Make sure the dot has a sane position.
903
    dot = Math.min(dot, textComponent.getDocument().getLength());
904
    dot = Math.max(dot, 0);
905
 
906
    Rectangle rect = null;
907
 
908
    try
909
      {
910
        rect = textComponent.modelToView(dot);
911
      }
912
    catch (BadLocationException e)
913
      {
914
        // Let's ignore that. This shouldn't really occur. But if it
915
        // does (it seems that this happens when the model is mutating),
916
        // it causes no real damage. Uncomment this for debugging.
917
        // e.printStackTrace();
918
      }
919
 
920
    if (rect == null)
921
      return;
922
 
923
    // Check if paint has possibly been called directly, without a previous
924
    // call to damage(). In this case we need to do some cleanup first.
925
    if ((x != rect.x) || (y != rect.y))
926
      {
927
        repaint(); // Erase previous location of caret.
928
        x = rect.x;
929
        y = rect.y;
930
        width = 1;
931
        height = rect.height;
932
      }
933
 
934
    // Now draw the caret on the new position if visible.
935
    if (visible && active)
936
      {
937
        g.setColor(textComponent.getCaretColor());
938
        g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height - 1);
939
      }
940
  }
941
 
942
  /**
943
   * Returns all registered event listeners of the specified type.
944
   *
945
   * @param listenerType the type of listener to return
946
   *
947
   * @return all registered event listeners of the specified type
948
   */
949
  public <T extends EventListener> T[] getListeners(Class<T> listenerType)
950
  {
951
    return listenerList.getListeners(listenerType);
952
  }
953
 
954
  /**
955
   * Registers a {@link ChangeListener} that is notified whenever that state
956
   * of this <code>Caret</code> changes.
957
   *
958
   * @param listener the listener to register to this caret
959
   */
960
  public void addChangeListener(ChangeListener listener)
961
  {
962
    listenerList.add(ChangeListener.class, listener);
963
  }
964
 
965
  /**
966
   * Removes a {@link ChangeListener} from the list of registered listeners.
967
   *
968
   * @param listener the listener to remove
969
   */
970
  public void removeChangeListener(ChangeListener listener)
971
  {
972
    listenerList.remove(ChangeListener.class, listener);
973
  }
974
 
975
  /**
976
   * Returns all registered {@link ChangeListener}s of this <code>Caret</code>.
977
   *
978
   * @return all registered {@link ChangeListener}s of this <code>Caret</code>
979
   */
980
  public ChangeListener[] getChangeListeners()
981
  {
982
    return (ChangeListener[]) getListeners(ChangeListener.class);
983
  }
984
 
985
  /**
986
   * Notifies all registered {@link ChangeListener}s that the state
987
   * of this <code>Caret</code> has changed.
988
   */
989
  protected void fireStateChanged()
990
  {
991
    ChangeListener[] listeners = getChangeListeners();
992
 
993
    for (int index = 0; index < listeners.length; ++index)
994
      listeners[index].stateChanged(changeEvent);
995
  }
996
 
997
  /**
998
   * Returns the <code>JTextComponent</code> on which this <code>Caret</code>
999
   * is installed.
1000
   *
1001
   * @return the <code>JTextComponent</code> on which this <code>Caret</code>
1002
   *         is installed
1003
   */
1004
  protected final JTextComponent getComponent()
1005
  {
1006
    return textComponent;
1007
  }
1008
 
1009
  /**
1010
   * Returns the blink rate of this <code>Caret</code> in milliseconds.
1011
   * A value of <code>0</code> means that the caret does not blink.
1012
   *
1013
   * @return the blink rate of this <code>Caret</code> or <code>0</code> if
1014
   *         this caret does not blink
1015
   */
1016
  public int getBlinkRate()
1017
  {
1018
    return blinkRate;
1019
  }
1020
 
1021
  /**
1022
   * Sets the blink rate of this <code>Caret</code> in milliseconds.
1023
   * A value of <code>0</code> means that the caret does not blink.
1024
   *
1025
   * @param rate the new blink rate to set
1026
   */
1027
  public void setBlinkRate(int rate)
1028
  {
1029
    if (blinkTimer != null)
1030
      blinkTimer.setDelay(rate);
1031
    blinkRate = rate;
1032
  }
1033
 
1034
  /**
1035
   * Returns the current position of this <code>Caret</code> within the
1036
   * <code>Document</code>.
1037
   *
1038
   * @return the current position of this <code>Caret</code> within the
1039
   *         <code>Document</code>
1040
   */
1041
  public int getDot()
1042
  {
1043
    return dot;
1044
  }
1045
 
1046
  /**
1047
   * Moves the <code>dot</code> location without touching the
1048
   * <code>mark</code>. This is used when making a selection.
1049
   *
1050
   * <p>If the underlying text component has a {@link NavigationFilter}
1051
   * installed the caret will call the corresponding method of that object.</p>
1052
   *
1053
   * @param dot the location where to move the dot
1054
   *
1055
   * @see #setDot(int)
1056
   */
1057
  public void moveDot(int dot)
1058
  {
1059
    NavigationFilter filter = textComponent.getNavigationFilter();
1060
    if (filter != null)
1061
      filter.moveDot(getBypass(), dot, Bias.Forward);
1062
    else
1063
      moveDotImpl(dot);
1064
  }
1065
 
1066
  void moveDotImpl(int dot)
1067
  {
1068
    if (dot >= 0)
1069
      {
1070
        Document doc = textComponent.getDocument();
1071
        if (doc != null)
1072
          this.dot = Math.min(dot, doc.getLength());
1073
        this.dot = Math.max(this.dot, 0);
1074
 
1075
        handleHighlight();
1076
 
1077
        appear();
1078
      }
1079
  }
1080
 
1081
  /**
1082
   * Sets the current position of this <code>Caret</code> within the
1083
   * <code>Document</code>. This also sets the <code>mark</code> to the new
1084
   * location.
1085
   *
1086
   * <p>If the underlying text component has a {@link NavigationFilter}
1087
   * installed the caret will call the corresponding method of that object.</p>
1088
   *
1089
   * @param dot
1090
   *          the new position to be set
1091
   * @see #moveDot(int)
1092
   */
1093
  public void setDot(int dot)
1094
  {
1095
    NavigationFilter filter = textComponent.getNavigationFilter();
1096
    if (filter != null)
1097
      filter.setDot(getBypass(), dot, Bias.Forward);
1098
    else
1099
      setDotImpl(dot);
1100
  }
1101
 
1102
  void setDotImpl(int dot)
1103
  {
1104
    if (dot >= 0)
1105
      {
1106
        Document doc = textComponent.getDocument();
1107
        if (doc != null)
1108
          this.dot = Math.min(dot, doc.getLength());
1109
        this.dot = Math.max(this.dot, 0);
1110
        this.mark = this.dot;
1111
 
1112
        clearHighlight();
1113
 
1114
        appear();
1115
      }
1116
  }
1117
 
1118
  /**
1119
   * Show the caret (may be hidden due blinking) and adjust the timer not to
1120
   * hide it (possibly immediately).
1121
   *
1122
   * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
1123
   */
1124
  void appear()
1125
  {
1126
    // All machinery is only required if the carret is blinking.
1127
    if (blinkListener != null)
1128
      {
1129
        blinkListener.ignoreNextEvent = true;
1130
 
1131
        // If the caret is visible, erase the current position by repainting
1132
        // over.
1133
        if (visible)
1134
          repaint();
1135
 
1136
        // Draw the caret in the new position.
1137
        visible = true;
1138
 
1139
        Rectangle area = null;
1140
        int dot = getDot();
1141
        try
1142
          {
1143
            area = getComponent().modelToView(dot);
1144
          }
1145
        catch (BadLocationException e)
1146
          {
1147
            // Let's ignore that. This shouldn't really occur. But if it
1148
            // does (it seems that this happens when the model is mutating),
1149
            // it causes no real damage. Uncomment this for debugging.
1150
            // e.printStackTrace();
1151
          }
1152
        if (area != null)
1153
          {
1154
            adjustVisibility(area);
1155
            if (getMagicCaretPosition() == null)
1156
              setMagicCaretPosition(new Point(area.x, area.y));
1157
            damage(area);
1158
          }
1159
      }
1160
    repaint();
1161
  }
1162
 
1163
  /**
1164
   * Returns <code>true</code> if this <code>Caret</code> is blinking,
1165
   * and <code>false</code> if not. The returned value is independent of
1166
   * the visiblity of this <code>Caret</code> as returned by {@link #isVisible()}.
1167
   *
1168
   * @return <code>true</code> if this <code>Caret</code> is blinking,
1169
   *         and <code>false</code> if not.
1170
   * @see #isVisible()
1171
   * @since 1.5
1172
   */
1173
  public boolean isActive()
1174
  {
1175
    if (blinkTimer != null)
1176
      return blinkTimer.isRunning();
1177
 
1178
    return false;
1179
  }
1180
 
1181
  /**
1182
   * Returns <code>true</code> if this <code>Caret</code> is currently visible,
1183
   * and <code>false</code> if it is not.
1184
   *
1185
   * @return <code>true</code> if this <code>Caret</code> is currently visible,
1186
   *         and <code>false</code> if it is not
1187
   */
1188
  public boolean isVisible()
1189
  {
1190
    return visible && active;
1191
  }
1192
 
1193
  /**
1194
   * Sets the visibility state of the caret. <code>true</code> shows the
1195
   * <code>Caret</code>, <code>false</code> hides it.
1196
   *
1197
   * @param v the visibility to set
1198
   */
1199
  public void setVisible(boolean v)
1200
  {
1201
    if (v != visible)
1202
      {
1203
        visible = v;
1204
        updateTimerStatus();
1205
        Rectangle area = null;
1206
        int dot = getDot();
1207
        try
1208
          {
1209
            area = getComponent().modelToView(dot);
1210
          }
1211
        catch (BadLocationException e)
1212
          {
1213
            AssertionError ae;
1214
            ae = new AssertionError("Unexpected bad caret location: " + dot);
1215
            ae.initCause(e);
1216
            throw ae;
1217
          }
1218
        if (area != null)
1219
          damage(area);
1220
      }
1221
  }
1222
 
1223
  /**
1224
   * Returns the {@link Highlighter.HighlightPainter} that should be used
1225
   * to paint the selection.
1226
   *
1227
   * @return the {@link Highlighter.HighlightPainter} that should be used
1228
   *         to paint the selection
1229
   */
1230
  protected Highlighter.HighlightPainter getSelectionPainter()
1231
  {
1232
    return DefaultHighlighter.DefaultPainter;
1233
  }
1234
 
1235
  /**
1236
   * Updates the carets rectangle properties to the specified rectangle and
1237
   * repaints the caret.
1238
   *
1239
   * @param r the rectangle to set as the caret rectangle
1240
   */
1241
  protected void damage(Rectangle r)
1242
  {
1243
    if (r == null)
1244
      return;
1245
    x = r.x;
1246
    y = r.y;
1247
    width = 1;
1248
    // height is normally set in paint and we leave it untouched. However, we
1249
    // must set a valid value here, since otherwise the painting mechanism
1250
    // sets a zero clip and never calls paint.
1251
    if (height <= 0)
1252
      try
1253
        {
1254
          height = textComponent.modelToView(dot).height;
1255
        }
1256
      catch (BadLocationException ble)
1257
        {
1258
          // Should not happen.
1259
          throw new InternalError("Caret location not within document range.");
1260
        }
1261
 
1262
    repaint();
1263
  }
1264
 
1265
  /**
1266
   * Adjusts the text component so that the caret is visible. This default
1267
   * implementation simply calls
1268
   * {@link JComponent#scrollRectToVisible(Rectangle)} on the text component.
1269
   * Subclasses may wish to change this.
1270
   */
1271
  protected void adjustVisibility(Rectangle rect)
1272
  {
1273
    getComponent().scrollRectToVisible(rect);
1274
  }
1275
 
1276
  /**
1277
   * Initializes the blink timer.
1278
   */
1279
  private void initBlinkTimer()
1280
  {
1281
    // Setup the blink timer.
1282
    blinkListener = new BlinkTimerListener();
1283
    blinkTimer = new Timer(getBlinkRate(), blinkListener);
1284
    blinkTimer.setRepeats(true);
1285
  }
1286
 
1287
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.