android.text.Layout#getLineStart ( )源码实例Demo

下面列出了android.text.Layout#getLineStart ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。

源代码1 项目: ankihelper   文件: UnderlinedTextView.java
@Override
protected void onDraw(Canvas canvas) {
    int count = getLineCount();

    final Layout layout = getLayout();
    float xStart, xStop, xDiff;
    int firstCharInLine, lastCharInLine;

    for (int i = 0; i < count; i++) {
        int baseline = getLineBounds(i, mRect);
        firstCharInLine = layout.getLineStart(i);
        lastCharInLine = layout.getLineEnd(i);

        xStart = layout.getPrimaryHorizontal(firstCharInLine);
        xDiff = layout.getPrimaryHorizontal(firstCharInLine + 1) - xStart;
        xStop = layout.getPrimaryHorizontal(lastCharInLine - 1) + xDiff;

        canvas.drawLine(xStart,
                baseline + mStrokeWidth,
                xStop,
                baseline + mStrokeWidth,
                mPaint);
    }

    super.onDraw(canvas);
}
 
源代码2 项目: BigApp_Discuz_Android   文件: JustifyTextView.java
@Override
protected void onDraw(Canvas canvas) {
    TextPaint paint = getPaint();
    paint.setColor(getCurrentTextColor());
    paint.drawableState = getDrawableState();
    mViewWidth = getMeasuredWidth();
    String text = (String) getText();
    mLineY = 0;
    mLineY += getTextSize();
    Layout layout = getLayout();
    for (int i = 0; i < layout.getLineCount(); i++) {
        int lineStart = layout.getLineStart(i);
        int lineEnd = layout.getLineEnd(i);
        String line = text.substring(lineStart, lineEnd);

        float width = StaticLayout.getDesiredWidth(text, lineStart, lineEnd, getPaint());
        if (needScale(line)) {
            drawScaledText(canvas, lineStart, line, width);
        } else {
            canvas.drawText(line, 0, mLineY, paint);
        }

        mLineY += getLineHeight();
    }
}
 
源代码3 项目: customview-samples   文件: StrokeTextView.java
private void drawStrokeBorder(Canvas canvas){
    Layout layout = getLayout();
    if(layout!=null){
        String text = getText().toString();
        int lineCount = getLineCount();
        //第一行文字顶部与控件顶部的距离
        int totalPaddingTop = getTotalPaddingTop();
        for (int i = 0; i < lineCount; i++) {
            int start = layout.getLineStart(i);
            int end = layout.getLineEnd(i);
            String rowStr = text.substring(start, end);

            float x = layout.getLineLeft(i)+getPaddingLeft();
            int y = totalPaddingTop + getBaseLine(i);

            Path textPath = new Path();
            getPaint().getTextPath(rowStr,0,rowStr.length(),x,y,textPath);
            mBorderPaint.setColor(mBorderColor);
            canvas.drawPath(textPath,mBorderPaint);
        }
    }
}
 
源代码4 项目: FoldText_Java   文件: SpannableFoldTextView.java
private void translateText(Layout layout, BufferType type) {
    if (layout.getLineCount() > mShowMaxLine) {
        SpannableStringBuilder span = new SpannableStringBuilder();
        int start = layout.getLineStart(mShowMaxLine - 1);
        int end = layout.getLineVisibleEnd(mShowMaxLine - 1);
        TextPaint paint = getPaint();
        StringBuilder builder = new StringBuilder(ELLIPSIZE_END);
        if (mTipGravity == END) {
            builder.append("  ").append(mFoldText);
            end -= paint.breakText(mOriginalText, start, end, false, paint.measureText(builder.toString()), null) + 1;
            float x = getWidth() - getPaddingLeft() - getPaddingRight() - getTextWidth(ELLIPSIZE_END.concat(mFoldText));
            while (layout.getPrimaryHorizontal(end - 1) + getTextWidth(mOriginalText.subSequence(end - 1, end).toString()) < x) {
                end++;
            }
            end -= 2;
        } else {
            end -= paint.breakText(mOriginalText, start, end, false, paint.measureText(builder.toString()), null) + 1;
        }
        CharSequence ellipsize = mOriginalText.subSequence(0, end);
        span.append(ellipsize);
        span.append(ELLIPSIZE_END);
        addTip(span, type);
    }
}
 
源代码5 项目: android_9.0.0_r45   文件: BaseKeyListener.java
private boolean deleteLine(View view, Editable content) {
    if (view instanceof TextView) {
        final Layout layout = ((TextView) view).getLayout();
        if (layout != null) {
            final int line = layout.getLineForOffset(Selection.getSelectionStart(content));
            final int start = layout.getLineStart(line);
            final int end = layout.getLineEnd(line);
            if (end != start) {
                content.delete(start, end);
                return true;
            }
        }
    }
    return false;
}
 
源代码6 项目: AdvancedTextView   文件: SelectableTextView.java
/**
 * 重绘文字,两端对齐
 *
 * @param canvas
 */
private void drawTextWithJustify(Canvas canvas) {
    // 文字画笔
    TextPaint textPaint = getPaint();
    textPaint.setColor(getCurrentTextColor());
    textPaint.drawableState = getDrawableState();

    String text_str = getText().toString();
    // 当前所在行的Y向偏移
    int currentLineOffsetY = getPaddingTop();
    currentLineOffsetY += getTextSize();

    Layout layout = getLayout();

    //循环每一行,绘制文字
    for (int i = 0; i < layout.getLineCount(); i++) {
        int lineStart = layout.getLineStart(i);
        int lineEnd = layout.getLineEnd(i);
        //获取到TextView每行中的内容
        String line_str = text_str.substring(lineStart, lineEnd);
        // 获取每行字符串的宽度(不包括字符间距)
        float desiredWidth = StaticLayout.getDesiredWidth(text_str, lineStart, lineEnd, getPaint());

        if (isLineNeedJustify(line_str)) {
            //最后一行不需要重绘
            if (i == layout.getLineCount() - 1) {
                canvas.drawText(line_str, getPaddingLeft(), currentLineOffsetY, textPaint);
            } else {
                drawJustifyTextForLine(canvas, line_str, desiredWidth, currentLineOffsetY);
            }
        } else {
            canvas.drawText(line_str, getPaddingLeft(), currentLineOffsetY, textPaint);
        }
        //更新行Y向偏移
        currentLineOffsetY += getLineHeight();
    }
}
 
源代码7 项目: AlignTextView   文件: JustifyTextView.java
@Override
protected void onDraw(Canvas canvas) {
	TextPaint paint = getPaint();
	paint.setColor(getCurrentTextColor());
	paint.drawableState = getDrawableState();
	mViewWidth = getMeasuredWidth();
	String text = getText().toString();
	mLineY = 0;
	mLineY += getTextSize();
	Layout layout = getLayout();

	// layout.getLayout()在4.4.3出现NullPointerException
	if (layout == null) {
		return;
	}

	Paint.FontMetrics fm = paint.getFontMetrics();

	int textHeight = (int) (Math.ceil(fm.descent - fm.ascent));
	textHeight = (int) (textHeight * layout.getSpacingMultiplier() + layout
			.getSpacingAdd());

	for (int i = 0; i < layout.getLineCount(); i++) {
		int lineStart = layout.getLineStart(i);
		int lineEnd = layout.getLineEnd(i);
		float width = StaticLayout.getDesiredWidth(text, lineStart,
				lineEnd, getPaint());
		String line = text.substring(lineStart, lineEnd);
		if (needScale(line)) {
			drawScaledText(canvas, lineStart, line, width);
		} else {
			canvas.drawText(line, 0, mLineY, paint);
		}
		mLineY += textHeight;
	}
}
 
源代码8 项目: litho   文件: TextSpec.java
/**
 * Truncates text which is too long and appends the given custom ellipsis CharSequence to the end
 * of the visible text.
 *
 * @param text Text to truncate
 * @param customEllipsisText Text to append to the end to indicate truncation happened
 * @param newLayout A Layout object populated with measurement information for this text
 * @param ellipsizedLineNumber The line number within the text at which truncation occurs (i.e.
 *     the last visible line).
 * @return The provided text truncated in such a way that the 'customEllipsisText' can appear at
 *     the end.
 */
private static CharSequence truncateText(
    CharSequence text,
    CharSequence customEllipsisText,
    Layout newLayout,
    int ellipsizedLineNumber,
    float layoutWidth) {
  Rect bounds = new Rect();
  newLayout
      .getPaint()
      .getTextBounds(customEllipsisText.toString(), 0, customEllipsisText.length(), bounds);
  // Identify the X position at which to truncate the final line:
  // Note: The left position of the line is needed for the case of RTL text.
  final float ellipsisTarget =
      layoutWidth - bounds.width() + newLayout.getLineLeft(ellipsizedLineNumber);
  // Get character offset number corresponding to that X position:
  int ellipsisOffset = newLayout.getOffsetForHorizontal(ellipsizedLineNumber, ellipsisTarget);
  if (ellipsisOffset > 0) {
    // getOffsetForHorizontal returns the closest character, but we need to guarantee no
    // truncation, so subtract 1 from the result:
    ellipsisOffset -= 1;

    // Ensure that we haven't chosen an ellipsisOffset that's past the end of the ellipsis start.
    // This can occur in several cases, including when the width of the customEllipsisText is less
    // than the width of the default ellipsis character, and when in RTL mode and there is
    // whitespace to the left of the text. In these cases, getOffsetForHorizontal will return the
    // end of the string because our ellipsisTarget was in the middle of the ellipsis character.
    if (newLayout.getEllipsisCount(ellipsizedLineNumber) > 0) {
      final int ellipsisStart =
          newLayout.getLineStart(ellipsizedLineNumber)
              + newLayout.getEllipsisStart(ellipsizedLineNumber);
      if (ellipsisOffset > ellipsisStart) {
        ellipsisOffset = ellipsisStart;
      }
    }
    return TextUtils.concat(text.subSequence(0, ellipsisOffset), customEllipsisText);
  } else {
    return text;
  }
}
 
源代码9 项目: java-n-IDE-for-Android   文件: HighlightEditor.java
/**
 * @param line   - current line
 * @param column - column of line
 * @return Position (in pixels) for edittext at line and column
 */
public Point getDebugPosition(int line, int column, int gravity) {
    Layout layout = getLayout();
    if (layout != null) {
        int pos = layout.getLineStart(line) + column;

        int baseline = layout.getLineBaseline(line);
        int ascent = layout.getLineAscent(line);

        int offsetHorizontal = (int) layout.getPrimaryHorizontal(pos) + mLinePadding; //x

        float y;
        int offsetVertical = 0;

        if (gravity == Gravity.BOTTOM) {
            y = baseline + ascent;
            if (verticalScroll != null) {
                offsetVertical = (int) ((y + mCharHeight) - verticalScroll.getScrollY());
            } else {
                offsetVertical = (int) ((y + mCharHeight) - getScrollY());
            }
            return new Point(offsetHorizontal, offsetVertical);
        } else if (gravity == Gravity.TOP) {
            y = layout.getLineTop(line);
            if (verticalScroll != null) {
                offsetVertical = (int) (y - verticalScroll.getScrollY());
            } else {
                offsetVertical = (int) (y - getScrollY());
            }
            return new Point(offsetHorizontal, offsetVertical);
        }

        return new Point(offsetHorizontal, offsetVertical);
    }
    return new Point();
}
 
源代码10 项目: java-n-IDE-for-Android   文件: LineUtils.java
/**
 * Gets the lineInfo from the index of the letter in the text
 */
public static int getLineFromIndex(int index, int lineCount, Layout layout) {
    int line;
    int currentIndex = 0;

    for (line = 0; line < lineCount; line++) {
        currentIndex += layout.getLineEnd(line) - layout.getLineStart(line);
        if (currentIndex >= index) {
            break;
        }
    }
    return line;
}
 
源代码11 项目: java-n-IDE-for-Android   文件: LineUtils.java
public static int getStartIndexAtLine(EditText editable, int line) {
    Layout layout = editable.getLayout();
    if (layout != null) {
        return layout.getLineStart(line);
    }
    return 0;
}
 
源代码12 项目: zone-sdk   文件: ExpandTextView.java
/**
 * 得到最多行那个 最后一个字符的位置,与第一个位置 测出宽度 与 扩展字符 相加 如果大于行宽, 就把endPos--,知道小于。
 * 最后 截取字符  0-endPos 然后拼接  扩展字符 去显示即可!
 */
private void setExpand() {
    calExpand = true;
    Layout layout = getLayout();
    int endPos = layout.getLineEnd(maxLine - 1);
    int startPos = layout.getLineStart(maxLine - 1);
    TextPaint pt = getPaint();

    //如果宽度>测量宽度 就继续 --
    while (Layout.getDesiredWidth(getText(), startPos, endPos, pt) + Layout.getDesiredWidth(new SpannedString(EXPAND), pt) > layout.getWidth()
            && --endPos > startPos) {
    }
    SpannableStringBuilder nowSb = new SpannableStringBuilder(getText().subSequence(0, endPos)).append(EXPAND);
    setText(nowSb);
}
 
源代码13 项目: customview-samples   文件: AutoProcessor.java
/**
 * 判断是否需要更新文本
 * @return
 */
private boolean isUpdateText(Layout layout,String text){
    boolean update = false;
    if(!mLastText.equals(text)) {
        update = true;
    }else{
        int lineCount = layout.getLineCount();
        int size = mLineDataList.size();
        if(lineCount != size){
            update = true;
        }else{
            for (int i = 0; i < lineCount; i++) {
                int start = layout.getLineStart(i);
                int end =layout.getLineEnd(i);
                String rowStr = text.substring(start, end);
                //去除掉换行符
                if(rowStr.contains(SYM_CHANGE_LINE)){
                    rowStr = rowStr.replace(SYM_CHANGE_LINE,"");
                }
                LineData lineData = mLineDataList.get(i);
                String lineText = lineData.getLineText();
                if (!rowStr.equals(lineText)) {
                    //原本的每行文字跟现在的每行文字不相同,说明排版变了,需要重新更新文本
                    update = true;
                    break;
                }
            }
        }
    }
    return  update;
}
 
源代码14 项目: customview-samples   文件: SpanLineEditText.java
private void refresh(){
    Layout layout = getLayout();
    if(layout != null){
        List<CustomSpanData> copySpanData = new ArrayList<>();
        copySpanData.addAll(mCustomTextSpanDataList);
        mCustomTextSpanDataList.clear();

        int lineCount = layout.getLineCount();
        String text = layout.getText().toString();
        int index = text.indexOf("\n");
        Log.d("hyh", "SpanLineEditText: refresh: index="+index);
        Log.d("hyh", "SpanLineEditText: refresh: text="+text+" ,lineCount="+lineCount);
        for (int i = 0; i < lineCount; i++) {
            int start = layout.getLineStart(i);
            int end = layout.getLineEnd(i);
            String rowStr = text.substring(start,end);
            Log.d("hyh", "SpanLineEditText: refresh: rowStr="+rowStr+" ,start="+start+" ,end="+end);
            CustomSpanData customTextSpanData = new CustomSpanData.Builder(text,start,end)
                .setTextSize(getRangeRandomInt())
                .setSpanType(TYPE_ABS_SIZE_SPAN)
                .build();
            mCustomTextSpanDataList.add(customTextSpanData);
        }
        if(!mLastText.equals(text)) {
            updateText(text);
        }
        mLastText = text;
    }
}
 
源代码15 项目: HeaderDialog   文件: ContentTextViewJustified.java
@Override
protected void onDraw(Canvas canvas) {
    TextPaint paint = getPaint();
    paint.setColor(getCurrentTextColor());
    paint.drawableState = getDrawableState();

    String text = getText().toString();

    float lineY = 0;
    lineY += getTextSize() * 1f;

    final Layout layout = getLayout();
    final float desiredLineWidth = getMeasuredWidth();

    for (int i = 0, lineCount = layout.getLineCount(); i < lineCount; i++) {
        int lineStart = layout.getLineStart(i);
        int lineEnd = layout.getLineEnd(i);
        String line = text.substring(lineStart, lineEnd);

        if (needScale(line) && i < lineCount - 1) {
            drawScaledText(line, canvas, lineY, desiredLineWidth, paint);
        } else {
            canvas.drawText(line, 0, lineY, paint);
        }

        lineY += getLineHeight();
    }
}
 
源代码16 项目: javaide   文件: LineUtils.java
/**
 * Gets the lineInfo from the index of the letter in the text
 */
public static int getLineFromIndex(int index, int lineCount, Layout layout) {
    int line;
    int currentIndex = 0;

    for (line = 0; line < lineCount; line++) {
        currentIndex += layout.getLineEnd(line) - layout.getLineStart(line);
        if (currentIndex >= index) {
            break;
        }
    }
    return line;
}
 
源代码17 项目: Nimingban   文件: LinkMovementMethod2.java
private boolean action(int what, TextView widget, Spannable buffer) {
    Layout layout = widget.getLayout();

    int padding = widget.getTotalPaddingTop() +
            widget.getTotalPaddingBottom();
    int areatop = widget.getScrollY();
    int areabot = areatop + widget.getHeight() - padding;

    int linetop = layout.getLineForVertical(areatop);
    int linebot = layout.getLineForVertical(areabot);

    int first = layout.getLineStart(linetop);
    int last = layout.getLineEnd(linebot);

    ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class);

    int a = Selection.getSelectionStart(buffer);
    int b = Selection.getSelectionEnd(buffer);

    int selStart = Math.min(a, b);
    int selEnd = Math.max(a, b);

    if (selStart < 0) {
        if (buffer.getSpanStart(FROM_BELOW) >= 0) {
            selStart = selEnd = buffer.length();
        }
    }

    if (selStart > last)
        selStart = selEnd = Integer.MAX_VALUE;
    if (selEnd < first)
        selStart = selEnd = -1;

    switch (what) {
        case CLICK:
            if (selStart == selEnd) {
                return false;
            }

            ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);

            if (link.length != 1)
                return false;

            link[0].onClick(widget);
            break;

        case UP:
            int beststart, bestend;

            beststart = -1;
            bestend = -1;

            for (int i = 0; i < candidates.length; i++) {
                int end = buffer.getSpanEnd(candidates[i]);

                if (end < selEnd || selStart == selEnd) {
                    if (end > bestend) {
                        beststart = buffer.getSpanStart(candidates[i]);
                        bestend = end;
                    }
                }
            }

            if (beststart >= 0) {
                Selection.setSelection(buffer, bestend, beststart);
                return true;
            }

            break;

        case DOWN:
            beststart = Integer.MAX_VALUE;
            bestend = Integer.MAX_VALUE;

            for (int i = 0; i < candidates.length; i++) {
                int start = buffer.getSpanStart(candidates[i]);

                if (start > selStart || selStart == selEnd) {
                    if (start < beststart) {
                        beststart = start;
                        bestend = buffer.getSpanEnd(candidates[i]);
                    }
                }
            }

            if (bestend < Integer.MAX_VALUE) {
                Selection.setSelection(buffer, beststart, bestend);
                return true;
            }

            break;
    }

    return false;
}
 
源代码18 项目: EhViewer   文件: LinkMovementMethod2.java
private boolean action(int what, TextView widget, Spannable buffer) {
    Layout layout = widget.getLayout();

    int padding = widget.getTotalPaddingTop() +
            widget.getTotalPaddingBottom();
    int areatop = widget.getScrollY();
    int areabot = areatop + widget.getHeight() - padding;

    int linetop = layout.getLineForVertical(areatop);
    int linebot = layout.getLineForVertical(areabot);

    int first = layout.getLineStart(linetop);
    int last = layout.getLineEnd(linebot);

    ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class);

    int a = Selection.getSelectionStart(buffer);
    int b = Selection.getSelectionEnd(buffer);

    int selStart = Math.min(a, b);
    int selEnd = Math.max(a, b);

    if (selStart < 0) {
        if (buffer.getSpanStart(FROM_BELOW) >= 0) {
            selStart = selEnd = buffer.length();
        }
    }

    if (selStart > last)
        selStart = selEnd = Integer.MAX_VALUE;
    if (selEnd < first)
        selStart = selEnd = -1;

    switch (what) {
        case CLICK:
            if (selStart == selEnd) {
                return false;
            }

            ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);

            if (link.length != 1)
                return false;

            link[0].onClick(widget);
            break;

        case UP:
            int beststart, bestend;

            beststart = -1;
            bestend = -1;

            for (int i = 0; i < candidates.length; i++) {
                int end = buffer.getSpanEnd(candidates[i]);

                if (end < selEnd || selStart == selEnd) {
                    if (end > bestend) {
                        beststart = buffer.getSpanStart(candidates[i]);
                        bestend = end;
                    }
                }
            }

            if (beststart >= 0) {
                Selection.setSelection(buffer, bestend, beststart);
                return true;
            }

            break;

        case DOWN:
            beststart = Integer.MAX_VALUE;
            bestend = Integer.MAX_VALUE;

            for (int i = 0; i < candidates.length; i++) {
                int start = buffer.getSpanStart(candidates[i]);

                if (start > selStart || selStart == selEnd) {
                    if (start < beststart) {
                        beststart = start;
                        bestend = buffer.getSpanEnd(candidates[i]);
                    }
                }
            }

            if (bestend < Integer.MAX_VALUE) {
                Selection.setSelection(buffer, beststart, bestend);
                return true;
            }

            break;
    }

    return false;
}
 
源代码19 项目: iBeebo   文件: LongClickableLinkMovementMethod.java
private boolean action(int what, TextView widget, Spannable buffer) {
    Layout layout = widget.getLayout();

    int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
    int areatop = widget.getScrollY();
    int areabot = areatop + widget.getHeight() - padding;

    int linetop = layout.getLineForVertical(areatop);
    int linebot = layout.getLineForVertical(areabot);

    int first = layout.getLineStart(linetop);
    int last = layout.getLineEnd(linebot);

    MyURLSpan[] candidates = buffer.getSpans(first, last, MyURLSpan.class);

    int a = Selection.getSelectionStart(buffer);
    int b = Selection.getSelectionEnd(buffer);

    int selStart = Math.min(a, b);
    int selEnd = Math.max(a, b);

    if (selStart < 0) {
        if (buffer.getSpanStart(FROM_BELOW) >= 0) {
            selStart = selEnd = buffer.length();
        }
    }

    if (selStart > last) {
        selStart = selEnd = Integer.MAX_VALUE;
    }
    if (selEnd < first) {
        selStart = selEnd = -1;
    }

    switch (what) {
        case CLICK:
            if (selStart == selEnd) {
                return false;
            }

            MyURLSpan[] link = buffer.getSpans(selStart, selEnd, MyURLSpan.class);

            if (link.length != 1) {
                return false;
            }

            link[0].onClick(widget);
            break;

        case UP:
            int best_start = -1;
            int best_end = -1;

            for (MyURLSpan candidate1 : candidates) {
                int end = buffer.getSpanEnd(candidate1);

                if (end < selEnd || selStart == selEnd) {
                    if (end > best_end) {
                        best_start = buffer.getSpanStart(candidate1);
                        best_end = end;
                    }
                }
            }

            if (best_start >= 0) {
                Selection.setSelection(buffer, best_end, best_start);
                return true;
            }

            break;

        case DOWN:
            best_start = Integer.MAX_VALUE;
            best_end = Integer.MAX_VALUE;

            for (MyURLSpan candidate : candidates) {
                int start = buffer.getSpanStart(candidate);

                if (start > selStart || selStart == selEnd) {
                    if (start < best_start) {
                        best_start = start;
                        best_end = buffer.getSpanEnd(candidate);
                    }
                }
            }

            if (best_end < Integer.MAX_VALUE) {
                Selection.setSelection(buffer, best_start, best_end);
                return true;
            }

            break;
    }

    return false;
}
 
源代码20 项目: ForPDA   文件: LinkMovementMethod.java
private boolean action(int what, TextView widget, Spannable buffer) {
    Layout layout = widget.getLayout();

    int padding = widget.getTotalPaddingTop() +
            widget.getTotalPaddingBottom();
    int areatop = widget.getScrollY();
    int areabot = areatop + widget.getHeight() - padding;

    int linetop = layout.getLineForVertical(areatop);
    int linebot = layout.getLineForVertical(areabot);

    int first = layout.getLineStart(linetop);
    int last = layout.getLineEnd(linebot);

    ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class);

    int a = Selection.getSelectionStart(buffer);
    int b = Selection.getSelectionEnd(buffer);

    int selStart = Math.min(a, b);
    int selEnd = Math.max(a, b);

    if (selStart < 0) {
        if (buffer.getSpanStart(FROM_BELOW) >= 0) {
            selStart = selEnd = buffer.length();
        }
    }

    if (selStart > last)
        selStart = selEnd = Integer.MAX_VALUE;
    if (selEnd < first)
        selStart = selEnd = -1;

    switch (what) {
        case CLICK:
            if (selStart == selEnd) {
                return false;
            }

            ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);

            if (link.length != 1)
                return false;

            link[0].onClick(widget);
            break;

        case UP:
            int beststart, bestend;

            beststart = -1;
            bestend = -1;

            for (ClickableSpan candidate1 : candidates) {
                int end = buffer.getSpanEnd(candidate1);

                if (end < selEnd || selStart == selEnd) {
                    if (end > bestend) {
                        beststart = buffer.getSpanStart(candidate1);
                        bestend = end;
                    }
                }
            }

            if (beststart >= 0) {
                Selection.setSelection(buffer, bestend, beststart);
                return true;
            }

            break;

        case DOWN:
            beststart = Integer.MAX_VALUE;
            bestend = Integer.MAX_VALUE;

            for (ClickableSpan candidate : candidates) {
                int start = buffer.getSpanStart(candidate);

                if (start > selStart || selStart == selEnd) {
                    if (start < beststart) {
                        beststart = start;
                        bestend = buffer.getSpanEnd(candidate);
                    }
                }
            }

            if (bestend < Integer.MAX_VALUE) {
                Selection.setSelection(buffer, beststart, bestend);
                return true;
            }

            break;
    }

    return false;
}