/* Copyright (c) 2008 Bluendo S.r.L.
* See about.html for details about license.
*
* $Id: UITextField.java 1512 2009-05-18 14:29:27Z luca $
*/
package it.yup.ui;
import java.util.Vector;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
/**
* Resembles a {@link TextField} from standard microedition.lcdui package,
* representing a text box with a label. When the select button is pressed on
* this item, a TextBox screen is opened and used to let the user enter text.
*/
public class UITextField extends UIItem implements CommandListener {
// the buttons below are set like this to fix a perverted behavior
// of nokia N95 that leaves the "cancel" button alone and mixes
// all the others button on the same menus!!!
/** ok command for the TextBox */
private static Command cmd_ok = new Command("OK", Command.OK, 1);
/** ok command for the TextBox */
private static Command cmd_cancel = new Command("CANCEL", Command.CANCEL, 2);
/** the TextBox used */
private static TextBox tb;
private UITextPanel innerPanel;
private boolean groupSelected = false;
/*
* Must be set to true when the UITextfield must automatically unexpand
* when loosing focus
*/
private boolean autoUnexpand = true;
/*
* Must be set to true when the UITextfield can be expanded and unexpanded
* by the control itself in response to a keyPress (programmatically it still can be
* epanded or unexpanded )
*/
private boolean expandable = true;
/** the label */
private String label;
/** the max size for the text. */
private int maxSize;
private boolean wrappable = false;
/**
* the constraints on the text. These constraints should be the same
* constants defined in {@link TextField} (such as {@link TextField#ANY},
* {@link TextField#PASSWORD}, ...). These are set as given in the
* constraints of the TextBox that is opened on the pressure of the select
* button on this item
*/
private int constraints;
private int maxLines = 4;
private int minLines = 1;
private boolean isEditable() {
return !((constraints & TextField.UNEDITABLE) > 0);
}
public UITextField(String label, String text, int maxSize, int constraints) {
this.label = label == null ? "" : label;
this.innerPanel = new UITextPanel();
this.innerPanel.setContainer(this);
this.innerPanel.setText(text == null ? "" : text);
innerPanel.setBg_color(UIConfig.input_color);
// innerPanel.setFg_color(UIConfig.fg_color);
// innerPanel.setSelectedColor(UIConfig.input_color);
this.maxSize = maxSize;
this.constraints = constraints;
setFocusable(true);
this.innerPanel.setFocusable(true);
dirty = true;
// the minimum height is the one of a UILabel
this.setMaxHeight(UIConfig.font_body.getHeight() + 2);
}
/**
* Sets the label value
*
* @param label
* the new label value
*/
public void setLabel(String label) {
this.label = label;
dirty = true;
}
/**
* @return the current field label
*/
public String getLabel() {
return label;
}
/**
* Changes the text shown in the field.
*
* @param text
* the text to set
*/
public void setText(String text) {
if (text.length() > maxSize) {
text = text.substring(0, maxSize);
}
this.innerPanel.setText(text);
dirty = true;
}
/**
* @return the current text
*/
public String getText() {
return this.innerPanel.getText();
}
protected void paint(Graphics g, int w, int h) {
this.width = w;
Font xFont = g.getFont();
Font lfont = Font.getFont(xFont.getFace(), Font.STYLE_BOLD, xFont
.getSize());
Font tfont = Font.getFont(xFont.getFace(), Font.STYLE_PLAIN, xFont
.getSize());
this.innerPanel.setFont(tfont);
int tempBc_color = getBg_color() >= 0 ? getBg_color()
: UIConfig.bg_color;
g.setColor(tempBc_color);
g.fillRect(0, 0, w, h);
int offset = (h - this.getHeight(g)) / 2 - 1;
if (offset < 0) offset = 0;
int loffset = 0;
if (label.length() > 0) loffset = lfont.getHeight();
if (wrappable && selected && groupSelected) {
g.setColor(tempBc_color);
g.fillRect(0, offset, w, loffset);
}
g.setColor(UIConfig.fg_color);
g.setFont(lfont);
g.drawString(label, 2, 1 + offset, Graphics.LEFT | Graphics.TOP);
g.setFont(tfont);
int innerLabelHeight = this.innerPanel.getHeight(g);
// first draw the outer borders and then the inner one
int x0 = 1, y0 = 2 + loffset + offset, x1 = w - 3 + x0, y1 = 3
+ innerLabelHeight + y0;
drawInput(g, x0, y0, x1, y1);
g.setColor(UIConfig.fg_color);
String innerText = this.innerPanel.getText();
String t = innerText;
if (this.wrappable == false) {
if ((constraints & TextField.PASSWORD) != 0 && t != null
&& t.length() > 0) {
/* text should be obscured */
t = "*******";
}
if (tfont.stringWidth(t) > w - 9) {
int l = 0;
while (l < innerText.length()
&& tfont.substringWidth(innerText, 0, l)
+ tfont.stringWidth("...") < w - 9) {
l++;
}
l--;
t = this.innerPanel.getText().substring(0, l) + "...";
}
this.innerPanel.setText(t);
}
g.translate(3, 4 + loffset + offset);
this.innerPanel.paint0(g, w - 6, innerLabelHeight);
// I don't want my Panel to be "clicked"
this.screen.removePaintedItem(innerPanel);
if (this.wrappable == false) {
this.innerPanel.setText(innerText);
}
g.setFont(xFont);
}
public int getHeight(Graphics g) {
if (dirty) {
// if the new height is computed computeRealHeight is mandatory to compute
// real height of the textfield and the innerPanel
//if (this.wrappable) computeRealHeight();
Font xFont = g.getFont();
Font lfont = Font.getFont(xFont.getFace(), Font.STYLE_BOLD, xFont
.getSize());
height = this.innerPanel.getHeight(g) + 7;
// if label is different from "" add its text
if (label.length() > 0) height += lfont.getHeight();
}
return height;
}
public void setSelected(boolean _selected) {
super.setSelected(_selected);
this.innerPanel.setSelected(_selected);
if (_selected == false) {
this.groupSelected = false;
}
}
/**
* @return the wrappable
*/
public boolean isWrappable() {
return this.wrappable;
}
/**
* @param wrappable
* the wrappable to set
*/
public void setWrappable(boolean wrappable) {
this.wrappable = wrappable;
// depending on the real height of the textPanel
// I recompute the needed height
if (this.wrappable == true) {
computeRealHeight();
this.setDirty(true);
this.askRepaint();
}
}
private void computeRealHeight() {
UILabel tempLabel = new UILabel(this.innerPanel.getText());
//needed for the borders
int w = this.width - 14;
if (w < 0) w = UICanvas.getInstance().getWidth() - 14
- UIConfig.scrollbarWidth;
tempLabel.setWrappable(true, w);
tempLabel.computeTextLines(UIConfig.font_body, w);
Vector textLines = tempLabel.getTextLines();
int nLines = textLines.size();
if (nLines > this.maxLines) nLines = this.maxLines;
if (nLines < this.minLines) nLines = this.minLines;
int textHeight = nLines * (UIConfig.font_body.getHeight() + 2) + 1;
// if nothing is inside at least one row should be present
if (textHeight == 1) textHeight = (UIConfig.font_body.getHeight() + 2) + 1;
this.setMaxHeight(textHeight);
}
public boolean keyPressed(int key) {
int ga = UICanvas.getInstance().getGameAction(key);
if (wrappable && groupSelected) {
if (ga != Canvas.FIRE) {
boolean innerKeyKeep = this.innerPanel.keyPressed(key);
if (innerKeyKeep == false && this.autoUnexpand
&& this.expandable) {
unExpand();
}
return innerKeyKeep;
} else {
if (this.expandable) unExpand();
return true;
}
}
// if a keyNum has been pressed open the textField
// and print it!!!
int keyNum = -1;
switch (key) {
case Canvas.KEY_NUM0:
case Canvas.KEY_NUM1:
case Canvas.KEY_NUM2:
case Canvas.KEY_NUM3:
case Canvas.KEY_NUM4:
case Canvas.KEY_NUM5:
case Canvas.KEY_NUM6:
case Canvas.KEY_NUM7:
case Canvas.KEY_NUM8:
case Canvas.KEY_NUM9:
keyNum = key;
}
if (keyNum == -1 && ga != Canvas.FIRE) { return false; }
// the only need for expansion is when:
// 1) fire is pressed
// 2) the object is wrappable and hence could need the innerPanel to open
// 3) this has not yet been selected.
// 4) this object is editable
// 5) the scrollbar is visible
// 6) the UITextfield is expandable
if (ga == Canvas.FIRE && this.wrappable && this.groupSelected == false
&& isEditable() == false && this.innerPanel.needScrollbar
&& this.expandable) {
expand();
return true;
}
if (isEditable()) {
// some mobile phones crash when a label is ""
handleScreen();
tb.setCommandListener(this);
UICanvas.display(tb);
}
return true;
}
public void unExpand() {
this.groupSelected = false;
this.innerPanel.setSelected(true);
this.setWrappable(true);
this.setDirty(true);
this.askRepaint();
}
public void expand() {
this.groupSelected = true;
this.innerPanel.setSelected(false);
Vector textLines = this.innerPanel.getTextLines();
if (textLines != null) {
int nLines = textLines.size();
int neededHeight = nLines * (UIConfig.font_body.getHeight() + 2)
+ 1;
Graphics g = this.screen.getGraphics();
int maxHeight = UICanvas.getInstance().getClipHeight();
maxHeight -= this.screen.headerLayout.getHeight(g);
maxHeight -= this.screen.footer.getHeight(g);
// a little bit of margin for the label...
maxHeight -= 35;
if (neededHeight > maxHeight) neededHeight = maxHeight;
this.setMaxHeight(neededHeight);
}
this.setDirty(true);
this.askRepaint();
}
public void handleScreen() {
String tempLabel = null;
tempLabel = (label == null || label.compareTo("") == 0) ? "_" : label;
tb = new TextBox(tempLabel, this.innerPanel.getText(), maxSize,
constraints);
tb.addCommand(cmd_cancel);
tb.addCommand(cmd_ok);
tb.setCommandListener(this);
UICanvas.display(tb);
}
public void commandAction(Command cmd, Displayable disp) {
if (cmd == cmd_ok) {
setText(tb.getString());
screen.itemAction(this);
UICanvas.display(null);
} else if (cmd == cmd_cancel) {
UICanvas.display(null);
}
this.dirty = true;
this.askRepaint();
}
public void setMaxHeight(int maxHeight) {
this.innerPanel.setMaxHeight(maxHeight);
}
public void setMaxLines(int maxLines) {
this.maxLines = maxLines;
}
public void setScreen(UIScreen _us) {
screen = _us;
this.innerPanel.setScreen(_us);
}
public void setDirty(boolean _dirty) {
this.dirty = _dirty;
this.innerPanel.setDirty(_dirty);
}
public boolean isDirty() {
return this.dirty || innerPanel.isDirty();
}
public void setAutoUnexpand(boolean autoUnexpand) {
this.autoUnexpand = autoUnexpand;
}
private boolean isAutoUnexpand() {
return autoUnexpand;
}
public void setExpandable(boolean expandable) {
this.expandable = expandable;
}
public boolean isExpandable() {
return expandable;
}
public void setMinLines(int minLines) {
this.minLines = minLines;
}
public int getMinLines() {
return minLines;
}
}