/*
* This file is part of lanterna (http://code.google.com/p/lanterna/).
*
* lanterna is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2010-2012 Martin
*/
package com.googlecode.lanterna.gui.component;
import com.googlecode.lanterna.gui.TextGraphics;
import com.googlecode.lanterna.gui.Theme.Category;
import com.googlecode.lanterna.input.Key;
import com.googlecode.lanterna.terminal.TerminalPosition;
import com.googlecode.lanterna.terminal.TerminalSize;
/**
*
* @author Martin
*/
public class TextBox extends AbstractInteractableComponent
{
private final int forceWidth;
private String backend;
private int editPosition;
private int visibleLeftPosition;
private int lastKnownWidth;
/**
* Creates a text box component, where the user can enter text by typing on
* the keyboard. It will be empty and 10 columns wide.
*/
public TextBox()
{
this("");
}
/**
* Creates a text box component, where the user can enter text by typing on
* the keyboard. The width of the text box will be calculated from the size
* of the initial content, passed in as the first argument. The text box
* will at least be 10 columns wide, regardless of the content though.
* @param initialContent Initial text content of the text box
*/
public TextBox(String initialContent)
{
this(initialContent, 0);
}
/**
* Creates a text box component, where the user can enter text by typing on
* the keyboard.
* @param initialContent Initial text content of the text box
* @param width Width, in columns, of the text box. Set to 0 if you
* want to autodetect the width based on initial content (will be set to
* 10 columns in width at minimum)
*/
public TextBox(String initialContent, int width)
{
if(initialContent == null)
initialContent = "";
if(width <= 0) {
if(initialContent.length() > 10)
width = initialContent.length();
else
width = 10;
}
this.forceWidth = width;
this.backend = initialContent;
this.editPosition = initialContent.length();
this.visibleLeftPosition = 0;
this.lastKnownWidth = 0;
}
public String getText()
{
return backend;
}
public void setText(String text)
{
backend = text;
editPosition = backend.length();
invalidate();
}
public void setEditPosition(int editPosition)
{
if(editPosition < 0)
editPosition = 0;
if(editPosition > backend.length())
editPosition = backend.length();
this.editPosition = editPosition;
invalidate();
}
public int getEditPosition() {
return editPosition;
}
protected String prerenderTransformation(String textboxString)
{
return textboxString;
}
@Override
public void repaint(TextGraphics graphics)
{
if(hasFocus())
graphics.applyTheme(Category.TEXTBOX_FOCUSED);
else
graphics.applyTheme(Category.TEXTBOX);
graphics.fillArea(' ');
String displayString = prerenderTransformation(backend).substring(visibleLeftPosition);
if(displayString.length() > graphics.getWidth())
displayString = displayString.substring(0, graphics.getWidth()-1);
graphics.drawString(0, 0, displayString);
setHotspot(graphics.translateToGlobalCoordinates(new TerminalPosition(editPosition - visibleLeftPosition, 0)));
lastKnownWidth = graphics.getWidth();
}
@Override
protected TerminalSize calculatePreferredSize() {
return new TerminalSize(forceWidth, 1);
}
@Override
public Result keyboardInteraction(Key key)
{
try {
switch(key.getKind()) {
case Tab:
case Enter:
return Result.NEXT_INTERACTABLE_RIGHT;
case ArrowDown:
return Result.NEXT_INTERACTABLE_DOWN;
case ReverseTab:
return Result.PREVIOUS_INTERACTABLE_LEFT;
case ArrowUp:
return Result.PREVIOUS_INTERACTABLE_UP;
case ArrowRight:
if(editPosition == backend.length())
break;
editPosition++;
if(editPosition - visibleLeftPosition >= lastKnownWidth)
visibleLeftPosition++;
break;
case ArrowLeft:
if(editPosition == 0)
break;
editPosition--;
if(editPosition - visibleLeftPosition < 0)
visibleLeftPosition--;
break;
case End:
editPosition = backend.length();
if(editPosition - visibleLeftPosition >= lastKnownWidth)
visibleLeftPosition = editPosition - lastKnownWidth + 1;
break;
case Home:
editPosition = 0;
visibleLeftPosition = 0;
break;
case Delete:
if(editPosition == backend.length())
break;
backend = backend.substring(0, editPosition) + backend.substring(editPosition + 1);
break;
case Backspace:
if(editPosition == 0)
break;
editPosition--;
if(editPosition - visibleLeftPosition < 0)
visibleLeftPosition--;
backend = backend.substring(0, editPosition) + backend.substring(editPosition + 1);
break;
case NormalKey:
//Add character
if(Character.isISOControl(key.getCharacter()) || key.getCharacter() > 127)
break;
backend = backend.substring(0, editPosition) + (char)key.getCharacter() + backend.substring(editPosition);
editPosition++;
if(editPosition - visibleLeftPosition >= lastKnownWidth)
visibleLeftPosition++;
break;
default:
return Result.EVENT_NOT_HANDLED;
}
return Result.EVENT_HANDLED;
}
finally {
invalidate();
}
}
}