/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* 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
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package javax.microedition.lcdui;
import com.sun.midp.lcdui.PhoneDial;
import com.sun.midp.lcdui.DynamicCharacterArray;
import com.sun.midp.lcdui.Text;
import com.sun.midp.lcdui.TextCursor;
import com.sun.midp.lcdui.TextInfo;
import com.sun.midp.lcdui.TextPolicy;
import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;
import com.sun.midp.chameleon.CGraphicsUtil;
import com.sun.midp.chameleon.input.*;
import com.sun.midp.chameleon.skins.*;
import com.sun.midp.chameleon.skins.resources.*;
import com.sun.midp.chameleon.layers.ScrollBarLayer;
/**
* This is the look &s; feel implementation for TextBox.
*/
class TextBoxLFImpl extends TextFieldLFImpl implements TextFieldLF {
/**
* Contains line-break information for a blob of text
*/
protected TextInfo myInfo;
/**
* A flag indicating the scroll indicator has been initialized
* for this textbox. This happens only once when the textbox
* first paints its contents.
*/
protected boolean scrollInitialized;
/**
* Creates TextFieldLF for the passed in TextField.
* @param tf The TextField associated with this TextFieldLF
*/
TextBoxLFImpl(TextField tf) {
super(tf);
if (myInfo == null) {
myInfo = new TextInfo(4); // IMPL NOTE: add initial size to skin
}
drawsTraversalIndicator = false;
}
// *****************************************************
// Public methods defined in interfaces
// *****************************************************
/**
* Notifies L&F of a content change in the corresponding TextBox.
*/
public void lSetChars() {
cursor.index = tf.buffer.length(); // cursor at the end
cursor.option = Text.PAINT_USE_CURSOR_INDEX;
myInfo.scrollY = myInfo.isModified = true;
updateTextInfo();
}
/**
* Update text info if required
*
*/
private void updateTextInfo() {
int w = contentBounds[WIDTH];
int h = contentBounds[HEIGHT];
// bounds are already initialized
if (w > 0 && h > 0) {
w -= 2 * TextFieldSkin.BOX_MARGIN + 2 * TextFieldSkin.PAD_H;
h -= ((2 * TextFieldSkin.BOX_MARGIN) +
(inputModeIndicator.getDisplayMode() != null ?
Font.getDefaultFont().getHeight() : 0));
Text.updateTextInfo(tf.buffer.toString(),
ScreenSkin.FONT_INPUT_TEXT,
w, h, 0, Text.NORMAL,
cursor, myInfo);
if (setVerticalScroll()) {
lRequestInvalidate(true,true);
} else {
lRequestPaint();
}
}
}
/**
* Set new cursor position. Update text info if cursor position is changed
* @param pos new position
*/
protected void setCaretPosition(int pos) {
int oldPos = cursor.index;
super.setCaretPosition(pos);
cursor.option = Text.PAINT_USE_CURSOR_INDEX;
myInfo.isModified = myInfo.scrollY |= (oldPos != cursor.index);
updateTextInfo();
}
/**
* Notifies L&s;F of a character insertion in the corresponding
* TextBox.
* @param data the source of the character data
* @param offset the beginning of the region of characters copied
* @param length the number of characters copied
* @param position the position at which insertion occurred
*/
public void lInsert(char data[], int offset, int length, int position) {
if (data != null) {
if (editable) {
if (position <= cursor.index) {
cursor.index += length;
cursor.option = Text.PAINT_USE_CURSOR_INDEX;
}
}
myInfo.isModified = myInfo.scrollY = true;
updateTextInfo();
}
}
/**
* Notifies L&amsp;F of character deletion in the corresponding
* TextField.
* @param offset the beginning of the deleted region
* @param length the number of characters deleted
*
* @exception IllegalArgumentException if the resulting contents
* would be illegal for the current
* @exception StringIndexOutOfBoundsException if <code>offset</code>
* and <code>length</code> do not
* specify a valid range within the contents of the <code>TextField</code>
*/
public void lDelete(int offset, int length) {
if (editable) {
if (cursor.index >= offset) {
int diff = cursor.index - offset;
cursor.index -= (diff < length) ? diff : length;
cursor.option = Text.PAINT_USE_CURSOR_INDEX;
}
}
myInfo.isModified = myInfo.scrollY = true;
updateTextInfo();
}
/**
* Notifies L&s;F of a maximum size change in the corresponding
* TextBox.
* @param maxSize - the new maximum size
*/
public void lSetMaxSize(int maxSize) {
if (editable) {
if (cursor.index >= maxSize) {
cursor.index = maxSize;
cursor.option = Text.PAINT_USE_CURSOR_INDEX;
}
}
myInfo.isModified = myInfo.scrollY = true;
updateTextInfo();
}
/**
* Notifies L&s;F that constraints have to be changed.
*/
public void lSetConstraints() {
setConstraintsCommon(false);
setVerticalScroll();
// reset cursor position if needed
if (editable && myInfo != null) {
int pos = cursor.y / ScreenSkin.FONT_INPUT_TEXT.getHeight();
int newPos = pos;
if (pos <= myInfo.topVis) {
newPos = myInfo.topVis + 1;
} else if (pos > myInfo.topVis + myInfo.visLines) {
newPos = myInfo.topVis + myInfo.visLines;
}
if (newPos != pos) {
cursor.y = newPos * ScreenSkin.FONT_INPUT_TEXT.getHeight();
cursor.option = Text.PAINT_GET_CURSOR_INDEX;
myInfo.isModified = myInfo.scrollY = true;
updateTextInfo();
}
}
lRequestPaint();
}
/**
* Paint the text, linewrapping when necessary
*
* @param g the Graphics to use to paint with. If g is null then
* only the first four arguments are used and nothing is
* painted. Use this to return just the displayed string
* @param dca the text to paint
* @param opChar if opChar > 0 then an optional character to paint.
* @param constraints text constraints
* @param font the font to use to paint the text
* @param fgColor foreground color
* @param w the available width for the text
* @param h the available height for the text
* @param offset the first line pixel offset
* @param options any of Text.[NORMAL | INVERT | HYPERLINK | TRUNCATE]
* @param cursor text cursor object to use to draw vertical bar
* @param info TextInfo structure to use for paint
*/
public void paint(Graphics g,
DynamicCharacterArray dca,
char opChar,
int constraints,
Font font,
int fgColor,
int w,
int h,
int offset,
int options,
TextCursor cursor,
TextInfo info)
{
if (opChar != 0) {
cursor = new TextCursor(cursor);
info.isModified = true;
}
String str = getDisplayString(dca, opChar, constraints,
cursor, true);
info.isModified |= !bufferedTheSameAsDisplayed(tf.constraints);
Text.updateTextInfo(str, font, w, h, offset, options, cursor, info);
Text.paintText(info, g, str, font, fgColor, 0xffffff - fgColor,
w, h, offset, options, cursor);
// just correct cursor index if the charracter has
// been already committed
if (str != null && str.length() > 0) {
getBufferString(new DynamicCharacterArray(str),
constraints, cursor, true);
}
// We'll double check our anchor point in case the Form
// has scrolled and we need to update our InputModeLayer's
// location on the screen
if (hasFocus) {
moveInputModeIndicator();
}
// has to be moved to correct place. It's incorrect to change
// the layer's dirty bounds in paint context
showPTPopup((int)0, cursor, w, h);
}
/**
* Sets the content size in the passed in array.
* Content is calculated based on the availableWidth.
* size[WIDTH] and size[HEIGHT] should be set by this method.
* @param size The array that holds Item content size and location
* in Item internal bounds coordinate system.
* @param availableWidth The width available for this Item
*/
void lGetContentSize(int size[], int availableWidth) {
int oldWidth = size[WIDTH];
int oldHeight = size[HEIGHT];
try {
// We size to the maximum allowed, minus the padding
// defined in the skin.
size[WIDTH] = ((DisplayableLFImpl)tf.owner.getLF()).
getDisplayableWidth() - 2 * TextFieldSkin.BOX_MARGIN;
// Note: tf.owner is the original TextBox for this LFImpl
size[HEIGHT] = ((DisplayableLFImpl)tf.owner.getLF()).
getDisplayableHeight() - 2 * TextFieldSkin.BOX_MARGIN;
} catch (Throwable t) {
// NOTE: the above call to getCurrent() will size the textbox
// appropriately if there is a title, ticker, etc. Calling
// this method depends on the textbox being current however.
size[WIDTH] = 100;
size[HEIGHT] = 100;
// IMPL NOTE: Log this as an error
}
if (oldHeight != size[HEIGHT] || oldWidth != size[WIDTH]) {
myInfo.scrollY = myInfo.isModified = true;
updateTextInfo();
}
}
/**
* Paints the content area of this TextField.
* Graphics is translated to contents origin.
* @param g The graphics where Item content should be painted
* @param width The width available for the Item's content
* @param height The height available for the Item's content
*/
void lPaintContent(Graphics g, int width, int height) {
g.translate(TextFieldSkin.BOX_MARGIN, TextFieldSkin.BOX_MARGIN);
width -= (2 * TextFieldSkin.BOX_MARGIN);
height -= ((2 * TextFieldSkin.BOX_MARGIN) +
(inputModeIndicator.getDisplayMode() != null ?
Font.getDefaultFont().getHeight() : 0));
if (editable) {
if (TextFieldSkin.IMAGE_BG != null) {
CGraphicsUtil.draw9pcsBackground(g, 0, 0, width, height,
TextFieldSkin.IMAGE_BG);
} else {
CGraphicsUtil.drawDropShadowBox(g, 0, 0, width, height,
TextFieldSkin.COLOR_BORDER,
TextFieldSkin.COLOR_BORDER_SHD,
TextFieldSkin.COLOR_BG);
}
} else {
if (TextFieldSkin.IMAGE_BG_UE != null) {
CGraphicsUtil.draw9pcsBackground(g, 0, 0, width, height,
TextFieldSkin.IMAGE_BG_UE);
} else {
CGraphicsUtil.drawDropShadowBox(g, 0, 0, width, height,
TextFieldSkin.COLOR_BORDER_UE,
TextFieldSkin.COLOR_BORDER_SHD_UE,
TextFieldSkin.COLOR_BG_UE);
}
}
// We need to translate by 1 more pixel horizontally
// to reserve space for cursor in the empty textfield
g.translate(TextFieldSkin.PAD_H + 1, TextFieldSkin.PAD_V);
paint(g, tf.buffer,
inputSession.getPendingChar(),
tf.constraints,
ScreenSkin.FONT_INPUT_TEXT,
(editable ? TextFieldSkin.COLOR_FG : TextFieldSkin.COLOR_FG_UE),
width - (2 * (TextFieldSkin.PAD_H)), height, 0,
Text.NORMAL, cursor, myInfo);
if (!scrollInitialized) {
setVerticalScroll();
scrollInitialized = true;
}
g.translate(-(TextFieldSkin.PAD_H + 1), -(TextFieldSkin.PAD_V));
if (usePreferredX) {
cursor.preferredX = cursor.x +
(myInfo.lineStart[myInfo.cursorLine] == cursor.index ?
ScreenSkin.FONT_INPUT_TEXT.charWidth(
tf.buffer.charAt(cursor.index)) :
0);
}
g.translate(-TextFieldSkin.BOX_MARGIN, -TextFieldSkin.BOX_MARGIN);
}
/**
* Get character index at the pointer position
*
* @param x pointer x coordinate
* @param y pointer y coordinate
* @return the character index
*/
protected int getIndexAt(int x, int y) {
x -= contentBounds[X] +
TextFieldSkin.BOX_MARGIN +
TextFieldSkin.PAD_H;
y -= contentBounds[Y] +
TextFieldSkin.BOX_MARGIN +
TextFieldSkin.PAD_V;
int id = -1;
// the pointer is inside of the content
if (x >= 0 && y >= 0) {
int numLines = myInfo.topVis + y / ScreenSkin.FONT_INPUT_TEXT.getHeight();
id = tf.buffer.length();
// the cursor has to be moved to the symbol the pointer is clicked at
// if pointer is out of text just move the cursor at the last text position
// if pointer is out of line just move the cursor at the last line position
if (numLines < myInfo.numLines) {
char[] data = tf.buffer.toCharArray();
int i = 1;
int startId = myInfo.lineStart[numLines];
for (; i <= myInfo.lineEnd[numLines] - startId; i++) {
if (x <= ScreenSkin.FONT_INPUT_TEXT.charsWidth(data, startId, i)) {
break;
}
}
id = startId + i - 1;
}
}
return id;
}
/**
* Used internally to set the vertical scroll position
*/
boolean setVerticalScroll() {
ScreenLFImpl lf = null;
if (tf != null &&
tf.owner != null &&
(lf = (ScreenLFImpl)tf.owner.getLF()) != null &&
myInfo != null) {
return lf.setVerticalScroll(myInfo.getScrollPosition(),
myInfo.getScrollProportion());
}
return false;
}
/**
* Scroll content inside of the form.
* @param scrollType scrollType. Scroll type can be one of the following
* @see ScrollBarLayer.SCROLL_NONE
* @see ScrollBarLayer.SCROLL_PAGEUP
* @see ScrollBarLayer.SCROLL_PAGEDOWN
* @see ScrollBarLayer.SCROLL_LINEUP
* @see ScrollBarLayer.SCROLL_LINEDOWN or
* @see ScrollBarLayer.SCROLL_THUMBTRACK
* @param thumbPosition
*/
void uCallScrollContent(int scrollType, int thumbPosition) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI,
"TextBoxLFImpl.uCallScrollContent scrollType=" + scrollType +
" thumbPosition=" + thumbPosition);
}
switch (scrollType) {
case ScrollBarLayer.SCROLL_PAGEUP:
uScrollViewport(Canvas.UP);
break;
case ScrollBarLayer.SCROLL_PAGEDOWN:
uScrollViewport(Canvas.DOWN);
break;
case ScrollBarLayer.SCROLL_LINEUP:
uScrollByLine(Canvas.UP);
break;
case ScrollBarLayer.SCROLL_LINEDOWN:
uScrollByLine(Canvas.DOWN);
break;
case ScrollBarLayer.SCROLL_THUMBTRACK:
uScrollAt(thumbPosition);
break;
default:
break;
}
}
/**
* Perform a page flip in the given direction. This method will
* attempt to scroll the view to show as much of the next page
* as possible. It uses the locations and bounds of the items on
* the page to best determine a new location - taking into account
* items which may lie on page boundaries as well as items which
* may span several pages.
*
* @param dir the direction of the flip, either DOWN or UP
*/
protected void uScrollViewport(int dir) {
int lines = myInfo.scrollByPage(dir == Canvas.UP ?
TextInfo.BACK : TextInfo.FORWARD);
if (lines != 0) {
if (editable) {
cursor.y += ScreenSkin.FONT_INPUT_TEXT.getHeight() * lines;
cursor.option = Text.PAINT_GET_CURSOR_INDEX;
}
updateTextInfo();
}
}
/**
* Perform a line scrolling in the given direction. This method will
* attempt to scroll the view to show next/previous line.
*
* @param dir the direction of the flip, either DOWN or UP
*/
protected void uScrollByLine(int dir) {
int oldTopVis = myInfo.topVis;
if (myInfo.scroll(dir == Canvas.UP ? TextInfo.BACK : TextInfo.FORWARD)) {
if (editable) {
cursor.y += (myInfo.topVis - oldTopVis) * ScreenSkin.FONT_INPUT_TEXT.getHeight();
cursor.option = Text.PAINT_GET_CURSOR_INDEX;
}
updateTextInfo();
}
}
/**
* Perform a scrolling at the given position.
* @param context position
*/
protected void uScrollAt(int position) {
int oldTopVis = myInfo.topVis;
myInfo.topVis = ((myInfo.height - myInfo.visLines * ScreenSkin.FONT_INPUT_TEXT.getHeight()) *
position / 100) / ScreenSkin.FONT_INPUT_TEXT.getHeight();
if (myInfo.topVis < 0) {
myInfo.topVis = 0;
} else if (myInfo.topVis - myInfo.visLines > myInfo.numLines) {
myInfo.topVis = myInfo.numLines - myInfo.visLines;
}
if (myInfo.topVis != oldTopVis) {
if (editable) {
cursor.y += (myInfo.topVis - oldTopVis) * ScreenSkin.FONT_INPUT_TEXT.getHeight();
cursor.option = Text.PAINT_GET_CURSOR_INDEX;
}
myInfo.isModified = myInfo.scrollY = true;
updateTextInfo();
}
}
/**
* Move the text cursor in the given direction
*
* @param dir direction to move
* @return true if the cursor was moved, false otherwise
*/
boolean moveCursor(int dir) {
boolean keyUsed = false;
switch (dir) {
case Canvas.LEFT:
if (editable) {
keyClicked(dir);
if (cursor.index > 0) {
cursor.index--;
cursor.option = Text.PAINT_USE_CURSOR_INDEX;
myInfo.isModified = myInfo.scrollX = keyUsed = true;
}
} else {
keyUsed = myInfo.scroll(TextInfo.BACK);
}
break;
case Canvas.RIGHT:
if (editable) {
keyClicked(dir);
if (cursor.index < tf.buffer.length()) {
cursor.index++;
cursor.option = Text.PAINT_USE_CURSOR_INDEX;
myInfo.isModified = myInfo.scrollX = keyUsed = true;
}
} else {
keyUsed = myInfo.scroll(TextInfo.FORWARD);
}
break;
case Canvas.UP:
if (editable) {
keyClicked(dir);
cursor.y -= ScreenSkin.FONT_INPUT_TEXT.getHeight();
if (cursor.y > 0) {
cursor.option = Text.PAINT_GET_CURSOR_INDEX;
myInfo.isModified = myInfo.scrollY = keyUsed = true;
} else {
cursor.y += ScreenSkin.FONT_INPUT_TEXT.getHeight();
}
} else {
keyUsed = myInfo.scroll(TextInfo.BACK);
}
break;
case Canvas.DOWN:
if (editable) {
keyClicked(dir);
cursor.y += ScreenSkin.FONT_INPUT_TEXT.getHeight();
if (cursor.y <= myInfo.height) {
cursor.option = Text.PAINT_GET_CURSOR_INDEX;
myInfo.isModified = myInfo.scrollY = keyUsed = true;
} else {
cursor.y -= ScreenSkin.FONT_INPUT_TEXT.getHeight();
}
} else {
keyUsed = myInfo.scroll(TextInfo.FORWARD);
}
break;
default:
// no-op
break;
}
updateTextInfo();
return keyUsed;
}
/**
* Called by the system to notify this Item it is being shown
*
* <p>The default implementation of this method updates
* the 'visible' state
*/
void lCallShowNotify() {
super.lCallShowNotify();
this.scrollInitialized = false;
}
/**
* This is a utility function to calculate the anchor point
* for the InputModeIndicator layer. Override TextFieldLFImpl
* version for effeciency.
* @return anchor (x, y, w, h)
*/
protected int[] getInputModeAnchor() {
ScreenLFImpl sLF = (ScreenLFImpl)tf.owner.getLF();
int space = TextFieldSkin.BOX_MARGIN
+ Font.getDefaultFont().getHeight();
return new int[] {
sLF.viewport[WIDTH] - TextFieldSkin.BOX_MARGIN - 4
+ getCurrentDisplay().getWindow().getBodyAnchorX(),
getCurrentDisplay().getWindow().getBodyAnchorY(),
sLF.viewport[HEIGHT] - space - 4,
space};
}
}
|