/*
*
*
* 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.Text;
import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;
import com.sun.midp.configurator.Constants;
import com.sun.midp.chameleon.skins.ScreenSkin;
/**
* This is the look &s; feel implementation for Item.
*/
abstract class ItemLFImpl implements ItemLF {
/**
* Creates look and feel for the passed in item
* @param item the Item for which the Look and feel should be created
*/
ItemLFImpl(Item item) {
this.item = item;
actualBoundsInvalid = new boolean[] {true, true, true, true};
bounds = new int[] {0, 0, 0, 0};
target = new int[4];
labelBounds = new int[] {0, 0, 0, 0};
contentBounds = new int[] {0, 0, 0, 0};
}
// *****************************************************
// Public methods - ItemLF interface implementation
// *****************************************************
/**
* Get the minimum width of this Item
*
* @return the minimum width
*/
public int lGetMinimumWidth() {
// IMPL_NOTE minimum width should be less than preferred
return lGetPreferredWidth(-1);
}
/**
* 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) {
int availableWidth = lGetAvailableWidth();
if (cachedWidth == INVALID_SIZE || cachedWidth != availableWidth) {
lGetLabelSize(labelBounds, availableWidth);
lGetContentSize(contentBounds, availableWidth);
cachedWidth = availableWidth;
}
// no content
if (contentBounds[HEIGHT] == 0) {
return labelBounds[WIDTH];
}
// no label
if (labelBounds[HEIGHT] == 0) {
return contentBounds[WIDTH];
}
if (labelAndContentOnSameLine(labelBounds[HEIGHT]) &&
(labelBounds[WIDTH] + getHorizontalPad() +
contentBounds[WIDTH] <= availableWidth)) {
return labelBounds[WIDTH] + getHorizontalPad() +
contentBounds[WIDTH];
}
return (labelBounds[WIDTH] > contentBounds[WIDTH] ?
labelBounds[WIDTH] : contentBounds[WIDTH]);
}
/**
* Get the minimum height of this Item
*
* @return the minimum height
*/
public int lGetMinimumHeight() {
// minimum height will be reached if we give item the most width
return lGetPreferredHeight(-1);
}
/**
* 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) {
if (w == -1) {
w = lGetAvailableWidth();
}
if (cachedWidth == INVALID_SIZE || cachedWidth != w) {
lGetLabelSize(labelBounds, w);
lGetContentSize(contentBounds, w);
cachedWidth = w;
}
// no content
if (contentBounds[HEIGHT] == 0) {
return labelBounds[HEIGHT];
}
// no label
if (labelBounds[HEIGHT] == 0) {
return contentBounds[HEIGHT];
}
if (labelAndContentOnSameLine(labelBounds[HEIGHT]) &&
(labelBounds[WIDTH] + getHorizontalPad() +
contentBounds[WIDTH] <= w)) {
return labelBounds[HEIGHT] < contentBounds[HEIGHT] ?
contentBounds[HEIGHT] : labelBounds[HEIGHT];
}
return labelBounds[HEIGHT] + getVerticalPad() +
contentBounds[HEIGHT];
}
/**
* Returns if the pointer location (x, y, w.r.t. the Form origin)
* is within the bounds of the 'clickable' area of this
* ItemLFImpl. We exclude non-interactive areas such as the
* label. <p>
*
* Most items can use this method. The only case that needs
* overriding is the ChoiceGroupPopupLFImpl.
*/
boolean itemContainsPointer(int x, int y) {
int contentX = bounds[X] + contentBounds[X] + ScreenSkin.PAD_FORM_ITEMS - 2;
int contentY = bounds[Y] + contentBounds[Y] + ScreenSkin.PAD_FORM_ITEMS - 2;
int myX = x - contentX;
int myY = y - contentY;
return (myX >= 0 && myX <= contentBounds[WIDTH] + ScreenSkin.PAD_FORM_ITEMS - 2 &&
myY >= 0 && myY <= contentBounds[HEIGHT] + ScreenSkin.PAD_FORM_ITEMS - 2);
}
/**
* Notifies L&F of a label change in the corresponding Item.
* @param label the new label string
*/
public void lSetLabel(String label) {
int[] oldBounds = new int[]{labelBounds[X],labelBounds[Y],labelBounds[WIDTH],labelBounds[HEIGHT]};
lGetLabelSize(labelBounds, lGetAvailableWidth());
if (labelBounds[X] == oldBounds[X] && labelBounds[Y] == oldBounds[Y] &&
labelBounds[WIDTH] == oldBounds[WIDTH] && labelBounds[HEIGHT] == oldBounds[HEIGHT]) {
lRequestPaint(labelBounds[X],labelBounds[Y],labelBounds[WIDTH],labelBounds[HEIGHT]);
} else {
lRequestInvalidate(true,true);
}
}
/**
* Notifies L&F of a layout change in the corresponding Item.
* @param layout the new layout descriptor
*/
public void lSetLayout(int layout) {
lRequestInvalidate(true, true);
}
/**
* Notifies L&F of a command addition in the corresponding Item.
* @param cmd the newly added command
* @param i the index of the added command in the Item's
* commands[] array
*/
public void lAddCommand(Command cmd, int i) {
if (item.owner != null) {
((DisplayableLFImpl)item.owner.getLF()).updateCommandSet();
}
}
/**
* Notifies L&F of a command removal in the corresponding Item.
* @param cmd the newly removed command
* @param i the index of the removed command in the Item's
* commands[] array
*/
public void lRemoveCommand(Command cmd, int i) {
if (item.owner != null) {
((DisplayableLFImpl)item.owner.getLF()).updateCommandSet();
}
}
/**
* Notifies L&F of a preferred size change in the corresponding Item.
* @param width the value to which the width is locked, or
* <code>-1</code> if it is unlocked
* @param height the value to which the height is locked, or
* <code>-1</code> if it is unlocked
*/
public void lSetPreferredSize(int width, int height) {
// the "preferred size" is in "inner bounds" (contents) terms
if (width == getInnerBounds(WIDTH) &&
height == getInnerBounds(HEIGHT)) {
/* no need to invalidate */
return;
}
lRequestInvalidate(width != getInnerBounds(WIDTH),
height != getInnerBounds(HEIGHT));
}
/**
* Notifies L&F of the default command change in the corresponding Item.
* @param cmd the newly set default command
* @param i index of this new command in the ChoiceGroup's commands array
*/
public void lSetDefaultCommand(Command cmd, int i) {}
/**
* Notify this itemLF that its owner screen has changed.
* Clear internal state if its new owner is null.
*
* @param oldOwner old owner screen before this change. New owner
* can be found in Item model.
*/
public void lSetOwner(Screen oldOwner) {
if (item.owner == null) {
// Hide it
if (visible) {
// IMPL_NOTE: We are holding LCDUILock and this
// will call into app code on CustomItem.
// Need to schedule an event to do that.
uCallHideNotify();
}
// the deleted item may be added later, so we
// have to invalidate it.
actualBoundsInvalid[X] = true;
actualBoundsInvalid[Y] = true;
actualBoundsInvalid[WIDTH] = true;
actualBoundsInvalid[HEIGHT] = true;
hasFocus = false;
}
}
/**
* Called by the system to indicate the content has been scrolled
* inside of the form
*
* @param w the new width of the viewport of the screen
* @param h the new height of the viewport of the screen
*/
public void uCallScrollChanged(int newViewportX, int newViewportY) {
// do nothing by default.
}
/**
* Return whether the cached requested sizes are valid.
*
* @return <code>true</code> if the cached requested sizes are up to date.
* <code>false</code> if they have been invalidated.
*/
public final boolean isRequestedSizesValid() {
return (cachedWidth != INVALID_SIZE);
}
// *****************************************************
// Package private methods
// *****************************************************
/**
* Used by the Form Layout to set the size of this Item
*
* @param height the tentative content height in pixels
* @return the preferred width
*/
int lGetAdornedPreferredWidth(int height) {
if (height > 2 * ScreenSkin.PAD_FORM_ITEMS) {
height -= 2 * ScreenSkin.PAD_FORM_ITEMS;
} else {
height = -1;
}
return lGetPreferredWidth(height) + 2 * ScreenSkin.PAD_FORM_ITEMS;
}
/**
* Used by the Form Layout to set the size of this Item
*
* @param width the tentative content width in pixels
* @return the preferred height
*/
int lGetAdornedPreferredHeight(int width) {
if (width > 2 * ScreenSkin.PAD_FORM_ITEMS) {
width -= 2 * ScreenSkin.PAD_FORM_ITEMS;
} else {
width = -1;
}
return lGetPreferredHeight(width) + 2 * ScreenSkin.PAD_FORM_ITEMS;
}
/**
* Used by the Form Layout to set the size of this Item
* @return the minimum width that includes cell spacing
*/
int lGetAdornedMinimumWidth() {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_FORM_LAYOUT,
" [I] lGetAdornedMinimumWidth() " + this);
}
return lGetMinimumWidth() + 2 * ScreenSkin.PAD_FORM_ITEMS;
}
/**
* Used by the Form Layout to set the size of this Item
* @return the minimum height that includes cell spacing
*/
int lGetAdornedMinimumHeight() {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_FORM_LAYOUT,
" [I] lGetAdornedMinimumHeight() " + this);
}
return lGetMinimumHeight() + 2 * ScreenSkin.PAD_FORM_ITEMS;
}
/**
* Returns the width available for layout by default.
* @return The width available for the item by default
*/
int lGetAvailableWidth() {
int w = (item.owner != null) ?
((DisplayableLFImpl)item.owner.getLF()).lGetWidth() :
ScreenSkin.WIDTH - 2 * ScreenSkin.PAD_FORM_ITEMS;
return w;
}
/**
* Sets item's size
*
* @param w - the new width of the item
* @param h - the new height of the item
*/
void lSetSize(int w, int h) {
// System.out.println("set size: w=" + w + " h=" + h);
bounds[WIDTH] = w;
bounds[HEIGHT] = h;
}
/**
* Sets item's location.
*
* @param x - the new x location in form's content coordinate system
* @param y - the new y location in form's content coordinate system
*/
void lSetLocation(int x, int y) {
bounds[X] = x;
bounds[Y] = y;
}
/**
* Moves item's location by delta. Both deltaX and deltaY can be
* positive and negative.
*
* @param deltaX - the amount of pixels by which x item's location
* has to be moved
* @param deltaY - the amount of pixels by which y item's location
* has to be moved
*/
void lMove(int deltaX, int deltaY) {
bounds[X] += deltaX;
bounds[Y] += deltaY;
}
/**
* Paint the content of this Item while LCDUILock is unlocked.
* This function simply obtains LCDUILock and calls lCallPaint.
*
* @param g the Graphics object to be used for rendering the item
* @param w current width of the item in pixels
* @param h current height of the item in pixels
*/
void uCallPaint(Graphics g, int w, int h) {
synchronized (Display.LCDUILock) {
lCallPaint(g, w, h);
}
}
/**
* Paint the content of this Item while LCDUILock is locked.
*
* @param g the Graphics object to be used for rendering the item
* @param w current width of the item in pixels
* @param h current height of the item in pixels
*/
void lCallPaint(Graphics g, int w, int h) {
lDoInternalLayout(labelBounds, contentBounds, w, h);
g.translate(labelBounds[X], labelBounds[Y]);
paintLabel(g, labelBounds[WIDTH]);
g.translate(-labelBounds[X] + contentBounds[X],
-labelBounds[Y] + contentBounds[Y]);
// System.out.println("PRINT lCallPaint lPaintContent= contentBounds[WIDTH] " + contentBounds[WIDTH]);
lPaintContent(g, contentBounds[WIDTH], contentBounds[HEIGHT]);
g.translate(-contentBounds[X], -contentBounds[Y]);
}
/**
* Paints the content area of the Item. This method should be
* overridden by the subclasses. Graphics will be translated to
* contents origin.
* @param g The graphics where Item content should be painted
* @param w The width available for the Item's content
* @param h The height available for the Item's content
*/
void lPaintContent(Graphics g, int w, int h) {
}
/**
* Does internal Item layout which includes setting of location
* and size of label and content.
* @param labelBounds The array that will hold label location and size
* @param contentBounds The array that will hold content location and size
* @param w The width given to the Item
* @param h The height given to the Item
*/
void lDoInternalLayout(int labelBounds[], int contentBounds[],
int w, int h) {
if (cachedWidth == INVALID_SIZE || cachedWidth != w) {
if (actualBoundsInvalid[WIDTH] || actualBoundsInvalid[HEIGHT]) {
// Note: It is possible that lDoInternalLayout is called while
// invalidate is being processed. In that case we have to make
// sure that internal layout happens once paint is called after
// invalidate with correct new bounds.
cachedWidth = INVALID_SIZE;
layoutDone = false;
return;
}
lGetLabelSize(labelBounds, w);
lGetContentSize(contentBounds, w);
cachedWidth = w;
}
if (layoutDone) {
return;
}
labelBounds[X] = labelBounds[Y] = 0;
contentBounds[X] = contentBounds[Y] = 0;
int itemWidth, itemHeight;
// empty label
if (labelBounds[HEIGHT] == 0) {
// both label and content are empty
if (contentBounds[HEIGHT] == 0) {
layoutDone = true;
return;
}
itemWidth = contentBounds[WIDTH];
itemHeight = contentBounds[HEIGHT];
// empty content
} else if (contentBounds[HEIGHT] == 0) {
itemWidth = labelBounds[WIDTH];
itemHeight = labelBounds[HEIGHT];
} else { // both labelBounds[HEIGHT] && contentBounds[HEIGHT] != 0
itemWidth = contentBounds[WIDTH];
itemHeight = contentBounds[HEIGHT];
// label and content fit in the width available
if (labelAndContentOnSameLine(labelBounds[HEIGHT]) &&
(labelBounds[WIDTH] + getHorizontalPad() +
contentBounds[WIDTH]) <= w) {
if (contentBounds[HEIGHT] < labelBounds[HEIGHT]) {
contentBounds[Y] =
(labelBounds[HEIGHT] - contentBounds[HEIGHT]) / 2;
itemHeight = labelBounds[HEIGHT];
} else {
labelBounds[Y] =
(contentBounds[HEIGHT] - labelBounds[HEIGHT]) / 2;
itemHeight = contentBounds[HEIGHT];
}
contentBounds[X] =
labelBounds[WIDTH] + getHorizontalPad();
itemWidth = contentBounds[X] + contentBounds[WIDTH];
} else {
// label and content do NOT fit in width available
contentBounds[Y] =
labelBounds[HEIGHT] + getVerticalPad();
itemHeight = contentBounds[Y] + contentBounds[HEIGHT];
if (contentBounds[WIDTH] < labelBounds[WIDTH]) {
switch (item.layout & ImageItem.LAYOUT_CENTER) {
case Item.LAYOUT_CENTER:
contentBounds[X] =
(labelBounds[WIDTH] - contentBounds[WIDTH]) / 2;
break;
case Item.LAYOUT_RIGHT:
contentBounds[X] =
labelBounds[WIDTH] - contentBounds[WIDTH];
break;
default: // Item.LAYOUT_LEFT
break;
}
itemWidth = labelBounds[WIDTH];
} else {
// IMPL_NOTE check with ue what should happen if content is
// wider than label
switch (item.layout & ImageItem.LAYOUT_CENTER) {
case Item.LAYOUT_CENTER:
labelBounds[X] =
(contentBounds[WIDTH] - labelBounds[WIDTH]) / 2;
break;
case Item.LAYOUT_RIGHT:
// we do not right justify the label
// contentBounds[X] =
// contentBounds[WIDTH] - labelBounds[WIDTH];
break;
default: // Item.LAYOUT_LEFT
break;
}
itemWidth = contentBounds[WIDTH];
}
}
}
// find overall ImageItem location inside space provided
int x, y;
switch (item.layout & ImageItem.LAYOUT_CENTER) {
case Item.LAYOUT_CENTER:
x = (w - itemWidth) / 2;
break;
case Item.LAYOUT_RIGHT:
x = w - itemWidth;
break;
default: // Item.LAYOUT_LEFT and Default
x = 0;
}
switch (item.layout & ImageItem.LAYOUT_VCENTER) {
case Item.LAYOUT_VCENTER:
y = (h - itemHeight) / 2;
break;
case Item.LAYOUT_BOTTOM:
y = h - itemHeight;
break;
default: // Item.LAYOUT_TOP and Default
y = 0;
}
if (labelBounds[HEIGHT] > 0) {
labelBounds[X] += x;
labelBounds[Y] += y;
}
if (contentBounds[HEIGHT] > 0) {
contentBounds[X] += x;
contentBounds[Y] += y;
}
layoutDone = true;
}
/**
* Determine if this Item should horizontally shrink
*
* @return true if it should horizontally shrink
*/
boolean shouldHShrink() {
return ((item.layout & Item.LAYOUT_SHRINK) == Item.LAYOUT_SHRINK);
}
/**
* Determine if this Item should horizontally expand
*
* @return true if it should horizontally expand
*/
boolean shouldHExpand() {
return ((item.layout & Item.LAYOUT_EXPAND) == Item.LAYOUT_EXPAND);
}
/**
* Determine if this Item should vertically shrink
*
* @return true if it should vertically shrink
*/
boolean shouldVShrink() {
return ((item.layout & Item.LAYOUT_VSHRINK) == Item.LAYOUT_VSHRINK);
}
/**
* Determine if this Item should vertically expand
*
* @return true if it should vertically expand
*/
boolean shouldVExpand() {
return ((item.layout & Item.LAYOUT_VEXPAND) == Item.LAYOUT_VEXPAND);
}
/**
* Determine if this Item should have a newline after it
*
* @return true if it should have a newline after
*/
boolean equateNLA() {
return ((item.layout & Item.LAYOUT_NEWLINE_AFTER) ==
Item.LAYOUT_NEWLINE_AFTER);
}
/**
* Determine if this Item should have a newline before it
*
* @return true if it should have a newline before
*/
boolean equateNLB() {
return ((item.layout & Item.LAYOUT_NEWLINE_BEFORE) ==
Item.LAYOUT_NEWLINE_BEFORE);
}
/**
* Get the effective layout type of this Item
*
* @return layout The translated layout type.
*/
int getLayout() {
int l = item.layout;
if (l == Item.LAYOUT_DEFAULT) {
return Item.LAYOUT_BOTTOM | Item.LAYOUT_LEFT;
} else {
return l;
}
}
/**
* Determine if this Item should not be traversed to. By default,
* this method will return true only if the owner item has a
* default command or a number of item commands associated with it.
*
* @return true if this Item should not be traversed to
*/
boolean shouldSkipTraverse() {
return (item.defaultCommand == null && item.numCommands == 0);
}
/**
* Called by the system
* This function simply calls lCallTraverse() after obtaining LCDUILock.
*
* @param dir the direction of traversal
* @param viewportWidth the width of the container's viewport
* @param viewportHeight the height of the container's viewport
* @param visRect_inout passes the visible rectangle into the method, and
* returns the updated traversal rectangle from the method
* @return true if internal traversal had occurred, false if traversal
* should proceed out
*/
boolean uCallTraverse(int dir, int viewportWidth, int viewportHeight,
int[] visRect_inout) {
synchronized (Display.LCDUILock) {
return lCallTraverse(dir,
viewportWidth,
viewportHeight,
visRect_inout);
}
}
/**
* Called by the system
*
* <p>The default implementation of the traverse() method always returns
* false.</p>
*
* @param dir the direction of traversal
* @param viewportWidth the width of the container's viewport
* @param viewportHeight the height of the container's viewport
* @param visRect_inout passes the visible rectangle into the method, and
* returns the updated traversal rectangle from the method
* @return true if internal traversal had occurred, false if traversal
* should proceed out
*
* @see #getInteractionModes
* @see #traverseOut
* @see #TRAVERSE_HORIZONTAL
* @see #TRAVERSE_VERTICAL
*/
boolean lCallTraverse(int dir, int viewportWidth, int viewportHeight,
int[] visRect_inout) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_FORM_LAYOUT,
" [I] lCallTraverse()");
}
hasFocus = true;
return false;
}
/**
* Called by the system to indicate traversal has left this Item
* This function simply calls lCallTraverseOut() after obtaining LCDUILock.
*/
void uCallTraverseOut() {
synchronized (Display.LCDUILock) {
lCallTraverseOut();
}
}
/**
* Called by the system to indicate traversal has left this Item
*
* @see #getInteractionModes
* @see #traverse
* @see #TRAVERSE_HORIZONTAL
* @see #TRAVERSE_VERTICAL
*/
void lCallTraverseOut() {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_FORM_LAYOUT,
" [I] uCallTraverseOut()");
}
hasFocus = false;
}
/**
* Called by the system to notify this Item it is being shown
* This function simply calls lCallShowNotify() after obtaining LCDUILock.
*/
void uCallShowNotify() {
synchronized (Display.LCDUILock) {
lCallShowNotify();
}
}
/**
* 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() {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_FORM_LAYOUT,
" [I] uCallShowNotify()");
}
this.visible = true;
}
/**
* Called by the system to notify this Item it is being hidden.
* This function simply calls lCallHideNotify() after obtaining LCDUILock.
*/
void uCallHideNotify() {
synchronized (Display.LCDUILock) {
lCallHideNotify();
}
}
/**
* Called by the system to notify this Item it is being hidden.
*
* <p>The default implementation of this method updates
* the 'visible' state
*/
void lCallHideNotify() {
this.visible = false;
}
/**
* 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) { }
/**
* Called by the system to signal a key release
*
* @param keyCode the key code of the key that has been released
* @see #getInteractionModes
*/
void uCallKeyReleased(int keyCode) { }
/**
* Called by the system to signal a key repeat
*
* @param keyCode the key code of the key that has been repeated
* @see #getInteractionModes
*/
void uCallKeyRepeated(int keyCode) { }
/**
* Called by the system to signal a pointer press
*
* @param x the x coordinate of the pointer down
* @param y the y coordinate of the pointer down
*
* @see #getInteractionModes
*/
void uCallPointerPressed(int x, int y) {
itemWasPressed = true;
}
/**
* Called by the system to signal a pointer release
*
* @param x the x coordinate of the pointer up
* @param y the x coordinate of the pointer up
*
* @see #getInteractionModes
*/
void uCallPointerReleased(int x, int y) {
x -= contentBounds[X];
y -= contentBounds[Y];
if ( (x >= 0 && x <= contentBounds[WIDTH] && y >= 0 &&
y <= contentBounds[HEIGHT]) &&
(itemWasPressed && (hasFocus || item.owner.numCommands <= 1))) {
//should check the x,y is in item's content area
uCallKeyPressed(Constants.KEYCODE_SELECT);
}
itemWasPressed = false;
}
/**
* Called by the system to signal a pointer drag
*
* @param x the x coordinate of the pointer drag
* @param y the x coordinate of the pointer drag
*
* @see #getInteractionModes
*/
void uCallPointerDragged(int x, int y) { }
/**
* Called by the system to indicate the size available to this Item
* has changed
*
* @param w the new width of the item's content area
* @param h the new height of the item's content area
*/
void uCallSizeChanged(int w, int h) {
synchronized (Display.LCDUILock) {
layoutDone = false;
item.lUpdateLockedSize();
}
}
/**
* Called to commit any pending user interaction for the item
*/
public void lCommitPendingInteraction() { }
/**
* Optimized invalidate: the layout manager will do a more efficient
* re-layout if only the height was changed.
*
* request the event scheduler to schedule an invalidate event, that
* eventually will call uCallInvalidate(this item) of this item's
* DisplayableLFImpl
* @param width true if it was changed
* @param height true if it was changed
*/
void lRequestInvalidate(boolean width, boolean height) {
// note: we should also not call invalidate if size has not changed,
// and traversal has not changed.
actualBoundsInvalid[WIDTH] = actualBoundsInvalid[WIDTH] || width;
actualBoundsInvalid[HEIGHT] = actualBoundsInvalid[HEIGHT] || height;
cachedWidth = INVALID_SIZE;
layoutDone = false;
if (item.owner != null) {
((DisplayableLFImpl)item.owner.getLF()).lRequestInvalidate();
}
}
/**
* Called by subclasses to repaint this entire Item's bounds
*/
void uRequestPaint() {
synchronized (Display.LCDUILock) {
lRequestPaint();
}
}
/**
* Called by subclasses to repaint this entire Item's bounds
*/
void lRequestPaint() {
if (bounds != null) {
// "outer" bounds repaint
lRequestPaint(0, 0, bounds[WIDTH], bounds[HEIGHT]);
}
}
/**
* Called by subclasses to repaint a portion of this Item's bounds
*
* @param x the x coordinate of the origin of the dirty region
* @param y the y coordinate of the origin of the dirty region
* @param w the width of the dirty region
* @param h the height of the dirty region
*/
void lRequestPaint(int x, int y, int w, int h) {
// target[] is recalculated completely, so ne need
// to initialize it beforehand.
if (item.owner != null) {
if (x >= bounds[WIDTH] || y >= bounds[HEIGHT] ||
x + w <= 0 || y + h <= 0 || w <= 0 || h <= 0) {
return;
}
if (x < 0) {
w += x;
target[X] = 0;
} else {
target[X] = x;
}
if (y < 0) {
h += y;
target[Y] = 0;
} else {
target[Y] = y;
}
target[WIDTH] = bounds[WIDTH] - target[X];
if (w < target[WIDTH]) {
target[WIDTH] = w;
}
target[HEIGHT] = bounds[HEIGHT] - target[Y];
if (h < target[HEIGHT]) {
target[HEIGHT] = h;
}
if (item.owner.getLF() instanceof FormLFImpl) {
((FormLFImpl)item.owner.getLF()).lRequestPaintItem(item,
target[X],
target[Y],
target[WIDTH],
target[HEIGHT]);
} else if (item.owner.getLF() instanceof AlertLFImpl) {
// ((AlertLFImpl)item.owner.getLF()).lRequestPaintItem(item,
// x, y, w, h);
// Causes a paint error in Alert
// only a partial painting of the gauge...
((AlertLFImpl)item.owner.getLF()).lRequestPaint();
}
}
}
/**
* Paint an item - called by Form.
* IMPL_NOTE: This function must be called with LCDUILock unlocked.
* To be renamed to uPaintItem.
*
* @param g the Graphics object to paint to
* @param clip the original graphics clip to restore
* @param trX x co-ordinate used for computing location
* @param trY y co-ordinate used for computing location
*/
void paintItem(Graphics g, int[] clip, int trX, int trY) {
// SYNC NOTE: see uCallPaint()
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_ITEM_PAINT,
" [I] paintItem " + this +
"\t visible=" + visible);
// note: paintItem is called more times than needed.
}
// NOTE: Its possible, that an Item is in an invalid state
// during a requested repaint. Its ok to simply return,
// because it means there is a validation event coming on
// the event thread. When the form re-validates, the Item
// will be given a proper bounds and will be repainted
if (actualBoundsInvalid[X] || actualBoundsInvalid[Y] ||
actualBoundsInvalid[WIDTH] || actualBoundsInvalid[HEIGHT]) {
// I assume the invalid flag is turned to true before
// calling paint.
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_ITEM_PAINT,
" [I] ItemLFImpl: paintItem(..) " +
" BUG: invalid=true");
}
// should have returned here
}
// it cannot be null, since it's initialized in the CTOR, and
// it's never get nullified afterwards:
// if (bounds == null) {
// return;
// }
// "inner" bounds location in screen coordinates
int tX = getInnerBounds(X) + trX;
int tY = getInnerBounds(Y) + trY;
// If we're already beyond the clip, quit looping, as long
// as we're not validating the visibility of Items after a
// scroll (calling show/hideNotify())
if (((tY + getInnerBounds(HEIGHT) < clip[Y]) ||
(tY > (clip[Y] + clip[HEIGHT])))) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_ITEM_PAINT,
" [I] ItemLFImpl: paintItem(..) " +
"- cutting the loop");
}
return;
}
// Clip the dirty region to only include the item
// g.clipRect(tX, tY, bounds[WIDTH], bounds[HEIGHT]);
// "inner" bounds
g.clipRect(tX, tY,
getInnerBounds(WIDTH),
getInnerBounds(HEIGHT));
// If the Item is inside the clip, go ahead and paint it
if (g.getClipWidth() > 0 && g.getClipHeight() > 0) {
// Translate into the Item's coordinate space
g.translate(tX, tY);
// need revisit: call showNotify() on the Item first
// We translate the Graphics into the Item's
// coordinate space
uCallPaint(g, getInnerBounds(WIDTH),
getInnerBounds(HEIGHT));
// Its critical to undo any translates
// IMPL_NOTE: If CustomItems are poorly written, they
// will potentially ruin the translate. Harden this code,
// but DO NOT use g.reset() as the translate needs to remain
// for the surrounding layer mechanics.
g.translate(-tX, -tY);
}
// Restore the clip to its original context so
// future clipRect() calls will have the correct intersection
g.setClip(clip[X], clip[Y], clip[WIDTH], clip[HEIGHT]);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_ITEM_PAINT,
" [I] draw border? hasFocus="+hasFocus);
}
if (drawsTraversalIndicator && hasFocus && (contentBounds[HEIGHT] > 0)) {
g.clipRect(bounds[X] + trX,
bounds[Y] + trY,
bounds[WIDTH],
bounds[HEIGHT]);
paintTraversalIndicator(g, bounds[X] + trX, bounds[Y] + trY);
g.setClip(clip[X], clip[Y], clip[WIDTH], clip[HEIGHT]);
}
}
/**
* Paint the traversal indicator. The width/height are obtained from
* the current traversal item's bounds.
*
* @param g the Graphics to paint on
* @param x the x origin coordinate to paint the traversal indicator
* @param y the y origin coordinate to paint the traversal indicator
*/
// should move to ScreenLFImpl.paintTraversalIndicator(..) -
// could be used in Alert as well
void paintTraversalIndicator(Graphics g, int x, int y) {
// SYNC NOTE: see uCallPaint()
// ItemLFImpl itemLF = itemLFs[traverseIndex];
// NTS: This may need to special case StringItem?
g.setColor(ScreenSkin.COLOR_TRAVERSE_IND);
g.drawRect(x + contentBounds[X] + ScreenSkin.PAD_FORM_ITEMS - 2,
y + contentBounds[Y] + ScreenSkin.PAD_FORM_ITEMS - 2,
contentBounds[WIDTH] + ScreenSkin.PAD_FORM_ITEMS - 2,
contentBounds[HEIGHT] + ScreenSkin.PAD_FORM_ITEMS - 2);
}
/**
* 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.
* Subclasses need to override this method for correct layout.
* @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) {
}
/**
* Sets the label size in the passed in array.
* Content is calculated based on the availableWidth.
* size[WIDTH] and size[HEIGHT] should be set by this method.
* Subclasses need to override this method for correct layout.
* @param size The size array that holds Item label size and location
* in Item internal bounds coordinate system.
* @param availableWidth The width available for this Item
*/
void lGetLabelSize(int size[], int availableWidth) {
if (item.label == null || item.label.length() == 0) {
size[WIDTH] = size[HEIGHT] = 0;
return;
}
if (availableWidth == -1) {
availableWidth = lGetAvailableWidth();
}
Text.getSizeForWidth(size, availableWidth, item.label,
ScreenSkin.FONT_LABEL, 0);
}
/**
* Called by subclasses to paint this Item's label
*
* @param g the graphics to draw to
* @param width the allowable width for the label
*/
void paintLabel(Graphics g, int width) {
Text.paint(g, item.label, ScreenSkin.FONT_LABEL,
ScreenSkin.COLOR_FG, 0,
width, labelBounds[HEIGHT], 0, Text.NORMAL, null);
}
/**
* Returns true if label and content can be placed on the same line.
* If this function returns always false then content will be
* always put on a new line in relation to label.
*
* @param labelHeight The height available for the label
* @return true If label and content can be placed on the same line;
* otherwise - false.
*/
boolean labelAndContentOnSameLine(int labelHeight) {
return labelHeight <= ScreenSkin.FONT_LABEL.getHeight();
}
/**
* Returns the vertical padding used between label and content
* when those are laid out vertically. Subclasses should override it
* if the padding should be other than ScreenSkin.AFTER_LABEL_VERT_PAD
* @return the vertical padding between label and content
*/
int getVerticalPad() {
return ScreenSkin.PAD_LABEL_VERT;
}
/**
* Returns the horizontal padding used between label and content
* when those are laid out horizontally. Subclasses should override it
* if the padding should be other than ScreenSkin.AFTER_LABEL_HORZ_PAD.
* @return the horizontal padding between label and content
*/
int getHorizontalPad() {
return ScreenSkin.PAD_LABEL_HORIZ;
}
/**
* Return the current Display instance that this Item is displayed in.
* @return the current Display
*/
Display getCurrentDisplay() {
return (item.owner == null) ?
null :
item.owner.getLF().lGetCurrentDisplay();
}
/**
* Used by child classes to get their location and size.
* the bounds represent the "outer" bounds of the Item,
* i.e. location and size that include the space needed for
* painting the border.
* The "inner" bounds is the space for the Item to paint into.
* (alternatively, it's possible to do setOuterBounds(..) instead
* and store in this class both "bounds" for inner bounds, and
* "outerBounds" for use from form. It could be more efficient
* for painting the item).
* @param dimension <placeholder>
* @return the inner bounds.
*/
protected int getInnerBounds(int dimension) {
// if there should be space left for highlight:
if (dimension == X || dimension == Y) {
return bounds[dimension] + ScreenSkin.PAD_FORM_ITEMS;
} else {
return bounds[dimension] -
ScreenSkin.PAD_FORM_ITEMS -
ScreenSkin.PAD_FORM_ITEMS;
}
}
/**
* Returns the locked width of the Item, or -1 if it's not locked
* @return locked width plus adornment width, or -1 if it's not locked
*/
protected int lGetLockedWidth() {
if (item.lockedWidth == -1) {
return -1;
}
return item.lockedWidth + ScreenSkin.PAD_FORM_ITEMS +
ScreenSkin.PAD_FORM_ITEMS;
}
/**
* Returns the locked height of the Item, or -1 if it's not locked
* @return locked height plus adornment height, or -1 if it's not locked
*/
protected int lGetLockedHeight() {
if (item.lockedHeight == -1) {
return -1;
}
return item.lockedHeight + ScreenSkin.PAD_FORM_ITEMS +
ScreenSkin.PAD_FORM_ITEMS;
}
/** bounds[] array index to x coordinate */
final static int X = DisplayableLFImpl.X;
/** bounds[] array index to y coordinate */
final static int Y = DisplayableLFImpl.Y;
/** bounds[] array index to width */
final static int WIDTH = DisplayableLFImpl.WIDTH;
/** bounds[] array index to height */
final static int HEIGHT = DisplayableLFImpl.HEIGHT;
/**
* The owner of this view. Set in the constructor.
*/
Item item; // = null
/**
* An array of 4 elements, describing the x, y, width, height
* of this Item's bounds in the viewport coordinate space.
* The bounds include the space needed for highlight and cell spacing.
* If its null, it means the Item is currently not in the viewport
*/
int[] bounds;
/**
* A flag indicating this Item has the input focus. This is
* maintained by the Item superclass for easy use by subclass
* code.
*/
boolean hasFocus; // = false
/**
* A flag indicating the size of this Item has changed in a
* subsequent layout operation
*/
boolean sizeChanged; // = false
/**
* A flag indicating this Item is currently (at least partially)
* visible on the Form it is on. This is maintained by the Item
* superclass for easy use by subclass code.
*/
boolean visible; // = false
/**
* Flags that are set to true when handling invalidate on this item.
* If it is known that one of the dimensions remains the same, than
* the responding invalid flag will stay false.
* It will turn to false after it get laid out
*/
boolean[] actualBoundsInvalid;
/**
* This flag marks if the last layout performed placed
* this Item at the beginning of a line.
*/
boolean isNewLine = false;
/**
* Marks the height of the Row this Item is placed on.
* Note that the Item may be shorter than the height of the row
*/
int rowHeight = 0;
/**
* helper array used by lRequestPaint(x,y,w,h)
*/
int target[];
/**
* Is true only if this item can indicate that it has focus by
* changing appearance, requiring no external traversal indicator
*/
boolean drawsTraversalIndicator = true;
/**
* A constant used to indicate that Item sizes have to be recalculated.
*/
static final int INVALID_SIZE = -1;
/**
* Width used for last calculations. If the width passed into
* lGetPreferredWidth/Height and lCallPaint is the same as cachedWidth
* then values from labelBounds and contentBounds could be used
* without recalculation.
*/
int cachedWidth = INVALID_SIZE;
/**
* Current label location and size. This array is newed in the constructor.
* It holds location and size of the label in the internal bounds
* coordinate system. It can be referenced using X, Y, WIDTH, HEIGHT
* defined in this class.
*/
int labelBounds[]; // = null
/**
* Current content location and size. This array is newed in the
* constructor. It holds location and size of the label
* in the internal bounds coordinate system.
* It can be referenced using X, Y, WIDTH, HEIGHT defined in this class.
*/
int contentBounds[]; // = null
/**
* If true label and content locations are accurate in the
* labelBounds and contentBounds arrays otherwise they have to be
* recalculated.
*/
boolean layoutDone; // = false
/** true is the item has been focused before pointer down */
boolean itemWasPressed; // = false
}
|