From a6eb9ea5eb16769d6c6071170ed4b9eab67c9793 Mon Sep 17 00:00:00 2001 From: "David A. Mellis" Date: Fri, 2 Mar 2012 18:06:29 -0500 Subject: [PATCH] Fixing double- and triple-click selection behavior (Peter Lewis) Double clicking selects words, double-clicking and dragging does too. Triple-click for lines. http://code.google.com/p/arduino/issues/detail?id=824 --- .../processing/app/syntax/JEditTextArea.java | 205 +++++++++++------- 1 file changed, 132 insertions(+), 73 deletions(-) diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 72fb7bafd..fae0698cc 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -834,6 +834,17 @@ public class JEditTextArea extends JComponent return lineElement.getEndOffset(); } + /** + * Returns the end offset of the specified line, but not past the end of the text + * @param line The line + * @return The end offset of the specified line, safe to use for a selection, or -1 if the line is + * invalid. + */ + public int getSafeLineStopOffset(int line) + { + return Math.min(getLineStopOffset(line),getDocumentLength()); + } + /** * Returns the length of the specified line. * @param line The line @@ -1144,7 +1155,7 @@ public class JEditTextArea extends JComponent { throw new IllegalArgumentException("Bounds out of" + " range: " + newStart + "," + - newEnd); + newEnd + " [" + getDocumentLength() + "]"); } // If the new position is the same as the old, we don't @@ -1201,6 +1212,86 @@ public class JEditTextArea extends JComponent // getLineOfOffset(end)); } } + + private boolean isWordCharacter( char ch, String noWordSep ) + { + return Character.isLetterOrDigit(ch) || ch=='_' || noWordSep.indexOf(ch) != -1; + } + + protected void setNewSelectionWord( int line, int offset ) + { + if (getLineLength(line) == 0) { + newSelectionStart = getLineStartOffset(line); + newSelectionEnd = newSelectionStart; + return; + } + + String noWordSep = (String)document.getProperty("noWordSep"); + if(noWordSep == null) + noWordSep = ""; + + String lineText = getLineText(line); + + int wordStart = 0; + int wordEnd = lineText.length(); + + char ch = lineText.charAt(Math.max(0,offset - 1)); + + // special case for whitespace (fry 0122, bug #348) + // this is really nasty.. turns out that double-clicking any non-letter + // or digit char gets lumped together.. sooo, this quickly gets messy, + // because really it needs to check whether the chars are of the same + // type.. so a double space or double - might be grouped together, + // but what about a +=1? do + and - get grouped but not the 1? blech, + // coming back to this later. it's not a difficult fix, just a + // time-consuming one to track down all the proper cases. + /* + if (ch == ' ') { + //System.out.println("yeehaa"); + + for(int i = offset - 1; i >= 0; i--) { + if (lineText.charAt(i) == ' ') { + wordStart = i; + } else { + break; + } + } + for(int i = offset; i < lineText.length(); i++) { + if (lineText.charAt(i) == ' ') { + wordEnd = i + 1; + } else { + break; + } + } + + } else { + */ + + // If the user clicked on a non-letter char, + // we select the surrounding non-letters + boolean selectNoLetter = !isWordCharacter(ch,noWordSep); + + for(int i = offset - 1; i >= 0; i--) { + ch = lineText.charAt(i); + if (selectNoLetter ^ !isWordCharacter(ch,noWordSep)) { + wordStart = i + 1; + break; + } + } + + for(int i = offset; i < lineText.length(); i++) { + ch = lineText.charAt(i); + if(selectNoLetter ^ !isWordCharacter(ch,noWordSep)) { + wordEnd = i; + break; + } + } + //} + int lineStart = getLineStartOffset(line); + + newSelectionStart = lineStart + wordStart; + newSelectionEnd = lineStart + wordEnd; + } /** @@ -1684,6 +1775,14 @@ public class JEditTextArea extends JComponent protected int selectionEnd; protected int selectionEndLine; protected boolean biasLeft; + + protected int newSelectionStart; // hack to get around lack of multiple returns in Java + protected int newSelectionEnd; + + protected boolean selectWord; + protected boolean selectLine; + protected int selectionAncorStart; + protected int selectionAncorEnd; protected int bracketPosition; protected int bracketLine; @@ -2021,9 +2120,26 @@ public class JEditTextArea extends JComponent { if (popup != null && popup.isVisible()) return; - setSelectionRectangular((evt.getModifiers() - & InputEvent.CTRL_MASK) != 0); - select(getMarkPosition(),xyToOffset(evt.getX(),evt.getY())); + if ( !selectWord && !selectLine ) { + setSelectionRectangular((evt.getModifiers() + & InputEvent.CTRL_MASK) != 0); + select(getMarkPosition(),xyToOffset(evt.getX(),evt.getY())); + } else { + int line = yToLine(evt.getY()); + if ( selectWord ) { + setNewSelectionWord( line, xToOffset(line,evt.getX()) ); + } else { + newSelectionStart = getLineStartOffset(line); + newSelectionEnd = getSafeLineStopOffset(line); + } + if ( newSelectionStart < selectionAncorStart ) { + select(newSelectionStart,selectionAncorEnd); + } else if ( newSelectionEnd > selectionAncorEnd ) { + select(selectionAncorStart,newSelectionEnd); + } else { + select(newSelectionStart,newSelectionEnd); + } + } } final Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR); @@ -2094,6 +2210,9 @@ public class JEditTextArea extends JComponent int offset = xToOffset(line,evt.getX()); int dot = getLineStartOffset(line) + offset; + selectLine = false; + selectWord = false; + switch(evt.getClickCount()) { case 1: @@ -2159,74 +2278,11 @@ public class JEditTextArea extends JComponent bl.printStackTrace(); } - String noWordSep = (String)document.getProperty("noWordSep"); - if(noWordSep == null) - noWordSep = ""; - - // Ok, it's not a bracket... select the word - String lineText = getLineText(line); - - int wordStart = 0; - int wordEnd = lineText.length(); - - char ch = lineText.charAt(Math.max(0,offset - 1)); - - // special case for whitespace (fry 0122, bug #348) - // this is really nasty.. turns out that double-clicking any non-letter - // or digit char gets lumped together.. sooo, this quickly gets messy, - // because really it needs to check whether the chars are of the same - // type.. so a double space or double - might be grouped together, - // but what about a +=1? do + and - get grouped but not the 1? blech, - // coming back to this later. it's not a difficult fix, just a - // time-consuming one to track down all the proper cases. - /* - if (ch == ' ') { - //System.out.println("yeehaa"); - - for(int i = offset - 1; i >= 0; i--) { - if (lineText.charAt(i) == ' ') { - wordStart = i; - } else { - break; - } - } - for(int i = offset; i < lineText.length(); i++) { - if (lineText.charAt(i) == ' ') { - wordEnd = i + 1; - } else { - break; - } - } - - } else { - */ - - // If the user clicked on a non-letter char, - // we select the surrounding non-letters - boolean selectNoLetter = (!Character.isLetterOrDigit(ch) - && noWordSep.indexOf(ch) == -1); - - for(int i = offset - 1; i >= 0; i--) { - ch = lineText.charAt(i); - if (selectNoLetter ^ (!Character.isLetterOrDigit(ch) && - noWordSep.indexOf(ch) == -1)) { - wordStart = i + 1; - break; - } - } - - for(int i = offset; i < lineText.length(); i++) { - ch = lineText.charAt(i); - if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) && - noWordSep.indexOf(ch) == -1)) { - wordEnd = i; - break; - } - } - //} - - int lineStart = getLineStartOffset(line); - select(lineStart + wordStart,lineStart + wordEnd); + setNewSelectionWord( line, offset ); + select(newSelectionStart,newSelectionEnd); + selectWord = true; + selectionAncorStart = selectionStart; + selectionAncorEnd = selectionEnd; /* String lineText = getLineText(line); @@ -2242,7 +2298,10 @@ public class JEditTextArea extends JComponent private void doTripleClick(MouseEvent evt, int line, int offset, int dot) { - select(getLineStartOffset(line),getLineStopOffset(line)-1); + selectLine = true; + select(getLineStartOffset(line),getSafeLineStopOffset(line)); + selectionAncorStart = selectionStart; + selectionAncorEnd = selectionEnd; } }