001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package javax.microedition.lcdui;
028:
029: /**
030: * A <code>Form</code> is a <code>Screen</code> that contains
031: * an arbitrary mixture of items: images,
032: * read-only text fields, editable text fields, editable date fields, gauges,
033: * choice groups, and custom items. In general, any subclass of the
034: * {@link Item Item} class may be contained within a form.
035: * The implementation handles layout, traversal, and scrolling.
036: * The entire contents of the <code>Form</code> scrolls together.
037: *
038: * <h2>Item Management</h2>
039: * <p>
040: * The items contained within a <code>Form</code> may be edited
041: * using append, delete,
042: * insert, and set methods. <code>Items</code> within a
043: * <code>Form</code> are referred to by their
044: * indexes, which are consecutive integers in the range from zero to
045: * <code>size()-1</code>,
046: * with zero referring to the first item and <code>size()-1</code>
047: * to the last item. </p>
048: *
049: * <p> An item may be placed within at most one
050: * <code>Form</code>. If the application
051: * attempts to place an item into a <code>Form</code>, and the
052: * item is already owned by this
053: * or another <code>Form</code>, an
054: * <code>IllegalStateException</code> is thrown.
055: * The application must
056: * remove the item from its currently containing <code>Form</code>
057: * before inserting it into
058: * the new <code>Form</code>. </p>
059: *
060: * <p> If the <code>Form</code> is visible on the display when
061: * changes to its contents are
062: * requested by the application, updates to the display take place as soon
063: * as it is feasible for the implementation to do so.
064: * Applications need not take any special action to refresh a
065: * <code>Form's</code> display
066: * after its contents have been modified. </p>
067: *
068: * <a name="layout"></a>
069: * <h2>Layout</h2>
070: *
071: * <p>Layout policy in <code>Form</code> is organized around
072: * rows. Rows are typically
073: * related to the width of the screen, respective of margins, scroll bars, and
074: * such. All rows in a particular <code>Form</code> will have the
075: * same width. Rows do not
076: * vary in width based on the <code>Items</code> contained within
077: * the <code>Form</code>, although they
078: * may all change width in certain circumstances, such as when a scroll bar
079: * needs to be added or removed. <code>Forms</code> generally do not scroll
080: * horizontally.</p>
081: *
082: * <p><code>Forms</code> grow vertically and scroll vertically as
083: * necessary. The height
084: * of a <code>Form</code> varies depending upon the number of rows
085: * and the height of
086: * each row. The height of each row is determined by the items that are
087: * positioned on that row. Rows need not all have the same height.
088: * Implementations may also vary row heights to provide proper padding or
089: * vertical alignment of <code>Item</code> labels.</p>
090: *
091: * <p>An implementation may choose to lay out <code>Items</code> in a
092: * left-to-right or right-to-left direction depending upon the language
093: * conventions in use. The same choice of layout direction must apply to all
094: * rows within a particular <code>Form</code>.</p>
095: *
096: * <p>Prior to the start of the layout algorithm, the
097: * <code>Form</code> is considered to
098: * have one empty row at the top. The layout algorithm considers each Item
099: * in turn, starting at <code>Item</code> zero and proceeding in
100: * order through each <code>Item</code>
101: * until the last <code>Item</code> in the <code>Form</code>
102: * has been processed.
103: * If the layout direction (as described above) is left-to-right, the
104: * beginning of the row is the left edge of the <code>Form</code>. If the
105: * layout direction is right-to-left, the beginning of the row is the right
106: * edge of the <code>Form</code>. <code>Items</code> are laid out at the
107: * beginning of each row, proceeding across each row in the chosen layout
108: * direction, packing as many <code>Items</code> onto each row as will fit,
109: * unless a condition occurs that causes the packing of a row to be terminated
110: * early.
111: * A new row is then added, and
112: * <code>Items</code> are packed onto it
113: * as described above. <code>Items</code> are packed onto rows,
114: * and new rows are added
115: * below existing rows as necessary until all <code>Items</code>
116: * have been processed by
117: * the layout algorithm.</p>
118: *
119: * <p>The layout algorithm has a concept of a <em>current alignment</em>.
120: * It can have the value <code>LAYOUT_LEFT</code>,
121: * <code>LAYOUT_CENTER</code>, or <code>LAYOUT_RIGHT</code>.
122: * The value of the current alignment at the start of the layout algorithm
123: * depends upon the layout direction in effect for this <code>Form</code>. If
124: * the layout direction is left-to-right, the initial alignment value must be
125: * <code>LAYOUT_LEFT</code>. If the layout direction is right-to-left, the
126: * initial alignment value must be <code>LAYOUT_RIGHT</code>.
127: * The current alignment changes when the layout
128: * algorithm encounters an <code>Item</code> that has one of the layout
129: * directives <code>LAYOUT_LEFT</code>, <code>LAYOUT_CENTER</code>, or
130: * <code>LAYOUT_RIGHT</code>. If none of these directives is present on an
131: * <code>Item</code>, the current layout directive does not change. This
132: * rule has the effect of grouping the contents of the
133: * <code>Form</code> into sequences of consecutive <code>Items</code>
134: * sharing an alignment value. The alignment value of each <code>Item</code>
135: * is maintained internally to the <code>Form</code> and does not affect the
136: * <code>Items'</code> layout value as reported by the
137: * {@link Item#getLayout Item.getLayout} method.</p>
138: *
139: * <p>The layout algorithm generally attempts to place an item on the same
140: * row as the previous item, unless certain conditions occur that cause a
141: * "row break." When there is a row break, the current item
142: * will be placed
143: * at the beginning of a new row instead of being placed after
144: * the previous item, even if there is room.</p>
145: *
146: * <p>A row break occurs before an item if any of the following
147: * conditions occurs:</p>
148: *
149: * <ul>
150: * <li>the previous item has a row break after it;</li>
151: * <li>it has the <code>LAYOUT_NEWLINE_BEFORE</code> directive; or</li>
152: * <li>it is a <code>StringItem</code> whose contents starts with
153: * "\n";</li>
154: * <li>it is a
155: * <code>ChoiceGroup</code>, <code>DateField</code>,
156: * <code>Gauge</code>, or a <code>TextField</code>, and the
157: * <code>LAYOUT_2</code> directive is not set; or</li>
158: * <li>this <code>Item</code> has a <code>LAYOUT_LEFT</code>,
159: * <code>LAYOUT_CENTER</code>, or <code>LAYOUT_RIGHT</code> directive
160: * that differs from the <code>Form's</code> current alignment.</li>
161: * </ul>
162: *
163: * <p>A row break occurs after an item if any of the following
164: * conditions occurs:</p>
165: *
166: * <ul>
167: * <li>it is a <code>StringItem</code> whose contents ends with
168: * "\n"; or</li>
169: * <li>it has the <code>LAYOUT_NEWLINE_AFTER</code> directive; or</li>
170: * <li>it is a
171: * <code>ChoiceGroup</code>, <code>DateField</code>,
172: * <code>Gauge</code>, or a <code>TextField</code>, and the
173: * <code>LAYOUT_2</code> directive is not set.</li>
174: * </ul>
175: *
176: * <p>The presence of the <code>LAYOUT_NEWLINE_BEFORE</code> or
177: * <code>LAYOUT_NEWLINE_AFTER</code> directive does not cause
178: * an additional row break if there is one already present. For example,
179: * if a <code>LAYOUT_NEWLINE_BEFORE</code> directive appears on a
180: * <code>StringItem</code> whose contents starts with "\n",
181: * there is only a single row break. A similar rule applies with a
182: * trailing "\n" and <code>LAYOUT_NEWLINE_AFTER</code>.
183: * Also, there is only a single row
184: * break if an item has the <code>LAYOUT_NEWLINE_AFTER</code> directive
185: * and the next item has the <code>LAYOUT_NEWLINE_BEFORE</code> directive.
186: * However, the presence of consecutive "\n" characters,
187: * either within a single <code>StringItem</code> or in adjacent
188: * <code>StringItems</code>, will cause as many row breaks as there are
189: * "\n" characters. This will cause empty rows to be present.
190: * The height of an empty row is determined by the prevailing font height of
191: * the <code>StringItem</code> within which the "\n" that ends the
192: * row occurs.</p>
193: *
194: * <p>Implementations may provide additional conditions under which a row
195: * break occurs. For example, an implementation's layout policy may lay out
196: * labels specially, implicitly causing a break before every
197: * <code>Item</code> that has a
198: * label. Or, as another example, a particular implementation's user
199: * interface style may dictate that a DateField item always appears on a row
200: * by itself. In this case, this implementation may cause row breaks to occur
201: * both before and after every <code>DateField</code> item.</p>
202: *
203: * <p>Given two items with adjacent <code>Form</code> indexes, if
204: * none of the specified
205: * or implementation-specific conditions for a row break between them
206: * occurs, and if space permits, these items should be placed on the same
207: * row.</p>
208: *
209: * <p>When packing <code>Items</code> onto a row, the width of the
210: * item is compared with
211: * the remaining space on the row. For this purpose, the width used is the
212: * <code>Item's</code> preferred width, unless the
213: * <code>Item</code> has the <code>LAYOUT_SHRINK</code>
214: * directive,
215: * in which case the <code>Item's</code> minimum width is used. If
216: * the <code>Item</code> is too wide
217: * to fit in the space remaining on the row, the row is considered to be
218: * full, a new row is added beneath this one, and the
219: * <code>Item</code> is laid out on
220: * this new row.</p>
221: *
222: * <p>Once the contents of a row have been determined, the space available on
223: * the row is distributed by expanding items and by adding space between
224: * items. If any items on this row have the
225: * <code>LAYOUT_SHRINK</code> directive (that is,
226: * they are shrinkable), space is first distributed to these items. Space is
227: * distributed to each of these items proportionally to the difference between
228: * the each <code>Item's</code> preferred size and its minimum
229: * size. At this stage, no
230: * shrinkable item is expanded beyond its preferred width.</p>
231: *
232: * <p>For example, consider a row that has <code>30</code> pixels
233: * of space available and
234: * that has two shrinkable items <code>A</code> and
235: * <code>B</code>. Item <code>A's</code> preferred size is
236: * <code>15</code> and
237: * its minimum size is <code>10</code>. Item <code>B's</code>
238: * preferred size is <code>30</code> and its minimum
239: * size is <code>20</code>. The difference between
240: * <code>A's</code> preferred and minimum size is
241: * <code>5</code>,
242: * and <code>B's</code> difference is <code>10</code>. The
243: * <code>30</code> pixels are distributed to these items
244: * proportionally to these differences. Therefore, <code>10</code>
245: * pixels are
246: * distributed to item <code>A</code> and <code>20</code>
247: * pixels to item <code>B</code>.</p>
248: *
249: * <p>If after expanding all the shrinkable items to their preferred widths,
250: * there is still space left on the row, this remaining space is distributed
251: * equally among the Items that have the
252: * <code>LAYOUT_EXPAND</code> directive (the
253: * stretchable <code>Items</code>). The presence of any
254: * stretchable items on a row will
255: * cause the <code>Items</code> on this row to occupy the full
256: * width of the row.</p>
257: *
258: * <p>If there are no stretchable items on this row, and there is still space
259: * available on this row, the <code>Items</code> are packed as tightly as
260: * possible and are placed on the row according to the alignment value shared
261: * by the <code>Items</code> on this row. (Since changing the current
262: * alignment causes a row break, all <code>Items</code> on the same row must
263: * share the same alignment value.) If the alignment value is
264: * <code>LAYOUT_LEFT</code>, the <code>Items</code> are positioned at the left
265: * end of the row and the remaining space is placed at the right end of the
266: * row. If the alignment value is <code>LAYOUT_RIGHT</code>, the
267: * <code>Items</code> are positioned at the right end of the row and the
268: * remaining space is placed at the left end of the row. If the alignment
269: * value is <code>LAYOUT_CENTER</code>, the <code>Items</code> are positioned
270: * in the middle of the row such that the remaining space on the row is
271: * divided evenly between the left and right ends of the row.</p>
272: *
273: * <p>Given the set of items on a particular row, the heights of these
274: * <code>Items</code> are inspected. For each <code>Item</code>, the height
275: * that is used is the preferred height, unless the <code>Item</code> has the
276: * <code>LAYOUT_VSHRINK</code> directive, in which case the
277: * <code>Item's</code> minimum height is used.
278: * The height of the tallest
279: * <code>Item</code> determines the
280: * height of the row. <code>Items</code> that have the
281: * <code>LAYOUT_VSHRINK</code> directive are expanded to their preferred
282: * height or to the height of the row, whichever is smaller.
283: * <code>Items</code> that are still shorter than the
284: * row height and that
285: * have the <code>LAYOUT_VEXPAND</code> directive will expand to
286: * the height of the row.
287: * The <code>LAYOUT_VEXPAND</code> directive on an item will never
288: * increase the height
289: * of a row.</p>
290: *
291: * <p>Remaining <code>Items</code> shorter than the row height
292: * will be positioned
293: * vertically within the row using the <code>LAYOUT_TOP</code>,
294: * <code>LAYOUT_BOTTOM</code>, and
295: * <code>LAYOUT_VCENTER</code> directives. If no vertical layout directive is
296: * specified, the item must be aligned along the bottom of the row.</p>
297: *
298: * <p><code>StringItems</code> are treated specially in the above
299: * algorithm. If the
300: * contents of a <code>StringItem</code> (its string value,
301: * exclusive of its label) contain
302: * a newline character ("\n"), the string should be split at
303: * that point and
304: * the remainder laid out starting on the next row.</p>
305: *
306: * <p>If one or both dimensions of the preferred size of
307: * a <code>StringItem</code> have been locked, the <code>StringItem</code>
308: * is wrapped to fit that width and height and is treated as a
309: * rectangle whose minimum and preferred width and height are the width and
310: * height of this rectangle. In this case, the
311: * <code>LAYOUT_SHRINK</code>, <code>LAYOUT_EXPAND</code>,
312: * and <code>LAYOUT_VEXPAND</code> directives are ignored.</p>
313: *
314: * <p>If both dimensions of the preferred size of a <code>StringItem</code>
315: * are unlocked, the text from the <code>StringItem</code> may be wrapped
316: * across multiple rows. At the point in the layout algorithm where the width
317: * of the <code>Item</code> is compared to the remaining space on the row, as
318: * much text is taken from the beginning of the <code>StringItem</code> as
319: * will fit onto the current row. The contents of this row are then
320: * positioned according to the current alignment value. The remainder of the
321: * text in the <code>StringItem</code> is line-wrapped to the full width of as
322: * many new rows as are necessary to accommodate the text. Each full row is
323: * positioned according to the current alignment value. The last line of the
324: * text might leave space available on its row. If there is no row break
325: * following this <code>StringItem</code>, subsequent <code>Items</code> are
326: * packed into the remaining space and the contents of the row are positioned
327: * according to the current alignment value. This rule has the effect of
328: * displaying the contents of a <code>StringItem</code> as a paragraph of text
329: * set flush-left, flush-right, or centered, depending upon whether the
330: * current alignment value is <code>LAYOUT_LEFT</code>,
331: * <code>LAYOUT_RIGHT</code>, or <code>LAYOUT_CENTER</code>, respectively.
332: * The preferred width and height of a <code>StringItem</code> wrapped across
333: * multiple rows, as reported by the
334: * {@link Item#getPreferredWidth Item.getPreferredWidth} and
335: * {@link Item#getPreferredHeight Item.getPreferredHeight}
336: * methods, describe the width and height of the bounding rectangle of the
337: * wrapped text.</p>
338: *
339: * <p><code>ImageItems</code> are also treated specially by the above
340: * algorithm. The foregoing rules concerning the horizontal alignment value
341: * and the <code>LAYOUT_LEFT</code>, <code>LAYOUT_RIGHT</code>, and
342: * <code>LAYOUT_CENTER</code> directives, apply to <code>ImageItems</code>
343: * only when the <code>LAYOUT_2</code> directive is also present on that item.
344: * If the <code>LAYOUT_2</code> directive is not present on an
345: * <code>ImageItem</code>, the behavior of the <code>LAYOUT_LEFT</code>,
346: * <code>LAYOUT_RIGHT</code>, and <code>LAYOUT_CENTER</code> directives is
347: * implementation-specific.</p>
348: *
349: * <p>A <code>Form's</code> layout is recomputed automatically as
350: * necessary. This may
351: * occur because of a change in an <code>Item's</code> size caused
352: * by a change in its
353: * contents or because of a request by the application to change the Item's
354: * preferred size. It may also occur if an <code>Item's</code>
355: * layout directives are
356: * changed by the application. The application does not need to perform
357: * any specific action to cause the <code>Form's</code> layout to
358: * be updated.</p>
359: *
360: * <h2><a NAME="linebreak">Line Breaks and Wrapping</a></h2>
361: *
362: * <p>For all cases where text is wrapped,
363: * line breaks must occur at each newline character
364: * (<code>'\n'</code> = Unicode <code>'U+000A'</code>).
365: * If space does not permit
366: * the full text to be displayed it is truncated at line breaks.
367: * If there are no suitable line breaks, it is recommended that
368: * implementations break text at word boundaries.
369: * If there are no word boundaries, it is recommended that
370: * implementations break text at character boundaries. </p>
371: *
372: * <p>Labels that contain line breaks may be truncated at the line
373: * break and cause the rest of the label not to be shown.</p>
374: *
375: * <h2>User Interaction</h2>
376: *
377: * <p> When a <code>Form</code> is present on the display the user
378: * can interact
379: * with it and its <code>Items</code> indefinitely (for instance,
380: * traversing from <code>Item</code>
381: * to <code>Item</code>
382: * and possibly
383: * scrolling). These traversing and scrolling operations do not cause
384: * application-visible events. The system notifies
385: * the application when the user modifies the state of an interactive
386: * <code>Item</code>
387: * contained within the <code>Form</code>. This notification is
388: * accomplished by calling the
389: * {@link ItemStateListener#itemStateChanged itemStateChanged()}
390: * method of the listener declared to the <code>Form</code> with the
391: * {@link #setItemStateListener setItemStateListener()} method. </p>
392: *
393: * <p> As with other <code>Displayable</code> objects, a
394: * <code>Form</code> can declare
395: * {@link Command commands} and declare a command listener with the
396: * {@link Displayable#setCommandListener setCommandListener()} method.
397: * {@link CommandListener CommandListener}
398: * objects are distinct from
399: * {@link ItemStateListener ItemStateListener} objects, and they are declared
400: * and invoked separately. </p>
401: *
402: * <h2>Notes for Application Developers</h2>
403: *
404: * <UL>
405: * <LI>Although this class allows creation of arbitrary combination of
406: * components
407: * the application developers should keep the small screen size in mind.
408: * <code>Form</code> is designed to contain a <em>small number of
409: * closely related</em>
410: * UI elements. </LI>
411: *
412: * <LI>If the number of items does not fit on the screen, the
413: * implementation may choose to make it scrollable or to fold some components
414: * so that a separate screen appears when the element is edited.</LI>
415: * </UL>
416: *
417: * <p>
418: * </p>
419: *
420: * @see Item
421: * @since MIDP 1.0
422: */
423:
424: public class Form extends Screen {
425:
426: // ************************************************************
427: // Static initializer, constructor
428: // ************************************************************
429:
430: /**
431: * Creates a new, empty <code>Form</code>.
432: *
433: * @param title the <code>Form's</code> title, or
434: * <code>null</code> for no title
435: */
436: public Form(String title) {
437: this (title, null);
438: }
439:
440: /**
441: * Creates a new <code>Form</code> with the specified
442: * contents. This is identical to
443: * creating an empty <code>Form</code> and then using a set of
444: * <code>append</code>
445: * methods. The
446: * items array may be <code>null</code>, in which case the
447: * <code>Form</code> is created empty. If
448: * the items array is non-null, each element must be a valid
449: * <code>Item</code> not
450: * already contained within another <code>Form</code>.
451: *
452: * @param title the <code>Form's</code> title string
453: * @param items the array of items to be placed in the
454: * <code>Form</code>, or <code>null</code> if there are no
455: * items
456: * @throws IllegalStateException if one of the items is already owned by
457: * another container
458: * @throws NullPointerException if an element of the items array is
459: * <code>null</code>
460: */
461: public Form(String title, Item[] items) {
462: super (title);
463:
464: synchronized (Display.LCDUILock) {
465: if (items == null) {
466: this .items = new Item[GROW_SIZE];
467: // numOfItems was initialized to 0
468: // so there is no need to update it
469:
470: } else {
471: this .items = new Item[items.length > GROW_SIZE ? items.length
472: : GROW_SIZE];
473:
474: // We have to check all items first so that some
475: // items would not be added to a form that was not
476: // instantiated
477: for (int i = 0; i < items.length; i++) {
478: // NullPointerException will be thrown by
479: // items[i].owner if items[i] == null;
480: if (items[i].owner != null) {
481: throw new IllegalStateException();
482: }
483: }
484:
485: numOfItems = items.length;
486:
487: for (int i = 0; i < numOfItems; i++) {
488: items[i].lSetOwner(this );
489: this .items[i] = items[i];
490: }
491: }
492: displayableLF = formLF = LFFactory.getFactory().getFormLF(
493: this );
494: } // synchronized
495: }
496:
497: // ************************************************************
498: // public methods
499: // ************************************************************
500:
501: /**
502: * Adds an <code>Item</code> into the <code>Form</code>. The newly
503: * added <code>Item</code> becomes the last <code>Item</code> in the
504: * <code>Form</code>, and the size of the <code>Form</code> grows
505: * by one.
506: *
507: * @param item the {@link Item Item} to be added.
508: * @return the assigned index of the <code>Item</code>
509: * @throws IllegalStateException if the item is already owned by
510: * a container
511: * @throws NullPointerException if item is <code>null</code>
512: */
513: public int append(Item item) {
514: synchronized (Display.LCDUILock) {
515: // NullPointerException will be thrown
516: // by item.owner if item == null
517: if (item.owner != null) {
518: throw new IllegalStateException();
519: }
520: int i = insertImpl(numOfItems, item);
521: formLF.lInsert(i, item);
522: return i;
523: }
524: }
525:
526: /**
527: * Adds an item consisting of one <code>String</code> to the
528: * <code>Form</code>. The effect of
529: * this method is identical to
530: *
531: * <p> <code>
532: * append(new StringItem(null, str))
533: * </code> </p>
534: *
535: * @param str the <code>String</code> to be added
536: * @return the assigned index of the <code>Item</code>
537: * @throws NullPointerException if str is <code>null</code>
538: */
539: public int append(String str) {
540: if (str == null) {
541: throw new NullPointerException();
542: }
543:
544: synchronized (Display.LCDUILock) {
545: Item item = new StringItem(null, str);
546: int i = insertImpl(numOfItems, item);
547: formLF.lInsert(i, item);
548: return i;
549: }
550: }
551:
552: /**
553: * Adds an item consisting of one <code>Image</code> to the
554: * <code>Form</code>. The effect of
555: * this method is identical to
556: *
557: * <p> <code>
558: * append(new ImageItem(null, img, ImageItem.LAYOUT_DEFAULT, null))
559: * </code> </p>
560: *
561: * @param img the image to be added
562: * @return the assigned index of the <code>Item</code>
563: * @throws NullPointerException if <code>img</code> is <code>null</code>
564: */
565: public int append(Image img) {
566: if (img == null) {
567: throw new NullPointerException();
568: }
569:
570: synchronized (Display.LCDUILock) {
571: Item item = new ImageItem(null, img,
572: ImageItem.LAYOUT_DEFAULT, null);
573: int i = insertImpl(numOfItems, item);
574: formLF.lInsert(i, item);
575: return i;
576: }
577: }
578:
579: /**
580: * Inserts an item into the <code>Form</code> just prior to
581: * the item specified.
582: * The size of the <code>Form</code> grows by one. The
583: * <code>itemNum</code> parameter must be
584: * within the range <code>[0..size()]</code>, inclusive.
585: * The index of the last item is <code>size()-1</code>, and
586: * so there is actually no item whose index is
587: * <code>size()</code>. If this value
588: * is used for <code>itemNum</code>, the new item is inserted
589: * immediately after
590: * the last item. In this case, the effect is identical to
591: * {@link #append(Item) append(Item)}.
592: *
593: * <p> The semantics are otherwise identical to
594: * {@link #append(Item) append(Item)}. </p>
595: *
596: * @param itemNum the index where insertion is to occur
597: * @param item the item to be inserted
598: * @throws IndexOutOfBoundsException if <code>itemNum</code> is invalid
599: * @throws IllegalStateException if the item is already owned by
600: * a container
601: * @throws NullPointerException if <code>item</code> is
602: * <code>null</code>
603: */
604: public void insert(int itemNum, Item item) {
605: synchronized (Display.LCDUILock) {
606: // NullPointerException will be thrown
607: // by item.owner if item == null
608: if (item.owner != null) {
609: throw new IllegalStateException();
610: }
611:
612: if (itemNum < 0 || itemNum > numOfItems) {
613: throw new IndexOutOfBoundsException();
614: }
615: insertImpl(itemNum, item);
616: formLF.lInsert(itemNum, item);
617: }
618: }
619:
620: /**
621: * Deletes the <code>Item</code> referenced by
622: * <code>itemNum</code>. The size of the <code>Form</code>
623: * shrinks by one. It is legal to delete all items from a
624: * <code>Form</code>.
625: * The <code>itemNum</code> parameter must be
626: * within the range <code>[0..size()-1]</code>, inclusive.
627: *
628: * @param itemNum the index of the item to be deleted
629: * @throws IndexOutOfBoundsException if <code>itemNum</code> is invalid
630: */
631: public void delete(int itemNum) {
632: Item deletedItem;
633: synchronized (Display.LCDUILock) {
634: if (itemNum < 0 || itemNum >= numOfItems) {
635: throw new IndexOutOfBoundsException();
636: }
637:
638: deletedItem = items[itemNum];
639:
640: numOfItems--;
641:
642: if (itemNum < numOfItems) {
643: System.arraycopy(items, itemNum + 1, items, itemNum,
644: numOfItems - itemNum);
645: }
646:
647: // Delete reference to the last item
648: // that was left after array copy
649: items[numOfItems] = null;
650:
651: // The Form is clear; reset its state
652: if (numOfItems == 0 && items.length > GROW_SIZE) {
653: items = new Item[GROW_SIZE]; // start fresh
654: }
655:
656: formLF.lDelete(itemNum, deletedItem);
657:
658: } // synchronized
659:
660: deletedItem.itemDeleted();
661: }
662:
663: /**
664: * Deletes all the items from this <code>Form</code>, leaving
665: * it with zero items.
666: * This method does nothing if the <code>Form</code> is already empty.
667: *
668: */
669: public void deleteAll() {
670: Item[] itemsCopy;
671: synchronized (Display.LCDUILock) {
672: if (numOfItems == 0) {
673: return;
674: }
675: itemsCopy = new Item[numOfItems];
676:
677: for (int x = 0; x < numOfItems; x++) {
678: itemsCopy[x] = items[x];
679: items[x] = null;
680: }
681: if (items.length > GROW_SIZE) {
682: items = new Item[GROW_SIZE]; // start fresh
683: }
684:
685: // Reset form state
686: numOfItems = 0;
687:
688: formLF.lDeleteAll();
689: }
690:
691: for (int x = 0; x < itemsCopy.length; x++) {
692: itemsCopy[x].itemDeleted();
693: }
694: }
695:
696: /**
697: * Sets the item referenced by <code>itemNum</code> to the
698: * specified item,
699: * replacing the previous item. The previous item is removed
700: * from this <code>Form</code>.
701: * The <code>itemNum</code> parameter must be
702: * within the range <code>[0..size()-1]</code>, inclusive.
703: *
704: * <p>The end result is equal to
705: * <code>insert(n, item); delete(n+1);</code><br>
706: * although the implementation may optimize the repainting
707: * and usage of the array that stores the items. <P>
708: *
709: * @param itemNum the index of the item to be replaced
710: * @param item the new item to be placed in the <code>Form</code>
711: *
712: * @throws IndexOutOfBoundsException if <code>itemNum</code> is invalid
713: * @throws IllegalStateException if the item is already owned by
714: * a container
715: * @throws NullPointerException if <code>item</code> is
716: * <code>null</code>
717: */
718: public void set(int itemNum, Item item) {
719: synchronized (Display.LCDUILock) {
720: // NullPointerException will be thrown
721: // by item.owner if item == null
722: if (item.owner != null) {
723: throw new IllegalStateException();
724: }
725:
726: if (itemNum < 0 || itemNum >= numOfItems) {
727: throw new IndexOutOfBoundsException();
728: }
729:
730: items[itemNum].lSetOwner(null);
731: item.lSetOwner(this );
732:
733: items[itemNum] = item;
734:
735: formLF.lSet(itemNum, item);
736: }
737: }
738:
739: /**
740: * Gets the item at given position. The contents of the
741: * <code>Form</code> are left
742: * unchanged.
743: * The <code>itemNum</code> parameter must be
744: * within the range <code>[0..size()-1]</code>, inclusive.
745: *
746: * @param itemNum the index of item
747: *
748: * @return the item at the given position
749: *
750: * @throws IndexOutOfBoundsException if <code>itemNum</code> is invalid
751: */
752: public Item get(int itemNum) {
753: synchronized (Display.LCDUILock) {
754: if (itemNum < 0 || itemNum >= numOfItems) {
755: throw new IndexOutOfBoundsException();
756: }
757:
758: return items[itemNum];
759: }
760: }
761:
762: /**
763: * Sets the <code>ItemStateListener</code> for the
764: * <code>Form</code>, replacing any previous
765: * <code>ItemStateListener</code>. If
766: * <code>iListener</code> is <code>null</code>, simply
767: * removes the previous <code>ItemStateListener</code>.
768: * @param iListener the new listener, or <code>null</code> to remove it
769: */
770: public void setItemStateListener(ItemStateListener iListener) {
771: synchronized (Display.LCDUILock) {
772: itemStateListener = iListener;
773: }
774: }
775:
776: /**
777: * Gets the number of items in the <code>Form</code>.
778: * @return the number of items
779: */
780: public int size() {
781: synchronized (Display.LCDUILock) {
782: return numOfItems;
783: }
784: }
785:
786: /**
787: * Returns the width in pixels of the displayable area available for items.
788: * The value may depend on how the device uses the screen and may be
789: * affected by the presence or absence of the ticker, title, or commands.
790: * The <code>Items</code> of the <code>Form</code> are
791: * laid out to fit within this width.
792: * @return the width of the <code>Form</code> in pixels
793: */
794: public int getWidth() {
795: synchronized (Display.LCDUILock) {
796: return formLF.lGetWidth();
797: }
798: }
799:
800: /**
801: * Returns the height in pixels of the displayable area available
802: * for items.
803: * This value is the height of the form that can be displayed without
804: * scrolling.
805: * The value may depend on how the device uses the screen and may be
806: * affected by the presence or absence of the ticker, title, or commands.
807: * @return the height of the displayable area of the
808: * <code>Form</code> in pixels
809: */
810: public int getHeight() {
811: synchronized (Display.LCDUILock) {
812: return formLF.lGetHeight();
813: }
814: }
815:
816: // ************************************************************
817: // protected methods
818: // ************************************************************
819:
820: // ************************************************************
821: // package private methods
822: // ************************************************************
823:
824: /**
825: * Retrieve the ItemStateListener for this Form.
826: * NOTE: calls to this method should only occur from within
827: * a lock on LCDUILock.
828: *
829: * @return ItemStateListener The ItemStateListener of this Form,
830: * null if there isn't one set
831: */
832: ItemStateListener getItemStateListener() {
833: return itemStateListener;
834: }
835:
836: /**
837: * Used by the event handler to notify the ItemStateListener
838: * of a change in the given item
839: *
840: * @param item the Item which state was changed
841: */
842: void uCallItemStateChanged(Item item) {
843: // get a copy of the object reference to ItemStateListener
844: ItemStateListener isl = itemStateListener;
845: if (isl == null || item == null) {
846: return;
847: }
848:
849: // Protect from any unexpected application exceptions
850: try {
851: // SYNC NOTE: We lock on calloutLock around any calls
852: // into application code
853: synchronized (Display.calloutLock) {
854: isl.itemStateChanged(item);
855: }
856: } catch (Throwable thr) {
857: Display.handleThrowable(thr);
858: }
859: }
860:
861: // ************************************************************
862: // private methods
863: // ************************************************************
864:
865: /**
866: * Insert an Item into this Form
867: *
868: * @param itemNum The index into the Item array to insert this Item
869: * @param item The Item to insert
870: * @return int The index at which the newly inserted Item can be found
871: */
872: private int insertImpl(int itemNum, Item item) {
873:
874: if (items.length == numOfItems) {
875: Item newItems[] = new Item[numOfItems + GROW_SIZE];
876: System.arraycopy(items, 0, newItems, 0, itemNum);
877: System.arraycopy(items, itemNum, newItems, itemNum + 1,
878: numOfItems - itemNum);
879: items = newItems;
880: } else {
881: // if we're not appending
882: if (itemNum != numOfItems) {
883: System.arraycopy(items, itemNum, items, itemNum + 1,
884: numOfItems - itemNum);
885: }
886: }
887:
888: numOfItems++;
889:
890: //
891: // the arraycopy copied the reference to the item at this
892: // spot. if we call setImpl without setting the index to null
893: // setImpl will set the items owner to null.
894: //
895: item.lSetOwner(this );
896: items[itemNum] = item;
897:
898: return itemNum;
899: }
900:
901: // ************************************************************
902: // public member variables
903: // ************************************************************
904:
905: // ************************************************************
906: // protected member variables
907: // ************************************************************
908:
909: // ************************************************************
910: // package private member variables
911: // ************************************************************
912:
913: /** Array of Items that were added to this form. */
914: Item items[];
915:
916: /** The number of actual Items added is numOfItems. */
917: int numOfItems; // = 0;
918:
919: /** The Form look&feel object associated with this Form */
920: FormLF formLF;
921:
922: // ************************************************************
923: // private member variables
924: // ************************************************************
925:
926: /**
927: * This is the rate at which the internal array of Items grows if
928: * it gets filled up
929: */
930: private static final int GROW_SIZE = 4;
931:
932: /** itemStateListener that has to be notified of any state changes */
933: private ItemStateListener itemStateListener;
934:
935: } // class Form
|