/*
*
*
* 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 javax.microedition.lcdui.KeyConverter; */
import com.sun.midp.i18n.Resource;
import com.sun.midp.i18n.ResourceConstants;
import com.sun.midp.lcdui.Text;
import java.util.Timer;
import java.util.TimerTask;
import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;
import com.sun.midp.chameleon.skins.AlertSkin;
import com.sun.midp.chameleon.skins.resources.AlertResources;
import com.sun.midp.chameleon.layers.AlertLayer;
/**
* This is the look & feel implementation for Alert.
* See DisplayableLF for naming convention.
*/
class AlertLFImpl extends ScreenLFImpl implements AlertLF {
/**
* Creates an AlertLF for the passed in Alert instance.
* @param a The Alert associated with this look & feel
*/
AlertLFImpl(Alert a) {
super(a);
alert = a;
AlertResources.load();
// SYNC NOTE: Hold the lock to prevent changes to indicator
// internal state
synchronized (Display.LCDUILock) {
layout();
}
// The viewport is equal to the height of the alert
viewport[HEIGHT] = AlertSkin.HEIGHT - AlertSkin.PAD_VERT;
}
/**
* Returns the actual needed height for the Alert instead of
* the maximum possible height.
* @return height of the area available to the application
*/
public int lGetHeight() {
// This should return the height available for content
// within the Alert dialog. It can be used by applications
// to choose appropriately sized content.
return AlertSkin.HEIGHT - AlertSkin.TITLE_HEIGHT;
}
// ************************************************************
// public methods - FormLF interface implementation
// ************************************************************
/**
* Determines if alert associated with this view is modal.
*
* @return true if this AlertLF should be displayed as modal
*/
public boolean lIsModal() {
if (alert.numCommands > 1) {
return true;
}
if (!isLayoutValid) {
layout();
}
return (maxScroll > 0);
}
/**
* Gets default timeout for the alert associated with this view
* @return the default timeout
*/
public int lGetDefaultTimeout() {
return AlertSkin.TIMEOUT;
}
/**
* Get command that Alert.DISMISS_COMMAND is mapped to.
*
* @return command that Alert.DISMISS_COMMAND is mapped to
*/
public Command lGetDismissCommand() {
return OK;
}
/**
* Notifies look&feel object of a timeout change.
*
* @param timeout - the new timeout set in the corresponding Alert.
*/
public void lSetTimeout(int timeout) {
try {
if (timerTask != null) {
timerTask.cancel();
}
if (timeout == Alert.FOREVER) {
timerTask = null;
} else {
timerTask = new TimeoutTask();
if (timeoutTimer == null) {
timeoutTimer = new Timer();
}
timeoutTimer.schedule(timerTask, timeout);
}
} catch (Throwable t) {
if (Logging.REPORT_LEVEL <= Logging.WARNING) {
Logging.report(Logging.WARNING, LogChannels.LC_HIGHUI,
"Throwable while lSetTimeout");
}
}
}
/**
* Notifies look&feel object of a Alert type change.
*
* @param type - the new AlertType set in the corresponding Alert.
*/
public void lSetType(AlertType type) {
lRequestInvalidate();
}
/**
* Notifies look&feel object of a string change.
*
* @param oldString - the old string set in the corresponding Alert.
* @param newString - the new string set in the corresponding Alert.
*/
public void lSetString(String oldString, String newString) {
lRequestInvalidate();
}
/**
* Notifies look&feel object of an image change.
*
* @param oldImg - the old image set in the corresponding Alert.
* @param newImg - the new image set in the corresponding Alert.
*/
public void lSetImage(Image oldImg, Image newImg) {
lRequestInvalidate();
}
/**
* Notifies look&feel object of an indicator change.
*
* @param oldIndicator - the old indicator set in the corresponding Alert
* @param newIndicator - the new indicator set in the corresponding Alert
*/
public void lSetIndicator(Gauge oldIndicator, Gauge newIndicator) {
lRequestInvalidate();
}
/**
* This method is responsible for:
* (1) Re-layout the contents
* (2) setup the viewable/scroll position
* (3) repaint contents
*/
public void uCallInvalidate() {
boolean wasModal = maxScroll > 0 || alert.numCommands > 1;
super.uCallInvalidate();
synchronized (Display.LCDUILock) {
if (wasModal != lIsModal()) {
lSetTimeout(alert.getTimeout());
}
lRequestPaint();
}
setVerticalScroll();
}
/**
* Notify return screen about screen size change
*/
public void uCallSizeChanged(int w, int h) {
super.uCallSizeChanged(w,h);
Displayable returnScreen = alert.getReturnScreen();
if (returnScreen != null) {
(returnScreen.displayableLF).uCallSizeChanged(w,h);
}
}
/**
* Notifies look & feel object of a command addition
* to the <code>Displayable</code>.
*
* SYNC NOTE: The caller of this method handles synchronization.
*
* @param cmd the command that was added
* @param i the index of the added command in Displayable.commands[]
* array
*/
public void lAddCommand(Command cmd, int i) {
super.lAddCommand(cmd, i);
// make alert Modal
if (alert.numCommands == 2) {
lSetTimeout(alert.getTimeout());
}
}
/**
* Notifies look & feel object of a command removal
* from the <code>Displayable</code>.
*
* SYNC NOTE: The caller of this method handles synchronization.
*
* @param cmd the command that was removed
* @param i the index of the removed command in Displayable.commands[]
* array
*/
public void lRemoveCommand(Command cmd, int i) {
super.lRemoveCommand(cmd, i);
// remove modality if it was forced by command presence
if (alert.numCommands == 1) {
lSetTimeout(alert.getTimeout());
}
}
/**
* Paint the contents of this Alert given the graphics context.
*
* @param g The Graphics object to paint this Alert to
* @param target the target Object of this repaint
*/
public void uCallPaint(Graphics g, Object target) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_FORM_LAYOUT,
" # in AlertLFImpl: uCallPaint "+
viewable[X]+","+
viewable[Y]+","+
viewable[WIDTH]+","+
viewable[HEIGHT]);
}
clipx = g.getClipX();
clipy = g.getClipY();
clipw = g.getClipWidth();
cliph = g.getClipHeight();
synchronized (Display.LCDUILock) {
// titleHeight will be AlertSkin.TITLE_HEIGHT
lPaintTitleBar(g);
// Restore the clip
g.setClip(clipx, AlertSkin.TITLE_HEIGHT,
clipw, cliph - AlertSkin.TITLE_HEIGHT);
// translate the viewport to offset for the titlebar
g.translate(0, AlertSkin.TITLE_HEIGHT);
// translate to accommodate any scrolling we've done
g.translate(0, -viewable[Y]);
// paint the indicator
int indHeight = lPaintIndicator(g);
// translate to offset for the indicator
g.translate(0, indHeight);
// paint the image
int imgHeight = lPaintImage(g);
// translate to offset for the image
g.translate(0, imgHeight);
// paint the body text
lPaintContent(g);
// restore the translate
g.translate(-g.getTranslateX(), -g.getTranslateY());
} // synchronized
setVerticalScroll();
}
// *****************************************************
// Package private methods
// *****************************************************
/**
* Paint the title bar area for this alert.
*
* @param g the Graphics to draw too
*/
void lPaintTitleBar(Graphics g) {
if (alert.type == null && alert.title == null) {
return;
}
icon = (alert.type == null) ? null : getIcon(alert.type);
title = getTitle(alert.type);
titlew = AlertSkin.FONT_TITLE.stringWidth(title);
if (icon != null) {
iconw = icon.getWidth();
titlew += (AlertSkin.PAD_HORIZ + iconw);
iconh = icon.getHeight();
// We vertically center the icon
icony = 0;
if (iconh < AlertSkin.TITLE_HEIGHT) {
icony = (AlertSkin.TITLE_HEIGHT - iconh) / 2;
}
} else {
iconw = 0;
}
if (titlew > AlertSkin.WIDTH - (2 * AlertSkin.TITLE_MARGIN)) {
titlew = AlertSkin.WIDTH - (2 * AlertSkin.TITLE_MARGIN);
}
// We vertically center the title text
titley =
(AlertSkin.TITLE_HEIGHT - AlertSkin.FONT_TITLE.getHeight()) / 2;
switch (AlertSkin.TITLE_ALIGN) {
case Graphics.RIGHT:
titlex = AlertSkin.WIDTH - AlertSkin.TITLE_MARGIN - titlew;
break;
case Graphics.HCENTER:
titlex = (AlertSkin.WIDTH - titlew) / 2;
break;
case Graphics.LEFT:
default:
titlex = AlertSkin.TITLE_MARGIN;
break;
}
// We'll clip down the "box" for the title just in case
// its a really long string
// g.clipRect(titlex, 0, titlew, AlertSkin.TITLE_HEIGHT);
if (icon != null) {
g.drawImage(icon, titlex, icony,
Graphics.LEFT | Graphics.TOP);
titlex += (AlertSkin.PAD_HORIZ + iconw);
titlew -= (AlertSkin.PAD_HORIZ + iconw);
}
g.translate(titlex, titley);
Text.drawTruncString(g, title,
AlertSkin.FONT_TITLE, AlertSkin.COLOR_TITLE, titlew);
g.translate(-titlex, -titley);
}
/**
* Paint the gauge indicator for this alert, if there is one.
*
* @param g the Graphics to draw to
* @return the height occupied by the indicator
*/
int lPaintIndicator(Graphics g) {
if (alert.indicator == null) {
return 0;
}
GaugeLFImpl indicatorLF = (GaugeLFImpl)alert.indicator.gaugeLF;
// We center the gauge
int offsetx = (int)
((AlertSkin.WIDTH - indicatorLF.bounds[WIDTH]) / 2);
g.translate(offsetx, 0);
// SYNC NOTE: paint in gauge does not involve app code.
// So it's OK to call it from LCDUILock block.
indicatorLF.lCallPaint(g, indicatorLF.bounds[WIDTH],
indicatorLF.bounds[HEIGHT]);
g.translate(-offsetx, 0);
return indicatorLF.bounds[HEIGHT];
}
/**
* Paint the application-supplied image for this alert, if there is one.
*
* @param g the Graphics to draw to
* @return the height occupied by the image
*/
int lPaintImage(Graphics g) {
if (alert.image == null) {
return 0;
}
// We center the image
int offsetx = (int)
((AlertSkin.WIDTH - alert.image.getWidth()) / 2);
g.drawImage(alert.image, offsetx, 0, Graphics.TOP | Graphics.LEFT);
return alert.image.getHeight();
}
/**
* Paint the text content of this alert, if there is any
*
* @param g the Graphics to draw to
*/
void lPaintContent(Graphics g) {
if (alert.text != null) {
g.translate(AlertSkin.MARGIN_H, 0);
Text.paint(g, alert.text, AlertSkin.FONT_TEXT,
AlertSkin.COLOR_FG, 0,
viewable[WIDTH], viewable[HEIGHT],
0, Text.NORMAL, null);
g.translate(-AlertSkin.MARGIN_H, 0);
}
}
/**
* Handle a key press
*
* @param keyCode the key which was pressed
*/
void uCallKeyPressed(int keyCode) {
int gameAction = KeyConverter.getGameAction(keyCode);
synchronized (Display.LCDUILock) {
switch (gameAction) {
case Canvas.UP:
if (viewable[Y] > 0) {
viewable[Y] -= AlertSkin.SCROLL_AMOUNT;
if (viewable[Y] < 0) {
viewable[Y] = 0;
}
lRequestPaint();
}
break;
case Canvas.DOWN:
if (viewable[Y] < maxScroll) {
viewable[Y] += AlertSkin.SCROLL_AMOUNT;
if (viewable[Y] > maxScroll) {
viewable[Y] = maxScroll;
}
lRequestPaint();
}
break;
}
}
setVerticalScroll();
}
/**
* Handle a key repeat
*
* @param keyCode the key which was repeated
*/
void uCallKeyRepeated(int keyCode) {
uCallKeyPressed(keyCode);
}
/**
* Notify this Alert that is being displayed on the
* given Display and whether it needs to initialize its
* highlight
*/
void lCallShow() {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_HIGHUI_FORM_LAYOUT,
"# in AlertLFImpl: lCallShow");
}
super.lCallShow();
if (alert.type != null) {
currentDisplay.playAlertSound(alert.type);
}
if (alert.indicator != null) {
((GaugeLFImpl)alert.indicator.gaugeLF).lCallShowNotify();
}
if (!isLayoutValid) {
layout();
}
lSetTimeout(alert.getTimeout());
// We reset any scrolling done in a previous showing
viewable[Y] = 0;
setVerticalScroll();
}
/**
* Notify this Alert that it will no longer be displayed
* on the given Display
*/
void lCallHide() {
super.lCallHide();
if (alert.indicator != null) {
((GaugeLFImpl)alert.indicator.gaugeLF).lCallHideNotify();
}
if (timerTask != null) {
timerTask.cancel();
timerTask = null;
}
}
/**
* Cancel the timer whenever the alert is frozen. Alert is frozen
* when the display does not have foreground
*/
void lCallFreeze() {
super.lCallFreeze();
if (timerTask != null) {
timerTask.cancel();
timerTask = null;
}
}
/**
* Called upon content change to schedule a request for relayout and
* repaint.
*/
void lRequestInvalidate() {
super.lRequestInvalidate();
isLayoutValid = false;
}
/**
* Set the vertical scroll indicators for this Screen.
* We override this from our superclass because the viewport[] is
* set to the entire Alert dialog, but scrolling is confined to the
* "inner" viewport we've constructed beneath the title to scroll
* the text content. This scrolling behavior is maintained by
* 'maxScroll' - which represents the maximum number of pixels
* needed to scroll in order to reach the bottom of the scrollable
* content. If maxScroll is 0, no scrolling is necessary.
*/
void setVerticalScroll() {
if (maxScroll == 0) {
setVerticalScroll(0, 100);
} else {
setVerticalScroll((viewable[Y] * 100 / (maxScroll)),
((AlertSkin.HEIGHT - AlertSkin.TITLE_HEIGHT)
* 100 / viewable[HEIGHT]));
}
}
/**
* Layout the content of this Alert given the width and
* height parameters
*/
void layout() {
super.layout();
// layout() is called from DisplayableLFImpl constructor
// and at that time alert is not initialized
if (alert == null) {
maxScroll = 0;
return;
}
// The width of the viewable area is equal to the width of
// the alert minus a left and right margin
viewable[WIDTH] = AlertSkin.WIDTH - (2 * AlertSkin.MARGIN_H);
// height of activity indicator, if any
int indHeight = 0;
if (alert.indicator != null) {
GaugeLFImpl indicatorLF = (GaugeLFImpl)alert.indicator.gaugeLF;
if (indicatorLF.bounds == null) {
indicatorLF.bounds = new int[4];
}
int pW = indicatorLF.lGetPreferredWidth(-1);
if (pW > viewable[WIDTH] - (2 * AlertSkin.PAD_HORIZ)) {
pW = viewable[WIDTH] - (2 * AlertSkin.PAD_HORIZ);
}
indHeight = indicatorLF.lGetPreferredHeight(pW);
// We assign the item a bounds which is its pixel location,
// width, and height in coordinates which represent offsets
// of the viewport origin (that is, are in the viewport
// coordinate space)
indicatorLF.bounds[X] = 0;
indicatorLF.bounds[Y] = 0;
indicatorLF.bounds[WIDTH] = pW;
indicatorLF.bounds[HEIGHT] = indHeight;
}
// height of the alert's image, if any
int imageHeight = (alert.image == null) ? 0 : alert.image.getHeight();
// height of the alert's text content, if any
int textHeight = (alert.text == null) ? 0 :
Text.getHeightForWidth(alert.text, AlertSkin.FONT_TEXT,
viewable[WIDTH], 0);
// This gives us the height of the scrollable area
viewable[HEIGHT] = AlertSkin.PAD_VERT;
if (indHeight > 0) {
viewable[HEIGHT] += (indHeight + AlertSkin.PAD_VERT);
}
if (imageHeight > 0) {
viewable[HEIGHT] += (imageHeight + AlertSkin.PAD_VERT);
}
if (textHeight > 0) {
viewable[HEIGHT] += (textHeight + AlertSkin.PAD_VERT);
}
maxScroll = viewable[HEIGHT] -
(AlertSkin.HEIGHT - AlertSkin.TITLE_HEIGHT);
if (maxScroll < 0) {
maxScroll = 0;
}
isLayoutValid = true;
}
/**
* Returns the system image to draw in title area.
* If AlertType is not set, no image is drawn.
* @param alertType The type of the Alert
* @return the image to draw in title area
*/
static Image getIcon(AlertType alertType) {
if (alertType == null) {
return null;
}
if (alertType.equals(AlertType.INFO)) {
return AlertSkin.IMAGE_ICON_INFO;
} else if (alertType.equals(AlertType.WARNING)) {
return AlertSkin.IMAGE_ICON_WARN;
} else if (alertType.equals(AlertType.ERROR)) {
return AlertSkin.IMAGE_ICON_ERRR;
} else if (alertType.equals(AlertType.ALARM)) {
return AlertSkin.IMAGE_ICON_ALRM;
} else {
return AlertSkin.IMAGE_ICON_CNFM;
}
}
/**
* Returns the system image to draw in title area.
* If AlertType is not set, no image is drawn.
* @param alertType The type of the Alert
* @return the image to draw in title area
*/
String getTitle(AlertType alertType) {
if (alert.title != null) {
return alert.title;
}
if (alertType.equals(AlertType.INFO)) {
return AlertSkin.TEXT_TITLE_INFO;
} else if (alertType.equals(AlertType.WARNING)) {
return AlertSkin.TEXT_TITLE_WARN;
} else if (alertType.equals(AlertType.ERROR)) {
return AlertSkin.TEXT_TITLE_ERRR;
} else if (alertType.equals(AlertType.ALARM)) {
return AlertSkin.TEXT_TITLE_ALRM;
} else {
return AlertSkin.TEXT_TITLE_CNFM;
}
}
/**
* Calculate the height a displayable would occupy if it was to
* be displayed.
*
* @return the height a displayable would occupy
*/
public int getDisplayableHeight() {
return AlertSkin.HEIGHT;
}
/**
* Calculate the width a displayable would occupy if it was to
* be displayed
*
* @return the width a displayable would occupy
*/
public int getDisplayableWidth() {
return AlertSkin.WIDTH;
}
/**
* The maximum amount of scroll needed to see all the contents
* @return get the maximum scroll amount
*/
protected int getMaxScroll() {
return maxScroll;
}
/**
* This is the number of pixels left from the previous "page"
* when a page up or down occurs. The same value is used for line by
* line scrolling
* @return the number of pixels.
*/
protected int getScrollAmount() {
return AlertSkin.SCROLL_AMOUNT;
}
/**
* Static default Command for "OK"
*/
static final Command OK =
new Command(Resource.getString(ResourceConstants.DONE),
Command.OK, 0);
/**
* A Timer which serves all Alert objects to schedule
* their timeout tasks
*/
static Timer timeoutTimer;
/**
* Variables to hold the clip coordinates
*/
int clipx, clipy, clipw, cliph;
/**
* The maximum amount of scroll needed to see all the contents
* of the Alert
*/
int maxScroll;
/**
* The total maximum height of this Alert.
* This will be <= AlertSkin.MAX_HEIGHT.
*/
int totalHeight;
/**
* Alert associated with this view
*/
Alert alert;
/**
* The icon for the Alert
*/
Image icon;
/**
* A TimerTask which will be set to expire this Alert after
* its timeout period has elapsed.
*/
TimerTask timerTask;
/**
* A flag indicates that whether the layout of the alert
* is known.
*/
boolean isLayoutValid; // Default is false
/** local variable for the paint method (title x location) */
int titlex;
/** local variable for the paint method (title y location) */
int titley;
/** local variable for the paint method (title width) */
int titlew;
/** local variable for the paint method (title height) */
int titleh;
/** local variable for the paint method (icon y location) */
int icony;
/** local variable for the paint method (icon width) */
int iconw;
/** local variable for the paint method (icon height) */
int iconh;
/** Alert's title */
String title;
// *****************************************************
// Internal Class
// *****************************************************
/**
* A TimerTask subclass which will notify the Display to
* make the 'returnScreen' of this Alert the new current screen.
*/
private class TimeoutTask extends TimerTask {
/**
* Create a new timeout task
*/
TimeoutTask() { }
/**
* Simply set the Display's current screen to be this
* Alert's return screen
*/
public void run() {
// It could be this timeout task got scheduled and in
// the meantime the alert's contents were updated. There is
// a timing condition whereby an old timeout task could dismiss
// an alert which is now updated, so we stop and check to make
// sure the alert is not modal before we go ahead and dismiss it
synchronized (Display.LCDUILock) {
if (lIsModal() || alert.getTimeout() == Alert.FOREVER) {
return;
}
}
alert.uNotifyTimeout();
}
} // TimeoutTask
}
|