/*
*
*
* 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>Form</code> is a <code>Screen</code> that contains
* an arbitrary mixture of items: images,
* read-only text fields, editable text fields, editable date fields, gauges,
* choice groups, and custom items. In general, any subclass of the
* {@link Item Item} class may be contained within a form.
* The implementation handles layout, traversal, and scrolling.
* The entire contents of the <code>Form</code> scrolls together.
*
* <h2>Item Management</h2>
* <p>
* The items contained within a <code>Form</code> may be edited
* using append, delete,
* insert, and set methods. <code>Items</code> within a
* <code>Form</code> are referred to by their
* indexes, which are consecutive integers in the range from zero to
* <code>size()-1</code>,
* with zero referring to the first item and <code>size()-1</code>
* to the last item. </p>
*
* <p> An item may be placed within at most one
* <code>Form</code>. If the application
* attempts to place an item into a <code>Form</code>, and the
* item is already owned by this
* or another <code>Form</code>, an
* <code>IllegalStateException</code> is thrown.
* The application must
* remove the item from its currently containing <code>Form</code>
* before inserting it into
* the new <code>Form</code>. </p>
*
* <p> If the <code>Form</code> is visible on the display when
* changes to its contents are
* requested by the application, updates to the display take place as soon
* as it is feasible for the implementation to do so.
* Applications need not take any special action to refresh a
* <code>Form's</code> display
* after its contents have been modified. </p>
*
* <a name="layout"></a>
* <h2>Layout</h2>
*
* <p>Layout policy in <code>Form</code> is organized around
* rows. Rows are typically
* related to the width of the screen, respective of margins, scroll bars, and
* such. All rows in a particular <code>Form</code> will have the
* same width. Rows do not
* vary in width based on the <code>Items</code> contained within
* the <code>Form</code>, although they
* may all change width in certain circumstances, such as when a scroll bar
* needs to be added or removed. <code>Forms</code> generally do not scroll
* horizontally.</p>
*
* <p><code>Forms</code> grow vertically and scroll vertically as
* necessary. The height
* of a <code>Form</code> varies depending upon the number of rows
* and the height of
* each row. The height of each row is determined by the items that are
* positioned on that row. Rows need not all have the same height.
* Implementations may also vary row heights to provide proper padding or
* vertical alignment of <code>Item</code> labels.</p>
*
* <p>An implementation may choose to lay out <code>Items</code> in a
* left-to-right or right-to-left direction depending upon the language
* conventions in use. The same choice of layout direction must apply to all
* rows within a particular <code>Form</code>.</p>
*
* <p>Prior to the start of the layout algorithm, the
* <code>Form</code> is considered to
* have one empty row at the top. The layout algorithm considers each Item
* in turn, starting at <code>Item</code> zero and proceeding in
* order through each <code>Item</code>
* until the last <code>Item</code> in the <code>Form</code>
* has been processed.
* If the layout direction (as described above) is left-to-right, the
* beginning of the row is the left edge of the <code>Form</code>. If the
* layout direction is right-to-left, the beginning of the row is the right
* edge of the <code>Form</code>. <code>Items</code> are laid out at the
* beginning of each row, proceeding across each row in the chosen layout
* direction, packing as many <code>Items</code> onto each row as will fit,
* unless a condition occurs that causes the packing of a row to be terminated
* early.
* A new row is then added, and
* <code>Items</code> are packed onto it
* as described above. <code>Items</code> are packed onto rows,
* and new rows are added
* below existing rows as necessary until all <code>Items</code>
* have been processed by
* the layout algorithm.</p>
*
* <p>The layout algorithm has a concept of a <em>current alignment</em>.
* It can have the value <code>LAYOUT_LEFT</code>,
* <code>LAYOUT_CENTER</code>, or <code>LAYOUT_RIGHT</code>.
* The value of the current alignment at the start of the layout algorithm
* depends upon the layout direction in effect for this <code>Form</code>. If
* the layout direction is left-to-right, the initial alignment value must be
* <code>LAYOUT_LEFT</code>. If the layout direction is right-to-left, the
* initial alignment value must be <code>LAYOUT_RIGHT</code>.
* The current alignment changes when the layout
* algorithm encounters an <code>Item</code> that has one of the layout
* directives <code>LAYOUT_LEFT</code>, <code>LAYOUT_CENTER</code>, or
* <code>LAYOUT_RIGHT</code>. If none of these directives is present on an
* <code>Item</code>, the current layout directive does not change. This
* rule has the effect of grouping the contents of the
* <code>Form</code> into sequences of consecutive <code>Items</code>
* sharing an alignment value. The alignment value of each <code>Item</code>
* is maintained internally to the <code>Form</code> and does not affect the
* <code>Items'</code> layout value as reported by the
* {@link Item#getLayout Item.getLayout} method.</p>
*
* <p>The layout algorithm generally attempts to place an item on the same
* row as the previous item, unless certain conditions occur that cause a
* "row break." When there is a row break, the current item
* will be placed
* at the beginning of a new row instead of being placed after
* the previous item, even if there is room.</p>
*
* <p>A row break occurs before an item if any of the following
* conditions occurs:</p>
*
* <ul>
* <li>the previous item has a row break after it;</li>
* <li>it has the <code>LAYOUT_NEWLINE_BEFORE</code> directive; or</li>
* <li>it is a <code>StringItem</code> whose contents starts with
* "\n";</li>
* <li>it is a
* <code>ChoiceGroup</code>, <code>DateField</code>,
* <code>Gauge</code>, or a <code>TextField</code>, and the
* <code>LAYOUT_2</code> directive is not set; or</li>
* <li>this <code>Item</code> has a <code>LAYOUT_LEFT</code>,
* <code>LAYOUT_CENTER</code>, or <code>LAYOUT_RIGHT</code> directive
* that differs from the <code>Form's</code> current alignment.</li>
* </ul>
*
* <p>A row break occurs after an item if any of the following
* conditions occurs:</p>
*
* <ul>
* <li>it is a <code>StringItem</code> whose contents ends with
* "\n"; or</li>
* <li>it has the <code>LAYOUT_NEWLINE_AFTER</code> directive; or</li>
* <li>it is a
* <code>ChoiceGroup</code>, <code>DateField</code>,
* <code>Gauge</code>, or a <code>TextField</code>, and the
* <code>LAYOUT_2</code> directive is not set.</li>
* </ul>
*
* <p>The presence of the <code>LAYOUT_NEWLINE_BEFORE</code> or
* <code>LAYOUT_NEWLINE_AFTER</code> directive does not cause
* an additional row break if there is one already present. For example,
* if a <code>LAYOUT_NEWLINE_BEFORE</code> directive appears on a
* <code>StringItem</code> whose contents starts with "\n",
* there is only a single row break. A similar rule applies with a
* trailing "\n" and <code>LAYOUT_NEWLINE_AFTER</code>.
* Also, there is only a single row
* break if an item has the <code>LAYOUT_NEWLINE_AFTER</code> directive
* and the next item has the <code>LAYOUT_NEWLINE_BEFORE</code> directive.
* However, the presence of consecutive "\n" characters,
* either within a single <code>StringItem</code> or in adjacent
* <code>StringItems</code>, will cause as many row breaks as there are
* "\n" characters. This will cause empty rows to be present.
* The height of an empty row is determined by the prevailing font height of
* the <code>StringItem</code> within which the "\n" that ends the
* row occurs.</p>
*
* <p>Implementations may provide additional conditions under which a row
* break occurs. For example, an implementation's layout policy may lay out
* labels specially, implicitly causing a break before every
* <code>Item</code> that has a
* label. Or, as another example, a particular implementation's user
* interface style may dictate that a DateField item always appears on a row
* by itself. In this case, this implementation may cause row breaks to occur
* both before and after every <code>DateField</code> item.</p>
*
* <p>Given two items with adjacent <code>Form</code> indexes, if
* none of the specified
* or implementation-specific conditions for a row break between them
* occurs, and if space permits, these items should be placed on the same
* row.</p>
*
* <p>When packing <code>Items</code> onto a row, the width of the
* item is compared with
* the remaining space on the row. For this purpose, the width used is the
* <code>Item's</code> preferred width, unless the
* <code>Item</code> has the <code>LAYOUT_SHRINK</code>
* directive,
* in which case the <code>Item's</code> minimum width is used. If
* the <code>Item</code> is too wide
* to fit in the space remaining on the row, the row is considered to be
* full, a new row is added beneath this one, and the
* <code>Item</code> is laid out on
* this new row.</p>
*
* <p>Once the contents of a row have been determined, the space available on
* the row is distributed by expanding items and by adding space between
* items. If any items on this row have the
* <code>LAYOUT_SHRINK</code> directive (that is,
* they are shrinkable), space is first distributed to these items. Space is
* distributed to each of these items proportionally to the difference between
* the each <code>Item's</code> preferred size and its minimum
* size. At this stage, no
* shrinkable item is expanded beyond its preferred width.</p>
*
* <p>For example, consider a row that has <code>30</code> pixels
* of space available and
* that has two shrinkable items <code>A</code> and
* <code>B</code>. Item <code>A's</code> preferred size is
* <code>15</code> and
* its minimum size is <code>10</code>. Item <code>B's</code>
* preferred size is <code>30</code> and its minimum
* size is <code>20</code>. The difference between
* <code>A's</code> preferred and minimum size is
* <code>5</code>,
* and <code>B's</code> difference is <code>10</code>. The
* <code>30</code> pixels are distributed to these items
* proportionally to these differences. Therefore, <code>10</code>
* pixels are
* distributed to item <code>A</code> and <code>20</code>
* pixels to item <code>B</code>.</p>
*
* <p>If after expanding all the shrinkable items to their preferred widths,
* there is still space left on the row, this remaining space is distributed
* equally among the Items that have the
* <code>LAYOUT_EXPAND</code> directive (the
* stretchable <code>Items</code>). The presence of any
* stretchable items on a row will
* cause the <code>Items</code> on this row to occupy the full
* width of the row.</p>
*
* <p>If there are no stretchable items on this row, and there is still space
* available on this row, the <code>Items</code> are packed as tightly as
* possible and are placed on the row according to the alignment value shared
* by the <code>Items</code> on this row. (Since changing the current
* alignment causes a row break, all <code>Items</code> on the same row must
* share the same alignment value.) If the alignment value is
* <code>LAYOUT_LEFT</code>, the <code>Items</code> are positioned at the left
* end of the row and the remaining space is placed at the right end of the
* row. If the alignment value is <code>LAYOUT_RIGHT</code>, the
* <code>Items</code> are positioned at the right end of the row and the
* remaining space is placed at the left end of the row. If the alignment
* value is <code>LAYOUT_CENTER</code>, the <code>Items</code> are positioned
* in the middle of the row such that the remaining space on the row is
* divided evenly between the left and right ends of the row.</p>
*
* <p>Given the set of items on a particular row, the heights of these
* <code>Items</code> are inspected. For each <code>Item</code>, the height
* that is used is the preferred height, unless the <code>Item</code> has the
* <code>LAYOUT_VSHRINK</code> directive, in which case the
* <code>Item's</code> minimum height is used.
* The height of the tallest
* <code>Item</code> determines the
* height of the row. <code>Items</code> that have the
* <code>LAYOUT_VSHRINK</code> directive are expanded to their preferred
* height or to the height of the row, whichever is smaller.
* <code>Items</code> that are still shorter than the
* row height and that
* have the <code>LAYOUT_VEXPAND</code> directive will expand to
* the height of the row.
* The <code>LAYOUT_VEXPAND</code> directive on an item will never
* increase the height
* of a row.</p>
*
* <p>Remaining <code>Items</code> shorter than the row height
* will be positioned
* vertically within the row using the <code>LAYOUT_TOP</code>,
* <code>LAYOUT_BOTTOM</code>, and
* <code>LAYOUT_VCENTER</code> directives. If no vertical layout directive is
* specified, the item must be aligned along the bottom of the row.</p>
*
* <p><code>StringItems</code> are treated specially in the above
* algorithm. If the
* contents of a <code>StringItem</code> (its string value,
* exclusive of its label) contain
* a newline character ("\n"), the string should be split at
* that point and
* the remainder laid out starting on the next row.</p>
*
* <p>If one or both dimensions of the preferred size of
* a <code>StringItem</code> have been locked, the <code>StringItem</code>
* is wrapped to fit that width and height and is treated as a
* rectangle whose minimum and preferred width and height are the width and
* height of this rectangle. In this case, the
* <code>LAYOUT_SHRINK</code>, <code>LAYOUT_EXPAND</code>,
* and <code>LAYOUT_VEXPAND</code> directives are ignored.</p>
*
* <p>If both dimensions of the preferred size of a <code>StringItem</code>
* are unlocked, the text from the <code>StringItem</code> may be wrapped
* across multiple rows. At the point in the layout algorithm where the width
* of the <code>Item</code> is compared to the remaining space on the row, as
* much text is taken from the beginning of the <code>StringItem</code> as
* will fit onto the current row. The contents of this row are then
* positioned according to the current alignment value. The remainder of the
* text in the <code>StringItem</code> is line-wrapped to the full width of as
* many new rows as are necessary to accommodate the text. Each full row is
* positioned according to the current alignment value. The last line of the
* text might leave space available on its row. If there is no row break
* following this <code>StringItem</code>, subsequent <code>Items</code> are
* packed into the remaining space and the contents of the row are positioned
* according to the current alignment value. This rule has the effect of
* displaying the contents of a <code>StringItem</code> as a paragraph of text
* set flush-left, flush-right, or centered, depending upon whether the
* current alignment value is <code>LAYOUT_LEFT</code>,
* <code>LAYOUT_RIGHT</code>, or <code>LAYOUT_CENTER</code>, respectively.
* The preferred width and height of a <code>StringItem</code> wrapped across
* multiple rows, as reported by the
* {@link Item#getPreferredWidth Item.getPreferredWidth} and
* {@link Item#getPreferredHeight Item.getPreferredHeight}
* methods, describe the width and height of the bounding rectangle of the
* wrapped text.</p>
*
* <p><code>ImageItems</code> are also treated specially by the above
* algorithm. The foregoing rules concerning the horizontal alignment value
* and the <code>LAYOUT_LEFT</code>, <code>LAYOUT_RIGHT</code>, and
* <code>LAYOUT_CENTER</code> directives, apply to <code>ImageItems</code>
* only when the <code>LAYOUT_2</code> directive is also present on that item.
* If the <code>LAYOUT_2</code> directive is not present on an
* <code>ImageItem</code>, the behavior of the <code>LAYOUT_LEFT</code>,
* <code>LAYOUT_RIGHT</code>, and <code>LAYOUT_CENTER</code> directives is
* implementation-specific.</p>
*
* <p>A <code>Form's</code> layout is recomputed automatically as
* necessary. This may
* occur because of a change in an <code>Item's</code> size caused
* by a change in its
* contents or because of a request by the application to change the Item's
* preferred size. It may also occur if an <code>Item's</code>
* layout directives are
* changed by the application. The application does not need to perform
* any specific action to cause the <code>Form's</code> layout to
* be updated.</p>
*
* <h2><a NAME="linebreak">Line Breaks and Wrapping</a></h2>
*
* <p>For all cases where text is wrapped,
* line breaks must occur at each newline character
* (<code>'\n'</code> = Unicode <code>'U+000A'</code>).
* If space does not permit
* the full text to be displayed it is truncated at line breaks.
* If there are no suitable line breaks, it is recommended that
* implementations break text at word boundaries.
* If there are no word boundaries, it is recommended that
* implementations break text at character boundaries. </p>
*
* <p>Labels that contain line breaks may be truncated at the line
* break and cause the rest of the label not to be shown.</p>
*
* <h2>User Interaction</h2>
*
* <p> When a <code>Form</code> is present on the display the user
* can interact
* with it and its <code>Items</code> indefinitely (for instance,
* traversing from <code>Item</code>
* to <code>Item</code>
* and possibly
* scrolling). These traversing and scrolling operations do not cause
* application-visible events. The system notifies
* the application when the user modifies the state of an interactive
* <code>Item</code>
* contained within the <code>Form</code>. This notification is
* accomplished by calling the
* {@link ItemStateListener#itemStateChanged itemStateChanged()}
* method of the listener declared to the <code>Form</code> with the
* {@link #setItemStateListener setItemStateListener()} method. </p>
*
* <p> As with other <code>Displayable</code> objects, a
* <code>Form</code> can declare
* {@link Command commands} and declare a command listener with the
* {@link Displayable#setCommandListener setCommandListener()} method.
* {@link CommandListener CommandListener}
* objects are distinct from
* {@link ItemStateListener ItemStateListener} objects, and they are declared
* and invoked separately. </p>
*
* <h2>Notes for Application Developers</h2>
*
* <UL>
* <LI>Although this class allows creation of arbitrary combination of
* components
* the application developers should keep the small screen size in mind.
* <code>Form</code> is designed to contain a <em>small number of
* closely related</em>
* UI elements. </LI>
*
* <LI>If the number of items does not fit on the screen, the
* implementation may choose to make it scrollable or to fold some components
* so that a separate screen appears when the element is edited.</LI>
* </UL>
*
* <p>
* </p>
*
* @see Item
* @since MIDP 1.0
*/
public class Form extends Screen {
// ************************************************************
// Static initializer, constructor
// ************************************************************
/**
* Creates a new, empty <code>Form</code>.
*
* @param title the <code>Form's</code> title, or
* <code>null</code> for no title
*/
public Form(String title) {
this(title, null);
}
/**
* Creates a new <code>Form</code> with the specified
* contents. This is identical to
* creating an empty <code>Form</code> and then using a set of
* <code>append</code>
* methods. The
* items array may be <code>null</code>, in which case the
* <code>Form</code> is created empty. If
* the items array is non-null, each element must be a valid
* <code>Item</code> not
* already contained within another <code>Form</code>.
*
* @param title the <code>Form's</code> title string
* @param items the array of items to be placed in the
* <code>Form</code>, or <code>null</code> if there are no
* items
* @throws IllegalStateException if one of the items is already owned by
* another container
* @throws NullPointerException if an element of the items array is
* <code>null</code>
*/
public Form(String title, Item[] items) {
super(title);
synchronized (Display.LCDUILock) {
if (items == null) {
this.items = new Item[GROW_SIZE];
// numOfItems was initialized to 0
// so there is no need to update it
} else {
this.items = new Item[items.length > GROW_SIZE ?
items.length : GROW_SIZE];
// We have to check all items first so that some
// items would not be added to a form that was not
// instantiated
for (int i = 0; i < items.length; i++) {
// NullPointerException will be thrown by
// items[i].owner if items[i] == null;
if (items[i].owner != null) {
throw new IllegalStateException();
}
}
numOfItems = items.length;
for (int i = 0; i < numOfItems; i++) {
items[i].lSetOwner(this);
this.items[i] = items[i];
}
}
displayableLF = formLF = LFFactory.getFactory().getFormLF(this);
} // synchronized
}
// ************************************************************
// public methods
// ************************************************************
/**
* Adds an <code>Item</code> into the <code>Form</code>. The newly
* added <code>Item</code> becomes the last <code>Item</code> in the
* <code>Form</code>, and the size of the <code>Form</code> grows
* by one.
*
* @param item the {@link Item Item} to be added.
* @return the assigned index of the <code>Item</code>
* @throws IllegalStateException if the item is already owned by
* a container
* @throws NullPointerException if item is <code>null</code>
*/
public int append(Item item) {
synchronized (Display.LCDUILock) {
// NullPointerException will be thrown
// by item.owner if item == null
if (item.owner != null) {
throw new IllegalStateException();
}
int i = insertImpl(numOfItems, item);
formLF.lInsert(i, item);
return i;
}
}
/**
* Adds an item consisting of one <code>String</code> to the
* <code>Form</code>. The effect of
* this method is identical to
*
* <p> <code>
* append(new StringItem(null, str))
* </code> </p>
*
* @param str the <code>String</code> to be added
* @return the assigned index of the <code>Item</code>
* @throws NullPointerException if str is <code>null</code>
*/
public int append(String str) {
if (str == null) {
throw new NullPointerException();
}
synchronized (Display.LCDUILock) {
Item item = new StringItem(null, str);
int i = insertImpl(numOfItems, item);
formLF.lInsert(i, item);
return i;
}
}
/**
* Adds an item consisting of one <code>Image</code> to the
* <code>Form</code>. The effect of
* this method is identical to
*
* <p> <code>
* append(new ImageItem(null, img, ImageItem.LAYOUT_DEFAULT, null))
* </code> </p>
*
* @param img the image to be added
* @return the assigned index of the <code>Item</code>
* @throws NullPointerException if <code>img</code> is <code>null</code>
*/
public int append(Image img) {
if (img == null) {
throw new NullPointerException();
}
synchronized (Display.LCDUILock) {
Item item = new ImageItem(null, img,
ImageItem.LAYOUT_DEFAULT, null);
int i = insertImpl(numOfItems, item);
formLF.lInsert(i, item);
return i;
}
}
/**
* Inserts an item into the <code>Form</code> just prior to
* the item specified.
* The size of the <code>Form</code> grows by one. The
* <code>itemNum</code> parameter must be
* within the range <code>[0..size()]</code>, inclusive.
* The index of the last item is <code>size()-1</code>, and
* so there is actually no item whose index is
* <code>size()</code>. If this value
* is used for <code>itemNum</code>, the new item is inserted
* immediately after
* the last item. In this case, the effect is identical to
* {@link #append(Item) append(Item)}.
*
* <p> The semantics are otherwise identical to
* {@link #append(Item) append(Item)}. </p>
*
* @param itemNum the index where insertion is to occur
* @param item the item to be inserted
* @throws IndexOutOfBoundsException if <code>itemNum</code> is invalid
* @throws IllegalStateException if the item is already owned by
* a container
* @throws NullPointerException if <code>item</code> is
* <code>null</code>
*/
public void insert(int itemNum, Item item) {
synchronized (Display.LCDUILock) {
// NullPointerException will be thrown
// by item.owner if item == null
if (item.owner != null) {
throw new IllegalStateException();
}
if (itemNum < 0 || itemNum > numOfItems) {
throw new IndexOutOfBoundsException();
}
insertImpl(itemNum, item);
formLF.lInsert(itemNum, item);
}
}
/**
* Deletes the <code>Item</code> referenced by
* <code>itemNum</code>. The size of the <code>Form</code>
* shrinks by one. It is legal to delete all items from a
* <code>Form</code>.
* The <code>itemNum</code> parameter must be
* within the range <code>[0..size()-1]</code>, inclusive.
*
* @param itemNum the index of the item to be deleted
* @throws IndexOutOfBoundsException if <code>itemNum</code> is invalid
*/
public void delete(int itemNum) {
Item deletedItem;
synchronized (Display.LCDUILock) {
if (itemNum < 0 || itemNum >= numOfItems) {
throw new IndexOutOfBoundsException();
}
deletedItem = items[itemNum];
numOfItems--;
if (itemNum < numOfItems) {
System.arraycopy(items, itemNum + 1, items, itemNum,
numOfItems - itemNum);
}
// Delete reference to the last item
// that was left after array copy
items[numOfItems] = null;
// The Form is clear; reset its state
if (numOfItems == 0 && items.length > GROW_SIZE) {
items = new Item[GROW_SIZE]; // start fresh
}
formLF.lDelete(itemNum, deletedItem);
} // synchronized
deletedItem.itemDeleted();
}
/**
* Deletes all the items from this <code>Form</code>, leaving
* it with zero items.
* This method does nothing if the <code>Form</code> is already empty.
*
*/
public void deleteAll() {
Item[] itemsCopy;
synchronized (Display.LCDUILock) {
if (numOfItems == 0) {
return;
}
itemsCopy = new Item[numOfItems];
for (int x = 0; x < numOfItems; x++) {
itemsCopy[x] = items[x];
items[x] = null;
}
if (items.length > GROW_SIZE) {
items = new Item[GROW_SIZE]; // start fresh
}
// Reset form state
numOfItems = 0;
formLF.lDeleteAll();
}
for (int x = 0; x < itemsCopy.length; x++) {
itemsCopy[x].itemDeleted();
}
}
/**
* Sets the item referenced by <code>itemNum</code> to the
* specified item,
* replacing the previous item. The previous item is removed
* from this <code>Form</code>.
* The <code>itemNum</code> parameter must be
* within the range <code>[0..size()-1]</code>, inclusive.
*
* <p>The end result is equal to
* <code>insert(n, item); delete(n+1);</code><br>
* although the implementation may optimize the repainting
* and usage of the array that stores the items. <P>
*
* @param itemNum the index of the item to be replaced
* @param item the new item to be placed in the <code>Form</code>
*
* @throws IndexOutOfBoundsException if <code>itemNum</code> is invalid
* @throws IllegalStateException if the item is already owned by
* a container
* @throws NullPointerException if <code>item</code> is
* <code>null</code>
*/
public void set(int itemNum, Item item) {
synchronized (Display.LCDUILock) {
// NullPointerException will be thrown
// by item.owner if item == null
if (item.owner != null) {
throw new IllegalStateException();
}
if (itemNum < 0 || itemNum >= numOfItems) {
throw new IndexOutOfBoundsException();
}
items[itemNum].lSetOwner(null);
item.lSetOwner(this);
items[itemNum] = item;
formLF.lSet(itemNum, item);
}
}
/**
* Gets the item at given position. The contents of the
* <code>Form</code> are left
* unchanged.
* The <code>itemNum</code> parameter must be
* within the range <code>[0..size()-1]</code>, inclusive.
*
* @param itemNum the index of item
*
* @return the item at the given position
*
* @throws IndexOutOfBoundsException if <code>itemNum</code> is invalid
*/
public Item get(int itemNum) {
synchronized (Display.LCDUILock) {
if (itemNum < 0 || itemNum >= numOfItems) {
throw new IndexOutOfBoundsException();
}
return items[itemNum];
}
}
/**
* Sets the <code>ItemStateListener</code> for the
* <code>Form</code>, replacing any previous
* <code>ItemStateListener</code>. If
* <code>iListener</code> is <code>null</code>, simply
* removes the previous <code>ItemStateListener</code>.
* @param iListener the new listener, or <code>null</code> to remove it
*/
public void setItemStateListener(ItemStateListener iListener) {
synchronized (Display.LCDUILock) {
itemStateListener = iListener;
}
}
/**
* Gets the number of items in the <code>Form</code>.
* @return the number of items
*/
public int size() {
synchronized (Display.LCDUILock) {
return numOfItems;
}
}
/**
* Returns the width in pixels of the displayable area available for items.
* The value may depend on how the device uses the screen and may be
* affected by the presence or absence of the ticker, title, or commands.
* The <code>Items</code> of the <code>Form</code> are
* laid out to fit within this width.
* @return the width of the <code>Form</code> in pixels
*/
public int getWidth() {
synchronized (Display.LCDUILock) {
return formLF.lGetWidth();
}
}
/**
* Returns the height in pixels of the displayable area available
* for items.
* This value is the height of the form that can be displayed without
* scrolling.
* The value may depend on how the device uses the screen and may be
* affected by the presence or absence of the ticker, title, or commands.
* @return the height of the displayable area of the
* <code>Form</code> in pixels
*/
public int getHeight() {
synchronized (Display.LCDUILock) {
return formLF.lGetHeight();
}
}
// ************************************************************
// protected methods
// ************************************************************
// ************************************************************
// package private methods
// ************************************************************
/**
* Retrieve the ItemStateListener for this Form.
* NOTE: calls to this method should only occur from within
* a lock on LCDUILock.
*
* @return ItemStateListener The ItemStateListener of this Form,
* null if there isn't one set
*/
ItemStateListener getItemStateListener() {
return itemStateListener;
}
/**
* Used by the event handler to notify the ItemStateListener
* of a change in the given item
*
* @param item the Item which state was changed
*/
void uCallItemStateChanged(Item item) {
// get a copy of the object reference to ItemStateListener
ItemStateListener isl = itemStateListener;
if (isl == null || item == null) {
return;
}
// Protect from any unexpected application exceptions
try {
// SYNC NOTE: We lock on calloutLock around any calls
// into application code
synchronized (Display.calloutLock) {
isl.itemStateChanged(item);
}
} catch (Throwable thr) {
Display.handleThrowable(thr);
}
}
// ************************************************************
// private methods
// ************************************************************
/**
* Insert an Item into this Form
*
* @param itemNum The index into the Item array to insert this Item
* @param item The Item to insert
* @return int The index at which the newly inserted Item can be found
*/
private int insertImpl(int itemNum, Item item) {
if (items.length == numOfItems) {
Item newItems[] = new Item[numOfItems + GROW_SIZE];
System.arraycopy(items, 0, newItems, 0, itemNum);
System.arraycopy(items, itemNum, newItems, itemNum + 1,
numOfItems - itemNum);
items = newItems;
} else {
// if we're not appending
if (itemNum != numOfItems) {
System.arraycopy(items, itemNum, items, itemNum + 1,
numOfItems - itemNum);
}
}
numOfItems++;
//
// the arraycopy copied the reference to the item at this
// spot. if we call setImpl without setting the index to null
// setImpl will set the items owner to null.
//
item.lSetOwner(this);
items[itemNum] = item;
return itemNum;
}
// ************************************************************
// public member variables
// ************************************************************
// ************************************************************
// protected member variables
// ************************************************************
// ************************************************************
// package private member variables
// ************************************************************
/** Array of Items that were added to this form. */
Item items[];
/** The number of actual Items added is numOfItems. */
int numOfItems; // = 0;
/** The Form look&feel object associated with this Form */
FormLF formLF;
// ************************************************************
// private member variables
// ************************************************************
/**
* This is the rate at which the internal array of Items grows if
* it gets filled up
*/
private static final int GROW_SIZE = 4;
/** itemStateListener that has to be notified of any state changes */
private ItemStateListener itemStateListener;
} // class Form
|