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>ChoiceGroup</code> is a group of selectable elements intended to be
031: * placed within a
032: * {@link Form}. The group may be created with a mode that requires a
033: * single choice to be made or that allows multiple choices. The
034: * implementation is responsible for providing the graphical representation of
035: * these modes and must provide visually different graphics for different
036: * modes. For example, it might use "radio buttons" for the
037: * single choice
038: * mode and "check boxes" for the multiple choice mode.
039: *
040: * <p> <strong>Note:</strong> most of the essential methods have been
041: * specified in the {@link Choice Choice} interface.</p>
042: * @since MIDP 1.0
043: */
044:
045: public class ChoiceGroup extends Item implements Choice {
046:
047: /**
048: * Creates a new, empty <code>ChoiceGroup</code>, specifying its
049: * title and its type.
050: * The type must be one of <code>EXCLUSIVE</code>,
051: * <code>MULTIPLE</code>, or <code>POPUP</code>. The
052: * <code>IMPLICIT</code>
053: * choice type is not allowed within a <code>ChoiceGroup</code>.
054: *
055: * @param label the item's label (see {@link Item Item})
056: * @param choiceType <code>EXCLUSIVE</code>, <code>MULTIPLE</code>,
057: * or <code>POPUP</code>
058: * @throws IllegalArgumentException if <code>choiceType</code>
059: * is not one of
060: * <code>EXCLUSIVE</code>, <code>MULTIPLE</code>, or <code>POPUP</code>
061: * @see Choice#EXCLUSIVE
062: * @see Choice#MULTIPLE
063: * @see Choice#IMPLICIT
064: * @see Choice#POPUP
065: */
066: public ChoiceGroup(String label, int choiceType) {
067: this (label, choiceType, new String[] {}, null);
068: }
069:
070: /**
071: * Creates a new <code>ChoiceGroup</code>, specifying its title,
072: * the type of the
073: * <code>ChoiceGroup</code>, and an array of <code>Strings</code>
074: * and <code>Images</code> to be used as its
075: * initial contents.
076: *
077: * <p>The type must be one of <code>EXCLUSIVE</code>,
078: * <code>MULTIPLE</code>, or <code>POPUP</code>. The
079: * <code>IMPLICIT</code>
080: * type is not allowed for <code>ChoiceGroup</code>.</p>
081: *
082: * <p>The <code>stringElements</code> array must be non-null and
083: * every array element
084: * must also be non-null. The length of the
085: * <code>stringElements</code> array
086: * determines the number of elements in the <code>ChoiceGroup</code>. The
087: * <code>imageElements</code> array
088: * may be <code>null</code> to indicate that the
089: * <code>ChoiceGroup</code> elements have no images.
090: * If the
091: * <code>imageElements</code> array is non-null, it must be the
092: * same length as the
093: * <code>stringElements</code> array. Individual elements of the
094: * <code>imageElements</code> array
095: * may be <code>null</code> in order to indicate the absence of an
096: * image for the
097: * corresponding <code>ChoiceGroup</code> element. Non-null elements
098: * of the
099: * <code>imageElements</code> array may refer to mutable or
100: * immutable images.</p>
101: *
102: * @param label the item's label (see {@link Item Item})
103: * @param choiceType <code>EXCLUSIVE</code>, <code>MULTIPLE</code>,
104: * or <code>POPUP</code>
105: * @param stringElements set of strings specifying the string parts of the
106: * <code>ChoiceGroup</code> elements
107: * @param imageElements set of images specifying the image parts of
108: * the <code>ChoiceGroup</code> elements
109: *
110: * @throws NullPointerException if <code>stringElements</code>
111: * is <code>null</code>
112: * @throws NullPointerException if the <code>stringElements</code>
113: * array contains
114: * any <code>null</code> elements
115: * @throws IllegalArgumentException if the <code>imageElements</code>
116: * array is non-null
117: * and has a different length from the <code>stringElements</code> array
118: * @throws IllegalArgumentException if <code>choiceType</code>
119: * is not one of
120: * <code>EXCLUSIVE</code>, <code>MULTIPLE</code>, or <code>POPUP</code>
121: *
122: * @see Choice#EXCLUSIVE
123: * @see Choice#MULTIPLE
124: * @see Choice#IMPLICIT
125: * @see Choice#POPUP
126: */
127: public ChoiceGroup(String label, int choiceType,
128: String[] stringElements, Image[] imageElements) {
129:
130: this (label, choiceType, stringElements, imageElements, false);
131: }
132:
133: /**
134: * Special constructor used by List
135: *
136: * @param label the item's label (see {@link Item Item})
137: * @param choiceType EXCLUSIVE or MULTIPLE
138: * @param stringElements set of strings specifying the string parts of the
139: * ChoiceGroup elements
140: * @param imageElements set of images specifying the image parts of
141: * the ChoiceGroup elements
142: * @param implicitAllowed Flag to allow implicit selection
143: *
144: * @throws NullPointerException if stringElements is null
145: * @throws NullPointerException if the stringElements array contains
146: * any null elements
147: * @throws IllegalArgumentException if the imageElements array is non-null
148: * and has a different length from the stringElements array
149: * @throws IllegalArgumentException if choiceType is neither
150: * EXCLUSIVE nor MULTIPLE
151: * @throws IllegalArgumentException if any image in the imageElements
152: * array is mutable
153: *
154: * @see Choice#EXCLUSIVE
155: * @see Choice#MULTIPLE
156: * @see Choice#IMPLICIT
157: */
158: ChoiceGroup(String label, int choiceType, String[] stringElements,
159: Image[] imageElements, boolean implicitAllowed) {
160:
161: super (label);
162:
163: if (!((choiceType == Choice.MULTIPLE)
164: || (choiceType == Choice.EXCLUSIVE)
165: || ((choiceType == Choice.IMPLICIT) && implicitAllowed) || (choiceType == Choice.POPUP))) {
166: throw new IllegalArgumentException();
167: }
168:
169: // If stringElements is null NullPointerException will be thrown
170: // as expected
171: for (int x = 0; x < stringElements.length; x++) {
172: if (stringElements[x] == null) {
173: throw new NullPointerException();
174: }
175: }
176:
177: if (imageElements != null) {
178: if (stringElements.length != imageElements.length) {
179: throw new IllegalArgumentException();
180: }
181: }
182:
183: synchronized (Display.LCDUILock) {
184: this .choiceType = choiceType;
185: numOfEls = stringElements.length;
186:
187: cgElements = new CGElement[numOfEls + GROW_FACTOR];
188:
189: if (imageElements != null) {
190:
191: for (int i = 0; i < numOfEls; i++) {
192: cgElements[i] = new CGElement(stringElements[i],
193: imageElements[i]);
194: }
195:
196: } else {
197:
198: for (int i = 0; i < numOfEls; i++) {
199: cgElements[i] = new CGElement(stringElements[i],
200: null /* image */);
201: }
202: }
203:
204: itemLF = choiceGroupLF = LFFactory.getFactory()
205: .getChoiceGroupLF(this );
206:
207: // initialize fonts to default one in all elements;
208: // this has to be done after ChoiceGroupLF is created
209: for (int i = 0; i < numOfEls; i++) {
210: cgElements[i].setFont(null);
211: }
212: } // synchronized
213: }
214:
215: /**
216: * Returns the number of elements in the <code>ChoiceGroup</code>.
217: * @return the number of elements in the <code>ChoiceGroup</code>
218: */
219: public int size() {
220: // SYNC NOTE: return of atomic value, no locking necessary
221: return numOfEls;
222: }
223:
224: /**
225: * Gets the <code>String</code> part of the element referenced by
226: * <code>elementNum</code>.
227: *
228: * @param elementNum the index of the element to be queried
229: * @return the string part of the element
230: * @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
231: * @see #getImage(int)
232: */
233: public String getString(int elementNum) {
234: synchronized (Display.LCDUILock) {
235: checkIndex(elementNum);
236: // return stringEls[elementNum];
237: return cgElements[elementNum].stringEl;
238: }
239: }
240:
241: /**
242: * Gets the <code>Image</code> part of the element referenced by
243: * <code>elementNum</code>.
244: *
245: * @param elementNum the number of the element to be queried
246: * @return the image part of the element, or null if there is no image
247: * @throws IndexOutOfBoundsException if elementNum is invalid
248: * @see #getString(int)
249: */
250: public Image getImage(int elementNum) {
251: synchronized (Display.LCDUILock) {
252: checkIndex(elementNum);
253:
254: // return as mutable, if possible
255: return (cgElements[elementNum].mutableImageEl == null ? cgElements[elementNum].imageEl
256: : cgElements[elementNum].mutableImageEl);
257: }
258: }
259:
260: /**
261: * Appends an element to the <code>ChoiceGroup</code>.
262: *
263: * @param stringPart the string part of the element to be added
264: * @param imagePart the image part of the element to be added, or
265: * <code>null</code> if there is no image part
266: * @return the assigned index of the element
267: * @throws NullPointerException if <code>stringPart</code> is
268: * <code>null</code>
269: */
270: public int append(String stringPart, Image imagePart) {
271: int elementNum = -1;
272:
273: synchronized (Display.LCDUILock) {
274: checkNull(stringPart);
275: if ((elementNum = insertImpl(numOfEls, stringPart,
276: imagePart)) >= 0) {
277: choiceGroupLF
278: .lInsert(elementNum, stringPart, imagePart);
279: }
280: }
281: return elementNum;
282: }
283:
284: /**
285: * Inserts an element into the <code>ChoiceGroup</code> just prior to
286: * the element specified.
287: *
288: * @param elementNum the index of the element where insertion is to occur
289: * @param stringPart the string part of the element to be inserted
290: * @param imagePart the image part of the element to be inserted,
291: * or <code>null</code> if there is no image part
292: * @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
293: * @throws NullPointerException if <code>stringPart</code>
294: * is <code>null</code>
295: */
296: public void insert(int elementNum, String stringPart,
297: Image imagePart) {
298:
299: synchronized (Display.LCDUILock) {
300: if (elementNum < 0 || elementNum > numOfEls) {
301: throw new IndexOutOfBoundsException();
302: }
303: checkNull(stringPart);
304: if (insertImpl(elementNum, stringPart, imagePart) >= 0) {
305: choiceGroupLF
306: .lInsert(elementNum, stringPart, imagePart);
307: }
308: }
309: }
310:
311: /**
312: * Deletes the element referenced by <code>elementNum</code>.
313: *
314: * @param elementNum the index of the element to be deleted
315: * @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
316: */
317: public void delete(int elementNum) {
318:
319: synchronized (Display.LCDUILock) {
320: checkIndex(elementNum);
321:
322: --numOfEls;
323:
324: // setup new elements array
325: if (elementNum != numOfEls) {
326: System.arraycopy(cgElements, elementNum + 1,
327: cgElements, elementNum, numOfEls - elementNum);
328: }
329:
330: // free some memory... (efficient for very large arrays)
331: if (cgElements.length > (GROW_FACTOR * 10)
332: && cgElements.length / numOfEls >= 2) {
333: CGElement[] newArray = new CGElement[numOfEls
334: + GROW_FACTOR];
335: System.arraycopy(cgElements, 0, newArray, 0, numOfEls);
336: cgElements = newArray;
337: newArray = null;
338: }
339:
340: cgElements[numOfEls] = null;
341:
342: // notify l&f
343: choiceGroupLF.lDelete(elementNum);
344:
345: } // synchronized
346:
347: }
348:
349: /**
350: * Deletes all elements from this <code>ChoiceGroup</code>.
351: */
352: public void deleteAll() {
353: synchronized (Display.LCDUILock) {
354:
355: cgElements = new CGElement[GROW_FACTOR]; // initial size
356:
357: numOfEls = 0;
358:
359: choiceGroupLF.lDeleteAll();
360: }
361: }
362:
363: /**
364: * Sets the <code>String</code> and <code>Image</code> parts of the
365: * element referenced by <code>elementNum</code>,
366: * replacing the previous contents of the element.
367: *
368: * @param elementNum the index of the element to be set
369: * @param stringPart the string part of the new element
370: * @param imagePart the image part of the element, or <code>null</code>
371: * if there is no image part
372: * @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
373: * @throws NullPointerException if <code>stringPart</code> is
374: * <code>null</code>
375: */
376: public void set(int elementNum, String stringPart, Image imagePart) {
377: synchronized (Display.LCDUILock) {
378: checkIndex(elementNum);
379: checkNull(stringPart);
380:
381: cgElements[elementNum].set(stringPart, imagePart);
382:
383: choiceGroupLF.lSet(elementNum, stringPart, imagePart);
384: }
385: }
386:
387: /**
388: * Gets a boolean value indicating whether this element is selected.
389: *
390: * @param elementNum the index of the element to be queried
391: *
392: * @return selection state of the element
393: *
394: * @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
395: */
396: public boolean isSelected(int elementNum) {
397: synchronized (Display.LCDUILock) {
398: checkIndex(elementNum);
399:
400: return choiceGroupLF.lIsSelected(elementNum);
401: }
402: }
403:
404: /**
405: * Returns the index number of an element in the
406: * <code>ChoiceGroup</code> that is
407: * selected. For <code>ChoiceGroup</code> objects of type
408: * <code>EXCLUSIVE</code> and <code>POPUP</code>
409: * there is at most one element selected, so
410: * this method is useful for determining the user's choice.
411: * Returns <code>-1</code> if
412: * there are no elements in the <code>ChoiceGroup</code>.
413: *
414: * <p>For <code>ChoiceGroup</code> objects of type
415: * <code>MULTIPLE</code>, this always
416: * returns <code>-1</code> because no
417: * single value can in general represent the state of such a
418: * <code>ChoiceGroup</code>.
419: * To get the complete state of a <code>MULTIPLE</code>
420: * <code>Choice</code>, see {@link
421: * #getSelectedFlags getSelectedFlags}.</p>
422: *
423: * @return index of selected element, or <code>-1</code> if none
424: * @see #setSelectedIndex
425: */
426: public int getSelectedIndex() {
427: synchronized (Display.LCDUILock) {
428: return choiceGroupLF.lGetSelectedIndex();
429: }
430: }
431:
432: /**
433: * Queries the state of a <code>ChoiceGroup</code> and returns the state of
434: * all elements in the
435: * boolean array
436: * <code>selectedArray_return</code>. <strong>Note:</strong> this
437: * is a result parameter.
438: * It must be at least as long as the size
439: * of the <code>ChoiceGroup</code> as returned by <code>size()</code>.
440: * If the array is longer, the extra
441: * elements are set to <code>false</code>.
442: *
443: * <p>For <code>ChoiceGroup</code> objects of type
444: * <code>MULTIPLE</code>, any
445: * number of elements may be selected and set to true in the result
446: * array. For <code>ChoiceGroup</code> objects of type
447: * <code>EXCLUSIVE</code> and <code>POPUP</code>
448: * exactly one element will be selected, unless there are
449: * zero elements in the <code>ChoiceGroup</code>. </p>
450: *
451: * @return the number of selected elements in the <code>ChoiceGroup</code>
452: *
453: * @param selectedArray_return array to contain the results
454: * @throws IllegalArgumentException if <code>selectedArray_return</code>
455: * is shorter than the size of the <code>ChoiceGroup</code>
456: * @throws NullPointerException if <code>selectedArray_return</code>
457: * is null
458: * @see #setSelectedFlags
459: */
460: public int getSelectedFlags(boolean[] selectedArray_return) {
461: checkFlag(selectedArray_return);
462:
463: synchronized (Display.LCDUILock) {
464: int numSelected = 0;
465: if (numOfEls > 0) {
466: numSelected = choiceGroupLF
467: .lGetSelectedFlags(selectedArray_return);
468: }
469:
470: for (int i = numOfEls; i < selectedArray_return.length; i++) {
471: selectedArray_return[i] = false;
472: }
473: return numSelected;
474: }
475: }
476:
477: /**
478: * For <code>ChoiceGroup</code> objects of type
479: * <code>MULTIPLE</code>, this simply sets an
480: * individual element's selected state.
481: *
482: * <P>For <code>ChoiceGroup</code> objects of type
483: * <code>EXCLUSIVE</code> and <code>POPUP</code>, this can be used only to
484: * select an element. That is, the <code> selected </code> parameter must
485: * be <code> true </code>. When an element is selected, the previously
486: * selected element is deselected. If <code> selected </code> is <code>
487: * false </code>, this call is ignored.</P>
488: *
489: * <p>For both list types, the <code>elementNum</code> parameter
490: * must be within
491: * the range
492: * <code>[0..size()-1]</code>, inclusive. </p>
493: *
494: * @param elementNum the number of the element. Indexing of the
495: * elements is zero-based
496: * @param selected the new state of the element <code>true=selected</code>,
497: * <code>false=not</code> selected
498: * @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
499: * @see #getSelectedIndex
500: */
501: public void setSelectedIndex(int elementNum, boolean selected) {
502: checkIndex(elementNum);
503:
504: synchronized (Display.LCDUILock) {
505: choiceGroupLF.lSetSelectedIndex(elementNum, selected);
506: } // synchronized
507: }
508:
509: /**
510: * Attempts to set the selected state of every element in the
511: * <code>ChoiceGroup</code>. The array
512: * must be at least as long as the size of the
513: * <code>ChoiceGroup</code>. If the array is
514: * longer, the additional values are ignored. <p>
515: *
516: * For <code>ChoiceGroup</code> objects of type
517: * <code>MULTIPLE</code>, this sets the selected
518: * state of every
519: * element in the <code>Choice</code>. An arbitrary number of
520: * elements may be selected.
521: * <p>
522: *
523: * For <code>ChoiceGroup</code> objects of type
524: * <code>EXCLUSIVE</code> and <code>POPUP</code>, exactly one array
525: * element must have the value <code>true</code>. If no element is
526: * <code>true</code>,
527: * the first element
528: * in the <code>Choice</code> will be selected. If two or more
529: * elements are <code>true</code>, the
530: * implementation will choose the first <code>true</code> element
531: * and select it. <p>
532: *
533: * @param selectedArray an array in which the method collect the
534: * selection status
535: * @throws IllegalArgumentException if <code>selectedArray</code>
536: * is shorter than the size of the <code>ChoiceGroup</code>
537: * @throws NullPointerException if the <code>selectedArray</code>
538: * is <code>null</code>
539: * @see #getSelectedFlags
540: */
541: public void setSelectedFlags(boolean[] selectedArray) {
542: synchronized (Display.LCDUILock) {
543: checkFlag(selectedArray);
544:
545: if (numOfEls == 0) {
546: return;
547: }
548:
549: if (choiceType == Choice.MULTIPLE) {
550: for (int i = 0; i < numOfEls; i++) {
551: cgElements[i].setSelected(selectedArray[i]);
552: }
553: choiceGroupLF.lSetSelectedFlags(selectedArray);
554: } else {
555: for (int i = 0; i < numOfEls; i++) {
556: if (selectedArray[i]) {
557: choiceGroupLF.lSetSelectedIndex(i, true);
558: return;
559: }
560: }
561: choiceGroupLF.lSetSelectedIndex(0, true);
562: }
563:
564: } // synchronized
565: }
566:
567: /**
568: * Sets the application's preferred policy for fitting
569: * <code>Choice</code> element
570: * contents to the available screen space. The set policy applies for all
571: * elements of the <code>Choice</code> object. Valid values are
572: * {@link #TEXT_WRAP_DEFAULT}, {@link #TEXT_WRAP_ON},
573: * and {@link #TEXT_WRAP_OFF}. Fit policy is a hint, and the
574: * implementation may disregard the application's preferred policy.
575: *
576: * @param fitPolicy preferred content fit policy for choice elements
577: * @throws IllegalArgumentException if <code>fitPolicy</code> is invalid
578: * @see #getFitPolicy
579: */
580: public void setFitPolicy(int fitPolicy) {
581: if (fitPolicy < TEXT_WRAP_DEFAULT || fitPolicy > TEXT_WRAP_OFF) {
582: throw new IllegalArgumentException();
583: }
584: synchronized (Display.LCDUILock) {
585: if (this .fitPolicy != fitPolicy) {
586: this .fitPolicy = fitPolicy;
587: choiceGroupLF.lSetFitPolicy(fitPolicy);
588: }
589: }
590: }
591:
592: /**
593: * Gets the application's preferred policy for fitting
594: * <code>Choice</code> element
595: * contents to the available screen space. The value returned is the
596: * policy that had been set by the application, even if that value had
597: * been disregarded by the implementation.
598: *
599: * @return one of {@link #TEXT_WRAP_DEFAULT}, {@link #TEXT_WRAP_ON}, or
600: * {@link #TEXT_WRAP_OFF}
601: * @see #setFitPolicy
602: */
603: public int getFitPolicy() {
604: // SYNC NOTE: return of atomic value, no locking necessary
605: return fitPolicy;
606: }
607:
608: /**
609: * Sets the application's preferred font for
610: * rendering the specified element of this <code>Choice</code>.
611: * An element's font is a hint, and the implementation may disregard
612: * the application's preferred font.
613: *
614: * <p> The <code>elementNum</code> parameter must be within the range
615: * <code>[0..size()-1]</code>, inclusive.</p>
616: *
617: * <p> The <code>font</code> parameter must be a valid <code>Font</code>
618: * object or <code>null</code>. If the <code>font</code> parameter is
619: * <code>null</code>, the implementation must use its default font
620: * to render the element.</p>
621: *
622: * @param elementNum the index of the element, starting from zero
623: * @param font the preferred font to use to render the element
624: * @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
625: * @see #getFont
626: */
627: public void setFont(int elementNum, Font font) {
628: synchronized (Display.LCDUILock) {
629: checkIndex(elementNum);
630:
631: cgElements[elementNum].setFont(font);
632:
633: choiceGroupLF.lSetFont(elementNum, cgElements[elementNum]
634: .getFont());
635: }
636: }
637:
638: /**
639: * Gets the application's preferred font for
640: * rendering the specified element of this <code>Choice</code>. The
641: * value returned is the font that had been set by the application,
642: * even if that value had been disregarded by the implementation.
643: * If no font had been set by the application, or if the application
644: * explicitly set the font to <code>null</code>, the value is the default
645: * font chosen by the implementation.
646: *
647: * <p> The <code>elementNum</code> parameter must be within the range
648: * <code>[0..size()-1]</code>, inclusive.</p>
649: *
650: * @param elementNum the index of the element, starting from zero
651: * @return the preferred font to use to render the element
652: * @throws IndexOutOfBoundsException if <code>elementNum</code> is invalid
653: * @see #setFont(int elementNum, Font font)
654: */
655: public Font getFont(int elementNum) {
656: synchronized (Display.LCDUILock) {
657: checkIndex(elementNum);
658:
659: return cgElements[elementNum].getFont();
660: }
661: }
662:
663: // ***********************************************************
664: // package private
665: // ***********************************************************
666:
667: /**
668: * Return whether the Item takes user input focus.
669: *
670: * @return return <code>true</code> if contents is not null or have
671: * abstract commands.
672: */
673: boolean acceptFocus() {
674: return super .acceptFocus() || numOfEls > 0;
675: }
676:
677: // ***********************************************************
678: // private
679: // ***********************************************************
680:
681: /**
682: * Insert a particular element of this ChoiceGroup
683: *
684: * @param elementNum The index to insert the element
685: * @param stringPart The string part of the element to insert
686: * @param imagePart The image part of the element to insert
687: * @return int The index of the newly inserted element
688: */
689: private int insertImpl(int elementNum, String stringPart,
690: Image imagePart) {
691: // cgElements is created in the constructor and cannot be null
692: // full capacity reached
693: if (numOfEls == cgElements.length) {
694: CGElement[] newCGEls = new CGElement[numOfEls + GROW_FACTOR];
695: System.arraycopy(cgElements, 0, newCGEls, 0, elementNum);
696: System.arraycopy(cgElements, elementNum, newCGEls,
697: elementNum + 1, numOfEls - elementNum);
698: cgElements = newCGEls; // swap them
699:
700: } else if (elementNum != numOfEls) {
701: // if we're not appending
702: System.arraycopy(cgElements, elementNum, cgElements,
703: elementNum + 1, numOfEls - elementNum);
704: }
705:
706: numOfEls++;
707:
708: cgElements[elementNum] = new CGElement(stringPart, imagePart);
709:
710: return elementNum;
711:
712: }
713:
714: /**
715: * Check the validity of a given element index
716: *
717: * @param elementNum The index to check
718: * @throws IndexOutOfBoundsException If no element exists at the
719: * that index
720: */
721: private void checkIndex(int elementNum) {
722: if (elementNum < 0 || elementNum >= numOfEls) {
723: throw new IndexOutOfBoundsException();
724: }
725: }
726:
727: /**
728: * Check the given values for null.
729: *
730: * @param stringPart The string part of the element
731: * @throws NullPointerException If the string part is null
732: */
733: private void checkNull(String stringPart) {
734: if (stringPart == null) {
735: throw new NullPointerException();
736: }
737: }
738:
739: /**
740: * Check the validity of the selection array
741: *
742: * @param flag The array of boolean flags representing the
743: * selected state of the elements
744: * @throws NullPointerException If the flag array is null
745: * @throws IllegalArgumentException If the flag array is not
746: * the same size as the element array
747: */
748: private void checkFlag(boolean[] flag) {
749: if (flag == null) {
750: throw new NullPointerException();
751: }
752:
753: if (flag.length < numOfEls) {
754: throw new IllegalArgumentException();
755: }
756: }
757:
758: /**
759: * The look&feel associated with this ChoiceGroup.
760: * Set in the constructor.
761: */
762: ChoiceGroupLF choiceGroupLF; // = null
763:
764: /**
765: * The type of this ChoiceGroup
766: */
767: int choiceType;
768:
769: /**
770: * The string fit policy for this ChoiceGroup
771: * '0' by default, which is Choice.TEXT_WRAP_DEFAULT
772: */
773: int fitPolicy;
774:
775: /*
776: * NOTE: If this is a POPUP choice group, regardless of
777: * the set fit policy the system will behave as though
778: * fitPolicy == Choice.TEXT_WRAP_OFF. Popup choice elements
779: * will never wrap, and may be truncated.
780: */
781:
782: /**
783: * The number of elements in this ChoiceGroup
784: */
785: int numOfEls;
786:
787: /**
788: * The array containing the Font of each element (null if no setFont()
789: * method was ever called). If fontEls is non-null, only the elements
790: * which were set by setFont() are non-null.
791: */
792:
793: // see class on the bottom of this file
794: CGElement[] cgElements;
795:
796: /**
797: * Optimization for CGElement array size management.
798: * Notice that cgElements.length is not equal to numOfEls.
799: * Use numOfEls only when accessing the array.
800: */
801: static final int GROW_FACTOR = 4;
802:
803: /**
804: * Helper method, used solely by the native method
805: * call: updatePopupElements()
806: * @return an array of string elements
807: */
808: String[] getStringElements() {
809: String[] ret = new String[numOfEls];
810: for (int i = 0; i < numOfEls; i++) {
811: ret[i] = cgElements[i].stringEl;
812: }
813: return ret;
814: }
815:
816: /**
817: * Helper method, used solely by the native method
818: * call: updatePopupElements()
819: * @return an array of image elements
820: */
821: Image[] getImageElements() {
822: Image[] ret = new Image[numOfEls];
823: for (int i = 0; i < numOfEls; i++) {
824: ret[i] = cgElements[i].imageEl;
825: }
826: return ret;
827: }
828:
829: /**
830: * Class that groups information about a single ChoiceGroup element
831: * (such as its string, image, font). It also contains current state
832: * information like selection.
833: */
834: class CGElement {
835:
836: /**
837: * Creates CGElement
838: * @param str - the string to be used for this ChoiceGroup element
839: * @param img - the image to be used for this ChoiceGroup element
840: */
841: CGElement(String str, Image img) {
842: set(str, img);
843:
844: // If CGElement is created from ChoiceGroup constructor
845: // choiceGroupLF is not yet created there
846: if (choiceGroupLF != null) {
847: fontEl = choiceGroupLF.getDefaultFont();
848: }
849: }
850:
851: /**
852: * Sets the string and image.
853: * @param str - the string to be used for this ChoiceGroup element
854: * @param img - the image to be used for this ChoiceGroup element
855: */
856: void set(String str, Image img) {
857: stringEl = str;
858:
859: if (img != null && img.isMutable()) {
860: // Save original, mutable Image
861: mutableImageEl = img;
862: // Create a snapshot for display
863: imageEl = Image.createImage(img);
864: } else {
865: // Save the immutable image for display
866: imageEl = img;
867: mutableImageEl = null;
868: }
869:
870: if (imageEl != null) {
871: imageDataEl = imageEl.getImageData();
872: }
873: }
874:
875: /**
876: * Set the selection.
877: * @param sel the selection
878: */
879: void setSelected(boolean sel) {
880: selected = sel;
881: }
882:
883: /**
884: * Returns the font that was set by the application.
885: * If it was set to null or was unset, null will be returned.
886: * @return the font
887: */
888: Font getFont() {
889: return fontEl;
890: }
891:
892: /**
893: * Sets the font.
894: * @param f - the font to set.
895: */
896: void setFont(Font f) {
897: if (f == null) {
898: f = choiceGroupLF.getDefaultFont();
899: }
900: fontEl = f;
901: }
902:
903: /**
904: * Needed for CGElementLFImpl
905: * @return boolean indicating whether text wrap is on or off
906: */
907: boolean isWrap() {
908: return (fitPolicy == TEXT_WRAP_OFF);
909: }
910:
911: /** String portion of this ChoiceGroup element */
912: String stringEl; // = null;
913:
914: /** Image portion of this ChoiceGroup element (non-mutable) */
915: Image imageEl; // = null;
916:
917: /** Image portion of this ChoiceGroup element (if mutable) */
918: Image mutableImageEl; // = null;
919:
920: /** ImageData portion of this ChoiceGroup element (non-mutable) */
921: private ImageData imageDataEl; // = null;
922:
923: /** Selected state of this ChoiceGroup element */
924: boolean selected; // = false;
925:
926: /**
927: * Font to be used for rendering this ChoiceGroup element.
928: * It should be null if setFont() was
929: * not called for this element.
930: */
931: private Font fontEl; // = null;
932: }
933: }
|