0001: /*
0002: *
0003: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU General Public License version
0008: * 2 only, as published by the Free Software Foundation.
0009: *
0010: * This program is distributed in the hope that it will be useful, but
0011: * WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * General Public License version 2 for more details (a copy is
0014: * included at /legal/license.txt).
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * version 2 along with this work; if not, write to the Free Software
0018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0019: * 02110-1301 USA
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0022: * Clara, CA 95054 or visit www.sun.com if you need additional
0023: * information or have any questions.
0024: */
0025:
0026: package javax.microedition.lcdui;
0027:
0028: import com.sun.midp.log.Logging;
0029: import com.sun.midp.log.LogChannels;
0030: import com.sun.midp.configurator.Constants;
0031:
0032: /**
0033: * Look and feel class for <code>Form</code>.
0034: */
0035: class FormLFImpl extends DisplayableLFImpl implements FormLF {
0036: /**
0037: * Creates <code>FormLF</code> associated with passed in form.
0038: * <code>FormLFImpl</code> maintains an array of views associated
0039: * with its items.
0040: *
0041: * @param form the <code>Form</code> object associated with this
0042: * <code>FormLF</code>
0043: * @param items the array of Items using which the passed in
0044: * <code>Form</code> was created
0045: * @param numOfItems current number of elements
0046: */
0047: FormLFImpl(Form form, Item items[], int numOfItems) {
0048: super (form);
0049:
0050: // Initialize the in-out rect for Item traversal
0051: visRect = new int[4];
0052:
0053: width -= Constants.VERT_SCROLLBAR_WIDTH;
0054:
0055: if (items == null) {
0056: itemLFs = new ItemLFImpl[GROW_SIZE];
0057: // numOfLFs was initialized to 0
0058: // so there is no need to update it
0059: } else {
0060: this .itemLFs = new ItemLFImpl[items.length > GROW_SIZE ? items.length
0061: : GROW_SIZE];
0062:
0063: for (int i = 0; i < numOfItems; i++) {
0064: itemLFs[i] = (ItemLFImpl) items[i].getLF();
0065: }
0066:
0067: // right now we have the same number of views as items
0068: numOfLFs = numOfItems;
0069: }
0070: }
0071:
0072: /**
0073: * Creates <code>FormLF</code> for the passed in screen.
0074: * Passed in <code>ItemLF</code> is added as the only itemLF present.
0075: * This constructor is used by <code>List</code> and <code>TextBox</code>.
0076: *
0077: * @param screen the <code>Screen</code> object associated with this
0078: * <code>FormLFImpl</code>
0079: * @param item the <code>Item</code> to be added to this screen
0080: */
0081: FormLFImpl(Screen screen, Item item) {
0082: super (screen);
0083:
0084: itemLFs = new ItemLFImpl[1];
0085: itemLFs[0] = (ItemLFImpl) item.getLF();
0086: numOfLFs = 1;
0087:
0088: // Initialize the in-out rect for Item traversal
0089: visRect = new int[4];
0090: }
0091:
0092: // ************************************************************
0093: // public methods - FormLF interface implementation
0094: // ************************************************************
0095:
0096: /**
0097: * Returns the width in pixels of the displayable area available for
0098: * items.
0099: * The value may depend on how the device uses the screen and may be
0100: * affected by the presence or absence of the ticker, title,
0101: * or commands.
0102: * The <code>Item</code>s of the <code>Form</code> are
0103: * laid out to fit within this width.
0104: *
0105: * @return the width of the <code>Form</code> in pixels
0106: */
0107: public int lGetWidth() {
0108: return width;
0109: }
0110:
0111: /**
0112: * Returns the height in pixels of the displayable area available
0113: * for items.
0114: * This value is the height of the form that can be displayed without
0115: * scrolling.
0116: * The value may depend on how the device uses the screen and may be
0117: * affected by the presence or absence of the ticker, title,
0118: * or commands.
0119: *
0120: * @return the height of the displayable area of the
0121: * <code>Form</code> in pixels
0122: */
0123: public int lGetHeight() {
0124: return height;
0125: }
0126:
0127: /**
0128: * Set the current traversal location to the given <code>Item</code>.
0129: * This call has no effect if the given <code>Item</code> is the
0130: * current traversal item, or if the given <code>Item</code> is not
0131: * part of this <code>Form</code>. Note that null can be passed in
0132: * clear the previously set current item.
0133: *
0134: * @param item the <code>Item</code> to make the current traversal item
0135: */
0136: public void uItemMakeVisible(Item i) {
0137:
0138: synchronized (Display.LCDUILock) {
0139:
0140: if (i == null) {
0141: pendingCurrentItem = null;
0142: }
0143:
0144: /**
0145: * Display could be made visible using display.setCurrentItem()
0146: * call. In those cases foregroung will be granted after there
0147: * there is a screen change event and after uItemMakeVisible()
0148: * is called. In such cases we need to set pendingCurrentItem and
0149: * call uItemMakeVisible() again when the FormLF changes its
0150: * state to SHOWN.
0151: */
0152: if (state != SHOWN) {
0153: pendingCurrentItem = i;
0154: return;
0155: }
0156: }
0157: }
0158:
0159: /**
0160: * Notifies look&feel object of an item set in the corresponding
0161: * <code>Form</code>.
0162: *
0163: * @param itemNum the index of the item set
0164: * @param item the item set in the corresponding <code>Form</code>
0165: */
0166: public void lSet(int itemNum, Item item) {
0167:
0168: itemLFs[itemNum] = (ItemLFImpl) item.getLF();
0169: itemsModified = true;
0170:
0171: // Focus index remains at the same location
0172:
0173: lRequestInvalidate();
0174: }
0175:
0176: /**
0177: * Notifies look&feel object of an item inserted in the corresponding
0178: * <code>Form</code>.
0179: *
0180: * @param itemNum the index of the inserted item
0181: * @param item the item inserted in the corresponding <code>Form</code>
0182: */
0183: public void lInsert(int itemNum, Item item) {
0184: if (itemLFs.length == numOfLFs) {
0185: ItemLFImpl newItemLFs[] = new ItemLFImpl[numOfLFs
0186: + GROW_SIZE];
0187: System.arraycopy(itemLFs, 0, newItemLFs, 0, itemNum);
0188: System.arraycopy(itemLFs, itemNum, newItemLFs, itemNum + 1,
0189: numOfLFs - itemNum);
0190: itemLFs = newItemLFs;
0191: } else {
0192: // if we're not appending
0193: if (itemNum != numOfLFs) {
0194: System.arraycopy(itemLFs, itemNum, itemLFs,
0195: itemNum + 1, numOfLFs - itemNum);
0196: }
0197: }
0198:
0199: itemLFs[itemNum] = (ItemLFImpl) item.getLF();
0200:
0201: numOfLFs++;
0202: itemsModified = true;
0203: // Focus remains on the same item
0204: if (traverseIndex >= itemNum) {
0205: traverseIndex++;
0206: }
0207: lRequestInvalidate();
0208: }
0209:
0210: /**
0211: * Notifies look&feel object of an item deleted in the corresponding
0212: * <code>Form</code>.
0213: *
0214: * @param itemNum the index of the deleted item
0215: * @param deleteditem the item deleted in the corresponding form
0216: */
0217: public void lDelete(int itemNum, Item deleteditem) {
0218:
0219: // if the previous item has new line after, or the next item has
0220: // new line before, and it's not the last item,
0221: // than we could just mark the next item as actualBoundsInvalid[Y]
0222: if (itemNum < (numOfLFs - 1)) {
0223: if (((itemNum > 0) && (itemLFs[itemNum - 1].equateNLA()) || itemLFs[itemNum + 1]
0224: .equateNLB())
0225: && itemLFs[itemNum + 1].isNewLine) {
0226:
0227: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0228: Logging.report(Logging.INFORMATION,
0229: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0230: " setting actualBoundsInvalid[Y] #"
0231: + (itemNum + 1));
0232: if (itemNum > 0) {
0233: Logging.report(Logging.INFORMATION,
0234: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0235: " | itemLFs[itemNum-1] = "
0236: + itemLFs[itemNum - 1]);
0237: }
0238: Logging
0239: .report(Logging.INFORMATION,
0240: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0241: " | itemLFs[itemNum] = "
0242: + itemLFs[itemNum]);
0243: if (itemNum < numOfLFs - 1) {
0244: Logging.report(Logging.INFORMATION,
0245: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0246: " | itemLFs[itemNum+1] = "
0247: + itemLFs[itemNum + 1]);
0248: }
0249: }
0250: itemLFs[itemNum + 1].actualBoundsInvalid[Y] = true;
0251: } else {
0252: itemLFs[itemNum + 1].actualBoundsInvalid[X] = true;
0253: }
0254: }
0255:
0256: if (traverseIndex == itemNum) {
0257: lastTraverseItem = itemLFs[traverseIndex];
0258: }
0259:
0260: if (traverseIndex >= 0 && traverseIndex >= itemNum) {
0261: traverseIndex--;
0262: }
0263:
0264: numOfLFs--;
0265: itemsModified = true;
0266:
0267: if (itemNum < numOfLFs) {
0268: System.arraycopy(itemLFs, itemNum + 1, itemLFs, itemNum,
0269: numOfLFs - itemNum);
0270: }
0271:
0272: // Delete reference to the last item view
0273: // that was left after array copy
0274: itemLFs[numOfLFs] = null;
0275:
0276: if (pendingCurrentItem == deleteditem) {
0277: pendingCurrentItem = null;
0278: }
0279:
0280: lRequestInvalidate();
0281: }
0282:
0283: /**
0284: * Notifies look&feel object that all items are deleted in
0285: * the corresponding <code>Form</code>.
0286: */
0287: public void lDeleteAll() {
0288: if (traverseIndex != -1) {
0289: lastTraverseItem = itemLFs[traverseIndex];
0290: }
0291: // Dereference all ItemLFImpls so they can be GC'ed
0292: while (numOfLFs > 0) {
0293: itemLFs[--numOfLFs] = null;
0294: }
0295: traverseIndex = -1;
0296: itemsModified = true;
0297: pendingCurrentItem = null;
0298: lRequestInvalidate();
0299: }
0300:
0301: /**
0302: * This method is responsible for:
0303: * (1) Re-validate the contents of this <code>Form</code>, possibly due
0304: * to an individual item
0305: * (2) setup the viewable/scroll position
0306: * (3) repaint the currently visible <code>Item</code>s
0307: */
0308: public void uCallInvalidate() {
0309:
0310: super .uCallInvalidate();
0311:
0312: int new_width = Display.getScreenWidth0();
0313: int new_height = Display.getScreenHeight0();
0314:
0315: // It could be that setCurrentItem() was called and we
0316: // have done an 'artificial' traversal. In this case, we
0317: // manually call traverseOut() on the last traversed item
0318: // if there is one.
0319:
0320: if (lastTraverseItem != null) {
0321: try {
0322: lastTraverseItem.uCallTraverseOut();
0323: } catch (Throwable t) {
0324: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
0325: Logging.report(Logging.WARNING,
0326: LogChannels.LC_HIGHUI,
0327: "Throwable while traversing out");
0328: }
0329: }
0330: lastTraverseItem = null;
0331: updateCommandSet();
0332: }
0333:
0334: synchronized (Display.LCDUILock) {
0335: // Do nothing if paint is suspended in current Display
0336: if (!lIsShown()) {
0337: return;
0338: }
0339: // Do not reset the Form from the top since this is an update
0340: resetToTop = false;
0341: }
0342:
0343: // Setup items and show form native resource
0344: // SYNC NOTE:
0345: // called without LCDUILock, since it may end up calling into a MIDlet
0346: if (new_width != width || new_height != height) {
0347: width = new_width;
0348: height = new_height;
0349: firstShown = true;
0350: }
0351: // IMPL NOTES: Remove this line after UDPATE_LAYOUT is fixed
0352: firstShown = true;
0353: // Update contents
0354: uShowContents(false);
0355:
0356: // SYNC NOTE:
0357: // 1. We are on event dispatch thread, currentDisplay won't change.
0358: // 2. We are on event dispatch thread, call paint synchronously.
0359: // 3. Since we could call into app's functions, like traverse(),
0360: // showNotify() and paint(), do this outside LCDUILock block.
0361: currentDisplay.callPaint(0, 0, width, height, null);
0362: }
0363:
0364: /**
0365: * Paint the contents of this <code>Form</code>.
0366: *
0367: * @param g the <code>Graphics</code> object to paint on
0368: * @param target the target Object of this repaint
0369: */
0370: public void uCallPaint(Graphics g, Object target) {
0371: int count;
0372: synchronized (Display.LCDUILock) {
0373: // super.lCallPaint(g, target); -- obsolete
0374:
0375: if (numOfLFs == 0) {
0376: return;
0377: }
0378:
0379: // SYNC NOTE: since we may call into CustomItem.paint(),
0380: // we have to do it outside LCDUILock. So make a copy of the
0381: // itemLFs array.
0382: if (target instanceof Item) {
0383: if (((Item) target).owner == this .owner) {
0384: ensureDispatchItemArray(1);
0385: dispatchItemLFs[0] = (ItemLFImpl) ((Item) target).itemLF;
0386: count = 1;
0387: } else {
0388: count = 0;
0389: }
0390: } else {
0391: ensureDispatchItemArray(numOfLFs);
0392: System.arraycopy(itemLFs, 0, dispatchItemLFs, 0,
0393: numOfLFs);
0394: count = numOfLFs;
0395: }
0396: }
0397:
0398: // Call paint on the copied itemLFs array
0399: for (int i = 0; i < count; i++) {
0400: uPaintItem(dispatchItemLFs[i], g);
0401: }
0402:
0403: // Dereference ItemLFImpl objects in dispatchItemLFs
0404: // But leave the shrinking to uCallHide
0405: resetDispatchItemArray(false);
0406: }
0407:
0408: /**
0409: * Notify this <code>Form</code> that it is being shown.
0410: */
0411: public void uCallShow() {
0412: // Create native resources with title and ticker
0413: super .uCallShow();
0414: // Setup items and show form native resource
0415: // SYNC NOTE: May call into app code to collect sizes.
0416: // Call it outside LCDUILock
0417: uShowContents(true);
0418:
0419: synchronized (Display.LCDUILock) {
0420:
0421: if (pendingCurrentItem != null) {
0422: lScrollToItem(pendingCurrentItem);
0423: pendingCurrentItem = null;
0424: }
0425: }
0426: }
0427:
0428: /**
0429: * Notify this <code>Form</code> that it is being hidden.
0430: */
0431:
0432: public void uCallHide() {
0433:
0434: synchronized (Display.LCDUILock) {
0435: pendingCurrentItem = null;
0436: }
0437:
0438: uCallItemHide();
0439: // Delete Form's native resource including title and ticker
0440: super .uCallHide();
0441: }
0442:
0443: /**
0444: * Notify this <code>Form</code> that it is being frozen.
0445: */
0446:
0447: public void uCallFreeze() {
0448:
0449: if (state == SHOWN) {
0450: resetToTop = false;
0451: }
0452: uCallItemHide();
0453: // Delete Form's native resource including title and ticker
0454: super .uCallFreeze();
0455: }
0456:
0457: /**
0458: * Hide items when Form is frozen or hidden
0459: */
0460: void uCallItemHide() {
0461: // No more than one custom item can be in focus at a time
0462: ItemLFImpl customItemToTraverseOut = null;
0463: ItemLFImpl[] itemsCopy = null;
0464: int count = 0;
0465:
0466: synchronized (Display.LCDUILock) {
0467:
0468: // We need to loop through our Items to identify those
0469: // that traverseOut and hideNotify need to be called.
0470: //
0471: // SYNC NOTE:
0472: // We cannot call into app code while holding LCDUILock.
0473: // For CustomItems, we postpone calls to outside this
0474: // sync. block.
0475:
0476: itemsCopy = new ItemLFImpl[numOfLFs];
0477:
0478: for (int x = 0; x < numOfLFs; x++) {
0479: try {
0480: // callTraverseOut needs to happen on the item in focus
0481: if (itemLFs[x].hasFocus) {
0482: if (itemLFs[x] instanceof CustomItemLFImpl) {
0483: customItemToTraverseOut = itemLFs[x];
0484: } else {
0485: // SYNC NOTE: Items other than CustomItem do not
0486: // call into app code in their traverseOut.
0487: // We can call it while holding the LCDUILock.
0488: itemLFs[x].uCallTraverseOut();
0489: }
0490: }
0491:
0492: itemLFs[x].lHideNativeResource();
0493: // Free native resource of each ItemLF
0494: itemLFs[x].deleteNativeResource();
0495:
0496: // Items that are visible in the viewport
0497: // should set their visibleInViewport flag to false and
0498: // CustomItems should call app's hideNotify() as well
0499: if (itemLFs[x].visibleInViewport) {
0500: if (itemLFs[x] instanceof CustomItemLFImpl) {
0501: // Remember it in temporary array
0502: itemsCopy[count++] = itemLFs[x];
0503: } else {
0504: itemLFs[x].lCallHideNotify();
0505: }
0506: }
0507:
0508: } catch (Throwable t) {
0509: // do nothing... move on
0510: }
0511: }
0512:
0513: } // synchronized
0514:
0515: // Call CustomItem traverseOut outside LCDUILock
0516: if (customItemToTraverseOut != null) {
0517: customItemToTraverseOut.uCallTraverseOut();
0518: }
0519:
0520: // Call CustomItem hideNotify outside LCDUILock
0521: for (count--; count >= 0; count--) {
0522: itemsCopy[count].uCallHideNotify();
0523: }
0524: }
0525:
0526: /**
0527: * Called by <code>Display</code> to notify an <code>ItemLF</code>
0528: * in current <code>FormLF</code> of a change in its peer state.
0529: * If the the peerId matches the nativeId of this <code>FormLF</code>,
0530: * uViewportChanged() will be called to process the scroll
0531: * notification.
0532: * Otherwise, if there is an <code>ItemLF</code> that matches the peerId,
0533: * the <code>ItemLF</code> will be called to process this notification.
0534: * Otherwise, this is treated as a special notification to this
0535: * <code>FormLF</code> upon focus changed between items, and
0536: * parameter 'hint' will contain the index of the new current
0537: * <code>ItemLF</code>.
0538: *
0539: * @param modelVersion the version of the peer's data model
0540: * @param peerId one of the following:
0541: * <ul> <li> the id of this <code>FormLF</code> if viewport
0542: * has changed in the corresponding native resource
0543: * of this <code>FormLF</code>
0544: * (current scroll position is passed as hint)
0545: * <li> the id of the <code>ItemLF</code> whose peer state
0546: * has changed
0547: * <li> <code>INVALID_NATIVE_ID</code> if a focus
0548: * changed notification.
0549: * @param hint some value that is interpreted only between the peers
0550: */
0551: public void uCallPeerStateChanged(int modelVersion, int peerId,
0552: int hint) {
0553: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0554: Logging.report(Logging.INFORMATION,
0555: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0556: "-=-=- FormLF: dsPeerStateChanged " + peerId + "/"
0557: + hint);
0558: }
0559:
0560: int notifyType;
0561: ItemLFImpl oldFocus = null, itemLFToNotify = null;
0562:
0563: synchronized (Display.LCDUILock) {
0564: if (modelVersion != super .modelVersion) {
0565: return; // model version out of sync, ignore the event
0566: }
0567:
0568: // If not matching ItemLF, this is a focus changed notification
0569: // 'hint' is the id of the new focused itemLF
0570: if (peerId == INVALID_NATIVE_ID) {
0571: notifyType = 1; // focus changed
0572: oldFocus = getItemInFocus();
0573: itemLFToNotify = id2Item(hint);
0574: } else if (peerId == nativeId) {
0575: // there is a scroll event from the native peer,
0576: // we call show/hide Notify outside of the synchronized block
0577: notifyType = 2; // viewport changed
0578: } else {
0579: // peerId identified the ItemLF, notify it
0580: notifyType = 3; // item peer state changed
0581: itemLFToNotify = id2Item(peerId);
0582: }
0583: }
0584:
0585: // SYNC NOTE: Following calls may end in app code.
0586: // So do it outside LCDUILock
0587: switch (notifyType) {
0588:
0589: case 1: // Focus notification
0590: uFocusChanged(itemLFToNotify);
0591: break;
0592:
0593: case 2: // Scrolling notification
0594: // 'hint' is the new viewport position
0595: uViewportChanged(hint, hint + viewportHeight);
0596:
0597: // Spec requires CustomItem's paint() to be called after
0598: // its showNotify() is called and before hideNotify()
0599: // it is safe to pass null as both parameters
0600: // because only CustomItems will be repainted and they
0601: // use their own Graphics
0602: uCallPaint(null, null);
0603: break;
0604:
0605: case 3: // Item peer notification
0606: if (itemLFToNotify != null
0607: && itemLFToNotify.uCallPeerStateChanged(hint)) {
0608: // Notify the itemStateListener
0609: owner.uCallItemStateChanged(itemLFToNotify.item);
0610: }
0611: break;
0612:
0613: default:
0614: // for safety/completeness.
0615: Logging.report(Logging.WARNING,
0616: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0617: "FormLFImpl: notifyType=" + notifyType);
0618: break;
0619: }
0620: }
0621:
0622: /**
0623: * Update current item index and notify related item of the change.
0624: * Item specific abstract commands will also be shown.
0625: *
0626: * @param newFocus the new item in focus.
0627: */
0628: private void uFocusChanged(ItemLFImpl newFocus) {
0629: ItemLFImpl oldFocus = null;
0630: synchronized (Display.LCDUILock) {
0631: pendingCurrentItem = null;
0632: int focusIndex = item2Index(newFocus); // Could be -1
0633: if (focusIndex == traverseIndex) {
0634: oldFocus = newFocus;
0635: } else {
0636: oldFocus = traverseIndex > 0 ? itemLFs[traverseIndex]
0637: : null;
0638: traverseIndex = focusIndex;
0639: }
0640: }
0641:
0642: if (oldFocus != newFocus) {
0643: if (oldFocus != null) {
0644: oldFocus.uCallTraverseOut();
0645: }
0646: if (newFocus != null) {
0647: itemTraverse = uCallItemTraverse(newFocus,
0648: CustomItem.NONE);
0649: }
0650: updateCommandSet();
0651: // call paint for custom items
0652: uRequestPaint();
0653: }
0654: }
0655:
0656: /**
0657: * This method is used in repaint, in order to determine the translation
0658: * of the draw coordinates.
0659: *
0660: * @return <code>true</code>, if the scroll responsibility is on the
0661: * native platform.
0662: * <code>false</code>, if the scroll is done at Java level.
0663: */
0664: public boolean uIsScrollNative() {
0665: // only native form overrides this and returns true
0666: return true;
0667: }
0668:
0669: // *****************************************************
0670: // Package private methods
0671: // *****************************************************
0672:
0673: /**
0674: * Check the key and return true if it's navigation key
0675: * @param key key code
0676: * @return true if the key is navigation key false otherwise
0677: */
0678: private boolean isNavigationKey(int key) {
0679: return key == Canvas.UP || key == Canvas.LEFT
0680: || key == Canvas.DOWN || key == Canvas.RIGHT;
0681: }
0682:
0683: /**
0684: * Set status of screen rotation
0685: * @param newStatus
0686: * @return
0687: */
0688: public boolean uSetRotatedStatus(boolean newStatus) {
0689: boolean status = super .uSetRotatedStatus(newStatus);
0690: if (status) {
0691: firstShown = true;
0692: }
0693: return status;
0694: }
0695:
0696: /**
0697: * Handle a key press.
0698: *
0699: * @param keyCode the key code of the key which was pressed
0700: */
0701: void uCallKeyPressed(int keyCode) {
0702: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0703: Logging.report(Logging.INFORMATION,
0704: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0705: "got callKeyPressed: " + keyCode);
0706: }
0707: int dir = KeyConverter.getGameAction(keyCode);
0708: if (isNavigationKey(dir)) {
0709: uTraverse(dir);
0710: } else {
0711: ItemLFImpl v = null;
0712: synchronized (Display.LCDUILock) {
0713: v = getItemInFocus();
0714: }
0715:
0716: // pass the keypress onto the current item
0717: if (v != null && v instanceof CustomItemLFImpl) {
0718:
0719: // NOTE: customItem.getInteractionModes() determines
0720: // the supported events. The Zaurus platform implementation
0721: // does not support traversal in any direction.
0722: // if it is desired to support horizontal and/or vertical
0723: // traversal, than the proper flags must be set accordingly.
0724:
0725: // pass all key events to the CustomItem, including arrows
0726: v.uCallKeyPressed(keyCode);
0727: }
0728: }
0729: }
0730:
0731: /**
0732: * Handle a key release event.
0733: *
0734: * @param keyCode the key which was released
0735: */
0736: void uCallKeyReleased(int keyCode) {
0737: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0738: Logging.report(Logging.INFORMATION,
0739: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0740: "got callKeyReleased: " + keyCode);
0741: }
0742:
0743: if (!isNavigationKey(KeyConverter.getGameAction(keyCode))) {
0744: ItemLFImpl v = null;
0745: synchronized (Display.LCDUILock) {
0746: v = getItemInFocus();
0747: } // synchronized
0748:
0749: // SYNC NOTE: formMode can only change as a result of a
0750: // traversal, which can only occur serially on the event
0751: // thread, so its safe to use it outside of the lock
0752:
0753: if (v != null && v instanceof CustomItemLFImpl) {
0754: v.uCallKeyReleased(keyCode);
0755: }
0756: }
0757: }
0758:
0759: /**
0760: * Handle a key repeat.
0761: *
0762: * @param keyCode the key code of the key which was repeated
0763: */
0764: void uCallKeyRepeated(int keyCode) {
0765: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0766: Logging.report(Logging.INFORMATION,
0767: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0768: "got callKeyRepeated: " + keyCode);
0769: }
0770: if (isNavigationKey(KeyConverter.getGameAction(keyCode))) {
0771: uCallKeyPressed(keyCode);
0772: } else {
0773: ItemLFImpl v = null;
0774: synchronized (Display.LCDUILock) {
0775: v = getItemInFocus();
0776: } // synchronized
0777:
0778: // SYNC NOTE: formMode can only change as a result of a
0779: // traversal, which can only occur serially on the event
0780: // thread, so its safe to use it outside of the lock
0781:
0782: if (v != null && v instanceof CustomItemLFImpl) {
0783: v.uCallKeyRepeated(keyCode);
0784: }
0785: }
0786: }
0787:
0788: /**
0789: * Handle a pointer pressed event.
0790: *
0791: * @param x The x coordinate of the press
0792: * @param y The y coordinate of the press
0793: */
0794: void uCallPointerPressed(int x, int y) {
0795: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0796: Logging.report(Logging.INFORMATION,
0797: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0798: "got callPointerPressed: " + x + "," + y);
0799: }
0800:
0801: ItemLFImpl v = null;
0802:
0803: synchronized (Display.LCDUILock) {
0804:
0805: v = getItemInFocus();
0806:
0807: // stop here if no current item to handle the key
0808: if (v == null) {
0809: return;
0810: }
0811:
0812: } // synchronized
0813:
0814: // SYNC NOTE: formMode can only change as a result of a
0815: // traversal, which can only occur serially on the event
0816: // thread, so its safe to use it outside of the lock
0817:
0818: if (v instanceof CustomItemLFImpl) {
0819: v.uCallPointerPressed(x, y);
0820: }
0821: }
0822:
0823: /**
0824: * Handle a pointer released event.
0825: *
0826: * @param x The x coordinate of the release
0827: * @param y The y coordinate of the release
0828: */
0829: void uCallPointerReleased(int x, int y) {
0830: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0831: Logging.report(Logging.INFORMATION,
0832: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0833: "got callPointerReleased: " + x + "," + y);
0834: }
0835:
0836: ItemLFImpl v = null;
0837:
0838: synchronized (Display.LCDUILock) {
0839:
0840: v = getItemInFocus();
0841:
0842: // stop here if no current item to handle the key
0843: if (v == null) {
0844: return;
0845: }
0846:
0847: } // synchronized
0848:
0849: // SYNC NOTE: formMode can only change as a result of a
0850: // traversal, which can only occur serially on the event
0851: // thread, so its safe to use it outside of the lock
0852:
0853: if (v instanceof CustomItemLFImpl) {
0854: v.uCallPointerReleased(x, y);
0855: }
0856: }
0857:
0858: /**
0859: * Handle a pointer dragged event.
0860: *
0861: * @param x The x coordinate of the drag
0862: * @param y The y coordinate of the drag
0863: */
0864: void uCallPointerDragged(int x, int y) {
0865: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0866: Logging.report(Logging.INFORMATION,
0867: LogChannels.LC_HIGHUI_FORM_LAYOUT,
0868: "got callPointerDragged: " + x + "," + y);
0869: }
0870:
0871: ItemLFImpl v = null;
0872:
0873: synchronized (Display.LCDUILock) {
0874:
0875: v = getItemInFocus();
0876:
0877: // stop here if no current item to handle the key
0878: if (v == null) {
0879: return;
0880: }
0881:
0882: } // synchronized
0883:
0884: // SYNC NOTE: formMode can only change as a result of a
0885: // traversal, which can only occur serially on the event
0886: // thread, so its safe to use it outside of the lock
0887:
0888: if (v instanceof CustomItemLFImpl) {
0889: v.uCallPointerDragged(x, y);
0890: }
0891: }
0892:
0893: /**
0894: * Gets item currently in focus.
0895: *
0896: * @return the item currently in focus in this form;
0897: * if there are no items in focus, <code>null</code> is returned
0898: */
0899: public Item lGetCurrentItem() {
0900: ItemLFImpl v = getItemInFocus();
0901:
0902: if (v == null) {
0903: return null;
0904: }
0905:
0906: return v.item;
0907: }
0908:
0909: /**
0910: * Paint an item.
0911: *
0912: * @param itemLF the <code>ItemLFImpl</code> to paint
0913: * @param g the <code>Graphics</code> object to paint to
0914: */
0915: void uPaintItem(ItemLFImpl itemLF, Graphics g) {
0916: synchronized (Display.LCDUILock) {
0917: // NOTE: Its possible, that an Item is in an invalid state
0918: // during a requested repaint. Its ok to simply return,
0919: // because it means there is a validation event coming on
0920: // the event thread. When the form re-validates, the Item
0921: // will be given a proper bounds and will be repainted
0922: if (itemLF.actualBoundsInvalid[X]
0923: || itemLF.actualBoundsInvalid[Y]
0924: || itemLF.actualBoundsInvalid[WIDTH]
0925: || itemLF.actualBoundsInvalid[HEIGHT]
0926: || itemLF.nativeId == INVALID_NATIVE_ID) {
0927: return;
0928: }
0929: }
0930:
0931: // repaint only visible in viewport items
0932: if (itemLF.visibleInViewport) {
0933: // CustomItem uses its own off screen graphics for painting
0934: // and the rest of the items do not need to repaint
0935: itemLF.uCallPaint(null, itemLF.bounds[WIDTH],
0936: itemLF.bounds[HEIGHT]);
0937: }
0938: }
0939:
0940: /**
0941: * Paint an <code>Item</code> contained in this <code>Screen</code>.
0942: * The <code>Item</code> requests a paint in its own coordinate space.
0943: * <code>Screen</code> translates those coordinates into the overall
0944: * coordinate space and schedules the repaint
0945: *
0946: * @param item the <code>Item</code> requesting the repaint
0947: * @param x the x-coordinate of the origin of the dirty region
0948: * @param y the y-coordinate of the origin of the dirty region
0949: * @param w the width of the dirty region
0950: * @param h the height of the dirty region
0951: */
0952: void lRequestPaintItem(Item item, int x, int y, int w, int h) {
0953:
0954: ItemLFImpl iLF = (ItemLFImpl) item.getLF();
0955:
0956: lRequestPaint(iLF.bounds[X] + x, iLF.bounds[Y] + y, w, h, item);
0957: }
0958:
0959: /**
0960: * Create native resource for this <code>Form</code>.
0961: * <code>Item</code>s' resources are not created here.
0962: */
0963: void createNativeResource() {
0964: setScrollPosition0(0);
0965: nativeId = createNativeResource0(owner.title,
0966: owner.ticker == null ? null : owner.ticker.getString());
0967: }
0968:
0969: /**
0970: * Service method - returns the <code>ItemLFImpl</code> that has focus.
0971: *
0972: * @return the current <code>ItemLFImpl</code>, or <code>null</code>
0973: * if there is no current.
0974: */
0975: ItemLFImpl getItemInFocus() {
0976: if (traverseIndex < 0) {
0977: return null;
0978: } else {
0979: return itemLFs[traverseIndex];
0980: }
0981: }
0982:
0983: // ***************************************************************
0984:
0985: /**
0986: * Scroll to show an <code>Item</code> and give focus to it if possible.
0987: *
0988: * @param nativeId native resource id of the <code>Form</code>
0989: * @param itemId native resource id for the focused <code>Item</code>
0990: * @param yOffset offset for the y co-ordinate of the
0991: * focused <code>Item</code>
0992: */
0993: native void setCurrentItem0(int nativeId, int itemId, int yOffset);
0994:
0995: /**
0996: * Current Y position in a scrollable form.
0997: *
0998: * @return current scroll Y position
0999: */
1000: native int getScrollPosition0();
1001:
1002: /**
1003: * Set Y position in a scrollable form.
1004: *
1005: */
1006: native void setScrollPosition0(int pos);
1007:
1008: /**
1009: * Create the native resource of this <code>Form</code>.
1010: *
1011: * @param title the title text of the <code>Form</code>
1012: * @param tickerText the text of the <code>Ticker</code>,
1013: * <code>Null</code> if no ticker.
1014: *
1015: * @return native resource id
1016: *
1017: * @exception OutOfMemoryException - if out of native resource
1018: */
1019: private native int createNativeResource0(String title,
1020: String tickerText);
1021:
1022: /**
1023: * Populate the native <code>Form</code> with visible <code>ItemLF</code>s
1024: * and then show.
1025: *
1026: * @param nativeId native resource id
1027: * @param modelVersion initial model version number for this visible period
1028: * @param w width of the virtual Form without scrolling
1029: * @param h height of the virtual Form without scrolling
1030: *
1031: * @exception OutOfMemoryException - if out of native resource
1032: */
1033: private native void showNativeResource0(int nativeId,
1034: int modelVersion, int w, int h);
1035:
1036: /**
1037: * Current viewport height in the native resource
1038: *
1039: * @return current viewport height
1040: */
1041: private native int getViewportHeight0();
1042:
1043: /**
1044: * Make sure all items have native resource and
1045: * all <code>CustomItem</code>s have their minimum and preferred sizes
1046: * cached.
1047: */
1048: private void uEnsureResourceAndRequestedSizes() {
1049: int i, count = 0;
1050: ItemLFImpl[] itemsCopy = null;
1051:
1052: synchronized (Display.LCDUILock) {
1053: if (nativeId == INVALID_NATIVE_ID) {
1054: return;
1055: }
1056: // Make a temporary copy of ItemLFs we need to collect sizes from
1057: itemsCopy = new ItemLFImpl[numOfLFs];
1058:
1059: // Make sure each Item has native resource
1060: // and remember all the CustomItemLFImpls
1061: for (i = 0; i < numOfLFs; i++) {
1062: if (itemLFs[i].nativeId == INVALID_NATIVE_ID) {
1063: itemLFs[i].createNativeResource(super .nativeId);
1064: // layout(UPDATE_LAYOUT) later will not call
1065: // setSize/setLocation on an ItemLF that has valid bounds
1066: // already. But the native resource is recreated
1067: // above, we make up these two calls here.
1068: itemLFs[i].initNativeResource();
1069: // Every native resource is default to be visible in
1070: // viewport. It's up to the native container to maintain
1071: // viewport.
1072: itemLFs[i].lShowNativeResource();
1073: }
1074:
1075: if (itemLFs[i] instanceof CustomItemLFImpl) {
1076: // Remember this in temporary array
1077: itemsCopy[count++] = itemLFs[i];
1078: }
1079: }
1080: } // synchronized
1081:
1082: // Collect min and preferred sizes from CustomItems
1083: // SYNC NOTE: This may call into app code like
1084: // CustomItem.getPrefContentWidth(). So do it outside LCDUILock
1085: for (i = 0; i < count; i++) {
1086: ((CustomItemLFImpl) itemsCopy[i]).uCallSizeRefresh();
1087: }
1088:
1089: }
1090:
1091: /**
1092: * Show all items and give focus to current item.
1093: * SYNC NOTE: caller must NOT hold LCDUILock since this function may
1094: * call into app code like getPrefContentWidth(), sizeChanged or paint()
1095: * of CustomItem.
1096: * @param initialTraverse the flag to indicate this is the initial
1097: * traversal focus setup when this Form is being shown
1098: */
1099: private void uShowContents(boolean initialTraverse) {
1100:
1101: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1102: Logging.report(Logging.INFORMATION,
1103: LogChannels.LC_HIGHUI_FORM_LAYOUT,
1104: "\nFormLFImpl: showContents()");
1105: }
1106:
1107: synchronized (Display.LCDUILock) {
1108: if (firstShown) {
1109: for (int i = 0; i < numOfLFs; i++) {
1110: itemLFs[i].cachedWidth = ItemLFImpl.INVALID_SIZE;
1111: }
1112: }
1113: }
1114:
1115: // Ensure resources for all items and requested sizes for CustomItems
1116: uEnsureResourceAndRequestedSizes();
1117:
1118: ItemLFImpl[] itemsCopy = null;
1119: int itemsCopyCount = 0;
1120: int traverseIndexCopy = -1;
1121:
1122: // Layout
1123: synchronized (Display.LCDUILock) {
1124: if (nativeId == INVALID_NATIVE_ID) {
1125: return;
1126: }
1127:
1128: if (firstShown) {
1129: LayoutManager.instance().lLayout(
1130: LayoutManager.FULL_LAYOUT, itemLFs, numOfLFs,
1131: width, height, viewable);
1132: firstShown = false;
1133:
1134: } else {
1135: LayoutManager.instance().lLayout(
1136: LayoutManager.UPDATE_LAYOUT, itemLFs, numOfLFs,
1137: width, height, viewable);
1138: }
1139:
1140: if (resetToTop) {
1141: traverseIndex = -1;
1142: setScrollPosition0(0);
1143: }
1144:
1145: itemsCopy = new ItemLFImpl[numOfLFs];
1146: itemsCopyCount = numOfLFs;
1147: System.arraycopy(itemLFs, 0, itemsCopy, 0, numOfLFs);
1148: traverseIndexCopy = traverseIndex;
1149: itemsModified = false;
1150:
1151: // Set native Form's window viewable size (logical Form size)
1152: // and make it shown if not yet
1153: showNativeResource0(nativeId, modelVersion, width,
1154: viewable[HEIGHT]);
1155:
1156: // update viewport height
1157: viewportHeight = getViewportHeight0();
1158:
1159: // correct scroll position if any
1160: if (viewable[HEIGHT] <= viewportHeight) {
1161: // if viewable height is less than viewport
1162: // height just reset viewable y
1163: setScrollPosition0(0);
1164: } else if (getScrollPosition0() > (viewable[HEIGHT] - viewportHeight)) {
1165: // if viewable y exceeds the max value set it to the max
1166: // height just reset viewable y
1167: setScrollPosition0(viewable[HEIGHT] - viewportHeight);
1168: }
1169:
1170: } // synchronized
1171:
1172: uInitItemsInViewport(CustomItem.NONE, itemsCopy,
1173: traverseIndexCopy);
1174:
1175: if (initialTraverse) {
1176: updateCommandSet();
1177: }
1178:
1179: for (int index = 0; index < itemsCopyCount; index++) {
1180: if (itemsCopy[index].sizeChanged) {
1181: itemsCopy[index].uCallSizeChanged(
1182: itemsCopy[index].bounds[WIDTH],
1183: itemsCopy[index].bounds[HEIGHT]);
1184: itemsCopy[index].sizeChanged = false;
1185: }
1186: }
1187: }
1188:
1189: /**
1190: * Perform a traversal. This method handles traversal within a
1191: * "page" after the initial page has been shown via the
1192: * uInitItemsInViewport() routine. At the point this method is
1193: * called, the following conditions must be true:
1194: *
1195: * 1.) There are no interactive items at all on the current page
1196: * or
1197: * 2.) There is at least one interactive item on the current page
1198: * and the traverseIndex is currently set to that item. In this
1199: * case, itemTraverse represents the return value of that item's
1200: * initial traverse() call.
1201: *
1202: * Based on these conditions, this method will either:
1203: *
1204: * 1.) Continue the internal traversal on the current item (scrolling
1205: * as necessary to display the item's internal traversal location)
1206: * or
1207: * 2.) Perform a traversal to the next interactive item on the page
1208: * or
1209: * 3.) Perform a page flip (uScrollViewport()) and call the
1210: * uInitItemsInViewport() routine to select an appropriate
1211: * traversal item
1212: *
1213: * SYNC NOTE: Maybe call into CustomItem.traverse().
1214: * So caller must not hold LCDUILock.
1215: *
1216: * @param dir the direction of traversal
1217: */
1218: void uTraverse(int dir) {
1219:
1220: ItemLFImpl[] itemsCopy;
1221: int traverseIndexCopy;
1222: synchronized (Display.LCDUILock) {
1223: itemsCopy = new ItemLFImpl[numOfLFs];
1224: traverseIndexCopy = traverseIndex;
1225: System.arraycopy(itemLFs, 0, itemsCopy, 0, numOfLFs);
1226: itemsModified = false;
1227: }
1228:
1229: // itemTraverse indicates the return value of the
1230: // last call to the current item's traverse method.
1231: // 'true' indicates it is doing internal traversal,
1232: // 'false' indicates we may traverse out of that item
1233: // if we have something else to traverse to or scrolling
1234: // that needs to be done
1235: if (itemTraverse) {
1236:
1237: if (traverseIndexCopy == -1) {
1238: itemTraverse = false;
1239: return;
1240: }
1241:
1242: itemTraverse = uCallItemTraverse(
1243: itemsCopy[traverseIndexCopy], dir);
1244:
1245: if (itemTraverse) {
1246: // We may have to scroll to accommodate the new
1247: // traversal location
1248: if (scrollForBounds(dir, visRect)) {
1249: uRequestPaint();
1250: }
1251: return;
1252: }
1253: }
1254:
1255: // We are done with the traversal of the current item, so
1256: // we look to see if another interactive item is available on
1257: // current page
1258: int nextIndex = getNextInteractiveItem(itemsCopy, dir,
1259: traverseIndexCopy);
1260:
1261: if (nextIndex != -1) {
1262: // NOTE: In traverse(), if there is a "next" interactive
1263: // item, there must have been a "first" interactive item
1264: // (which was set initially in uInitItemsInViewport())
1265: // so traverseIndex should always be valid
1266:
1267: // We need to traverse out of the previous item, now that
1268: // we've found a new item to traverse to
1269:
1270: // NOTE WELL: traverseIndex (and thus traverseIndexCopy) may well
1271: // be invalid if there is no currently focused item, the app adds
1272: // a focusable item, and then the user traverses before the
1273: // resulting invalidation can be processed. Thus, this value must
1274: // be guarded anyway. See CR#6254765.
1275:
1276: if (traverseIndexCopy != -1) {
1277: itemsCopy[traverseIndexCopy].uCallTraverseOut();
1278: synchronized (Display.LCDUILock) {
1279: itemsCopy[traverseIndexCopy].lRequestPaint();
1280: }
1281: }
1282:
1283: /*
1284: * NOTE: Although we update traverseIndex in a synchronized block
1285: * and call "lRefreshItems()" to update itemsCopy[] &
1286: * traverseIndexCopy,
1287: * original itemLFs[] & traverseIndex can change after sync block -
1288: * so we still have a risk of referring to a non-existent item...
1289: */
1290: synchronized (Display.LCDUILock) {
1291: if (itemsModified) {
1292: // SYNCHRONIZE itemLFs & itemsCopy ...
1293: itemsCopy = lRefreshItems(itemsCopy,
1294: traverseIndexCopy, nextIndex);
1295: } else {
1296: // Update our traverse index to the new item
1297: traverseIndex = nextIndex;
1298: }
1299: traverseIndexCopy = traverseIndex;
1300: }
1301:
1302: if (traverseIndexCopy != -1) {
1303: // We then need to traverse to the next item
1304: itemTraverse = uCallItemTraverse(
1305: itemsCopy[traverseIndexCopy], dir);
1306:
1307: if (scrollForBounds(dir, visRect)) {
1308: uRequestPaint(); // request to paint contents area
1309: } else {
1310: synchronized (Display.LCDUILock) {
1311: itemsCopy[traverseIndexCopy].lRequestPaint();
1312: }
1313: }
1314: }
1315:
1316: int scrollPos = getScrollPosition0();
1317: // There is a special case when traversing to the very last
1318: // item on a Form
1319: if (traverseIndexCopy == (itemsCopy.length - 1)
1320: && !itemCompletelyVisible(itemsCopy[traverseIndexCopy])) {
1321: // Since its the last item, we may need to
1322: // perform a partial scroll to fit it.
1323: if (scrollPos + viewportHeight != itemsCopy[traverseIndexCopy].bounds[Y]
1324: + itemsCopy[traverseIndexCopy].bounds[HEIGHT]) {
1325: scrollPos = viewable[HEIGHT] - viewportHeight;
1326:
1327: // We make sure we don't go past the top of the
1328: // item, as we must have been going down to reach
1329: // the last item
1330: if (scrollPos > itemsCopy[traverseIndexCopy].bounds[Y]) {
1331: scrollPos = itemsCopy[traverseIndexCopy].bounds[Y];
1332: }
1333: uRequestPaint();
1334: }
1335: }
1336:
1337: // Likewise, there is a special case when traversing up to
1338: // the very first item on a Form
1339: if (traverseIndexCopy == 0) {
1340: // Since its the first item, we may need to
1341: // perform a partial scroll to fit it.
1342: if (scrollPos != itemsCopy[traverseIndexCopy].bounds[Y]) {
1343: scrollPos = itemsCopy[traverseIndexCopy].bounds[Y];
1344:
1345: // We make sure we don't go past the bottom of the
1346: // item, as we must have been going up to get to
1347: // the first item
1348: if (itemsCopy[traverseIndexCopy].bounds[HEIGHT] > viewportHeight) {
1349: scrollPos = itemsCopy[traverseIndexCopy].bounds[HEIGHT]
1350: - viewportHeight;
1351: }
1352: uRequestPaint();
1353: }
1354: }
1355: setScrollPosition0(scrollPos);
1356: updateCommandSet();
1357: } else {
1358:
1359: // There is no more interactive items wholly visible on
1360: // the current page. We may need to scroll to the next page,
1361: // if we do, then traverse out of the current item and
1362: // scroll the page
1363:
1364: int scrollPos = getScrollPosition0();
1365: if ((dir == Canvas.LEFT || dir == Canvas.UP)
1366: && scrollPos > 0) {
1367: // Special case. We're at the top-most interactive item, but
1368: // its internal traversal doesn't allow the very top to be
1369: // seen, we just scroll the view to show it
1370: if (traverseIndexCopy != -1
1371: && (scrollPos > itemsCopy[traverseIndexCopy].bounds[Y])) {
1372: scrollPos -= (viewportHeight - PIXELS_LEFT_ON_PAGE);
1373: if (scrollPos < 0) {
1374: scrollPos = 0;
1375: }
1376: setScrollPosition0(scrollPos);
1377: uRequestPaint();
1378: } else {
1379: // page up
1380: uScrollViewport(Canvas.UP, itemsCopy);
1381: uInitItemsInViewport(Canvas.UP, itemsCopy,
1382: traverseIndexCopy);
1383: updateCommandSet();
1384: return;
1385: }
1386: } else if ((dir == Canvas.RIGHT || dir == Canvas.DOWN)
1387: && (scrollPos + viewportHeight < viewable[HEIGHT])) {
1388: // Special case. We're at the bottom-most interactive item,
1389: // but its internal traversal doesn't allow the very bottom
1390: // to be seen, we just scroll the view to show it
1391: if (traverseIndexCopy != -1
1392: && ((itemsCopy[traverseIndexCopy].bounds[Y] + itemsCopy[traverseIndex].bounds[HEIGHT]) > (scrollPos + viewportHeight))) {
1393: scrollPos += (viewportHeight - PIXELS_LEFT_ON_PAGE);
1394: if (scrollPos > (viewable[HEIGHT] - viewportHeight)) {
1395: scrollPos = viewable[HEIGHT] - viewportHeight;
1396: }
1397: setScrollPosition0(scrollPos);
1398: uRequestPaint();
1399: } else {
1400: // page down
1401: uScrollViewport(Canvas.DOWN, itemsCopy);
1402: uInitItemsInViewport(Canvas.DOWN, itemsCopy,
1403: traverseIndexCopy);
1404: updateCommandSet();
1405: return;
1406: }
1407: }
1408:
1409: // If we don't need to scroll the page and there is nothing
1410: // to traverse to, we reset the itemTraverse result as if
1411: // the Item wishes to proceed with internal traversal (as long
1412: // as there was some initial traverse in the first place, ie,
1413: // traverseIndex != -1)
1414: if (traverseIndexCopy != -1) {
1415: itemTraverse = true;
1416: }
1417: updateCommandSet();
1418: }
1419: }
1420:
1421: /**
1422: * Perform a page flip in the given direction. This method will
1423: * attempt to scroll the view to show as much of the next page
1424: * as possible. It uses the locations and bounds of the items on
1425: * the page to best determine a new location - taking into account
1426: * items which may lie on page boundaries as well as items which
1427: * may span several pages.
1428: *
1429: * @param dir the direction of the flip, either DOWN or UP
1430: * @param items the set of items on the Form, used to determine
1431: * the best suited scroll locations
1432: */
1433: void uScrollViewport(int dir, ItemLFImpl[] items) {
1434: int scrollPos = getScrollPosition0();
1435:
1436: if (dir == Canvas.UP) {
1437: int newY = scrollPos
1438: - (viewportHeight - PIXELS_LEFT_ON_PAGE);
1439: if (newY < 0) {
1440: newY = 0;
1441: }
1442:
1443: // We loop upwards until we find the first item which is
1444: // currently at least partially visible
1445: int firstVis = items.length;
1446: for (int i = items.length - 1; i >= 0; i--) {
1447: if (items[i].visibleInViewport) {
1448: firstVis = i;
1449: }
1450: }
1451:
1452: // case 1. We're at the top of the item so just
1453: // traverse normally
1454: if (items[firstVis].bounds[Y] >= scrollPos) {
1455: scrollPos = newY;
1456: setScrollPosition0(scrollPos);
1457: return;
1458: }
1459:
1460: // case 2. We try to fit as much of the partially visible
1461: // item onscreen as possible.
1462: int fitY = (items[firstVis].bounds[Y] + items[firstVis].bounds[HEIGHT])
1463: - viewportHeight;
1464:
1465: if (fitY > newY && scrollPos > fitY) {
1466: newY = fitY;
1467: }
1468:
1469: scrollPos = newY;
1470: setScrollPosition0(scrollPos);
1471: return;
1472:
1473: } else if (dir == Canvas.DOWN) {
1474: int newY = scrollPos
1475: + (viewportHeight - PIXELS_LEFT_ON_PAGE);
1476: if (newY > viewable[HEIGHT] - viewportHeight) {
1477: newY = viewable[HEIGHT] - viewportHeight;
1478: }
1479:
1480: // We loop downwards until we find the last item which is
1481: // at least partially visible
1482: int lastVis = -1;
1483: for (int i = 0; i < items.length; i++) {
1484: if (items[i].visibleInViewport) {
1485: lastVis = i;
1486: }
1487: }
1488:
1489: // case 1. We're at the bottom of the item so just
1490: // traverse normally
1491: if (items[lastVis].bounds[Y]
1492: + items[lastVis].bounds[HEIGHT] <= scrollPos
1493: + viewportHeight) {
1494: scrollPos = newY;
1495: setScrollPosition0(scrollPos);
1496: return;
1497: }
1498:
1499: // case 2. We try to fit as much of the partially visible
1500: // item onscreen as possible unless we're already at the top
1501: // of the item from a previous scroll
1502: if (newY > items[lastVis].bounds[Y]
1503: && scrollPos < items[lastVis].bounds[Y]) {
1504: newY = items[lastVis].bounds[Y];
1505: }
1506:
1507: scrollPos = newY;
1508: setScrollPosition0(scrollPos);
1509: return;
1510: }
1511: }
1512:
1513: /**
1514: * Determine if scrolling is needed for a given bounding box,
1515: * and perform such scrolling if necessary.
1516: *
1517: * @param dir the direction of travel
1518: * @param bounds the bounding box of the traversal location
1519: * @return <code>true</code> if it was necessary to scroll the view
1520: * in order to best accommodate the bounding box
1521: */
1522: boolean scrollForBounds(int dir, int bounds[]) {
1523: if (bounds == null || bounds[0] == -1) {
1524: return false;
1525: }
1526:
1527: int scrollPos = getScrollPosition0();
1528:
1529: // There is a special case whereby the CustomItem
1530: // spec mandates the upper left corner of the internal
1531: // traversal rect be visible if the rect is larger than
1532: // the available viewport
1533: if (bounds[HEIGHT] >= viewportHeight && scrollPos != bounds[Y]) {
1534: setScrollPosition0(bounds[Y]);
1535: return true;
1536: }
1537:
1538: switch (dir) {
1539: case Canvas.LEFT:
1540: case Canvas.UP:
1541: if (bounds[Y] >= scrollPos) {
1542: return false;
1543: }
1544:
1545: scrollPos -= (viewportHeight - PIXELS_LEFT_ON_PAGE);
1546: if (scrollPos < 0) {
1547: scrollPos = 0;
1548: }
1549: setScrollPosition0(scrollPos);
1550: return true;
1551: case Canvas.RIGHT:
1552: case Canvas.DOWN:
1553: if (bounds[Y] + bounds[HEIGHT] <= scrollPos
1554: + viewportHeight) {
1555: return false;
1556: }
1557:
1558: scrollPos += (viewportHeight - PIXELS_LEFT_ON_PAGE);
1559: if (scrollPos > bounds[Y]) {
1560: scrollPos = bounds[Y];
1561: }
1562: if (scrollPos + viewportHeight > viewable[HEIGHT]) {
1563: scrollPos = viewable[HEIGHT] - viewportHeight;
1564: }
1565: setScrollPosition0(scrollPos);
1566: return true;
1567: default:
1568: // for safety/completeness, don't scroll.
1569: Logging.report(Logging.ERROR,
1570: LogChannels.LC_HIGHUI_FORM_LAYOUT,
1571: "FormLFImpl: bounds, dir=" + dir);
1572: break;
1573: }
1574: return false;
1575: }
1576:
1577: /**
1578: * This method will return the index of the next interactive
1579: * item which is wholly visible on the screen given the traversal
1580: * direction, or -1 if no visible items in that traversal direction
1581: * are interactive (or completely visible).
1582: *
1583: * @param items the set of items to search
1584: * @param dir the direction of traversal
1585: * @param index the "anchor" of the index to start from
1586: * @return the index of the next interactive item, or -1 if one is
1587: * not completely visible or available in the given direction
1588: */
1589: int getNextInteractiveItem(ItemLFImpl[] items, int dir, int index) {
1590:
1591: try {
1592: int scrollPos = getScrollPosition0();
1593:
1594: while (true) {
1595: switch (dir) {
1596: case Canvas.UP:
1597: case Canvas.LEFT:
1598: index -= 1;
1599: break;
1600: case Canvas.DOWN:
1601: case Canvas.RIGHT:
1602: index += 1;
1603: break;
1604: case CustomItem.NONE:
1605: // no - op
1606: break;
1607: default:
1608: // for safety/completeness.
1609: Logging.report(Logging.ERROR,
1610: LogChannels.LC_HIGHUI_FORM_LAYOUT,
1611: "FormLFImpl: dir=" + dir);
1612: return index;
1613: }
1614: // If we've exhausted the set, stop looking
1615: if (index < 0 || index >= items.length) {
1616: break;
1617: }
1618:
1619: // If we've found a non-interactive item, continue
1620:
1621: if (!items[index].item.acceptFocus()) {
1622: continue;
1623: }
1624:
1625: // If we've found a completely visible, interactive
1626: // item, stop and traverse to it
1627: if (itemCompletelyVisible(items[index])) {
1628: break;
1629: }
1630:
1631: // If we've found a partially visible, interactive
1632: // item, there is some special casing involved with
1633: // how to scroll appropriately
1634: if (itemPartiallyVisible(items[index])) {
1635: if (dir == Canvas.RIGHT || dir == Canvas.DOWN) {
1636:
1637: // If we're paging down and the item's top
1638: // is at the top of the viewport, stop and
1639: // traverse to that item (its bigger than the
1640: // viewport
1641: if (items[index].bounds[Y] == scrollPos) {
1642: break;
1643: }
1644:
1645: // If we're paging down and the item's bottom
1646: // is the very last thing in the view, stop and
1647: // keep traversal on that item (item is bigger
1648: // than the viewport and we can go no further)
1649: if (items[index].bounds[Y]
1650: + items[index].bounds[HEIGHT] == viewable[HEIGHT]) {
1651: break;
1652: }
1653: } else if (dir == Canvas.LEFT || dir == Canvas.UP) {
1654:
1655: // If we're paging up and the item's bottom is the
1656: // very bottom of the viewport, stop and keep
1657: // traversal on that item (item is bigger than the
1658: // viewport and we start at the bottom)
1659: if (items[index].bounds[Y]
1660: + items[index].bounds[HEIGHT] == viewable[HEIGHT]) {
1661: break;
1662: }
1663:
1664: // If we're paging up and the item's top is at
1665: // the top of the viewport, stop and traverse
1666: // to that item (its bigger than the viewport
1667: // and we should show the top of it before leaving)
1668: if (items[index].bounds[Y] == scrollPos
1669: && scrollPos == 0) {
1670: break;
1671: }
1672: }
1673: }
1674: } // while
1675: } catch (Throwable t) {
1676: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
1677: Logging
1678: .report(Logging.WARNING, LogChannels.LC_HIGHUI,
1679: "Throwable while finding next item for traversal");
1680: }
1681: return -1;
1682: }
1683:
1684: // This means there was no interactive item in the currently
1685: // visible viewport
1686: if (index < 0 || index >= items.length) {
1687: return -1;
1688: }
1689:
1690: return index;
1691: }
1692:
1693: /**
1694: * Determine if the given item is at least partially visible
1695: * in the current viewport.
1696: *
1697: * @param item the item to determine visibility
1698: * @return true if at least part of the item is visible
1699: */
1700: boolean itemPartiallyVisible(ItemLFImpl item) {
1701: // If the Form is hidden, all the items are
1702: // hidden and we just return false
1703: if (super .state == HIDDEN) {
1704: return false;
1705: }
1706: int scrollPos = getScrollPosition0();
1707: // If the Item's top is within the viewport, return true
1708: return !(item.bounds[Y] > scrollPos + viewportHeight || item.bounds[Y]
1709: + item.bounds[HEIGHT] < scrollPos);
1710: }
1711:
1712: /**
1713: * Determine if the given item is at completely visible
1714: * in the current viewport.
1715: *
1716: * @param item the item to determine visibility
1717: * @return true if at the item is entirely visible
1718: */
1719: boolean itemCompletelyVisible(ItemLFImpl item) {
1720: // If the Form is being hidden, all the items are
1721: // hidden and we just return false
1722: if (super .state == HIDDEN) {
1723: return false;
1724: }
1725:
1726: // If the Item's top and bottom are within the viewport,
1727: // return true
1728: int scrollPos = getScrollPosition0();
1729: return (item.bounds[Y] >= scrollPos)
1730: && (item.bounds[Y] + item.bounds[HEIGHT] <= scrollPos
1731: + viewportHeight);
1732: }
1733:
1734: /**
1735: * Calculate the rectangle representing the region of the item that is
1736: * currently visible. This region might have zero area if no part of the
1737: * item is visible, for example, if it is scrolled offscreen.
1738: * @param item item
1739: * @param visRect It must be an int[4] array. The information in this array is
1740: * a rectangle of the form [x,y,w,h] where (x,y) is the location of the
1741: * upper-left corner of the rectangle relative to the item's origin, and
1742: * (w,h) are the width and height of the rectangle.
1743: */
1744: private void setVisRect(ItemLFImpl item, int[] visRect) {
1745: synchronized (Display.LCDUILock) {
1746: // Initialize the in-out rect for traversal
1747: visRect[X] = 0;
1748: visRect[WIDTH] = width;
1749:
1750: // take the coordinates from the overall
1751: // coordinate space
1752:
1753: int itemY1 = item.bounds[Y];
1754: int itemY2 = item.bounds[Y] + item.bounds[HEIGHT];
1755:
1756: // vpY1 the y coordinate of the top left visible pixel
1757: // current scroll position
1758: int vpY1 = getScrollPosition0();
1759: ;
1760: // vpY2 the y coordinate of bottom left pixel
1761: int vpY2 = vpY1 + height;
1762:
1763: // return only the visible region of item
1764:
1765: // item completely visible in viewport
1766: visRect[Y] = 0;
1767: visRect[HEIGHT] = item.bounds[HEIGHT];
1768:
1769: if ((itemY1 >= vpY2) || (itemY2 <= vpY1)) {
1770: // no part of the item is visible
1771: // so this region has zero area
1772: visRect[WIDTH] = 0;
1773: visRect[HEIGHT] = 0;
1774: } else {
1775: if (itemY1 < vpY1) {
1776: // upper overlap
1777: visRect[Y] = vpY1 - itemY1;
1778: visRect[HEIGHT] -= (vpY1 - itemY1);
1779: }
1780: if (itemY2 > vpY2) {
1781: // lower overlap
1782: visRect[HEIGHT] -= (itemY2 - vpY2);
1783: }
1784: }
1785: }
1786: }
1787:
1788: /**
1789: * Perform an internal traversal on the given item in
1790: * the given direction. The only assertion here is that
1791: * the item provided must be interactive (or otherwise
1792: * be a CustomItem). When this method returns, visRect[]
1793: * will hold the bounding box of the item's internal
1794: * traversal (in the Form's coordinate space).
1795: *
1796: * @param item the item to traverse within
1797: * @param dir the direction of traversal
1798: * @return true if this item performed an internal traversal
1799: * in the given direction.
1800: */
1801: boolean uCallItemTraverse(ItemLFImpl item, int dir) {
1802:
1803: boolean ret = false;
1804:
1805: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1806: Logging.report(Logging.INFORMATION,
1807: LogChannels.LC_HIGHUI_FORM_LAYOUT,
1808: "[F] uCallItemTraverse: dir=" + dir
1809: + " traverseIndex=" + traverseIndex);
1810: }
1811:
1812: // The visRect is supposed to show the bounds of the Item
1813: // currently visible on the screen
1814: setVisRect(item, visRect);
1815:
1816: // Whether the item performs an internal traversal or not,
1817: // it has the current input focus
1818: //item.hasFocus = true;
1819:
1820: // Call traverse() outside LCDUILock
1821: if (item.uCallTraverse(dir, width, viewportHeight, visRect)) {
1822: synchronized (Display.LCDUILock) {
1823: // It's possible that this newFocus item has
1824: // been just removed from this Form since we
1825: // are outside LCDUILock. Check again.
1826: if (item.nativeId != INVALID_NATIVE_ID) {
1827: setCurrentItem0(nativeId, item.nativeId, visRect[Y]);
1828: }
1829: }
1830:
1831: ret = true;
1832: }
1833:
1834: // Since visRect is sent to the Item in its own coordinate
1835: // space, we translate it back into the overall Form's
1836: // coordinate space
1837: visRect[X] += item.bounds[X];
1838: visRect[Y] += item.bounds[Y];
1839:
1840: return ret;
1841: }
1842:
1843: /**
1844: * Scrolls to the passed in Item.
1845: * @param item The Item that should be shown on the screen.
1846: */
1847: private void lScrollToItem(Item item) {
1848:
1849: if (item == null || item.owner != owner) {
1850: return;
1851: }
1852:
1853: int index = -1;
1854:
1855: ItemLFImpl itemLF = null;
1856: if (traverseIndex != -1
1857: && (itemLFs[traverseIndex].item == item)) {
1858: index = traverseIndex;
1859: } else {
1860: for (int i = 0; i < numOfLFs; i++) {
1861: if (itemLFs[i].item == item) {
1862: index = i;
1863: break;
1864: }
1865: }
1866: }
1867:
1868: // item not found
1869: if (index == -1) {
1870: return;
1871: }
1872:
1873: itemLF = itemLFs[index];
1874:
1875: if (index != traverseIndex) {
1876: // Ensure the item is visible
1877: if (!itemCompletelyVisible(itemLF)) {
1878: int scrollPos = itemLF.bounds[Y];
1879: if (scrollPos + viewportHeight > viewable[HEIGHT]) {
1880: scrollPos = viewable[HEIGHT] - viewportHeight;
1881: }
1882: setScrollPosition0(scrollPos);
1883: }
1884:
1885: // We record the present traverseItem because if it
1886: // is valid, we will have to call traverseOut() on that
1887: // item when we process the invalidate call.
1888: if (traverseIndex != -1) {
1889: lastTraverseItem = itemLFs[traverseIndex];
1890: }
1891:
1892: // If the item is not interactive, we just leave it
1893: // visible on the screen, but set traverseIndex to -1
1894: // so that any interactive item which is visible will
1895: // be traversed to when the invalidate occurs
1896: traverseIndex = itemLF.item.acceptFocus() ? index : -1;
1897: lRequestInvalidate();
1898: } else {
1899: // Ensure the item is visible
1900: if (!itemPartiallyVisible(itemLF)) {
1901: int scrollPos = itemLF.bounds[Y];
1902: if (scrollPos + viewportHeight > viewable[HEIGHT]) {
1903: scrollPos = viewable[HEIGHT] - viewportHeight;
1904: }
1905: setScrollPosition0(scrollPos);
1906: }
1907: }
1908: }
1909:
1910: /**
1911: * Initialize the current page of items, perform a traverse if possible.
1912: * This method is always called when a page is initially "shown".
1913: * This occurs when the form gains visibility for the very first
1914: * time as well as after every page up/page down occurs.
1915: *
1916: * This method searches for the most appropriate item on the form
1917: * to receive the interaction focus.
1918: *
1919: * @param dir the direction of travel. Can be NONE when a page is
1920: * first shown or as the result of an invalidate.
1921: * @param itemsCopy a copy of the set of ItemLFImpls in this form.
1922: * @param traverseIndexCopy a copy of taverseIndex to work with itesCopy[]
1923: */
1924: void uInitItemsInViewport(int dir, ItemLFImpl[] itemsCopy,
1925: int traverseIndexCopy) {
1926: // Create a copy of the current index for comparisons, below.
1927: if (itemsCopy.length == 0) {
1928: return;
1929: }
1930:
1931: // Hide & Show Items
1932: int pos = getScrollPosition0();
1933: uViewportChanged(pos, pos + viewportHeight);
1934:
1935: // The result of an invalidate() call
1936: if (traverseIndexCopy != -1 && dir == CustomItem.NONE) {
1937: itemTraverse = uCallItemTraverse(
1938: itemsCopy[traverseIndexCopy], dir);
1939:
1940: uRequestPaint(); // request to paint contents area
1941: return;
1942: }
1943:
1944: // Special case: It could be that no item in the current view
1945: // is interactive, and thus the traverseIndexCopy is -1. If we
1946: // are scrolling upwards, we artificially set it to be the last
1947: // item on the form (+1) so that the getNextInteractiveItem()
1948: // routine will subsequently reduce it by 1 and start searching
1949: // for an interactive item from the bottom of the form upwards.
1950: if (dir == Canvas.UP && traverseIndexCopy == -1) {
1951: traverseIndexCopy = itemsCopy.length;
1952: }
1953:
1954: // If paging "down", we find the interactive item by moving
1955: // left to right - this ensures we move line by line searching
1956: // for an interactive item. When paging "up", we search from
1957: // right to left.
1958: int nextIndex = (dir == Canvas.DOWN || dir == CustomItem.NONE) ? getNextInteractiveItem(
1959: itemsCopy, Canvas.RIGHT, traverseIndexCopy)
1960: : getNextInteractiveItem(
1961:
1962: itemsCopy, Canvas.LEFT, traverseIndexCopy);
1963:
1964: if (traverseIndexCopy > -1
1965: && traverseIndexCopy < itemsCopy.length) {
1966: if (nextIndex != -1
1967: || !itemCompletelyVisible(itemsCopy[traverseIndexCopy])) {
1968: // It could be we need to traverse out of a current
1969: // item before paging
1970: itemsCopy[traverseIndexCopy].uCallTraverseOut();
1971: synchronized (Display.LCDUILock) {
1972: traverseIndex = -1; // reset real index
1973: traverseIndexCopy = traverseIndex;
1974: }
1975: }
1976: }
1977: /*
1978: * NOTE: between these two sync sections itemLFs[] & traverseIndex
1979: * can change again ...
1980: */
1981: synchronized (Display.LCDUILock) {
1982: if (itemsModified) {
1983: // SYNCHRONIZE itemLFs & itemsCopy, update traverseIndex ...
1984: itemsCopy = lRefreshItems(itemsCopy, traverseIndexCopy,
1985: nextIndex);
1986: } else if ((nextIndex > -1) && (nextIndex < numOfLFs)) {
1987: traverseIndex = nextIndex;
1988: }
1989: traverseIndexCopy = traverseIndex;
1990: }
1991:
1992: if (traverseIndexCopy == -1
1993: || traverseIndexCopy == itemsCopy.length) {
1994: // If there is no traversable item on the current page,
1995: // we simply return, and on the next 'traverse' we will
1996: // perform a page scroll and repeat this method
1997: } else {
1998: // If there is a traversable item, we go ahead and traverse
1999: // to it. We do *not* scroll at all under these circumstances
2000: // because we have just performed a fresh page view (or scroll)
2001: itemTraverse = uCallItemTraverse(
2002: itemsCopy[traverseIndexCopy], dir);
2003: }
2004:
2005: uRequestPaint(); // request to paint contents area
2006: }
2007:
2008: /**
2009: * Called by the system to notify that viewport scroll location
2010: * or height has been changed.
2011: *
2012: * @param vpY1 the y coordinate of the top left visible pixel
2013: * @param vpY2 the y coordinate of bottom left pixel
2014: * immediately below the viewport
2015: */
2016: private void uViewportChanged(int vpY1, int vpY2) {
2017:
2018: int i, showCount, hideCount, size;
2019: ItemLFImpl[] itemsCopy = null;
2020:
2021: synchronized (Display.LCDUILock) {
2022:
2023: itemsCopy = new ItemLFImpl[numOfLFs];
2024: size = numOfLFs;
2025:
2026: showCount = 0;
2027: hideCount = numOfLFs;
2028:
2029: for (i = 0; i < numOfLFs; i++) {
2030: if (itemLFs[i].bounds[Y] + itemLFs[i].bounds[HEIGHT]
2031: - 1 > vpY1
2032: && itemLFs[i].bounds[Y] < vpY2) {
2033: // should become visible
2034: if (itemLFs[i].visibleInViewport == false) {
2035: itemsCopy[showCount++] = itemLFs[i];
2036: }
2037: } else {
2038: // should not be visible
2039: if (itemLFs[i].visibleInViewport) {
2040: itemsCopy[--hideCount] = itemLFs[i];
2041: }
2042: }
2043: }
2044: } // synchronized (LCDUILock)
2045:
2046: for (i = 0; i < showCount; i++) {
2047: itemsCopy[i].uCallShowNotify();
2048: }
2049:
2050: for (i = hideCount; i < size; i++) {
2051: itemsCopy[i].uCallHideNotify();
2052: }
2053: }
2054:
2055: /**
2056: * Service method - find the <code>ItemLFImpl</code> from a given
2057: * native id.
2058: *
2059: * @param nativeId native id to search
2060: *
2061: * @return the <code>ItemLFImpl</code>, or <code>null</code> not found
2062: */
2063: private ItemLFImpl id2Item(int nativeId) {
2064:
2065: ItemLFImpl focus = getItemInFocus();
2066:
2067: if (focus != null && focus.nativeId == nativeId) {
2068: return focus;
2069: } else {
2070: for (int i = 0; i < numOfLFs; i++) {
2071: if (itemLFs[i].nativeId == nativeId) {
2072: return itemLFs[i];
2073: }
2074: }
2075: // there is no matching ItemLFImpl
2076: return null;
2077: }
2078: }
2079:
2080: /**
2081: * Service method - find the <code>ItemLFImpl</code> index.
2082: *
2083: * @param itemLF itemLF to map
2084: *
2085: * @return index of the item. -1 if not found.
2086: */
2087: private int item2Index(ItemLFImpl itemLF) {
2088:
2089: for (int i = 0; i < numOfLFs; i++) {
2090: if (itemLFs[i] == itemLF) {
2091: return i;
2092: }
2093: }
2094:
2095: return -1;
2096: }
2097:
2098: /**
2099: * Ensure that dispatchItemLFs array has enough space for use.
2100: * SYNC NOTE: This function must only be used in event dispatch thread.
2101: *
2102: * @param size maximum number of itemLFs needed
2103: */
2104: private static void ensureDispatchItemArray(int size) {
2105: if (size > dispatchItemLFs.length) {
2106: dispatchItemLFs = new ItemLFImpl[size];
2107: }
2108: }
2109:
2110: /**
2111: * Clear contents of dispatchItemLFs array after use.
2112: * SYNC NOTE: This function must only be used in event dispatch thread.
2113: *
2114: * @param alsoShrink true if the array size should be minimized
2115: */
2116: private static void resetDispatchItemArray(boolean alsoShrink) {
2117:
2118: if (alsoShrink
2119: && dispatchItemLFs.length > DISPATCH_ITEM_ARRAY_BLOCK) {
2120: dispatchItemLFs = new ItemLFImpl[DISPATCH_ITEM_ARRAY_BLOCK];
2121: } else {
2122: // Only clean up existing array contents
2123: for (int i = 0; i < dispatchItemLFs.length; i++) {
2124: dispatchItemLFs[i] = null;
2125: }
2126: }
2127: }
2128:
2129: /**
2130: * Synchronizes itemLFs[] array with itemsCopy[] array,
2131: * as well as traverseIndex with traverseIndexCopy & nextIndex.
2132: *
2133: * Since most of work with copies occurs outside of LCDUILock
2134: * (this is, BTW, the reason, why copies are used instead of original
2135: * fields), there is a risk, that
2136: * itemLFs[] content can be changed (ex. insert/delete/replace Item):
2137: * traverseIndexCopy can point to a different object
2138: * (including non-interactive),
2139: * or outside of changed itemLFs array (throws exception),
2140: * or we can refer to a deleted item (deleted in itemLFs,
2141: * but still exists in a copy).
2142: *
2143: * This method tries to find item, referred by nextIndex in itemsCopy[],
2144: * in itelLFs[], and if found, sets traverseIndex to foundItem,
2145: * else sets traverse index to -1.
2146: *
2147: * This method indended to be called in LCDUILock-protected code,
2148: * from uInitItemsInViewport(...) & uTraverse(...).
2149: *
2150: * @param itemsCopy a copy of the set of ItemLFImpls in this form.
2151: * @param traverseIndexCopy a copy of traverseIndex to work with itemsCopy
2152: * @param nextIndex suggested new value of traverseIndex,
2153: * the item from itemsCopy[] to be found in changed itemLFs[]
2154: *
2155: * @return updated itemsCopy[] array, synchronized with itemLFs[]
2156: */
2157: private ItemLFImpl[] lRefreshItems(ItemLFImpl[] itemsCopy,
2158: int traverseIndexCopy, int nextIndex) {
2159:
2160: final int nextIndexInLFs = nextIndex
2161: + (traverseIndex - traverseIndexCopy);
2162: traverseIndex = ((traverseIndex > -1) &&
2163: /* (traverseIndex < numOfLFs) && */
2164: (traverseIndexCopy > -1) &&
2165: /* (traverseIndexCopy < itemsCopy.length) && */
2166: (nextIndex > -1) &&
2167: /* (nextIndex < itemsCopy.length) && */
2168: (nextIndexInLFs > -1) && (nextIndexInLFs < numOfLFs) && !itemLFs[nextIndexInLFs].item
2169: .acceptFocus())
2170: /* (itemsCopy[nextIndex] == itemLFs[nextIndexInLFs])) */
2171: /*
2172: * Assume that:
2173: * 1). traverseIndex has always valid value:
2174: * i.e. -1 or within [0..numOfLFs[ range of itemLFs[] array.
2175: * 2). traverseIndexCopy & nextIndex have always valid values:
2176: * i.e. -1 or within [0..itemsCopy.length[ range
2177: * of itemsCopy[] array.
2178: * As the result we need to check them only for "-1" value.
2179: * Computed "nextIndexInLFs" needs to be checked for
2180: * being in bounds of itemLFs[].
2181: *
2182: * Need revisit : if last condition in the above "IF" is needed,
2183: * however it ensures, that the next current item
2184: * will be exactly the same item that has been found
2185: * by "getNext...".
2186: * Without thast statement we have a risk to point to
2187: * a completely different item: still valid &
2188: * in the range, but probably NON-interactive :-(
2189: * To avoid this, "shouldSkipTraverse()" could be used insead ...
2190: */
2191: ? nextIndexInLFs : -1;
2192:
2193: // refresh itemsCopy array ...
2194: itemsCopy = new ItemLFImpl[numOfLFs];
2195: System.arraycopy(itemLFs, 0, itemsCopy, 0, numOfLFs);
2196: itemsModified = false;
2197: return itemsCopy;
2198: }
2199:
2200: /**
2201: * A bit mask to capture the horizontal layout directive of an item.
2202: */
2203: final static int LAYOUT_HMASK = 0x03;
2204:
2205: /**
2206: * A bit mask to capture the vertical layout directive of an item.
2207: */
2208: final static int LAYOUT_VMASK = 0x30;
2209:
2210: /**
2211: * Do a full layout.
2212: */
2213: final static int FULL_LAYOUT = -1;
2214:
2215: /**
2216: * Only update layout.
2217: */
2218: final static int UPDATE_LAYOUT = -2;
2219:
2220: /**
2221: * This is the rate at which the internal array of Items grows if
2222: * it gets filled up.
2223: */
2224: private static final int GROW_SIZE = 4;
2225:
2226: /**
2227: * This is the number of pixels left from the previous "page"
2228: * when a page up or down occurs
2229: */
2230: static final int PIXELS_LEFT_ON_PAGE = 15;
2231:
2232: /** The item index which has the traversal focus */
2233: int traverseIndex = -1;
2234:
2235: /**
2236: * Item that was made visible using display.setCurrentItem() call
2237: * while FormLF was in HIDDEN or FROZEN state.
2238: */
2239: Item pendingCurrentItem = null;
2240:
2241: /**
2242: * This is a special case variable which tracks the last
2243: * traversed item when a new item is traversed to via the
2244: * setCurrentItem() call.
2245: */
2246: ItemLFImpl lastTraverseItem;
2247:
2248: /**
2249: * A flag indicating the return value of the currently
2250: * selected Item from its traverse() method
2251: */
2252: boolean itemTraverse;
2253:
2254: /**
2255: * A flag that shows that itemLFs[] storage has been modified
2256: * (items inserted/deleted) and earlier made copies (extends itemsCopy[])
2257: * are outdated.
2258: * flag is set by item insert/delete operations,
2259: * cleared when copy operation is performed.
2260: */
2261: boolean itemsModified;
2262:
2263: /**
2264: * When a Form calls an Item's traverse() method, it passes in
2265: * an in-out int[] that represents the Item's traversal
2266: * bounds. This gets cached in the visRect variable
2267: */
2268: int[] visRect;
2269:
2270: /**
2271: * Array of <code>ItemLF</code>s that correspond to the array of items
2272: * in <code>Form</code>.
2273: */
2274: private ItemLFImpl[] itemLFs;
2275:
2276: /**
2277: * Block size of the temporary array of <code>ItemLF</code>s used
2278: * in dispatch.
2279: */
2280: private final static int DISPATCH_ITEM_ARRAY_BLOCK = 10;
2281:
2282: /**
2283: * Temporary array of <code>ItemLF</code>s that is ONLY used in
2284: * dispatch thread during show, hide and re-layout this <code>Form</code>.
2285: *
2286: * ensureDispatchItemArray() should be called before use and
2287: * resetDispatchItemArray() should be called when it is no longer needed,
2288: * to allow <code>ItemLFImpl</code> objects been GC'ed.
2289: */
2290: private static ItemLFImpl[] dispatchItemLFs = new ItemLFImpl[DISPATCH_ITEM_ARRAY_BLOCK];
2291:
2292: /**
2293: * The number of views present in this <code>FormLF</code>.
2294: */
2295: private int numOfLFs;
2296:
2297: /**
2298: * This helps an optimization.
2299: */
2300: private boolean firstShown = true;
2301:
2302: /**
2303: * Screens should automatically reset to the top of the when
2304: * they are shown, except in cases where it is interrupted by
2305: * a system menu or an off-screen editor - in which case it
2306: * should be reshown exactly as it was.
2307: */
2308: boolean resetToTop = true;
2309:
2310: /**
2311: * Viewport height in the native resource
2312: */
2313: private int viewportHeight; // = 0;
2314:
2315: /**
2316: * Overall dimensions of the view. It is an array so it could be passed
2317: * as a reference to <code>LayoutManager</code>.
2318: */
2319: int viewable[] = new int[4];
2320:
2321: /**
2322: * Left to right layout is default.
2323: * Used by isImplicitLineBreak.
2324: */
2325: final static boolean ltr = true;
2326:
2327: } // class FormLFImpl
|