package org.ruboto.irb;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.TextPaint;
import android.text.DynamicLayout;
import android.text.SpannableStringBuilder;
import android.widget.EditText;
/*********************************************************************************************
*
* LineNumberEditText
* @author Scott Moyer
*
* Adds the ability to display line numbers next to text (mostly for displaying code).
*/
public class LineNumberEditText extends EditText {
private boolean showLineNumbers = true;
private int defaultLeftPadding = 0;
private int lineHeight = 0;
private int currentMagnitude = 0;
private int lineNumberWidth = 0;
private int lc = 1;
private int lineAscent;
private Paint paint = new Paint();
private TextPaint textPaint = null;
private DynamicLayout layout = null;
private SpannableStringBuilder text = null;
/*********************************************************************************************
*
* Constructors
*/
public LineNumberEditText(Context context) {
super(context);
init();
}
public LineNumberEditText(Context context, android.util.AttributeSet attributes) {
super(context, attributes);
init();
}
public LineNumberEditText(Context context, android.util.AttributeSet attributes, int style) {
super(context, attributes, style);
init();
}
/*********************************************************************************************
*
* Init after inflation
*/
public void init() {
setHorizontallyScrolling(true);
paint.setColor(getTextColors().getDefaultColor() & 0xAAFFFFFF);
paint.setTextSize(getTextSize() * 0.6f);
defaultLeftPadding = getPaddingLeft();
lineHeight = getLineHeight();
textPaint = new android.text.TextPaint(paint);
}
/*********************************************************************************************
*
* Show/hide line numbers
*/
public boolean getShowLineNumbers() {
return showLineNumbers;
}
public void setShowLineNumbers(boolean trueOrFalse) {
showLineNumbers = trueOrFalse;
buildLineNumbers();
invalidate();
}
/*********************************************************************************************
*
* onDraw (main method for adding functionality to Views)
*/
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (getShowLineNumbers() && getLineCount() > 0) {
if (layout == null || lc != getLineCount()) {
buildLineNumbers();
}
canvas.save();
canvas.translate(getScrollX() - 200 + lineNumberWidth, getExtendedPaddingTop() - lineAscent);
canvas.clipRect(200 - lineNumberWidth, getScrollY() - getExtendedPaddingTop(), getPaddingLeft() + 200,
getBottom() - getTop() - getExtendedPaddingBottom() + getScrollY() - getExtendedPaddingTop() + lineAscent);
layout.draw(canvas);
canvas.restore();
}
}
/*********************************************************************************************
*
* Everything needed to update the line numbers and positioning
*/
private void buildLineNumbers() {
if (layout == null) {
text = new android.text.SpannableStringBuilder("1");
layout = new android.text.DynamicLayout(text, textPaint, 200,
android.text.Layout.Alignment.ALIGN_OPPOSITE,
0.0f, lineHeight, false);
lineAscent = layout.getLineAscent(0);
}
int newMagnitude = (!getShowLineNumbers() || getLineCount() == 0) ? 0 : (int)((Math.log10(getLineCount()) + 0.0001) + 1);
if (currentMagnitude != newMagnitude) {
lineNumberWidth = newMagnitude * (int)paint.getTextSize();
post(
new Runnable() {
public void run() {
setPadding(defaultLeftPadding + lineNumberWidth, getPaddingTop(), getPaddingRight(), getPaddingBottom());
}
});
currentMagnitude = newMagnitude;
}
if (lc > getLineCount() && getLineCount() > 0) {
text.replace(calculateLineNumberSize(getLineCount()), text.length(), "");
} else {
for(int i = lc + 1; i <= getLineCount(); i++) {
text.append("\n" + i);
}
}
lc = getLineCount() == 0 ? 1 : getLineCount();
}
private int calculateLineNumberSize(int i) {
int size = (i * 2) - 1;
for(int x = 1; x <= (int)(Math.log10(i) + 0.0001); x++) {
size += (i - (int)Math.pow(10, x) + 1);
}
return size;
}
}