FileDocCategorySizeDatePackage
Alert.javaAPI DocphoneME MR2 API (J2ME)29770Wed May 02 18:00:22 BST 2007javax.microedition.lcdui

Alert.java

/*
 *  
 *
 * 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;


/**
 * An alert is a screen that shows data to the user and waits for a certain
 * period of time before proceeding to the next
 * <code>Displayable</code>. An alert can
 * contain a text string and an image.
 * The intended use of <code>Alert</code> is to inform the user about
 * errors and other
 * exceptional conditions.
 *
 * <P>The application can set the alert time to be infinity with
 * <code> setTimeout(Alert.FOREVER)</code>
 * in which case the <code>Alert</code> is considered to be <em>modal</em> and
 * the implementation provide a feature that allows the
 * user to "dismiss" the alert, whereupon the next
 * <code>Displayable</code>
 * is displayed as if the timeout had expired immediately.</P>
 *
 * <P>If an application specifies an alert to be of a
 * timed variety <em>and</em> gives it too much content such that it must
 * scroll,
 * then it automatically becomes a modal alert.</P>
 *
 * <P> An alert may have an <code>AlertType</code> associated with it
 * to provide an indication of the nature of the alert.
 * The implementation may use this type to play an
 * appropriate sound when the <code>Alert</code> is presented to the user.
 * See {@link AlertType#playSound(javax.microedition.lcdui.Display) 
 * AlertType.playSound()}.</P>
 *
 * <P>An alert may contain an optional <code>Image</code>.  The
 * <code>Image</code> may be mutable or
 * immutable.  If the <code>Image</code> is mutable, the effect is as
 * if a snapshot of its
 * contents is taken at the time the <code>Alert</code> is constructed
 * with this <code>Image</code> and
 * when <code>setImage</code> is called with an <code>Image</code>.
 * This snapshot is used whenever the contents of the
 * <code>Alert</code> are to be
 * displayed.  Even if the application subsequently draws into the
 * <code>Image</code>, the
 * snapshot is not modified until the next call to <code>setImage</code>.  The
 * snapshot is <em>not</em> updated when the <code>Alert</code>
 * becomes current or becomes
 * visible on the display.  (This is because the application does not have
 * control over exactly when <code>Displayables</code> appear and
 * disappear from the
 * display.)</P>
 *
 * <a name="indicator"></a>
 * <h3>Activity Indicators</h3>
 * 
 * <P>An alert may contain an optional {@link Gauge} object that is used as an 
 * activity or progress indicator.  By default, an <code>Alert</code>
 * has no activity
 * indicator; one may be set with the {@link #setIndicator} method.
 * The <code>Gauge</code>
 * object used for the activity indicator must conform to all of the following 
 * restrictions:</P>
 *
 * <ul>
 * <li>it must be non-interactive;</li>
 * <li>it must not be owned by another container (<code>Alert</code>
 * or <code>Form</code>);</li>
 * <li>it must not have any <code>Commands</code>;</li>
 * <li>it must not have an <code>ItemCommandListener</code>;</li>
 * <li>it must not have a label (that is, its label must be
 * <code>null</code>;</li>
 * <li>its preferred width and height must both be unlocked; and</li>
 * <li>its layout value must be <code>LAYOUT_DEFAULT</code>.</li>
 * </ul>
 *
 * <P>It is an error for the application to attempt to use a
 * <code>Gauge</code> object that
 * violates any of these restrictions.  In addition, when the
 * <code>Gauge</code> object is
 * being used as the indicator within an <code>Alert</code>, the
 * application is prevented
 * from modifying any of these pieces of the <code>Gauge's</code> state.</P>
 *
 * <a name="commands"></a>
 * <h3>Commands and Listeners</h3>
 *
 * <P>Like the other <code>Displayable</code> classes, an
 * <code>Alert</code> can accept <code>Commands</code>, which
 * can be delivered to a <code>CommandListener</code> set by the
 * application.  The <code>Alert</code>
 * class adds some special behavior for <code>Commands</code> and listeners.</P>
 *
 * <P>When it is created, an <code>Alert</code> implicitly has the
 * special <code>Command</code>
 * {@link #DISMISS_COMMAND} present on it.  If the application adds any 
 * other <code>Commands</code> to the <code>Alert</code>,
 * <code>DISMISS_COMMAND</code> is implicitly removed.  If the
 * application removes all other <code>Commands</code>,
 * <code>DISMISS_COMMAND</code> is implicitly
 * restored.  Attempts to add or remove <code>DISMISS_COMMAND</code>
 * explicitly are
 * ignored.  Thus, there is always at least one <code>Command</code>
 * present on an <code>Alert</code>.
 * </P>
 *
 * <P>If there are two or more <code>Commands</code> present on the
 * <code>Alert</code>, it is
 * automatically turned into a modal <code>Alert</code>, and the
 * timeout value is always
 * {@link #FOREVER}.  The <code>Alert</code> remains on the display
 * until a <code>Command</code> is
 * invoked.  If the Alert has one Command (whether it is DISMISS_COMMAND or it
 * is one provided by the application), the <code>Alert</code> may have
 * the timed behavior
 * as described above.  When a timeout occurs, the effect is the same as if
 * the user had invoked the <code>Command</code> explicitly.</P>
 *
 * <P>When it is created, an <code>Alert</code> implicitly has a
 * <code>CommandListener</code> called the
 * <em>default listener</em> associated with it.  This listener may be
 * replaced by an application-provided listener through use of the {@link
 * #setCommandListener} method.  If the application removes its listener by
 * passing <code>null</code> to the <code>setCommandListener</code> method,
 * the default listener is implicitly restored.</P>
 *
 * <P>The {@link Display#setCurrent(Alert, Displayable)} method and the {@link
 * Display#setCurrent(Displayable)} method (when called with an
 * <code>Alert</code>) define
 * special behavior for automatically advancing to another
 * <code>Displayable</code> after
 * the <code>Alert</code> is dismissed.  This special behavior occurs
 * only when the default
 * listener is present on the <code>Alert</code> at the time it is
 * dismissed or when a
 * command is invoked.  If the user invokes a <code>Command</code> and
 * the default listener
 * is present, the default listener ignores the <code>Command</code>
 * and implements the
 * automatic-advance behavior.</P>
 *
 * <P>If the application has set its own <code>CommandListener</code>, the
 * automatic-advance behavior is disabled.  The listener code is responsible
 * for advancing to another <code>Displayable</code>.  When the
 * application has provided a
 * listener, <code>Commands</code> are invoked normally by passing
 * them to the listener's
 * <code>commandAction</code> method.  The <code>Command</code> passed
 * will be one of the
 * <code>Commands</code> present on the <code>Alert</code>: either
 * <code>DISMISS_COMMAND</code> or one of the
 * application-provided <code>Commands</code>.</P>
 *
 * <P>The application can restore the default listener by passing
 * <code>null</code> to the <code>setCommandListener</code> method.</P>
 * 
 * <P>
 * <strong>Note:</strong> An application may set a {@link Ticker Ticker}
 * with {@link Displayable#setTicker Displayable.setTicker} on an
 * <code>Alert</code>, however it may not be displayed due to
 * implementation restrictions.
 * </P>
 *
 * @see AlertType
 * @since MIDP 1.0
 */

public class Alert extends Screen {

// *****************************************************
//  Public members
// *****************************************************

    /**
     * <code>FOREVER</code> indicates that an <code>Alert</code> is
     * kept visible until the user
     * dismisses it.  It is used as a value for the parameter to
     * {@link #setTimeout(int) setTimeout()}
     * to indicate that the alert is modal.  Instead of waiting for a
     * specified period of time, a modal <code>Alert</code> will wait
     * for the user to take
     * some explicit action, such as pressing a button, before proceeding to
     * the next <code>Displayable</code>.
     *
     * <P>Value <code>-2</code> is assigned to <code>FOREVER</code>.</P>
     */
    public final static int FOREVER = -2;

    /**
     * A <code>Command</code> delivered to a listener to indicate that
     * the <code>Alert</code> has been
     * dismissed.  This Command is implicitly present an on
     * <code>Alert</code> whenever
     * there are no other Commands present.  The field values of
     * <code>DISMISS_COMMAND</code> are as follows:
     *
     * <ul>
     * <li>label = "" (an empty string)</li>
     * <li>type = Command.OK</li>
     * <li>priority = 0</li>
     * </ul>
     *
     * <p>The label value visible to the application must be as specified 
     * above.  However, the implementation may display
     * <code>DISMISS_COMMAND</code> to the
     * user using an implementation-specific label.</p>
     *
     * <p>Attempting to add or remove <code>DISMISS_COMMAND</code>
     * from an <code>Alert</code> has no
     * effect.  However, <code>DISMISS_COMMAND</code> is treated as an
     * ordinary <code>Command</code> if
     * it is used with other <code>Displayable</code> types.</p>
     *
     */
    public final static Command DISMISS_COMMAND =
        new Command("", Command.OK, 0);

// *****************************************************
//  Constructor(s)
// *****************************************************

    /**
     * Constructs a new, empty <code>Alert</code> object with the
     * given title. If <code>null</code> is
     * passed, the <code>Alert</code> will have no title.  Calling
     * this constructor is
     * equivalent to calling
     *
     * <pre>
     *    <code>Alert(title, null, null, null)</code>
     * </pre>
     *
     * @param title the title string, or <code>null</code>
     *
     * @see #Alert(String, String, Image, AlertType)
     */
    public Alert(String title) {
        this(title, null, null, null);
    }

    /**
     * Constructs a new <code>Alert</code> object with the given title,
     * content
     * string and image, and alert type.
     * The layout of the contents is implementation dependent.
     * The timeout value of this new alert is the same value that is
     * returned by <code>getDefaultTimeout()</code>.
     * The <code>Image</code> provided may either be mutable or immutable.
     * The handling and behavior of specific <code>AlertTypes</code>
     * is described in
     * {@link AlertType}.  <code>null</code> is allowed as the value
     * of the <code>alertType</code>
     * parameter and indicates that the <code>Alert</code> is not to
     * have a specific alert
     * type.  <code>DISMISS_COMMAND</code> is the only
     * <code>Command</code> present on the new
     * <code>Alert</code>.  The <code>CommandListener</code>
     * associated with the new <code>Alert</code> is the
     * <em>default listener</em>.  Its behavior is described in more detail in 
     * the section <a href="#commands">Commands and Listeners</a>.
     *
     * @param title the title string, or <code>null</code> if there is no title
     * @param alertText the string contents, or <code>null</code> if there 
     * is no string
     * @param alertImage the image contents, or <code>null</code> if there 
     * is no image
     * @param alertType the type of the <code>Alert</code>, or
     * <code>null</code>
     * if the <code>Alert</code> has no
     * specific type
     */
    public Alert(String title, String alertText,
                 Image alertImage, AlertType alertType) {

        super(title);

        synchronized (Display.LCDUILock) {
            this.text = alertText;
            this.type = alertType;

            setImageImpl(alertImage);

            displayableLF = alertLF = LFFactory.getFactory().getAlertLF(this);

            this.time = alertLF.lGetDefaultTimeout();

	    // Call addCommandImpl to avoid notification to AlertLF
	    // cause we know this Alert is not yet visible
	    addCommandImpl(alertLF.lGetDismissCommand());

            // Note that we have to call super.setCommandListener since
            // this.setCommandListener will set application's command 
            // listener
            super.setCommandListener(implicitListener);
        }
    }

// *****************************************************
//  Public methods
// *****************************************************

    /**
     * Gets the default time for showing an <code>Alert</code>.  This
     * is either a
     * positive value, which indicates a time in milliseconds, or the special
     * value
     * {@link #FOREVER FOREVER},
     * which indicates that <code>Alerts</code> are modal by default.  The
     * value returned will vary across implementations and is presumably
     * tailored to be suitable for each.
     *
     * @return default timeout in milliseconds, or <code>FOREVER</code>
     */
    public int getDefaultTimeout() {
        synchronized (Display.LCDUILock) {
            return alertLF.lGetDefaultTimeout();
        }
    }

    /**
     * Gets the time this <code>Alert</code> will be shown.  This is
     * either a positive
     * value, which indicates a time in milliseconds, or the special value
     * <code>FOREVER</code>, which indicates that this
     * <code>Alert</code> is modal.  This value is not
     * necessarily the same value that might have been set by the
     * application
     * in a call to {@link #setTimeout}.  In particular, if the
     * <code>Alert</code> is made
     * modal because its contents is large enough to scroll, the value
     * returned by <code>getTimeout</code> will be <code>FOREVER</code>.
     *
     * @return timeout in milliseconds, or <code>FOREVER</code>
     * @see #setTimeout
     */
    public int getTimeout() {
        synchronized (Display.LCDUILock) {
            if (alertLF.lIsModal()) {
                return FOREVER;
            } else {
                return time;
            }
        }
    }

    /**
     * Set the time for which the <code>Alert</code> is to be shown.
     * This must either
     * be a positive time value in milliseconds, or the special value
     * <code>FOREVER</code>.
     *
     * @param time timeout in milliseconds, or <code>FOREVER</code>
     * @throws IllegalArgumentException if time is not positive and is
     * not <code>FOREVER</code>
     * @see #getTimeout
     */
    public void setTimeout(int time) {
        if (time <= 0 && time != FOREVER) {
            throw new IllegalArgumentException();
        }

        synchronized (Display.LCDUILock) {
            this.time = time;
            alertLF.lSetTimeout(time);
        }
    }

    /**
     * Gets the type of the <code>Alert</code>.
     * @return a reference to an instance of <code>AlertType</code>,
     * or <code>null</code>
     * if the <code>Alert</code>
     * has no specific type
     * @see #setType
     */
    public AlertType getType() {
        // SYNC NOTE: return of atomic value, no locking necessary
        return type;
    }

    /**
     * Sets the type of the <code>Alert</code>.
     * The handling and behavior of specific <code>AlertTypes</code>
     * is described in
     * {@link AlertType}.
     * @param type an <code>AlertType</code>, or <code>null</code> if the
     * <code>Alert</code> has no
     * specific type
     * @see #getType
     */
    public void setType(AlertType type) {
        synchronized (Display.LCDUILock) {
            if (this.type != type) {
                this.type = type;
                alertLF.lSetType(type);
            }
        }
    }

    /**
     * Gets the text string used in the <code>Alert</code>.
     * @return the <code>Alert's</code> text string, or <code>null</code> 
     * if there is no text
     * @see #setString
     */
    public String getString() {
        // SYNC NOTE: no locking necessary
        return text;
    }

    /**
     * Sets the text string used in the <code>Alert</code>.
     *
     * <p>If the <code>Alert</code> is visible on the display when its
     * contents are updated
     * through a call to <code>setString</code>, the display will be
     * updated with the new
     * contents as soon as it is feasible for the implementation to do so.
     * </p>
     *
     * @param str the <code>Alert's</code> text string, or <code>null</code>
     * if there is no text
     * @see #getString
     */
    public void setString(String str) {
        synchronized (Display.LCDUILock) {
            if (str == text || (str != null && str.equals(text))) {
                return;
            }

            String oldStr = text;
            text = str;
            alertLF.lSetString(oldStr, str);
        }
    }

    /**
     * Gets the <code>Image</code> used in the <code>Alert</code>.
     * @return the <code>Alert's</code> image, or <code>null</code> 
     * if there is no image
     * @see #setImage
     */
    public Image getImage() {
        synchronized (Display.LCDUILock) {
            if (mutableImage != null) {
                return mutableImage;
            } else {
                return image;
            }
        }
    }

    /**
     * Sets the <code>Image</code> used in the <code>Alert</code>.
     * The <code>Image</code> may be mutable or
     * immutable.  If <code>img</code> is <code>null</code>, specifies
     * that this <code>Alert</code> has no image.
     * If <code>img</code> is mutable, the effect is as if a snapshot is taken
     * of <code>img's</code> contents immediately prior to the call to
     * <code>setImage</code>.  This
     * snapshot is used whenever the contents of the
     * <code>Alert</code> are to be
     * displayed.  If <code>img</code> is already the
     * <code>Image</code> of this <code>Alert</code>, the effect
     * is as if a new snapshot of img's contents is taken.  Thus, after
     * painting into a mutable image contained by an <code>Alert</code>, the
     * application can call
     *
     * <TABLE BORDER="2">
     * <TR>
     * <TD ROWSPAN="1" COLSPAN="1">
     *    <pre><code>
     *    alert.setImage(alert.getImage());    </code></pre>
     * </TD>
     * </TR>
     * </TABLE>
     * <p>to refresh the <code>Alert's</code> snapshot of its
     * <code>Image</code>.</p>
     *
     * <p>If the <code>Alert</code> is visible on the display when its
     * contents are updated
     * through a call to <code>setImage</code>, the display will be
     * updated with the new
     * snapshot as soon as it is feasible for the implementation to do so.
     * </p>
     *
     * @param img the <code>Alert's</code> image, or <code>null</code>
     * if there is no image
     * @see #getImage
     */
    public void setImage(Image img) {
        synchronized (Display.LCDUILock) {
            Image oldImg = image;
            if (setImageImpl(img)) {
		// Always pass the immutable image snapshot as new image
                alertLF.lSetImage(oldImg, image);
            }
        }
    }

    /**
     * Sets an activity indicator on this <code>Alert</code>.  The
     * activity indicator is a
     * {@link Gauge} object.  It must be in a restricted state in order for it
     * to be used as the activity indicator for an <code>Alert</code>.
     * The restrictions
     * are listed <a href="#indicator">above</a>.  If the
     * <code>Gauge</code> object
     * violates any of these restrictions,
     * <code>IllegalArgumentException</code> is thrown.
     *
     * <p>If <code>indicator</code> is <code>null</code>, this removes any
     * activity indicator present on this <code>Alert</code>.</p>
     *
     * @param indicator the activity indicator for this <code>Alert</code>,
     * or <code>null</code> if 
     * there is to be none
     *
     * @throws IllegalArgumentException if <code>indicator</code> does not
     * meet the restrictions for its use in an <code>Alert</code>
     * @see #getIndicator
     */
    public void setIndicator(Gauge indicator) {

        synchronized (Display.LCDUILock) {
            // do nothing if indicator is the same 
            // that includes both indicators (old and new) being null
            if (this.indicator == indicator) {
                return;
            }

            Gauge oldIndicator = this.indicator;

            if (indicator == null) {
                // The Alert no longer owns this Gauge;
                // We do not need to check if this.indicator != null
                // since we already returned if they are both null
                oldIndicator.lSetOwner(null);
            } else {
                if (!isConformantIndicator(indicator)) {
                    throw new IllegalArgumentException("Gauge in wrong state");
                }
                indicator.lSetOwner(this);
            
                // if old indicator is not null
                // it is no longer is owned by this alert
                if (oldIndicator != null) {
                    oldIndicator.lSetOwner(null);
                }
            }

            this.indicator = indicator;
            
            alertLF.lSetIndicator(oldIndicator, indicator);
        }
    }

    /**
     * Gets the activity indicator for this <code>Alert</code>.
     * 
     * @return a reference to this <code>Alert's</code> activity indicator,
     * or <code>null</code> if 
     * there is none
     * @see #setIndicator
     */
    public Gauge getIndicator() {
        // SYNC NOTE: no locking necessary
        return indicator;
    }

    /**
     * Similar to {@link Displayable#addCommand}, however when the
     * application first adds a command to an <code>Alert</code>,
     * {@link #DISMISS_COMMAND} is implicitly removed.  Calling this
     * method with <code>DISMISS_COMMAND</code> as the parameter has
     * no effect.
     *
     * @param cmd the command to be added
     *
     * @throws NullPointerException if cmd is <code>null</code>
     */
    public void addCommand(Command cmd) {
        if (cmd == null) {
            throw new NullPointerException();
        }

        if (cmd == DISMISS_COMMAND) {
            return;
        }

        synchronized (Display.LCDUILock) {
	    Command dismissCmd = alertLF.lGetDismissCommand();

	    if (commands[0] == dismissCmd) {
		super.removeCommand(dismissCmd);
	    }
            super.addCommand(cmd);
        }
    }

    /**
     * Similar to {@link Displayable#removeCommand}, however when the
     * application removes the last command from an
     * <code>Alert</code>, {@link #DISMISS_COMMAND} is implicitly
     * added.  Calling this method with <code>DISMISS_COMMAND</code>
     * as the parameter has no effect.
     *
     * @param cmd the command to be removed
     */
    public void removeCommand(Command cmd) {
        if (cmd == DISMISS_COMMAND) {
            return;
        }

        synchronized (Display.LCDUILock) {
            super.removeCommand(cmd);
	    if (numCommands == 0) {
		super.addCommand(alertLF.lGetDismissCommand());
	    }
        }
    }

    /**
     * The same as {@link Displayable#setCommandListener} but with the 
     * following additional semantics.  If the listener parameter is
     * <code>null</code>, the <em>default listener</em> is restored.
     * See <a href="#commands">Commands and Listeners</a> for the definition 
     * of the behavior of the default listener.
     *
     * @param l the new listener, or <code>null</code>
     */
    public void setCommandListener(CommandListener l) {
        synchronized (Display.LCDUILock) {
            userCommandListener = l;
        }
    }

// *****************************************************
//  Package private methods
// *****************************************************

    /**
     * Set the screen that the display should return to
     * when this Alert is dismissed.
     *
     * @param d The Displayable to display when this Alert is completed
     */
    void setReturnScreen(Displayable d) {
        this.returnScreen = d;
    }

    /**
     * Get the screen that the display should return to
     * when this Alert is dismissed.
     * @return The Displayable to display when this Alert is completed
     */
    Displayable getReturnScreen() {
        return returnScreen;
    }

    /**
     * Dismisses this alert.
     */
    void lDismiss() {
        Display dpy = alertLF.lGetCurrentDisplay();
        if (dpy != null && returnScreen != null) {
            dpy.clearAlert(this, returnScreen);
        }
    }

    /**
     * LF notifies that the Alert is dismissed due to timeout,
     * instead of user action.
     * Commandlistener will be notified with the default Command.
     */
    void uNotifyTimeout() {
	try {
	    synchronized (Display.calloutLock) {
		listener.commandAction(commands[0], this);
	    }
	} catch (Throwable thr) {
	    Display.handleThrowable(thr);
	}
    }

// *****************************************************
//  Private methods
// *****************************************************

    /**
     * Verify the activity indicator is conformant with the spec
     * requirements for addition to an Alert.
     *
     * @param ind The indicator to be verified
     * @return boolean True if the gauge is conformant; false otherwise
     */
    private boolean isConformantIndicator(Gauge ind) {
        return ((ind.interactive == false) &&
                (ind.owner == null) &&
                (ind.numCommands == 0) &&
                (ind.commandListener == null) &&
                (ind.label == null) &&
                (ind.layout == Item.LAYOUT_DEFAULT) &&
                (ind.lockedWidth == -1) &&
                (ind.lockedHeight == -1));
    }

    /**
     * Set the Image for this Alert.
     *
     * @param img The img to use for this Alert
     * @return true if look&feel object has to be notified of an image change
     */
    private boolean setImageImpl(Image img) {
        if (img != null && img.isMutable()) {
            this.image = Image.createImage(img);   // use immutable copy of img
            this.mutableImage = img;
        } else { 
            if (image == img) {
                return false;
            }
            this.image = img;
            this.mutableImage = null;        // Make sure to clear mutableImage
        }
        return true;
    }

// *****************************************************
//  Package private members
// *****************************************************

    /**
     * The type of this alert
     */
    AlertType type;

    /**
     * The layout object for the alert text string
     */
    String text;

    /**
     * The image of this alert
     */
    Image image;

    /**
     * A reference to the original, mutable Image passed to setImage(). This
     * is only so getImage() can return the correct Image Object.
     */
    private Image mutableImage;

    /**
     * The activity indicator for this alert
     */
    Gauge indicator;

    /**
     * The timeout value of this alert
     */
    int time;

    /**
     * The screen which the display will return to when this Alert
     * is completed
     */
    Displayable returnScreen;
    
    /**
     * The application's command listener
     */
    CommandListener userCommandListener;

    /** The Alert look&feel object associated with this Alert */
    AlertLF alertLF;

// *****************************************************
//  Private members
// *****************************************************

    /**
     * Special CommandListener instance to handle execution of
     * the default "OK" Command
     */
    private CommandListener implicitListener = new CommandListener() {
        /**
         * Handle the execution of the given Command and Displayable.
         * Caller of this function should hold calloutLock and catch Throwable.
	 *
         * @param c The Command to execute
         * @param s The Displayable from which the Command originated
         */
        public void commandAction(Command c, Displayable s) {
	    CommandListener listenerToCall = null;

	    synchronized (Display.LCDUILock) {
		if (userCommandListener != null) {
		    // translate 'OK' to 'DISMISS_COMMAND
		    if (c == alertLF.lGetDismissCommand()) {
			c = DISMISS_COMMAND;
		    }
		    // To be called outside LCDUILock
		    listenerToCall = userCommandListener;
		} else {
		    // Treat all commands as if they were 'DISMISS'
                    lDismiss();
		}
	    } // synchronized

            // SYNC NOTE: We are protected by the calloutLock obtained
            // previously. (either in Display or the timeout task)
            // We do not need to re-acquire the calloutLock when
            // calling the application's command listener.
	    if (listenerToCall != null) {
		listenerToCall.commandAction(c, s);
	    }
        }
    };
}