/*
*
*
* 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 java.util.TimerTask;
import java.util.Timer;
import javax.microedition.lcdui.game.Sprite;
import com.sun.midp.chameleon.skins.ScreenSkin;
import com.sun.midp.chameleon.skins.GaugeSkin;
import com.sun.midp.chameleon.skins.ProgressBarSkin;
import com.sun.midp.chameleon.skins.BusyCursorSkin;
import com.sun.midp.chameleon.skins.UpdateBarSkin;
import com.sun.midp.chameleon.skins.resources.GaugeResources;
import com.sun.midp.chameleon.skins.resources.ProgressBarResources;
import com.sun.midp.chameleon.skins.resources.UpdateBarResources;
import com.sun.midp.chameleon.skins.resources.BusyCursorResources;
import com.sun.midp.chameleon.*;
import com.sun.midp.lcdui.Text;
import com.sun.midp.configurator.Constants;
import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;
/**
* This is the look & feel implementation for Gauge.
*/
class GaugeLFImpl extends ItemLFImpl implements GaugeLF {
/**
* Creates GaugeLF for the passed in Gauge.
* @param gauge the Gauge object associated with this look&feel.
*/
GaugeLFImpl(Gauge gauge) {
super(gauge);
this.gauge = gauge;
lSetMaxValue(0, gauge.maxValue);
lSetValue(0, gauge.value);
// IMPL NOTE: Make this smarter so that only the
// resources we need load. Also, replicate this
// code in the set* methods so that a gauge which
// changes on the fly has the proper resources
if (gauge.interactive) {
GaugeResources.load();
} else {
ProgressBarResources.load();
UpdateBarResources.load();
BusyCursorResources.load();
}
percentLoc = new int[2];
drawsTraversalIndicator = false;
}
// *****************************************************
// Public methods
// *****************************************************
/**
* Notifies L&F of a value change in the corresponding Gauge.
* @param oldValue - the old value set in the Gauge
* @param newValue - the new value set in the Gauge
*/
public void lSetValue(int oldValue, int newValue) {
synchronized (Display.LCDUILock) {
if (gauge.maxValue == Gauge.INDEFINITE) {
/**
* -- if there are more than one Gauge, we shouldn't
* stop other continuous gauges until we change Displayable.
* IMPL NOTE: see if this is the only Gauge on the form -au
*/
if (newValue == Gauge.CONTINUOUS_RUNNING) {
// If this gauge is already visible and its new value
// is changed to CONTINUOUS_RUNNING, then start update
// task here.
// Otherwise, delay update task creation until it becomes
// visible (I.e. lCallShow is called).
if (visible) {
startGaugeUpdateTask();
}
} else if (oldValue == Gauge.CONTINUOUS_RUNNING) {
cancelGaugeUpdateTask();
}
if (oldValue != newValue)
lRequestInvalidate(true, true);
else
lRequestPaint();
} else if (oldValue != newValue) {
lRequestPaint();
}
} // end sync
}
/**
* Gets the current value.
* @return the current value
*/
public int lGetValue() {
return gauge.value;
}
/**
* Notifies L&F of a maximum value change in the corresponding Gauge
* @param oldMaxValue - the old maximum value set in the Gauge
* @param newMaxValue - the new maximum value set in the Gauge
*/
public void lSetMaxValue(int oldMaxValue, int newMaxValue) {
// changing the max value will change the scale of the gauge
if (oldMaxValue != newMaxValue) {
lRequestInvalidate(true, true);
}
}
// *****************************************************
// Package private methods
// *****************************************************
/**
* Determine if this Gauge should not be traversed to
*
* @return true if this Gauge should not be traversed to
*/
boolean shouldSkipTraverse() {
// Only traverse to gauges which are interactive, or have
// item-specific commands added to them
return (gauge.interactive) ? false : super.shouldSkipTraverse();
}
/**
* 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) {
if (gauge.interactive) {
size[WIDTH] = GaugeSkin.WIDTH;
size[HEIGHT] = GaugeSkin.HEIGHT;
} else if (gauge.maxValue != Gauge.INDEFINITE) {
size[WIDTH] = ProgressBarSkin.WIDTH;
size[HEIGHT] = ProgressBarSkin.HEIGHT;
} else if ((gauge.value == Gauge.CONTINUOUS_RUNNING) ||
(gauge.value == Gauge.CONTINUOUS_IDLE)) {
size[WIDTH] = BusyCursorSkin.WIDTH;
size[HEIGHT] = BusyCursorSkin.HEIGHT;
} else {
size[WIDTH] = UpdateBarSkin.WIDTH;
size[HEIGHT] = UpdateBarSkin.HEIGHT;
}
}
/**
* Determine if this Item should have a newline after it
*
* @return true if it should have a newline after
*/
boolean equateNLA() {
if (super.equateNLA()) {
return true;
}
return ((gauge.layout & Item.LAYOUT_2) != Item.LAYOUT_2);
}
/**
* Determine if this Item should have a newline before it
*
* @return true if it should have a newline before
*/
boolean equateNLB() {
if (super.equateNLB()) {
return true;
}
return ((gauge.layout & Item.LAYOUT_2) != Item.LAYOUT_2);
}
/**
* Handle traversal within this Gauge
*
* @param dir the direction of traversal
* @param viewportWidth the width of the viewport
* @param viewportHeight the height of the viewport
* @param visRect the in/out rectangle for the internal traversal location
* @return True if traversal occurred within this Gauge
*/
boolean uCallTraverse(int dir, int viewportWidth, int viewportHeight,
int[] visRect)
{
super.uCallTraverse(dir, viewportWidth, viewportHeight, visRect);
// If its a non-interactive gauge, there is no internal traversal
// No need to lock either, we just access the boolean once.
// (In fact, traverse should never be called on a non-interactive
// gauge because shouldSkipTraverse() returns true in that case)
if (!gauge.interactive) {
return false;
}
// If it was an invalidate or something, just keep the focus
// button where it is and reflect there is internal traversal
if (dir == CustomItem.NONE) {
intTraverse = true;
} else {
// The standard horizontal gauge's orientation is RIGHT, all
// others accommodate vertical gauges and possible gauges for
// right-to-left languages (which would be a LEFT orientation)
switch (GaugeSkin.ORIENTATION) {
// Gauge increases left to right, horizontally
// (this is the default in chameleon)
case Graphics.RIGHT:
switch (dir) {
case Canvas.LEFT:
if (initialTraverse) {
intTraverse = true;
focusBtn = I_INC_BTN;
} else {
intTraverse = (focusBtn != I_DEC_BTN);
focusBtn = I_DEC_BTN;
}
break;
case Canvas.RIGHT:
if (initialTraverse) {
intTraverse = true;
focusBtn = I_DEC_BTN;
} else {
intTraverse = (focusBtn != I_INC_BTN);
focusBtn = I_INC_BTN;
}
break;
case Canvas.UP:
intTraverse = initialTraverse;
if (initialTraverse) {
focusBtn = I_INC_BTN;
}
break;
case Canvas.DOWN:
intTraverse = initialTraverse;
if (initialTraverse) {
focusBtn = I_DEC_BTN;
}
break;
default:
Logging.report(Logging.ERROR,
LogChannels.LC_HIGHUI,
"GaugeLFImpl: uCallTraverse, dir=" +dir);
break;
}
break;
// Gauge increases right to left, horizontally
// (think of right-to-left languages)
case Graphics.LEFT:
switch (dir) {
case Canvas.LEFT:
if (initialTraverse) {
intTraverse = true;
focusBtn = I_DEC_BTN;
} else {
intTraverse = (focusBtn != I_INC_BTN);
focusBtn = I_INC_BTN;
}
break;
case Canvas.RIGHT:
if (initialTraverse) {
intTraverse = true;
focusBtn = I_INC_BTN;
} else {
intTraverse = (focusBtn != I_DEC_BTN);
focusBtn = I_DEC_BTN;
}
break;
case Canvas.UP:
intTraverse = initialTraverse;
if (initialTraverse) {
focusBtn = I_DEC_BTN;
}
break;
case Canvas.DOWN:
intTraverse = initialTraverse;
if (initialTraverse) {
focusBtn = I_INC_BTN;
}
break;
default:
Logging.report(Logging.ERROR,
LogChannels.LC_HIGHUI,
"GaugeLFImpl: uCallTraverse, dir=" +dir);
break;
}
break;
// Gauge increases bottom to top, vertically
case Graphics.TOP:
switch (dir) {
case Canvas.LEFT:
intTraverse = initialTraverse;
if (initialTraverse) {
focusBtn = I_INC_BTN;
}
break;
case Canvas.RIGHT:
intTraverse = initialTraverse;
if (initialTraverse) {
focusBtn = I_DEC_BTN;
}
break;
case Canvas.UP:
if (initialTraverse) {
intTraverse = true;
focusBtn = I_DEC_BTN;
} else {
intTraverse = (focusBtn != I_INC_BTN);
focusBtn = I_INC_BTN;
}
break;
case Canvas.DOWN:
if (initialTraverse) {
intTraverse = true;
focusBtn = I_INC_BTN;
} else {
intTraverse = (focusBtn != I_DEC_BTN);
focusBtn = I_DEC_BTN;
}
break;
default:
Logging.report(Logging.ERROR,
LogChannels.LC_HIGHUI,
"GaugeLFImpl: uCallTraverse, dir=" +dir);
break;
}
break;
// Gauge increases top to bottom, vertically
case Graphics.BOTTOM:
switch (dir) {
case Canvas.LEFT:
intTraverse = initialTraverse;
if (initialTraverse) {
focusBtn = I_DEC_BTN;
}
break;
case Canvas.RIGHT:
intTraverse = initialTraverse;
if (initialTraverse) {
focusBtn = I_INC_BTN;
}
break;
case Canvas.UP:
if (initialTraverse) {
intTraverse = true;
focusBtn = I_INC_BTN;
} else {
intTraverse = (focusBtn != I_DEC_BTN);
focusBtn = I_DEC_BTN;
}
break;
case Canvas.DOWN:
if (initialTraverse) {
intTraverse = true;
focusBtn = I_DEC_BTN;
} else {
intTraverse = (focusBtn != I_INC_BTN);
focusBtn = I_INC_BTN;
}
break;
default:
Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
"GaugeLFImpl: uCallTraverse, dir=" +dir);
break;
}
break;
default:
Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
"GaugeLFImpl: uCallTraverse," +
"GaugeSkin.ORIENTATION=" +
GaugeSkin.ORIENTATION);
break;
}
}
switch (focusBtn) {
case I_INC_BTN:
visRect[X] = GaugeSkin.INC_BTN_X;
visRect[Y] = GaugeSkin.INC_BTN_Y;
if (GaugeSkin.IMAGE_INC_BTN != null) {
visRect[WIDTH] = GaugeSkin.IMAGE_INC_BTN.getWidth();
visRect[HEIGHT] = GaugeSkin.IMAGE_INC_BTN.getHeight();
} else {
visRect[WIDTH] = 15;
visRect[HEIGHT] = 15;
}
break;
case I_DEC_BTN:
visRect[X] = GaugeSkin.DEC_BTN_X;
visRect[Y] = GaugeSkin.DEC_BTN_Y;
if (GaugeSkin.IMAGE_DEC_BTN != null) {
visRect[WIDTH] = GaugeSkin.IMAGE_DEC_BTN.getWidth();
visRect[HEIGHT] = GaugeSkin.IMAGE_DEC_BTN.getHeight();
} else {
visRect[WIDTH] = 15;
visRect[HEIGHT] = 15;
}
break;
default:
Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
"GaugeLFImpl: uCallTraverse, focusBtn=" +focusBtn);
break;
}
// Gauge should always return true on at least the initial
// traverse, or any internal traverse between the two buttons
if (initialTraverse || intTraverse) {
initialTraverse = false;
uRequestPaint();
return true;
}
return false;
}
/**
* Called by the system to indicate traversal has left this Item.
*/
void uCallTraverseOut() {
super.uCallTraverseOut();
initialTraverse = true;
}
/**
* Called by the system to signal a key repeat
*
* @param keyCode the key code of the key that has been pressed
*/
void uCallKeyRepeated(int keyCode) {
uCallKeyPressed(keyCode);
}
/**
* Called by the system to signal a key press.
*
* @param keyCode the key code of the key that has been pressed
*/
void uCallKeyPressed(int keyCode) {
if (keyCode != Constants.KEYCODE_SELECT ||
!gauge.interactive) {
return;
}
Form form = null;
synchronized (Display.LCDUILock) {
int maxValue = gauge.maxValue;
int oldValue = gauge.value;
int value = oldValue;
switch (focusBtn) {
case I_INC_BTN:
value++;
break;
case I_DEC_BTN:
value--;
break;
default:
Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
"GaugeLFImpl: uCallKeyPressed, focusBtn=" +
focusBtn);
break;
}
gauge.setValueImpl(value);
// IMPL NOTE: paint optimization
lRequestPaint();
if (value != oldValue) {
// notify the ItemStateChangedListener
form = (Form)gauge.owner;
}
} // end synchronized
// SYNC NOTE: We make sure we notify the ItemStateChangedListener
// outside of LCDUILock
if (form != null) {
form.uCallItemStateChanged(gauge);
}
}
/**
* 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) {
if (gauge.interactive) {
pointerArea = getPointerArea(x, y);
switch(pointerArea) {
case DEC_BTN_AREA:
focusBtn = I_DEC_BTN;
uRequestPaint();
break;
case INC_BTN_AREA:
focusBtn = I_INC_BTN;
uRequestPaint();
break;
default:
break;
}
}
}
/**
* Get the area of gauge the pointer cliked in.
* @param x x coordinate of pointer
* @param y y coordinate of pointer
* @return the gauge area is returned. The possible values are
*
* @see #INVALID_AREA if pointer is out of gauge's bounds
* @see #INC_BTN_AREA if pointer is on the increment button
* @see #DEC_BTN_AREA if pointer is on the decrement button
* @see #METER_AREA if pointer is on the meter
*/
private int getPointerArea(int x, int y) {
int area = INVALID_AREA;
x -= contentBounds[X];
y -= contentBounds[Y];
if (area == INVALID_AREA &&
GaugeSkin.IMAGE_DEC_BTN != null &&
// check coordinates
x >= GaugeSkin.DEC_BTN_X &&
y > GaugeSkin.DEC_BTN_Y &&
x <= (GaugeSkin.DEC_BTN_X +
GaugeSkin.IMAGE_DEC_BTN.getWidth()) &&
y <= (GaugeSkin.DEC_BTN_Y +
GaugeSkin.IMAGE_DEC_BTN.getHeight())) {
area = DEC_BTN_AREA;
}
if (area == INVALID_AREA &&
GaugeSkin.IMAGE_INC_BTN != null &&
// check coordinates
x >= GaugeSkin.INC_BTN_X &&
y > GaugeSkin.INC_BTN_Y &&
x <= (GaugeSkin.INC_BTN_X +
GaugeSkin.IMAGE_INC_BTN.getWidth()) &&
y <= (GaugeSkin.INC_BTN_Y +
GaugeSkin.IMAGE_INC_BTN.getHeight())) {
area = INC_BTN_AREA;
}
if (area == INVALID_AREA &&
// check coordinates
x >= GaugeSkin.METER_X && y > GaugeSkin.METER_Y &&
x <= (GaugeSkin.METER_X +
GaugeSkin.IMAGE_METER_FULL.getWidth()) &&
y <= (GaugeSkin.METER_Y +
GaugeSkin.IMAGE_METER_FULL.getHeight())) {
area = METER_AREA;
}
return area;
}
/**
* 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
*/
void uCallPointerReleased(int x, int y) {
if (gauge.interactive) {
int newArea = getPointerArea(x, y);
if (pointerArea == newArea) {
switch (pointerArea) {
case DEC_BTN_AREA:
case INC_BTN_AREA:
uCallKeyPressed(Constants.KEYCODE_SELECT);
break;
case METER_AREA:
{
Form form = null;
synchronized (Display.LCDUILock) {
int oldValue = gauge.value;
int locationOnMeter = x - contentBounds[X] - GaugeSkin.METER_X;
float percent = locationOnMeter * 100 / GaugeSkin.IMAGE_METER_FULL.getWidth();
float value = percent / 100 * gauge.maxValue;
/* round the value */
int intValue = (int)value;
float remainder = value - intValue;
if (remainder > 0.5) {
intValue++;
}
gauge.setValueImpl(intValue);
lRequestPaint();
if (intValue != oldValue) {
// notify the ItemStateChangedListener
form = (Form)gauge.owner;
}
}
// SYNC NOTE: We make sure we notify the ItemStateChangedListener
// outside of LCDUILock
if (form != null) {
form.uCallItemStateChanged(gauge);
}
}
break;
default:
break;
} // end of switch
} // pointerArea == newArea
} // interactive gauge
}
/**
* 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();
// Start update task for CONTINUOUS_RUNNING gauge upon visible
if (gauge.maxValue == Gauge.INDEFINITE &&
gauge.value == Gauge.CONTINUOUS_RUNNING) {
startGaugeUpdateTask();
}
}
/**
* 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() {
super.lCallHideNotify();
cancelGaugeUpdateTask();
}
/**
* Paints the content area of this Gauge.
* Graphics is translated to contents origin.
* @param g The graphics where Gauge 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) {
if (gauge.interactive) {
lPaintInteractiveGauge(g, w, h, gauge.maxValue, gauge.value);
} else if (gauge.maxValue == Gauge.INDEFINITE) {
lPaintIndefinite(g, w, h, gauge.maxValue, gauge.value);
} else {
lPaintProgressBar(g, w, h, gauge.maxValue, gauge.value);
}
}
void lPaintInteractiveGauge(Graphics g, int w, int h,
int maxValue, int value)
{
// case: interactive gauge
// draw background, if present
if (GaugeSkin.IMAGE_BG != null) {
g.drawImage(GaugeSkin.IMAGE_BG, 0, 0,
Graphics.LEFT | Graphics.TOP);
} else {
g.setColor(0xCCCCCC);
g.fillRect(0, 0, GaugeSkin.WIDTH, GaugeSkin.HEIGHT);
g.setColor(0);
return;
// we return early on interactive gauge if there are
// no images because its rather complicated to draw
// otherwise, at least right now
}
// computation to decide how much of filled & unfilled
// portions to be displayed
int body_full;
int body_empty;
if (GaugeSkin.ORIENTATION == Graphics.LEFT ||
GaugeSkin.ORIENTATION == Graphics.RIGHT)
{
body_empty = GaugeSkin.IMAGE_METER_FULL.getWidth();
body_full = (((value * 100) / maxValue) * body_empty) / 100;
body_empty -= body_full;
if (GaugeSkin.ORIENTATION == Graphics.RIGHT) {
g.drawRegion(GaugeSkin.IMAGE_METER_FULL, 0, 0, body_full,
GaugeSkin.IMAGE_METER_FULL.getHeight(),
Sprite.TRANS_NONE, GaugeSkin.METER_X,
GaugeSkin.METER_Y,
Graphics.LEFT | Graphics.TOP);
g.drawRegion(GaugeSkin.IMAGE_METER_EMPTY, body_full, 0,
body_empty,
GaugeSkin.IMAGE_METER_EMPTY.getHeight(),
Sprite.TRANS_NONE, GaugeSkin.METER_X +
body_full, GaugeSkin.METER_Y,
Graphics.LEFT | Graphics.TOP);
} else {
g.drawRegion(GaugeSkin.IMAGE_METER_EMPTY, 0, 0, body_empty,
GaugeSkin.IMAGE_METER_EMPTY.getHeight(),
Sprite.TRANS_NONE, GaugeSkin.METER_X,
GaugeSkin.METER_Y,
Graphics.LEFT | Graphics.TOP);
g.drawRegion(GaugeSkin.IMAGE_METER_FULL, body_empty, 0,
body_full,
GaugeSkin.IMAGE_METER_FULL.getHeight(),
Sprite.TRANS_NONE, GaugeSkin.METER_X +
body_empty, GaugeSkin.METER_Y,
Graphics.LEFT | Graphics.TOP);
}
} else {
body_empty = GaugeSkin.IMAGE_METER_FULL.getHeight();
body_full = (((value * 100) / maxValue) * body_empty) / 100;
body_empty -= body_full;
if (GaugeSkin.ORIENTATION == Graphics.TOP) {
g.drawRegion(GaugeSkin.IMAGE_METER_FULL, 0, body_empty,
GaugeSkin.IMAGE_METER_FULL.getWidth(),
body_full,
Sprite.TRANS_NONE, GaugeSkin.METER_X,
GaugeSkin.METER_Y + body_empty,
Graphics.LEFT | Graphics.TOP);
g.drawRegion(GaugeSkin.IMAGE_METER_EMPTY, 0, 0,
GaugeSkin.IMAGE_METER_EMPTY.getWidth(),
body_empty,
Sprite.TRANS_NONE, GaugeSkin.METER_X,
GaugeSkin.METER_Y,
Graphics.LEFT | Graphics.TOP);
} else {
g.drawRegion(GaugeSkin.IMAGE_METER_EMPTY, 0, body_full,
GaugeSkin.IMAGE_METER_EMPTY.getWidth(),
body_empty,
Sprite.TRANS_NONE, GaugeSkin.METER_X,
GaugeSkin.METER_Y + body_full,
Graphics.LEFT | Graphics.TOP);
g.drawRegion(GaugeSkin.IMAGE_METER_FULL, 0, 0,
GaugeSkin.IMAGE_METER_FULL.getWidth(),
body_full,
Sprite.TRANS_NONE, GaugeSkin.METER_X,
GaugeSkin.METER_Y,
Graphics.LEFT | Graphics.TOP);
}
}
// display current value of gauge
drawNumber(g, GaugeSkin.IMAGE_VALUES, 11, value,
GaugeSkin.VALUE_X, GaugeSkin.VALUE_Y,
(value * 100) / maxValue);
if (GaugeSkin.IMAGE_DEC_BTN != null) {
// decrease button
g.drawImage(GaugeSkin.IMAGE_DEC_BTN,
GaugeSkin.DEC_BTN_X,
GaugeSkin.DEC_BTN_Y,
Graphics.LEFT | Graphics.TOP);
if (hasFocus && focusBtn == I_DEC_BTN) {
g.setColor(ScreenSkin.COLOR_TRAVERSE_IND);
g.drawRect(GaugeSkin.DEC_BTN_X,
GaugeSkin.DEC_BTN_Y,
GaugeSkin.IMAGE_DEC_BTN.getWidth(),
GaugeSkin.IMAGE_DEC_BTN.getHeight());
g.setColor(0);
}
} else {
g.drawString("-", GaugeSkin.DEC_BTN_X, GaugeSkin.DEC_BTN_Y,
Graphics.LEFT | Graphics.TOP);
if (hasFocus && focusBtn == I_DEC_BTN) {
g.setColor(ScreenSkin.COLOR_TRAVERSE_IND);
g.drawRect(GaugeSkin.DEC_BTN_X,
GaugeSkin.DEC_BTN_Y,
g.getFont().charWidth('-'),
g.getFont().charWidth('-'));
g.setColor(0);
}
}
if (GaugeSkin.IMAGE_INC_BTN != null) {
// increase button
g.drawImage(GaugeSkin.IMAGE_INC_BTN,
GaugeSkin.INC_BTN_X,
GaugeSkin.INC_BTN_Y,
Graphics.LEFT | Graphics.TOP);
if (hasFocus && focusBtn == I_INC_BTN) {
g.setColor(ScreenSkin.COLOR_TRAVERSE_IND);
g.drawRect(GaugeSkin.INC_BTN_X,
GaugeSkin.INC_BTN_Y,
GaugeSkin.IMAGE_INC_BTN.getWidth(),
GaugeSkin.IMAGE_INC_BTN.getHeight());
g.setColor(0);
}
} else {
g.drawString("+", GaugeSkin.INC_BTN_X, GaugeSkin.INC_BTN_Y,
Graphics.LEFT | Graphics.TOP);
if (hasFocus && focusBtn == I_INC_BTN) {
g.setColor(ScreenSkin.COLOR_TRAVERSE_IND);
g.drawRect(GaugeSkin.INC_BTN_X,
GaugeSkin.INC_BTN_Y,
g.getFont().charWidth('+'),
g.getFont().charWidth('+'));
g.setColor(0);
}
}
} // lPaintInteractiveGauge
void lPaintIndefinite(Graphics g, int w, int h,
int maxValue, int value)
{
switch (value) {
case Gauge.CONTINUOUS_RUNNING:
drawBusyCursor(g, nextFrame);
// increment the frame counter of the incremental gauge
// and remember to reset it when it hits the total number
// of frames, so it can start all over again
if (BusyCursorSkin.FRAME_SEQUENCE != null) {
nextFrame = ++nextFrame % BusyCursorSkin.FRAME_SEQUENCE.length;
}
break;
case Gauge.CONTINUOUS_IDLE:
drawBusyCursor(g, 0);
break;
case Gauge.INCREMENTAL_UPDATING:
drawUpdateBar(g, nextFrame);
// increment the frame counter of the incremental gauge
// and remember to reset it when it hits the total number
// of frames, so it can start all over again
if (UpdateBarSkin.FRAME_SEQUENCE != null) {
nextFrame = ++nextFrame % UpdateBarSkin.FRAME_SEQUENCE.length;
}
break;
case Gauge.INCREMENTAL_IDLE:
drawUpdateBar(g, 0);
break;
default:
Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
"GaugeLFImpl: lPaintIndefinite, value=" +value);
break;
}
}
void lPaintProgressBar(Graphics g, int w, int h,
int maxValue, int value)
{
// case: non-interactive definite range gauge/progressbar
// draw background, if present
if (ProgressBarSkin.IMAGE_BG != null) {
g.drawImage(ProgressBarSkin.IMAGE_BG, 0, 0,
Graphics.LEFT | Graphics.TOP);
} else {
g.setColor(0xCCCCCC);
g.fillRect(0, 0, ProgressBarSkin.WIDTH, ProgressBarSkin.HEIGHT);
g.setColor(0);
}
// first compute current value of gauge as a percentage
int n = (value * 100) / maxValue;
// decide how much of filled & unfilled portions to be shown
int body_full = 0;
if (ProgressBarSkin.IMAGE_METER_FULL != null) {
body_full =
(n * ProgressBarSkin.IMAGE_METER_FULL.getWidth()) / 100;
g.drawRegion(ProgressBarSkin.IMAGE_METER_FULL, 0, 0,
body_full,
ProgressBarSkin.IMAGE_METER_FULL.getHeight(),
Sprite.TRANS_NONE,
ProgressBarSkin.METER_X,
ProgressBarSkin.METER_Y,
Graphics.LEFT | Graphics.TOP);
} else {
body_full =
(n * ProgressBarSkin.WIDTH - ProgressBarSkin.VALUE_WIDTH -
10) / 100;
g.fillRect(ProgressBarSkin.METER_X, ProgressBarSkin.METER_Y,
body_full, ProgressBarSkin.HEIGHT -
(2 * ProgressBarSkin.METER_Y));
}
int body_empty = 0;
if (ProgressBarSkin.IMAGE_METER_FULL != null) {
body_empty =
ProgressBarSkin.IMAGE_METER_FULL.getWidth() - body_full;
g.drawRegion(ProgressBarSkin.IMAGE_METER_EMPTY, body_full,
0, body_empty,
ProgressBarSkin.IMAGE_METER_EMPTY.getHeight(),
Sprite.TRANS_NONE,
ProgressBarSkin.METER_X + body_full,
ProgressBarSkin.METER_Y,
Graphics.LEFT | Graphics.TOP);
} else {
body_empty =
(ProgressBarSkin.WIDTH - ProgressBarSkin.VALUE_WIDTH -
10) - body_full;
g.setColor(0xFFFFFF);
g.fillRect(ProgressBarSkin.METER_X + body_full,
ProgressBarSkin.METER_Y,
body_empty,
ProgressBarSkin.HEIGHT -
(2 * ProgressBarSkin.METER_Y));
}
// current value of gauge as a percentage
drawNumber(g, ProgressBarSkin.IMAGE_VALUES, 11, n,
ProgressBarSkin.VALUE_X, ProgressBarSkin.VALUE_Y,
(value * 100) / maxValue);
// the percentage symbol
drawPercentage(g, ProgressBarSkin.IMAGE_PERCENTS, value);
}
/**
* Called by the system to traverse this DateField.
*
* @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 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 lCallTraverse(int dir, int viewportWidth, int viewportHeight,
int[] visRect)
{
boolean res = super.lCallTraverse(dir, viewportWidth, viewportHeight, visRect);
visRect[X] = 0;
visRect[Y] = 0;
visRect[HEIGHT] = bounds[HEIGHT];
visRect[WIDTH] = bounds[WIDTH];
return res;
}
/**
* Paints the given frame of the incremental updating/idle gauge.
*
* @param g the graphics to paint to
* @param frameToDraw the frame index of the frame to be drawn
* from a frame-sequence
*/
void drawUpdateBar(Graphics g, int frameToDraw) {
if (UpdateBarSkin.FRAME_SEQUENCE == null ||
frameToDraw > UpdateBarSkin.FRAME_SEQUENCE.length)
{
return;
}
frameToDraw = UpdateBarSkin.FRAME_SEQUENCE[frameToDraw];
// draw background, if present
if (UpdateBarSkin.IMAGE_BG != null) {
g.drawImage(UpdateBarSkin.IMAGE_BG, 0, 0,
Graphics.LEFT | Graphics.TOP);
} else {
g.setColor(0xCCCCCC);
g.fillRect(0, 0, UpdateBarSkin.WIDTH, UpdateBarSkin.HEIGHT);
g.setColor(0);
}
if (UpdateBarSkin.IMAGE_FRAME != null
&& frameToDraw < UpdateBarSkin.IMAGE_FRAME.length)
{
// draw the frame
g.drawImage(UpdateBarSkin.IMAGE_FRAME[frameToDraw],
UpdateBarSkin.FRAME_X,
UpdateBarSkin.FRAME_Y,
Graphics.LEFT | Graphics.TOP);
} else {
g.fillRect(2, 2,
UpdateBarSkin.WIDTH - 4, UpdateBarSkin.HEIGHT - 4);
int width =
(UpdateBarSkin.WIDTH / UpdateBarSkin.FRAME_SEQUENCE.length) *
frameToDraw;
if (width < 0) {
width = 0;
}
g.setColor(0xFFFFFF);
g.fillRect(2, 2, width - 4, UpdateBarSkin.HEIGHT - 4);
g.setColor(0);
}
}
/**
* Paints the given frame of the continuous running/idle gauge.
*
* @param g the graphics to paint to
* @param frameToDraw the frame index of the frame to be drawn
* from a frame-sequence
*/
void drawBusyCursor(Graphics g, int frameToDraw) {
if (BusyCursorSkin.FRAME_SEQUENCE == null ||
frameToDraw > BusyCursorSkin.FRAME_SEQUENCE.length)
{
return;
}
frameToDraw = BusyCursorSkin.FRAME_SEQUENCE[frameToDraw];
// draw background, if present
if (BusyCursorSkin.IMAGE_BG != null) {
g.drawImage(BusyCursorSkin.IMAGE_BG, 0, 0,
Graphics.LEFT | Graphics.TOP);
} else {
g.setColor(0xCCCCCC);
g.fillRect(0, 0, BusyCursorSkin.WIDTH, BusyCursorSkin.HEIGHT);
g.setColor(0);
}
if (BusyCursorSkin.IMAGE_FRAME != null &&
frameToDraw < BusyCursorSkin.IMAGE_FRAME.length)
{
// draw the frame
g.drawImage(BusyCursorSkin.IMAGE_FRAME[frameToDraw],
BusyCursorSkin.FRAME_X,
BusyCursorSkin.FRAME_Y,
Graphics.LEFT | Graphics.TOP);
} else {
switch (frameToDraw % 3) {
case 0:
g.drawString("-",
(int)(BusyCursorSkin.WIDTH / 2),
(int)(BusyCursorSkin.HEIGHT / 2),
Graphics.HCENTER | Graphics.BOTTOM);
break;
case 1:
g.drawString("\\",
(int)(BusyCursorSkin.WIDTH / 2),
(int)(BusyCursorSkin.HEIGHT / 2),
Graphics.HCENTER | Graphics.BOTTOM);
break;
case 2:
g.drawString("|",
(int)(BusyCursorSkin.WIDTH / 2),
(int)(BusyCursorSkin.HEIGHT / 2),
Graphics.HCENTER | Graphics.BOTTOM);
break;
default:
g.drawString("/",
(int)(BusyCursorSkin.WIDTH / 2),
(int)(BusyCursorSkin.HEIGHT / 2),
Graphics.HCENTER | Graphics.BOTTOM);
break;
}
}
}
/**
* Accepts a numeric value and draws the number graphically.
*
* @param g the graphics to paint to
* @param valuesImg the image to clip from
* @param numDigits the number of digits in the image
* @param index the index of the digit that needs to be drawn graphically
* @param locationX the x co-ordinate of the location to start drawing
* the digits
* @param locationY the y co-ordinate of the location to start drawing
* the digits
* @param n the value as a percentage, equal to (value * 1000) / maxValue
*/
void drawNumber(Graphics g, Image valuesImg, int numDigits, int index,
int locationX, int locationY, int n)
{
/*
* Find the first leftmost, non-zero digit.
*
* Keep using a smaller divisor to get to a non-zero
* digit in n. If the divisor itself becomes zero,
* this is the case where n = 0. We break out of the
* loop, in that case.
*
* Note that the test for divisor == 0 is done before
* extracting the next digit in the "while" part, to
* avoid a divide-by-zero ArithmeticException.
*/
int divisor = 1000; // assume the max displayed is 4 digit num
int digit = 0;
int digitCount = 4;
int digitWidth = g.getFont().charWidth('9');
if (valuesImg != null) {
digitWidth = (int)(valuesImg.getWidth() / numDigits);
}
// keep whittling down the number until it's in range.
while (n > 9999) n /= 10;
// figure out how many digits... adjust divisor accordingly.
while (divisor != 0 && (digit = n / divisor) == 0) {
divisor /= 10;
digitCount--;
}
// special case for 0
if (digitCount == 0) digitCount = 1;
// position where to start displaying the gauge value
int start_x = locationX;
// offset by a digit's width in case of shorter values
// to make it more presentable
start_x += (digitCount < 3) ? digitWidth : 0;
// now show the digits
switch (divisor) {
case 1000:
digit = n / 1000;
n %= divisor;
divisor /= 10;
paintDigit(g, valuesImg, numDigits, digit, start_x, locationY);
start_x += digitWidth;
// fall through to handle next lower digit.
case 100:
digit = n / 100;
n %= divisor;
divisor /= 10;
paintDigit(g, valuesImg, numDigits, digit, start_x, locationY);
start_x += digitWidth;
// fall through to handle next lower digit.
case 10:
digit = n / 10;
n %= divisor;
divisor /= 10;
paintDigit(g, valuesImg, numDigits, digit, start_x, locationY);
start_x += digitWidth;
// fall through to handle next lower digit.
case 1:
digit = n;
paintDigit(g, valuesImg, numDigits, digit, start_x, locationY);
break;
case 0: // special case when n = 0
// paint the non-highlight 0
paintDigit(g, valuesImg, numDigits, numDigits - 1,
start_x, locationY);
break;
default:
Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
"GaugeLFImpl: drawNumber," +
" divisor=" +divisor);
break;
}
// ASSERT: Make sure this array gets filled with the x,y
// co-ordinates where the digits end, so as to facilitate
// drawing, say a percentage sign after the value, in some
// cases like the progressbar. Removing this notion will
// cause *malfunction* of the drawPercentage() method, in
// not being able to locate where to draw the percentage
// sign correctly.
percentLoc[0] = start_x + digitWidth;
percentLoc[1] = locationY;
}
/**
* Calculates the position of the digit to be clipped from the values
* image (based on the "digitLocation" parameter passed in) and
* paints it.
*
* @param g the graphics to paint t
* @param valuesImg the image to clip from
* @param numDigits the number of digits in the image
* @param digitIndex the pixel location of this digit inside the
* values image, used to extract the digit images
* from
* @param x the x-coordinate where to draw this digit
* @param y the y-coordinate where to draw this digit
*/
void paintDigit(Graphics g, Image valuesImg, int numDigits,
int digitIndex, int x, int y)
{
if (valuesImg != null) {
int digitWidth = (int)(valuesImg.getWidth() / numDigits);
g.drawRegion(valuesImg,
digitIndex * digitWidth, 0,
digitWidth,
valuesImg.getHeight(),
Sprite.TRANS_NONE, x, y,
Graphics.LEFT | Graphics.TOP);
} else {
if (digitIndex == numDigits - 1) {
digitIndex = 0;
}
g.drawString("" + digitIndex, x, y, Graphics.LEFT | Graphics.TOP);
}
}
/**
* Draws a percentage sign.
*
* @param g the graphics to paint to
* @param percentImg the image to clip from
* @param value the value of the gauge
*/
void drawPercentage(Graphics g, Image percentImg, int value) {
if (percentImg != null && percentWidth == -1) {
percentWidth = (int)(percentImg.getWidth() / 2);
percentHeight = percentImg.getHeight();
}
if (percentImg != null) {
// Note: The percentLoc[] array holds the x,y location
// where the percentage sign should be painted. This is filled
// by the drawNumber() method after it finishes drawing the
// numeric value graphically.
g.drawRegion(percentImg,
(value == 0) ? percentWidth : 0, 0,
percentWidth, percentHeight,
Sprite.TRANS_NONE,
percentLoc[0], percentLoc[1],
Graphics.LEFT | Graphics.TOP);
} else {
g.drawString("%", percentLoc[0], percentLoc[1],
Graphics.LEFT | Graphics.TOP);
}
}
/**
* A helper class to update a continuous running gauge.
*/
class GaugeUpdateTask extends TimerTask {
/**
* the gauge to repaint
*/
GaugeLFImpl myGaugeLF;
/**
* Construct a new GaugeUpdateTask.
*
* @param gaugeLF the gauge to repaint
*/
GaugeUpdateTask(GaugeLF gaugeLF) {
super();
myGaugeLF = (GaugeLFImpl)gaugeLF;
}
/**
* required method in TimerTask derivatives
*/
public final void run() {
// increment the frame counter of the continuous gauge
// and remember to reset it when it hits the total number
// of frames, so it can start all over again
myGaugeLF.lRequestPaint();
}
}
/**
* Start the GaugeUpdateTask running
*/
void startGaugeUpdateTask() {
if (updateHelper == null) {
updateHelper = new GaugeUpdateTask(this);
gaugeUpdateTimer.schedule(updateHelper, 100, 100);
}
}
/**
* Stop the GaugeUpdateTask from running.
*/
void cancelGaugeUpdateTask() {
if (updateHelper != null) {
updateHelper.cancel();
updateHelper = null;
}
}
/** Gauge instance associated with this view */
Gauge gauge;
/**
* A Timer which will handle scheduling repaints of an
* indefinite range gauge when value == Gauge.CONTINUOUS_RUNNING
*/
static Timer gaugeUpdateTimer;
/**
* Constant representing the increment button. It is an arbitrary
* integer. The focusBtn attribute is set to this value if, during
* traversal, the focus is gained by the increment button and later
* on used to check for focus highlight painting.
*/
static final int I_INC_BTN = 66;
/**
* Constant representing the decrement button. It is an arbitrary
* integer. The focusBtn attribute is set to this value if, during
* traversal, the focus is gained by the decrement button and later
* on used to check for focus highlight painting.
*/
static final int I_DEC_BTN = 33;
static {
/*
* The Timer to schedule update/repaint events with
* in Gauge.CONTINUOUS_RUNNING mode.
*/
gaugeUpdateTimer = new Timer();
} // static
/**
* A TimerTask which will schedule repaints of an indefinite
* range gauge when value == Gauge.CONTINUOUS_RUNNING
*/
GaugeUpdateTask updateHelper;
/**
* The cached value for the width of the graphical percent sign
*/
int percentWidth = -1;
/**
* The cached value for the height of the graphical percent sign
*/
int percentHeight = -1;
/**
* Used to remember internally the current button in focus
* (increment/decrement) when traversal happens and focus is gained
* by an interactive gauge from another widget, or during internal
* traversal, between increment and decrement buttons. The constant
* values it can assume could be:
* I_INC_BTN: if focus is on increment button
* I_DEC_BTN: if focus is on decrement button (default initial value)
* This is later used to check for focus highlight painting.
*/
int focusBtn = I_DEC_BTN;
/**
* An array storing the x,y co-ordinates of the location where the
* percentage sign should be displayed for a progressbar.
* This *MUST BE FILLED* inside the drawNumber() method,
* so that the drawPercentage() method knows where to draw the
* percentage sign correctly.
*/
int[] percentLoc;
/**
* An index that keeps track of the next image that should be drawn
* to animate a continuous running gauge, from an array of frames.
*/
int nextFrame = 0;
/**
* A flag to mark when the call to a gauge's traverse method is the
* initial traverse or not. True by default,
* (reset to true in traverseOut).
*/
boolean initialTraverse = true;
/**
* A flag to indicate that internal traversal has occurred within
* the gauge itself.
*/
boolean intTraverse;
/**
* the area accepting the pointer event
*/
private int pointerArea = INVALID_AREA;
/**
* the different areas of an interactive gauge, used for pointer events
*/
private static final int INVALID_AREA = -1;
private static final int DEC_BTN_AREA = 0;
private static final int INC_BTN_AREA = 1;
private static final int METER_AREA = 2;
} // GaugeView