diff options
Diffstat (limited to 'libjava/classpath/javax/swing/text/DefaultCaret.java')
-rw-r--r-- | libjava/classpath/javax/swing/text/DefaultCaret.java | 179 |
1 files changed, 156 insertions, 23 deletions
diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java index f2a68c00db1..c9369af2089 100644 --- a/libjava/classpath/javax/swing/text/DefaultCaret.java +++ b/libjava/classpath/javax/swing/text/DefaultCaret.java @@ -59,17 +59,48 @@ import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.EventListenerList; +import javax.swing.text.Position.Bias; /** * The default implementation of the {@link Caret} interface. * - * @author orgininal author unknown + * @author original author unknown * @author Roman Kennke (roman@kennke.org) */ public class DefaultCaret extends Rectangle implements Caret, FocusListener, MouseListener, MouseMotionListener { + + /** A text component in the current VM which currently has a + * text selection or <code>null</code>. + */ + static JTextComponent componentWithSelection; + + /** An implementation of NavigationFilter.FilterBypass which delegates + * to the corresponding methods of the <code>DefaultCaret</code>. + * + * @author Robert Schuster (robertschuster@fsfe.org) + */ + class Bypass extends NavigationFilter.FilterBypass + { + + public Caret getCaret() + { + return DefaultCaret.this; + } + public void moveDot(int dot, Bias bias) + { + DefaultCaret.this.moveDotImpl(dot); + } + + public void setDot(int dot, Bias bias) + { + DefaultCaret.this.setDotImpl(dot); + } + + } + /** * Controls the blinking of the caret. * @@ -294,12 +325,29 @@ public class DefaultCaret extends Rectangle private BlinkTimerListener blinkListener; /** + * A <code>NavigationFilter.FilterBypass</code> instance which + * is provided to the a <code>NavigationFilter</code> to + * unconditionally set or move the caret. + */ + NavigationFilter.FilterBypass bypass; + + /** * Creates a new <code>DefaultCaret</code> instance. */ public DefaultCaret() { // Nothing to do here. } + + /** Returns the caret's <code>NavigationFilter.FilterBypass</code> instance + * and creates it if it does not yet exist. + * + * @return The caret's <code>NavigationFilter.FilterBypass</code> instance. + */ + private NavigationFilter.FilterBypass getBypass() + { + return (bypass == null) ? bypass = new Bypass() : bypass; + } /** * Sets the Caret update policy. @@ -346,7 +394,8 @@ public class DefaultCaret extends Rectangle */ public void mouseDragged(MouseEvent event) { - moveCaret(event); + if (event.getButton() == MouseEvent.BUTTON1) + moveCaret(event); } /** @@ -379,7 +428,7 @@ public class DefaultCaret extends Rectangle { int count = event.getClickCount(); - if (count >= 2) + if (event.getButton() == MouseEvent.BUTTON1 && count >= 2) { int newDot = getComponent().viewToModel(event.getPoint()); JTextComponent t = getComponent(); @@ -387,29 +436,42 @@ public class DefaultCaret extends Rectangle try { if (count == 3) - t.select(Utilities.getRowStart(t, newDot), Utilities.getRowEnd(t, newDot)); + { + setDot(Utilities.getRowStart(t, newDot)); + moveDot( Utilities.getRowEnd(t, newDot)); + } else { - int nextWord = Utilities.getNextWord(t, newDot); + int wordStart = Utilities.getWordStart(t, newDot); // When the mouse points at the offset of the first character // in a word Utilities().getPreviousWord will not return that // word but we want to select that. We have to use - // Utilities.nextWord() to get it. - if (newDot == nextWord) - t.select(nextWord, Utilities.getNextWord(t, nextWord)); + // Utilities.getWordStart() to get it. + if (newDot == wordStart) + { + setDot(wordStart); + moveDot(Utilities.getWordEnd(t, wordStart)); + } else { + int nextWord = Utilities.getNextWord(t, newDot); int previousWord = Utilities.getPreviousWord(t, newDot); int previousWordEnd = Utilities.getWordEnd(t, previousWord); // If the user clicked in the space between two words, // then select the space. if (newDot >= previousWordEnd && newDot <= nextWord) - t.select(previousWordEnd, nextWord); + { + setDot(previousWordEnd); + moveDot(nextWord); + } // Otherwise select the word under the mouse pointer. else - t.select(previousWord, previousWordEnd); + { + setDot(previousWord); + moveDot(previousWordEnd); + } } } } @@ -417,8 +479,6 @@ public class DefaultCaret extends Rectangle { // TODO: Swallowing ok here? } - - dot = newDot; } } @@ -455,10 +515,35 @@ public class DefaultCaret extends Rectangle */ public void mousePressed(MouseEvent event) { - if (event.isShiftDown()) - moveCaret(event); - else - positionCaret(event); + int button = event.getButton(); + + // The implementation assumes that consuming the event makes the AWT event + // mechanism forget about this event instance and not transfer focus. + // By observing how the RI reacts the following behavior has been + // implemented (in regard to text components): + // - a left-click moves the caret + // - a left-click when shift is held down expands the selection + // - a right-click or click with any additionaly mouse button + // on a text component is ignored + // - a middle-click positions the caret and pastes the clipboard + // contents. + // - a middle-click when shift is held down is ignored + + if (button == MouseEvent.BUTTON1) + if (event.isShiftDown()) + moveCaret(event); + else + positionCaret(event); + else if(button == MouseEvent.BUTTON2) + if (event.isShiftDown()) + event.consume(); + else + { + positionCaret(event); + textComponent.paste(); + } + else + event.consume(); } /** @@ -636,10 +721,13 @@ public class DefaultCaret extends Rectangle { try { - if (highlightEntry == null) - highlightEntry = highlighter.addHighlight(0, 0, getSelectionPainter()); - else + if (highlightEntry != null) highlighter.changeHighlight(highlightEntry, 0, 0); + + // Free the global variable which stores the text component with an active + // selection. + if (componentWithSelection == textComponent) + componentWithSelection = null; } catch (BadLocationException e) { @@ -675,6 +763,17 @@ public class DefaultCaret extends Rectangle highlightEntry = highlighter.addHighlight(p0, p1, getSelectionPainter()); else highlighter.changeHighlight(highlightEntry, p0, p1); + + // If another component currently has a text selection clear that selection + // first. + if (componentWithSelection != null) + if (componentWithSelection != textComponent) + { + Caret c = componentWithSelection.getCaret(); + c.setDot(c.getDot()); + } + componentWithSelection = textComponent; + } catch (BadLocationException e) { @@ -776,7 +875,7 @@ public class DefaultCaret extends Rectangle if (visible) { g.setColor(textComponent.getCaretColor()); - g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height); + g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height - 1); } } @@ -888,12 +987,24 @@ public class DefaultCaret extends Rectangle * Moves the <code>dot</code> location without touching the * <code>mark</code>. This is used when making a selection. * + * <p>If the underlying text component has a {@link NavigationFilter} + * installed the caret will call the corresponding method of that object.</p> + * * @param dot the location where to move the dot * * @see #setDot(int) */ public void moveDot(int dot) { + NavigationFilter filter = textComponent.getNavigationFilter(); + if (filter != null) + filter.moveDot(getBypass(), dot, Bias.Forward); + else + moveDotImpl(dot); + } + + void moveDotImpl(int dot) + { if (dot >= 0) { Document doc = textComponent.getDocument(); @@ -902,8 +1013,8 @@ public class DefaultCaret extends Rectangle this.dot = Math.max(this.dot, 0); handleHighlight(); - adjustVisibility(this); appear(); + adjustVisibility(this); } } @@ -912,12 +1023,24 @@ public class DefaultCaret extends Rectangle * <code>Document</code>. This also sets the <code>mark</code> to the new * location. * + * <p>If the underlying text component has a {@link NavigationFilter} + * installed the caret will call the corresponding method of that object.</p> + * * @param dot * the new position to be set * @see #moveDot(int) */ public void setDot(int dot) { + NavigationFilter filter = textComponent.getNavigationFilter(); + if (filter != null) + filter.setDot(getBypass(), dot, Bias.Forward); + else + setDotImpl(dot); + } + + void setDotImpl(int dot) + { if (dot >= 0) { Document doc = textComponent.getDocument(); @@ -927,8 +1050,8 @@ public class DefaultCaret extends Rectangle this.mark = this.dot; clearHighlight(); - adjustVisibility(this); appear(); + adjustVisibility(this); } } @@ -1043,7 +1166,16 @@ public class DefaultCaret extends Rectangle // must set a valid value here, since otherwise the painting mechanism // sets a zero clip and never calls paint. if (height <= 0) - height = getComponent().getHeight(); + try + { + height = textComponent.modelToView(dot).height; + } + catch (BadLocationException ble) + { + // Should not happen. + throw new InternalError("Caret location not within document range."); + } + repaint(); } @@ -1068,4 +1200,5 @@ public class DefaultCaret extends Rectangle blinkTimer = new Timer(getBlinkRate(), blinkListener); blinkTimer.setRepeats(true); } + } |