/*
*
*
* 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;
/**
* A <code>Screen</code> containing list of choices. Most of its
* behavior is common with
* class {@link ChoiceGroup ChoiceGroup}, and their common API. The
* different <code>List</code> types in particular, are defined in
* interface {@link Choice
* Choice}. When a <code>List</code> is present on the display, the
* user can interact with
* it by selecting elements and possibly by traversing and scrolling among
* them. Traversing and scrolling operations do not cause application-visible
* events. The system notifies the application only when a {@link Command
* Command} is invoked by notifying its {@link CommandListener}. The
* <code>List</code>
* class also supports a select command that may be invoked specially
* depending upon the capabilities of the device.
*
* <p>The notion of a <em>select</em> operation on a <code>List</code>
* element is central
* to the user's interaction with the <code>List</code>. On devices
* that have a dedicated
* hardware "select" or "go" key, the select
* operation is implemented with
* that key. Devices that do not have a dedicated key must provide another
* means to do the select operation, for example, using a soft key. The
* behavior of the select operation within the different types of lists is
* described in the following sections.</p>
*
* <p><code>List</code> objects may be created with <code>Choice</code> types of
* {@link Choice#EXCLUSIVE}, {@link Choice#MULTIPLE}, and
* {@link Choice#IMPLICIT}. The <code>Choice</code> type {@link Choice#POPUP}
* is not allowed on <code>List</code> objects.</p>
*
* <h3>Selection in <code>EXCLUSIVE</code> and <code>MULTIPLE</code> Lists</h3>
*
* <p>The select operation is not associated with a
* <code>Command</code> object, so the
* application has no means of setting a label for it or being notified when
* the operation is performed. In <code>Lists</code> of type
* <code>EXCLUSIVE</code>, the select
* operation selects the target element and deselects the previously selected
* element. In <code>Lists</code> of type <code>MULTIPLE</code>, the
* select operation toggles the
* selected state of the target element, leaving the selected state of other
* elements unchanged. Devices that implement the select operation using a
* soft key will need to provide a label for it. The label should be something
* similar to "Select" for <code>Lists</code> of type
* <code>EXCLUSIVE</code>, and it should be something
* similar to "Mark" or "Unmark" for
* <code>Lists</code> of type <code>MULTIPLE</code>.</p>
*
* <h3>Selection in <code>IMPLICIT</code> Lists</h3>
*
* <p>The select operation is associated with a <code>Command</code>
* object referred to as
* the <em>select command</em>. When the user performs the select operation,
* the system will invoke the select command by notifying the
* <code>List's</code> {@link
* CommandListener CommandListener}. The default select command is the
* system-provided command <code>SELECT_COMMAND</code>. The select
* command may be modified
* by the application through use of the {@link #setSelectCommand(Command
* command) setSelectCommand} method. Devices that implement the select
* operation using a soft key will use the label from the select command. If
* the select command is <code>SELECT_COMMAND</code>, the device may
* choose to provide its
* own label instead of using the label attribute of
* <code>SELECT_COMMAND</code>.
* Applications should generally provide their own select command to replace
* <code>SELECT_COMMAND</code>. This allows applications to provide a
* meaningful label,
* instead of relying on the one provided by the system for
* <code>SELECT_COMMAND</code>.
* The implementation must <em>not</em> invoke the select command if there are
* no elements in the <code>List</code>, because if the
* <code>List</code> is empty the selection does
* not exist. In this case the implementation should remove or disable the
* select command if it would appear explicitly on a soft button or in a menu.
* Other commands can be invoked normally when the <code>List</code>
* is empty.</p>
*
* <h3>Use of <code>IMPLICIT</code> Lists</h3>
*
* <p> <code>IMPLICIT</code> <code>Lists</code> can be used to
* construct menus by providing operations
* as <code>List</code> elements. The application provides a
* <code>Command</code> that is used to
* select a <code>List</code> element and then defines this
* <code>Command</code> to be used as the
* select command. The application must also register a
* <code>CommandListener</code> that
* is called when the user selects or activates the <code>Command</code>:</p>
*
* <TABLE BORDER="2">
* <TR>
* <TD ROWSPAN="1" COLSPAN="1">
* <pre><code>
* String[] elements = { ... }; //Menu items as List elements
* List menuList = new List("Menu", List.IMPLICIT, elements, null);
* Command selectCommand = new Command("Open", Command.ITEM, 1);
* menuList.setSelectCommand(selectCommand);
* menuList.setCommandListener(...); </code></pre>
* </TD>
* </TR>
* </TABLE>
*
* <p>The listener can query the <code>List</code> to determine which
* element is selected
* and then perform the corresponding action. Note that setting a command as
* the select command adds it to the <code>List</code> as a side effect.</p>
*
* <p> The select command should be considered as a <em>default operation</em>
* that takes place when a select key is pressed. For example, a
* <code>List</code>
* displaying email headers might have three operations: read, reply, and
* delete. Read is considered to be the default operation. </p>
*
* <TABLE BORDER="2">
* <TR>
* <TD ROWSPAN="1" COLSPAN="1">
* <pre><code>
* List list = new List("Email", List.IMPLICIT, headers);
* readCommand = new Command("Read", Command.ITEM, 1);
* replyCommand = new Command("Reply", Command.ITEM, 2);
* deleteCommand = new Command("Delete", Command.ITEM, 3);
* list.setSelectCommand(readCommand);
* list.addCommand(replyCommand);
* list.addCommand(deleteCommand);
* list.setCommandListener(...); </code></pre>
* </TD>
* </TR>
* </TABLE>
* <p>On a device with a dedicated select key, pressing this key will invoke
* <code>readCommand</code>. On a device without a select key, the user is
* still able to invoke the read command, since it is also provided as an
* ordinary <code>Command</code>.</p>
*
* <p> It should be noted that this kind of default operation must be used
* carefully, and the usability of the resulting user interface must always
* kept in mind. The default operation should always be the most intuitive
* operation on a particular List. </p>
*
* @since MIDP 1.0
*/
public class List extends Screen implements Choice {
/**
* The default select command for <code>IMPLICIT</code> <code>Lists</code>.
* Applications using an <code>IMPLICIT</code> <code>List</code>
* should set their own select command
* using
* {@link #setSelectCommand(Command command) setSelectCommand}.
*
* <p>
* The field values of <code>SELECT_COMMAND</code> are:<br>
* - <code>label = ""</code> (an empty string)<br>
* - <code>type = SCREEN</code><br>
* - <code>priority = 0</code><br>
* </p>
* <p>(It would be more appropriate if the type were
* <code>ITEM</code>, but the type of
* <code>SCREEN</code> is retained for historical purposes.)</p>
* <p>
* The application should not use these values for recognizing
* the <code>SELECT_COMMAND</code>. Instead, object identities of
* the <code>Command</code> and
* <code>Displayable</code> (<code>List</code>) should be used.
* </p>
*
* <p><code>SELECT_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 SELECT_COMMAND =
new Command("", Command.SCREEN, 0);
// constructors //
/**
* Creates a new, empty <code>List</code>, specifying its title
* and the type of the
* list.
* @param title the screen's title (see {@link Displayable Displayable})
* @param listType one of <code>IMPLICIT</code>, <code>EXCLUSIVE</code>,
* or <code>MULTIPLE</code>
* @throws IllegalArgumentException if <code>listType</code> is not
* one of
* <code>IMPLICIT</code>,
* <code>EXCLUSIVE</code>, or <code>MULTIPLE</code>
* @see Choice
*/
public List(String title, int listType) {
this(title, listType, new String[] {}, new Image[] {});
}
/**
* Creates a new <code>List</code>, specifying its title, the type
* of the <code>List</code>, and
* an array of <code>Strings</code> and <code>Images</code> to be
* used as its initial contents.
*
* <p>The <code>stringElements</code> array must be non-null and
* every array element
* must also be non-null. The length of the
* <code>stringElements</code> array
* determines the number of elements in the <code>List</code>.
* The <code>imageElements</code> array
* may be <code>null</code> to indicate that the <code>List</code>
* elements have no images. If the
* <code>imageElements</code> array is non-null, it must be the
* same length as the
* <code>stringElements</code> array. Individual elements of the
* <code>imageElements</code> array
* may be <code>null</code> in order to indicate the absence of an
* image for the
* corresponding <code>List</code> element. Non-null elements of the
* <code>imageElements</code> array may refer to mutable or
* immutable images.</p>
*
* @param title the screen's title (see {@link Displayable Displayable})
* @param listType one of <code>IMPLICIT</code>, <code>EXCLUSIVE</code>,
* or <code>MULTIPLE</code>
* @param stringElements set of strings specifying the string parts of the
* <code>List</code> elements
* @param imageElements set of images specifying the image parts of
* the <code>List</code> elements
*
* @throws NullPointerException if <code>stringElements</code> is
* <code>null</code>
* @throws NullPointerException if the <code>stringElements</code>
* array contains any null elements
* @throws IllegalArgumentException if the <code>imageElements</code>
* array is non-null
* and has a different length from the <code>stringElements</code> array
* @throws IllegalArgumentException if <code>listType</code> is not one
* of <code>IMPLICIT</code>,
* <code>EXCLUSIVE</code>, or <code>MULTIPLE</code>
*
* @see Choice#EXCLUSIVE
* @see Choice#MULTIPLE
* @see Choice#IMPLICIT
*/
public List(String title, int listType, String[] stringElements,
Image[] imageElements) {
super(title);
if (!(listType == IMPLICIT ||
listType == EXCLUSIVE ||
listType == MULTIPLE)) {
throw new IllegalArgumentException();
}
synchronized (Display.LCDUILock) {
cg = new ChoiceGroup(null, listType,
stringElements, imageElements, true);
cg.lSetOwner(this);
displayableLF = listLF = LFFactory.getFactory().getListLF(this);
}
}
// *****************************************************
// Public methods
// *****************************************************
/**
* Gets the number of elements in the <code>List</code>.
* @return the number of elements in the <code>List</code>
*/
public int size() {
return cg.size();
}
/**
* Gets the <code>String</code> part of the element referenced by
* <code>elementNum</code>.
*
* @param elementNum the index of the element to be queried
* @return the string part of the element
* @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
* @see #getImage(int)
*/
public String getString(int elementNum) {
return cg.getString(elementNum);
}
/**
* Gets the <code>Image</code> part of the element referenced by
* <code>elementNum</code>.
*
* @param elementNum the number of the element to be queried
* @return the image part of the element, or <code>null</code>
* if there is no image
* @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
* @see #getString(int)
*/
public Image getImage(int elementNum) {
return cg.getImage(elementNum);
}
/**
* Appends an element to the <code>List</code>.
*
* @param stringPart the string part of the element to be added
* @param imagePart the image part of the element to be added, or
* <code>null</code> if there is no image part
* @return the assigned index of the element
* @throws NullPointerException if <code>stringPart</code> is
* <code>null</code>
*/
public int append(String stringPart, Image imagePart) {
return cg.append(stringPart, imagePart);
}
/**
* Inserts an element into the <code>List</code> just prior to
* the element specified.
*
* @param elementNum the index of the element where insertion is to occur
* @param stringPart the string part of the element to be inserted
* @param imagePart the image part of the element to be inserted,
* or <code>null</code> if there is no image part
* @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
* @throws NullPointerException if <code>stringPart</code> is
* <code>null</code>
*/
public void insert(int elementNum,
String stringPart, Image imagePart) {
cg.insert(elementNum, stringPart, imagePart);
}
/**
* Deletes the element referenced by <code>elementNum</code>.
*
* @param elementNum the index of the element to be deleted
* @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
*/
public void delete(int elementNum) {
cg.delete(elementNum);
}
/**
* Deletes all elements from this List.
*/
public void deleteAll() {
cg.deleteAll();
}
/**
* Sets the <code>String</code> and <code>Image</code> parts of the
* element referenced by <code>elementNum</code>,
* replacing the previous contents of the element.
*
* @param elementNum the index of the element to be set
* @param stringPart the string part of the new element
* @param imagePart the image part of the element, or <code>null</code>
* if there is no image part
*
* @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
* @throws NullPointerException if <code>stringPart</code> is
* <code>null</code>
*/
public void set(int elementNum, String stringPart, Image imagePart) {
cg.set(elementNum, stringPart, imagePart);
}
/**
* Gets a boolean value indicating whether this element is selected.
*
* @param elementNum index to element to be queried
*
* @return selection state of the element
*
* @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
*/
public boolean isSelected(int elementNum) {
return cg.isSelected(elementNum);
}
/**
* Returns the index number of an element in the <code>List</code>
* that is selected.
*
* @return index of selected element, or <code>-1</code> if none
* @see #setSelectedIndex
*/
public int getSelectedIndex() {
return cg.getSelectedIndex();
}
/**
* Queries the state of a <code>List</code> and returns the
* state of all elements
* in the
* boolean array
* <code>selectedArray_return</code>.
*
* @param selectedArray_return array to contain the results
*
* @return the number of selected elements in the <code>Choice</code>
*
* @throws IllegalArgumentException if <code>selectedArray_return</code>
* is shorter than the size of the List
* @throws NullPointerException if <code>selectedArray_return</code>
* is <code>null</code>
* @see #setSelectedFlags
*/
public int getSelectedFlags(boolean[] selectedArray_return) {
return cg.getSelectedFlags(selectedArray_return);
}
/**
* Sets the selected state of an element.
*
* @param elementNum the index of the element, starting from zero
* @param selected the state of the element, where <code>true</code> means
* selected and <code>false</code> means not selected
* @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
* @see #getSelectedIndex
*/
public void setSelectedIndex(int elementNum, boolean selected) {
cg.setSelectedIndex(elementNum, selected);
}
/**
* Sets the selected state of all elements of the <code>List</code>.
*
* @param selectedArray an array in which the method collect
* the selection status
* @throws IllegalArgumentException if <code>selectedArray</code> is
* shorter than the size of the <code>List</code>
* @throws NullPointerException if <code>selectedArray</code> is
* <code>null</code>
* @see #getSelectedFlags
*/
public void setSelectedFlags(boolean[] selectedArray) {
cg.setSelectedFlags(selectedArray);
}
/**
* The same as {@link Displayable#removeCommand Displayable.removeCommand}
* but with the following additional semantics.
*
* <p>If the command to be removed happens to be the select command, the
* <code>List</code> is set to have no select command, and the command is
* removed from the <code>List</code>.</p>
*
* <p>The following code: </P>
* <TABLE BORDER="2">
* <TR>
* <TD ROWSPAN="1" COLSPAN="1">
* <pre><code>
* // Command c is the select command on List list
* list.removeCommand(c); </code></pre>
* </TD>
* </TR>
* </TABLE>
* <P>
* is equivalent to the following code: </P>
* <TABLE BORDER="2">
* <TR>
* <TD ROWSPAN="1" COLSPAN="1">
* <pre><code>
* // Command c is the select command on List list
* list.setSelectCommand(null);
* list.removeCommand(c); </code></pre>
* </TD>
* </TR>
* </TABLE>
* @param cmd the command to be removed
*
*/
public void removeCommand(Command cmd) {
synchronized (Display.LCDUILock) {
int i = super.removeCommandImpl(cmd);
if (cmd == selectCommand) {
selectCommand = null;
}
if (i != -1) {
listLF.lRemoveCommand(cmd, i);
}
} // synchronized
}
/**
* Sets the <code>Command</code> to be used for an
* <code>IMPLICIT</code> <code>List</code> selection
* action.
* By default, an implicit selection of a List will result in the
* predefined <code>List.SELECT_COMMAND</code> being used. This
* behavior may be
* overridden by calling the <code>List.setSelectCommand()</code>
* method with an
* appropriate parameter value. If a <code>null</code> reference
* is passed, this
* indicates that no "select" action is appropriate for
* the contents
* of this <code>List</code>.
*
* <p> If a reference to a command object is passed, and
* it is not the special command <code>List.SELECT_COMMAND</code>, and
* it is not currently present on this <code>List</code> object,
* the command object is added to this <code>List</code> as if
* <code>addCommand(command)</code> had been called
* prior to the command being made the select command. This
* indicates that this command
* is to be invoked when the user performs the "select"
* on an element of
* this <code>List</code>. </p>
*
* <p> The select command should have a command type of
* <code>ITEM</code> to indicate
* that it operates on the currently selected object. It is not an error
* if the command is of some other type.
* (<code>List.SELECT_COMMAND</code> has a type
* of <code>SCREEN</code> for historical purposes.) For purposes
* of presentation and
* placement within its user interface, the implementation is allowed to
* treat the select command as if it were of type <code>ITEM</code>. </p>
*
* <p> If the select command is later removed from the <code>List</code>
* with <code>removeCommand()</code>, the <code>List</code> is set to have
* no select command as if <code>List.setSelectCommand(null)</code> had
* been called.</p>
*
* <p> The default behavior can be reestablished explicitly by calling
* <code>setSelectCommand()</code> with an argument of
* <code>List.SELECT_COMMAND</code>.</p>
*
* <p> This method has no effect if the type of the
* <code>List</code> is not <code>IMPLICIT</code>. </p>
*
* @param command the command to be used for an <code>IMPLICIT</code> list
* selection action, or <code>null</code> if there is none
*
*/
public void setSelectCommand(Command command) {
// If we're not an IMPLICIT List, ignore this method
// call entirely
if (cg.choiceType != Choice.IMPLICIT) {
return;
}
// Here we're just resetting the default behavior
// of this implicit List
if (command == List.SELECT_COMMAND) {
selectCommand = command;
return;
}
// Here we're deciding there is no appropriate default
// command for a selection
if (command == null) {
selectCommand = null;
return;
}
// SYNC NOTE: We grab the lock here because we need to determine
// if the command is in the Displayables command set AND we need
// to protect ourselves from the Command being removed from the
// set just after we've done the check. #See how we override the
// removeCommand() method in this class
synchronized (Display.LCDUILock) {
// We ensure that the provided Command has been added
// to this List.
addCommandImpl(command);
selectCommand = command;
}
}
/**
* Sets the application's preferred policy for fitting
* <code>Choice</code> element
* contents to the available screen space. The set policy applies for all
* elements of the <code>Choice</code> object. Valid values are
* {@link #TEXT_WRAP_DEFAULT}, {@link #TEXT_WRAP_ON},
* and {@link #TEXT_WRAP_OFF}. Fit policy is a hint, and the
* implementation may disregard the application's preferred policy.
*
* @param fitPolicy preferred content fit policy for choice elements
* @throws IllegalArgumentException if <code>fitPolicy</code> is invalid
* @see #getFitPolicy
*/
public void setFitPolicy(int fitPolicy) {
cg.setFitPolicy(fitPolicy);
}
/**
* Gets the application's preferred policy for fitting
* <code>Choice</code> element
* contents to the available screen space. The value returned is the
* policy that had been set by the application, even if that value had
* been disregarded by the implementation.
*
* @return one of {@link #TEXT_WRAP_DEFAULT}, {@link #TEXT_WRAP_ON}, or
* {@link #TEXT_WRAP_OFF}
* @see #setFitPolicy
*/
public int getFitPolicy() {
return cg.getFitPolicy();
}
/**
* Sets the application's preferred font for
* rendering the specified element of this <code>Choice</code>.
* An element's font is a hint, and the implementation may disregard
* the application's preferred font.
*
* <p> The <code>elementNum</code> parameter must be within the range
* <code>[0..size()-1]</code>, inclusive.</p>
*
* <p> The <code>font</code> parameter must be a valid <code>Font</code>
* object or <code>null</code>. If the <code>font</code> parameter is
* <code>null</code>, the implementation must use its default font
* to render the element.</p>
*
* @param elementNum the index of the element, starting from zero
* @param font the preferred font to use to render the element
* @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
* @see #getFont
*/
public void setFont(int elementNum, Font font) {
cg.setFont(elementNum, font);
}
/**
* Gets the application's preferred font for
* rendering the specified element of this <code>Choice</code>. The
* value returned is the font that had been set by the application,
* even if that value had been disregarded by the implementation.
* If no font had been set by the application, or if the application
* explicitly set the font to <code>null</code>, the value is the default
* font chosen by the implementation.
*
* <p> The <code>elementNum</code> parameter must be within the range
* <code>[0..size()-1]</code>, inclusive.</p>
*
* @param elementNum the index of the element, starting from zero
* @return the preferred font to use to render the element
* @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
* @see #setFont(int elementNum, Font font)
*/
public Font getFont(int elementNum) {
return cg.getFont(elementNum);
}
// package private members
// SYNC NOTE: The List constructor establishes 'cg' as non-null
// and which remains constant for the lifetime of this object.
// All public api calls are delegated to the 'cg' object and
// therefore no synchronization is necessary.
/**
* An internal choicegroup to handle the selections
*/
ChoiceGroup cg;
/**
* This is an internal Command which represents the callback
* to a selection event of an IMPLICIT list. By default, this
* command is the predefined List.SELECT_COMMAND. This can be
* overridden however using the setSelectCommand().
*/
Command selectCommand = SELECT_COMMAND;
/**
* Look & Feel object associated with this List
*/
FormLF listLF;
}
|