/*
*
*
* Copyright 1990-2009 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.Text;
import com.sun.midp.configurator.Constants;
import com.sun.midp.chameleon.CGraphicsUtil;
import com.sun.midp.chameleon.skins.ScreenSkin;
import com.sun.midp.chameleon.skins.StringItemSkin;
import com.sun.midp.chameleon.skins.resources.StringItemResources;
/**
* This is the look &s; feel implementation for StringItem.
*/
class StringItemLFImpl extends ItemLFImpl implements StringItemLF {
/**
* Creates a look&s;feel for a StringItem
* @param strItem The StringItem associated with this look&s;feel
*/
StringItemLFImpl(StringItem strItem) {
super(strItem);
this.strItem = strItem;
StringItemResources.load();
checkTraverse();
// when no commands are added actual appearance
// is PLAIN; actual appearance will be the same
// as appearance set in StringItem if a command is added
// this StringItem
this.appearanceMode = Item.PLAIN;
}
// *****************************************************
// Public methods (StringItemLF interface impl)
// *****************************************************
/**
* Get the preferred width of this Item
*
* @param h the tentative content height in pixels, or -1 if a
* tentative height has not been computed
* @return the preferred width
*/
public int lGetPreferredWidth(int h) {
// IMPL NOTE: we ignore the 'h' value and just return
// a basic width based on our contents.
// In BUTTON mode internal layout and sizing is done in
// ItemLFImpl
if (appearanceMode == Item.BUTTON) {
return super.lGetPreferredWidth(h);
}
// In PLAIN and HYPERLINK modes label and content string are
// almost concatenated together and wrapped together
// (if both are not empty there is a horizontal padding between them)
int size[] = contentBounds;
Text.getTwoStringsSize(size, strItem.label, strItem.str,
ScreenSkin.FONT_LABEL,
strItem.font, lGetAvailableWidth(),
getHorizontalPad());
return size[WIDTH];
}
/**
* Get the preferred height of this Item
*
* @param w the tentative content width in pixels, or -1 if a
* tentative width has not been computed
* @return the preferred height
*/
public int lGetPreferredHeight(int w) {
// In BUTTON and HIPERLINK mode internal layout and sizing is done in
// ItemLFImpl
if (appearanceMode == Item.BUTTON || appearanceMode == Item.HYPERLINK) {
return super.lGetPreferredHeight(w);
}
// In PLAIN and HYPERLINK modes label and content string are
// almost concatenated together and wrapped together
// (almost because there is a horizontal padding between them)
int size[] = contentBounds;
Text.getTwoStringsSize(size, strItem.label, strItem.str,
ScreenSkin.FONT_LABEL,
strItem.font,
w == -1 ? lGetAvailableWidth() : w,
getHorizontalPad());
return size[HEIGHT];
}
/**
* Get the minimum width of this Item.
* Calculate the minimum width as the width of double "W". If the calculated
* width is greater than available width just return available width.
*
* @return the minimum width
*/
public int lGetMinimumWidth() {
int minWidth = strItem.font.charWidth('W') * 2;
int availableWidth = lGetAvailableWidth();
return (minWidth > availableWidth ? availableWidth : minWidth);
}
/**
* Get the minimum height of this Item.
* Calculate the minimum height as the height of the font.
*
* @return the minimum height
*/
public int lGetMinimumHeight() {
return strItem.font.getHeight();
}
/**
* Notifies L&s;F of a command addition in the corresponding StringItem.
* @param cmd the newly added command
* @param i the index of the added command in the StringItem's
* commands[] array
*/
public void lAddCommand(Command cmd, int i) {
super.lAddCommand(cmd, i);
// restore the value of the original appearanceMode
if ((strItem.numCommands >= 1) && (appearanceMode == Item.PLAIN)) {
appearanceMode = strItem.appearanceMode == Item.BUTTON ?
Item.BUTTON : Item.HYPERLINK;
lRequestInvalidate(true, true);
}
// checkTraverse(); right now traversability is not command dependent
// lRequestInvalidate(true, true);
}
/**
* Notifies L&s;F of a command removal in the corresponding StringItem.
* @param cmd the newly removed command
* @param i the index of the removed command in the StringItem's
* commands[] array
*/
public void lRemoveCommand(Command cmd, int i) {
super.lRemoveCommand(cmd, i);
// default to PLAIN appearance if there are no commands left
if (strItem.numCommands < 1) {
appearanceMode = Item.PLAIN;
lRequestInvalidate(true, true);
}
// checkTraverse(); right now traversability is not command dependent
// lRequestInvalidate(true, true);
}
/**
* Notifies L&s;F of a string change in the corresponding StringItem.
* @param str - the new string set in the StringItem
*/
public void lSetText(String str) {
checkTraverse();
lRequestInvalidate(true, true);
}
/**
* Notifies L&s;F of a font change in the corresponding StringItem.
* @param font - the new font set in the StringItem
*/
public void lSetFont(Font font) {
lRequestInvalidate(true, true);
}
/**
* Gets default font to render text in StringItem if it was not
* set by the application.
* @return - the font to render text if it was not set by the app
*/
public Font getDefaultFont() {
return getTextFont(appearanceMode);
}
// *****************************************************
// Package private methods
// *****************************************************
/**
* Returns the font to render text in StringItem.
* @param appearance The appearance mode of the StringItem
* @return the font to render text in StringItem.
*/
static Font getTextFont(int appearance) {
switch (appearance) {
case Item.PLAIN:
return StringItemSkin.FONT;
case Item.HYPERLINK:
return StringItemSkin.FONT_LINK;
default: // BUTTON
return StringItemSkin.FONT_BUTTON;
}
}
/**
* Returns the foreground color to render text in StringItem.
* @param appearance The appearance mode of the StringItem
* @return the foreground color per appearance mode
*/
static int getForeground(int appearance) {
switch (appearance) {
case Item.PLAIN:
return ScreenSkin.COLOR_FG;
case Item.HYPERLINK:
return StringItemSkin.COLOR_FG_LINK;
default: // BUTTON
return StringItemSkin.COLOR_FG_BUTTON;
}
}
/**
* Returns the foreground color to render text in StringItem.
* @param appearance The appearance mode of the StringItem
* @return the foreground color per appearance mode
*/
static int getForegroundHilight(int appearance) {
switch (appearance) {
case Item.HYPERLINK:
return StringItemSkin.COLOR_FG_LINK_FOCUS;
case Item.PLAIN:
default: // BUTTON
return ScreenSkin.COLOR_FG;
}
}
/**
* 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 w The width available for this Item
*/
void lGetContentSize(int size[], int w) {
if (appearanceMode == Item.HYPERLINK) {
Text.getSizeForWidth(size, w,
strItem.str, strItem.font, 0);
} else {
Text.getSizeForWidth(size, w - (2 * StringItemSkin.PAD_BUTTON_H),
strItem.str, strItem.font, 0);
size[WIDTH] = size[WIDTH] + (2 * StringItemSkin.PAD_BUTTON_H);
}
size[HEIGHT] = strItem.font.getHeight() +
(2 * StringItemSkin.PAD_BUTTON_V);
}
/**
* Determine if this Item should have a newline before it
*
* @return true if it should have a newline before
*/
boolean equateNLB() {
String label = strItem.label;
String str = strItem.str;
// If label starts with a\n,
// put this StringItem on a newline no matter what
if (label != null && label.length() > 0) {
if (label.charAt(0) == '\n') {
return true;
}
} else if (str != null && str.length() > 0) {
// If there is no label and our content starts with a \n,
// this StringItem starts on a newline
if (str.charAt(0) == '\n') {
return true;
}
} else {
// empty StringItem
return false;
}
if ((strItem.layout & Item.LAYOUT_2) == Item.LAYOUT_2) {
return ((strItem.layout & Item.LAYOUT_NEWLINE_BEFORE)
== Item.LAYOUT_NEWLINE_BEFORE);
}
// in MIDP1.0 new any StringItem with a non-null label would
// go on a new line
return label != null && label.length() > 0;
// IMPL NOTE: if there is no label in MIDP1.0
// StringItem could go on the same line only with
// StringItems and ImageItems
}
/**
* Determine if this Item should have a newline after it
*
* @return true if it should have a newline after
*/
boolean equateNLA() {
String label = item.label;
String str = strItem.str;
// If content ends with a \n,
// there is a newline after this StringItem no matter what
if (str != null && str.length() > 0) {
if (str.charAt(str.length() - 1) == '\n') {
return true;
}
} else if (label != null && label.length() > 0) {
// If there is no content and our label ends with a \n,
// there is a newline after this StringItem
if (label.charAt(label.length() - 1) == '\n') {
return true;
}
} else {
// empty StringItem
return false;
}
if ((strItem.layout & Item.LAYOUT_2) == Item.LAYOUT_2) {
return ((item.layout & Item.LAYOUT_NEWLINE_AFTER)
== Item.LAYOUT_NEWLINE_AFTER);
}
return false;
}
/**
* Paint this StringItem
*
* @param g the Graphics object to paint to
* @param width the width of this item
* @param height the height of this item
*/
void lCallPaint(Graphics g, int width, int height) {
// In BUTTON mode internal layout and painting is done through
// ItemLFImpl
if (appearanceMode == Item.BUTTON || appearanceMode == Item.HYPERLINK) {
super.lCallPaint(g, width, height);
return;
}
// **************** Hyperlink and Plain *********************
lGetLabelSize(labelBounds, width);
int xOffset = 0;
int yOffset = 0;
if (labelBounds[HEIGHT] > 0) {
Font lFont = ScreenSkin.FONT_LABEL;
xOffset = Text.paint(g, strItem.label, lFont,
ScreenSkin.COLOR_FG, 0,
width, labelBounds[HEIGHT],
0, Text.NORMAL, null);
if (xOffset > 0) {
xOffset += getHorizontalPad();
}
yOffset = labelBounds[HEIGHT] - lFont.getHeight();
g.translate(0, yOffset);
}
int mode = Text.NORMAL;
if ((lGetLockedHeight() != -1) || (lGetLockedWidth() != -1)) {
mode |= Text.TRUNCATE;
}
Text.paint(g, strItem.str, strItem.font,
getForeground(appearanceMode),
getForegroundHilight(appearanceMode),
width, height - yOffset, xOffset, mode, null);
g.translate(0, -yOffset);
}
/**
* Paints the content area of this StringItem.
* Graphics is translated to contents origin.
* @param g The graphics where StringItem 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) {
// ********************* BUTTON and HYPERLINK Appearance ******************
// Graphics is translated to content's top left corner
switch (appearanceMode) {
case Item.HYPERLINK: {
int mode = Text.HYPERLINK;
if (hasFocus) {
mode |= Text.INVERT;
}
Text.paint(g, strItem.str, strItem.font,
getForeground(appearanceMode),
getForegroundHilight(appearanceMode),
contentBounds[WIDTH], contentBounds[HEIGHT], 0, mode, null);
}
break;
case Item.BUTTON: {
if (StringItemSkin.IMAGE_BUTTON == null) {
CGraphicsUtil.draw2ColorBorder(g, 0, 0,
contentBounds[WIDTH],
contentBounds[HEIGHT],
hasFocus,
StringItemSkin.COLOR_BORDER_DK,
StringItemSkin.COLOR_BORDER_LT,
StringItemSkin.BUTTON_BORDER_W);
} else {
CGraphicsUtil.draw9pcsBackground(g, 0, 1,
contentBounds[WIDTH],
contentBounds[HEIGHT],
StringItemSkin.IMAGE_BUTTON);
}
g.translate(StringItemSkin.PAD_BUTTON_H,
StringItemSkin.PAD_BUTTON_V);
Text.paint(g, strItem.str, strItem.font,
getForeground(appearanceMode),
getForegroundHilight(appearanceMode),
contentBounds[WIDTH] - (2 * StringItemSkin.PAD_BUTTON_H),
contentBounds[HEIGHT] - (2 * StringItemSkin.PAD_BUTTON_V),
0, Text.TRUNCATE, null);
g.translate(-StringItemSkin.PAD_BUTTON_H,
-StringItemSkin.PAD_BUTTON_V);
}
}
}
/**
* called by the system to signal a key press
*
* @param keyCode the key code of the key that has been pressed
* @see #getInteractionModes
*/
void uCallKeyPressed(int keyCode) {
ItemCommandListener cl;
Command defaultCmd;
if (keyCode != Constants.KEYCODE_SELECT) {
return;
}
synchronized (Display.LCDUILock) {
// StringItem takes focus only
// if there are one or more Item Commands
// attached to it
if (!(strItem.numCommands > 0) || strItem.commandListener == null) {
return;
}
cl = strItem.commandListener;
defaultCmd = strItem.defaultCommand;
} // synchronized
// SYNC NOTE: The call to the listener must occur outside
// of the lock. 'strItem' does not change. So we can use it without
// LCDUILock.
synchronized (Display.calloutLock) {
try {
if (defaultCmd != null) {
cl.commandAction(defaultCmd, strItem);
} else {
// IMPL NOTE: Needs HI decision
// either call the first command
// from the command list or
// invoke the menu
}
} catch (Throwable thr) {
Display.handleThrowable(thr);
}
} // synchronized(calloutLock)
}
// *****************************************************
// Private methods
// *****************************************************
/**
* Check that given the label, text, and commands, Form
* should traverse this StringItem. Updates the internal
* 'skipTraverse' variable.
*/
private void checkTraverse() {
String label = strItem.label;
String str = strItem.str;
if (str == null && label == null) {
skipTraverse = true;
} else if (str == null && label.trim().equals("")) {
skipTraverse = true;
} else if (label == null && str.trim().equals("")) {
skipTraverse = true;
} else {
skipTraverse = false;
}
}
/** StringItem associated with this view */
private StringItem strItem;
/**
* An internal flag. True if Form should not traverse
* to this StringItem
*/
private boolean skipTraverse;
/**
* The actual appearance used for this StringItem.
* It can be different than the hint set in strItem.
* Actual appearance is always PLAIN if there are no
* commands added. And is never PLAIN if there is at least
* one command added
*/
private int appearanceMode;
}