/*
 *   
 *
 * Copyright  1990-2006 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package javax.microedition.lcdui;

/* import  javax.microedition.lcdui.KeyConverter; */

import com.sun.midp.lcdui.EventConstants;
import com.sun.midp.lcdui.Text;
import com.sun.midp.configurator.Constants;
import com.sun.midp.chameleon.skins.ChoiceGroupSkin;
import com.sun.midp.chameleon.skins.resources.ChoiceGroupResources;
import com.sun.midp.chameleon.skins.ScrollIndSkin;
import com.sun.midp.chameleon.skins.ScreenSkin;
import com.sun.midp.chameleon.layers.ScrollablePopupLayer;
import com.sun.midp.chameleon.layers.ScrollIndLayer;
import com.sun.midp.chameleon.CGraphicsUtil;
import com.sun.midp.chameleon.skins.resources.ScrollIndResourcesConstants;


/**
 * This is the Look &amps; Feel implementation for ChoiceGroupPopup.
 */
class ChoiceGroupPopupLFImpl extends ChoiceGroupLFImpl {

    /**
     * Creates ChoiceGroupPopupLF for the passed in choiceGroup of
     * Choice.POPUP type.
     * @param choiceGroup the ChoiceGroup object associated with this view
     */
    ChoiceGroupPopupLFImpl(ChoiceGroup choiceGroup) {
        super(choiceGroup);
        
        ChoiceGroupResources.load();
        
        viewable = new int[4];
        popupLayer = new CGPopupLayer(this);
    }

    /**
     * Sets the content size in the passed in array.
     * Content is calculated based on the availableWidth.
     * size[WIDTH] and size[HEIGHT] should be set by this method.
     * @param size The array that holds Item content size and location 
     *             in Item internal bounds coordinate system.
     * @param width The width available for this Item
     */
    public void lGetContentSize(int size[], int width) {

        // no label and empty popup => nothing is drawn
        // no elements => only label is drawn
        if (cg.numOfEls == 0) {
            size[WIDTH] = size[HEIGHT] = 0;
            return;
        }

        int w = getAvailableContentWidth(Choice.POPUP, width);
        int maxContentWidth = getMaxElementWidth(w);

        viewable[HEIGHT] = calculateHeight(w);

        int s = (selectedIndex < 0) ? 0 : selectedIndex;
        size[HEIGHT] = cg.cgElements[s].getFont().getHeight() + 
            (2 * ChoiceGroupSkin.PAD_V);

        if (maxContentWidth < w) {
            size[WIDTH] = width - w + maxContentWidth;
        } else {
            size[WIDTH] = width;
        }
        viewable[WIDTH] = size[WIDTH];
    }

    // *****************************************************
    //  Package private methods
    // *****************************************************

    /**
     * Paints the content area of the ChoiceGroup POPUP. 
     * Graphics is translated to contents origin.
     * @param g The graphics where Item content should be painted
     * @param width The width available for the Item's content
     * @param height The height available for the Item's content
     */
    void lPaintContent(Graphics g, int width, int height) {
        // paint closed state of the popup

        // if there are no elements, we are done
        if (cg.numOfEls == 0) {
            return;
        }

        // draw background
        if (ChoiceGroupSkin.IMAGE_BG != null) {
            CGraphicsUtil.draw9pcsBackground(g, 0, 0, width, height,
                ChoiceGroupSkin.IMAGE_BG);
        } else {
            // draw widget instead of using images
            CGraphicsUtil.drawDropShadowBox(g, 0, 0, width, height,
                ChoiceGroupSkin.COLOR_BORDER,
                ChoiceGroupSkin.COLOR_BORDER_SHD,
                ChoiceGroupSkin.COLOR_BG);
        }

        if (hasFocus && !popupLayer.isPopupOpen()) {
            // hilight the background
            g.setColor(ScreenSkin.COLOR_TRAVERSE_IND);
            g.fillRect(2, 2, width - 3, height - 3);
        }

        // draw icon
        if (ChoiceGroupSkin.IMAGE_BUTTON_ICON != null) {
            int w = ChoiceGroupSkin.IMAGE_BUTTON_ICON.getWidth();
            int yOffset = height -
                ChoiceGroupSkin.IMAGE_BUTTON_ICON.getHeight();
            if (yOffset > 0) {
                yOffset = yOffset / 2;
            } else {
                yOffset = 0;
            }
            width -= (w + 1);
            if (ChoiceGroupSkin.IMAGE_BUTTON_BG != null) {
                CGraphicsUtil.draw9pcsBackground(
                    g, width, 1, w, height - 2,
                    ChoiceGroupSkin.IMAGE_BUTTON_BG);
            }
            g.drawImage(ChoiceGroupSkin.IMAGE_BUTTON_ICON,
                        width, yOffset + 1,
                        Graphics.LEFT | Graphics.TOP);
            width -= ChoiceGroupSkin.PAD_H;
        }

        g.translate(ChoiceGroupSkin.PAD_H, ChoiceGroupSkin.PAD_V);

        int s = selectedIndex < 0 ? 0 : selectedIndex;

        // paint value

        int textOffset = 0;

        if (cg.cgElements[s].imageEl != null) {
            int iX = g.getClipX();
            int iY = g.getClipY();
            int iW = g.getClipWidth();
            int iH = g.getClipHeight();

            g.clipRect(0, 0,
                       ChoiceGroupSkin.WIDTH_IMAGE,
                       ChoiceGroupSkin.HEIGHT_IMAGE);
            g.drawImage(cg.cgElements[s].imageEl,
                        0, 0,
                        Graphics.LEFT | Graphics.TOP);
            g.setClip(iX, iY, iW, iH);
            textOffset = ChoiceGroupSkin.WIDTH_IMAGE +
                ChoiceGroupSkin.PAD_H;
        }

        g.translate(textOffset, 0);
        Text.drawTruncString(g,
                        cg.cgElements[s].stringEl,
                        cg.cgElements[s].getFont(),
                        (hasFocus) ? ScreenSkin.COLOR_FG_HL :
                            ChoiceGroupSkin.COLOR_FG,
                        width);
        g.translate(-textOffset, 0);
        
        g.translate(-ChoiceGroupSkin.PAD_H, -ChoiceGroupSkin.PAD_V);

        if (popupLayer.isSizeChanged() && cachedWidth != INVALID_SIZE) {
            popupLayer.refresh();
            popupLayer.setSizeChanged(false);
        }
    }

    /**
     * Called by the system to indicate traversal has left this Item
     * This function simply calls lCallTraverseOut() after obtaining LCDUILock.
     */
    void uCallTraverseOut() {
        super.uCallTraverseOut();
        
        synchronized (Display.LCDUILock) {
            if (popupLayer.isPopupOpen()) {
                hilightedIndex = 0;
                popupLayer.hide();
            }
        }
    }

    /**
     * Handle traversal within this ChoiceGroup
     *
     * @param dir the direction of traversal
     * @param viewportWidth the width of the viewport
     * @param viewportHeight the height of the viewport
     * @param visRect the in/out rectangle for the internal traversal location
     * @return true if traversal occurred within this ChoiceGroup
     */
    boolean lCallTraverse(int dir, int viewportWidth, int viewportHeight,
                         int[] visRect) {

        boolean ret = false;
        // If we have no elements, or if the user pressed left/right,
        // don't bother with the visRect and just return false
        if (cg.numOfEls > 0) {

            // If we are a closed popup, don't bother with the visRect
            // and return true on the initial traverse, false on subsequent
            // traverses
            if (popupLayer.isPopupOpen()) {
                ret = super.lCallTraverse(dir, viewportWidth, viewportHeight, visRect);
            }
        }
        
        return ret;
    }

    /**
     * Handle traversal in the open popup
     * @param dir - the direction of traversal (Canvas.UP, Canvas.DOWN)
     * @param viewportWidth - the width of the viewport
     * @param viewportHeight - the height of the viewport
     * @return true if traverse event was handled, false - otherwise
     */
    boolean traverseInPopup(int dir, int viewportWidth, int viewportHeight) {
        boolean ret = false;
        if (popupLayer.isPopupOpen()) {
            if (cg.numOfEls > 1) {
                int prevIndex = hilightedIndex;
                int hilightY = 0;
                switch (dir) {
                case Canvas.UP:
                    if (hilightedIndex > 0) {
                        hilightedIndex--;
                    } else {
                        hilightedIndex = cg.numOfEls - 1;
                    }
                    break;
                case Canvas.DOWN:
                    if (hilightedIndex < (cg.numOfEls - 1)) {
                        hilightedIndex++;
                    } else {
                        hilightedIndex = 0;
                    }
                    break;
                default:
                    break;
                }
                
                if (ret = prevIndex != hilightedIndex) {
                    for (int i = 0; i < hilightedIndex; i++) {
                        hilightY += elHeights[i];
                    }
                    int y2= hilightY + elHeights[hilightedIndex];
                    
                    if (hilightY < viewable[Y]) {
                        viewable[Y] = hilightY;
                    } else if (y2 > viewable[Y] + viewportHeight) {
                        viewable[Y] = y2 - viewportHeight;
                    }
                    lRequestPaint();
                }
            } 
        } // popus is opened
        return ret;
    }

    /**
     * Handle a key press event
     *
     * @param keyCode the key which was pressed
     */
    void uCallKeyPressed(int keyCode) {

        Form form = null;

        synchronized (Display.LCDUILock) {

            if (cg.numOfEls == 0) {
                return;
            }
            
            if (!popupLayer.isPopupOpen()) {
                if (keyCode != Constants.KEYCODE_SELECT) {
                    return;
                }
                // show popup

                ScreenLFImpl sLF = (ScreenLFImpl)cg.owner.getLF();
                int x = getInnerBounds(X) - sLF.viewable[X] + contentBounds[X];
                int y = getInnerBounds(Y) - sLF.viewable[Y] + contentBounds[Y];
                hilightedIndex = selectedIndex > 0 ? selectedIndex : 0;

                popupLayer.show(x, y,
                                contentBounds[WIDTH], contentBounds[HEIGHT],
                                viewable[WIDTH], viewable[HEIGHT],
                                y, 
                                sLF.viewport[HEIGHT] - 
                                y - contentBounds[HEIGHT]);
            } else {

                // popup is closed when SELECT, LEFT or RIGHT is pressed;
                // popup selection is changed only when SELECT is pressed
                if (keyCode != Constants.KEYCODE_SELECT &&
                    keyCode != Constants.KEYCODE_LEFT && 
                    keyCode != Constants.KEYCODE_RIGHT) {
                    return;
                }

                // IMPL_NOTE Check if we need notification if selected element 
                // did not change
                if (keyCode == Constants.KEYCODE_SELECT) {
                    if (selectedIndex >= 0) {
                        lSetSelectedIndex(hilightedIndex, true);
                        form = (Form)cg.owner; // To be called outside the lock
                    }
                }
                hilightedIndex = 0;
                popupLayer.hide();
            }
            lRequestPaint();
        } // synchronized
        
        // Notify itemStateListener if necessary
        if (form != null) {
            form.uCallItemStateChanged(cg);
        }
    }

    /**
     * Check if the pointer is clicked to the item
     * @param x x coordinate of pointer 
     * @param y y coordinate of pointer
     * @return true if the item contains the pointer, otherwise - false
     */
    boolean itemContainsPointer(int x, int y) {
        if (!popupLayer.isPopupOpen()) {
            return super.itemContainsPointer(x, y);
        } else {
            // We grab the whole screen, so consider all clicks contained
            // by this item.
            return true;
        }
    }

    /**
     * Find the choice index inside of the list containing the pointer 
     * @param x x coordinate of pointer 
     * @param y y coordinate of pointer
     * @return choice index, -1 - if index is not found
     */
    int getIndexByPointer(int x, int y) {
        int popupLayer_bounds[]= popupLayer.getBounds();
        int id = -1;
        if (cg.numOfEls > 0) {
            ScreenLFImpl sLF = (ScreenLFImpl)cg.owner.getLF();
            x = x +(bounds[X] - sLF.viewable[X]) - popupLayer_bounds[X];
            y = y +(bounds[Y] - sLF.viewable[Y]) - popupLayer_bounds[Y];
            
            if (x >= 0 && x <= popupLayer_bounds[WIDTH] &&
                y >= 0 && y <= popupLayer_bounds[HEIGHT]) {
                int visY =  0;
                int i = 0;
                
                // calculate the scroll position and update the y coordinate accordingly.
                y += viewable[Y];
                
                for (i = 0; i < cg.numOfEls; i++) {
                    int h = elHeights[i];
                    if (y > visY && y < visY + h) {
                        break;
                    }
                    visY += h;
                }
                if (i < cg.numOfEls) {
                    id = i;
                }
            }
        }
        return id;
    }

    /**
     * Called by the system to signal a pointer press
     *
     * @param x the x coordinate of the pointer down
     * @param y the y coordinate of the pointer down
     */
    void uCallPointerPressed(int x, int y) {
        itemWasPressed = true;
        itemSelectedWhenPressed = false;
        if (popupLayer.isPopupOpen()) {
            // popupLayer.
            int i = getIndexByPointer(x, y);
            if (i >= 0) {
                itemSelectedWhenPressed = true;
                if (hilightedIndex != i) {
                    hilightedIndex = i;
                    uRequestPaint();//request paint as the highlighted changed
                    popupLayer.requestRepaint();//of course, we should repaint the popupLayer
                    getCurrentDisplay().serviceRepaints(cg.owner.getLF());
                }
            } else {
                hilightedIndex = 0;
                popupLayer.hide();
                uRequestPaint();
            }
        }
    }

    /**
     * Called by the system to signal a pointer release
     *
     * @param x the x coordinate of the pointer up
     * @param y the x coordinate of the pointer up
     */
    void uCallPointerReleased(int x, int y) {
        if (!itemWasPressed)
            return;
        
        if (popupLayer.isPopupOpen()) {
            // do not dismiss the popup until a new selection is made.
            int i = getIndexByPointer(x, y);
            if ( (i >= 0 && hilightedIndex == i && itemSelectedWhenPressed) ||
                 (!itemSelectedWhenPressed)) {
                uCallKeyPressed(itemSelectedWhenPressed ?
                                // close the popup with highlighted item selected
                                Constants.KEYCODE_SELECT :
                                // close the popup as cancel
                                Constants.KEYCODE_RIGHT);
            }
        } else if (super.itemContainsPointer(x + bounds[X], y + bounds[Y])) {
            uCallKeyPressed(Constants.KEYCODE_SELECT);
        }
        itemSelectedWhenPressed = false;
        itemWasPressed = false;

    }

    /**
     * Called by the system to indicate the size available to this Item
     * has changed
     *
     * @param w the new width of the item's content area
     * @param h the new height of the item's content area
     */
    void uCallSizeChanged(int w, int h) {
        super.uCallSizeChanged(w,h);
        synchronized (Display.LCDUILock) {
            popupLayer.setSizeChanged(true);
        }
    }

    // *****************************************************
    //  Private methods
    // *****************************************************        

    /** pressed on a valid item in popup layer **/
    private boolean itemSelectedWhenPressed = false;

    /** The PopupLayer that represents open state of this ChoiceGroup POPUP. */
    CGPopupLayer popupLayer;

    /** 
     * Content of the popupLayer is drawn in ChoiceGroupPopupLFImpl.
     * That content can be taller then the layer itself.
     * viewable holds information of the current scroll position
     * and the size of the content (X, Y, WIDTH, HEIGHT)
     */
    int viewable[];

    // *****************************************************
    //  Inner class
    // *****************************************************
    /**
     * The following is the implementation of the ChoiceGroup POPUP
     * open state. The popup is shown and hidden on a KEYCODE_SELECT.
     * It is placed above or below the ChoiceGroup POPUP button (closed
     * state) depending on the space available. If there are too
     * many elements to display in the popup 
     * scrollbar will be added on the right.
     * If possible popup should be displayed below the button.
     * If possible the entire content of the popup should be seen.
     */
    class CGPopupLayer extends ScrollablePopupLayer {
        
        /**
         * CGPopupLayer constructor. Sets ChoiceGroupPopupLFImpl that
         * is associated with this CGPopupLayer.
         * @param lf - The ChoiceGroupPopupLFImpl associated with this 
         *             CGPopupLayer
         */
        CGPopupLayer(ChoiceGroupPopupLFImpl lf) {
            super(ScrollIndSkin.MODE == 
                    ScrollIndResourcesConstants.MODE_ARROWS ?
                  ChoiceGroupSkin.IMAGE_POPUP_BG : null, 
                  ChoiceGroupSkin.COLOR_BG);
            this.lf = lf;
        }
                
        /**
         * Initializes internal structures of CGPopupLayer.
         */
        protected void initialize() {
            super.initialize();
            viewport =  new int[4];
        }
        
        /**
         * Handles key event in the open popup
         * @param type - The type of this key event (pressed, released)
         * @param code - The code of this key event
         * @return true if the key event was handled and false - otherwise
         */
        public boolean keyInput(int type, int code) {
            if (type == EventConstants.PRESSED && lf != null) {

                if (code == Constants.KEYCODE_UP
                    || code == Constants.KEYCODE_DOWN) 
                {
                    if (lf.traverseInPopup(KeyConverter.getGameAction(code),
                                           viewport[WIDTH], 
                                           viewport[HEIGHT])) {
                        // the viewable[Y] is correct after traverseInPopup() calls,
                        // but we should update scroll position
                        updateScrollIndicator();
                        requestRepaint();
                        return true;
                    }
                }

                lf.uCallKeyPressed(code);
            }
            // PopupLayers always swallow all key events
            return true;
        }

        /**
         * Paints popup background (including borders) and scrollbar
         * if it is present
         * @param g - The graphics object to paint background on
         */
        protected void paintBackground(Graphics g) {
            super.paintBackground(g);
            // draw border if there is no background image
            if (bgImage == null) {
                g.setColor(ChoiceGroupSkin.COLOR_BORDER);
                g.drawRect(0, 0, bounds[W] - 1, bounds[HEIGHT] - 1);
              
                g.setColor(ChoiceGroupSkin.COLOR_BORDER_SHD);
                g.drawLine(1, 1, 1, bounds[HEIGHT] - 2); 
            }
            
            if (sbVisible && ScrollIndSkin.MODE == 
                    ScrollIndResourcesConstants.MODE_ARROWS) {
                int sbX = bounds[WIDTH] - 
                    (ChoiceGroupSkin.WIDTH_SCROLL / 2) - 1;
                int sbY = ChoiceGroupSkin.PAD_V;
                int sbH = bounds[HEIGHT] - (2 * ChoiceGroupSkin.PAD_V);
                int thumbY = sbY + 4 + 
                    ((lf.viewable[Y] * 
                      (sbH - 8 - ChoiceGroupSkin.HEIGHT_THUMB)) /
                     (lf.viewable[HEIGHT] - viewport[HEIGHT]));
                
                if (bgImage == null) {                    
                    // draw scrollbar with arrrows
                    g.setColor(ChoiceGroupSkin.COLOR_SCROLL);

                    int sbY2 = sbY + sbH - 1;
                    g.drawLine(sbX, sbY, sbX, sbY2);

                    g.drawLine(sbX - 2, sbY + 2, sbX - 1, sbY + 1);
                    g.drawLine(sbX + 1, sbY + 1, sbX + 2, sbY + 2);
                    g.drawLine(sbX - 2, sbY2 - 2, sbX - 1, sbY2 - 1);
                    g.drawLine(sbX + 1, sbY2 - 1, sbX + 2, sbY2 - 2);
                }

                // draw scrollbar thumb
                g.setColor(ChoiceGroupSkin.COLOR_THUMB);
                g.fillRect(sbX - (ChoiceGroupSkin.WIDTH_THUMB / 2), 
                           thumbY,
                           ChoiceGroupSkin.WIDTH_THUMB,
                           ChoiceGroupSkin.HEIGHT_THUMB);
            }
        }

        /**
         * Paints the content area of ChoiceGroup popup
         * @param g - The Graphics object to paint content on
         */
        protected void paintBody(Graphics g) {
            g.clipRect(viewport[X], viewport[Y],
                       viewport[WIDTH], viewport[HEIGHT]);

            g.translate(viewport[X] - lf.viewable[X],
                        viewport[Y] - lf.viewable[Y]);


            lf.lPaintElements(g, bounds[WIDTH], viewable[HEIGHT]);

            g.translate(-viewport[X] + lf.viewable[X],
                        -viewport[Y] + lf.viewable[Y]);
        }

        /**
         * Shows popup for the ChoiceGroup POPUP button that
         * is drawn at the passed in location. 
         * It will determine if popup will be drawn above or
         * below the ChoiceGroup POPUP button depending on the
         * passed in info.
         * @param buttonX - the x location of ChoiceGroup POPUP button
         *                  in BodyLayer's coordinate system.
         * @param buttonY - the y location of ChoiceGroup POPUP button
         *                  in BodyLayer's coordinate system.
         * @param buttonW - the width of ChoiceGroup POPUP button
         * @param buttonH - the height of ChoiceGroup POPUP button
         * @param elementsWidth - the width of the widest element in 
         *                        the popup
         * @param elementsHeight - the height of all elements if they are
         *                         drawn vertically one after another
         * @param top - the amount of space available for popup above the
         *              ChoiceGroup POPUP button
         * @param bottom - the amount of space available for popup below the
         *                 ChoiceGroup POPUP button
         */
        void show(int buttonX, int buttonY,
                  int buttonW, int buttonH,
                  int elementsWidth, int elementsHeight,
                  int top, int bottom) {
            // popup with all elements displayed fits under the popup button
            
            if (elementsHeight + 1 <= bottom - ChoiceGroupSkin.PAD_V) {
                setBounds(buttonX,
                          buttonY + buttonH - 1, // hide top border
                          buttonW,
                          elementsHeight + 2); // border width
                popupDrawnDown = true;
                sbVisible = false;

            // popup with all elements displayed fits above the popup button
            } else if (elementsHeight + 1 <= top - ChoiceGroupSkin.PAD_V) {
                setBounds(buttonX,
                          buttonY - elementsHeight - 1, // show top border
                          buttonW,
                          elementsHeight + 2); // border width
                popupDrawnDown = false;
                sbVisible = false;

            } else if (bottom > top) { // there is more space at the bottom
                setBounds(buttonX,
                          buttonY + buttonH - 1, // hide top border width
                          buttonW,
                          bottom - ChoiceGroupSkin.PAD_V);
                popupDrawnDown = true;
                sbVisible = true;

            } else { // there is more space at the top
                setBounds(buttonX,
                          buttonY - top + buttonH + 1, // show top border
                          buttonW,
                          top - ChoiceGroupSkin.PAD_V + buttonH + 1);
                popupDrawnDown = false;
                sbVisible = true;
            }

            // set viewport in popup's coordinate system
            viewport[X] = 2; // border width
            viewport[Y] = 1; // border width
            viewport[WIDTH]  = viewable[WIDTH];
            viewport[HEIGHT] = bounds[HEIGHT] - 2; // border width

            // ASSERT: since we are receiving key events,
            //         currentDisplay cannot be null.

            lf.getCurrentDisplay().showPopup(popupLayer);
            popUpOpen = true;

            if (ScrollIndSkin.MODE == ScrollIndResourcesConstants.MODE_BAR) {
                setScrollInd(ScrollIndLayer.getInstance(ScrollIndSkin.MODE));
                setBackground(sbVisible ? null : ChoiceGroupSkin.IMAGE_POPUP_BG,
                              ChoiceGroupSkin.COLOR_BG);
            }
            updatePopupLayer(viewable[Y]);
        }

        /**
         * Hide popup for choice group 
         */
        
        void hide() {
            if (scrollInd != null) {
                scrollInd.setVisible(false);
                sbVisible = false;
                updateScrollIndicator();
                setScrollInd(null);
            }
            
            lf.getCurrentDisplay().hidePopup(popupLayer);
            popUpOpen = false;
        }

        
        /**
         * Scroll content inside of the CouiceGroup.
         * @param scrollType scrollType. Scroll type can be one of the following
         * @see ScrollIndLayer.SCROLL_NONE
         * @see ScrollIndLayer.SCROLL_PAGEUP
         * @see ScrollIndLayer.SCROLL_PAGEDOWN
         * @see ScrollIndLayer.SCROLL_LINEUP
         * @see ScrollIndLayer.SCROLL_LINEDOWN or
         * @see ScrollIndLayer.SCROLL_THUMBTRACK
         * @param thumbPosition
         */
        public void scrollContent(int scrollType, int thumbPosition) {
            switch (scrollType) {
                case ScrollIndLayer.SCROLL_PAGEUP:
                    uScrollViewport(Canvas.UP);
                    break;
                case ScrollIndLayer.SCROLL_PAGEDOWN:
                    uScrollViewport(Canvas.DOWN);
                    break;
                case ScrollIndLayer.SCROLL_LINEUP:
                    uScrollByLine(Canvas.UP);
                    break;
                case ScrollIndLayer.SCROLL_LINEDOWN:
                    uScrollByLine(Canvas.DOWN);
                    break;
                case ScrollIndLayer.SCROLL_THUMBTRACK:
                    uScrollAt(thumbPosition);
                    break;
                default:
                    break;
            }
        }

        /**
         * Perform a line scrolling in the given direction. This method will
         * attempt to scroll the view to show next/previous line.
         *
         * @param dir the direction of the flip, either DOWN or UP
         */
        private void uScrollByLine(int dir) {
            int newY = viewable[Y];
            switch (dir) {
            case Canvas.UP:
                newY -= PIXELS_LEFT_ON_PAGE;
                if (newY < 0) {
                    newY = 0;
                }
                break;
            case Canvas.DOWN:
                newY += PIXELS_LEFT_ON_PAGE;
                if (newY > viewable[HEIGHT] - viewport[HEIGHT]) {
                    newY = viewable[HEIGHT] - viewport[HEIGHT];
                }
                break;
            }
            updatePopupLayer(newY);
        }

        /**
         * Perform a page flip in the given direction. This method will
         * attempt to scroll the view to show as much of the next page
         * as possible. It uses the locations and bounds of the items on
         * the page to best determine a new location - taking into account
         * items which may lie on page boundaries as well as items which
         * may span several pages.
         *
         * @param dir the direction of the flip, either DOWN or UP
         */
        private void uScrollViewport(int dir) {
            int newY = viewable[Y];
            switch (dir) {
            case Canvas.UP:
                newY -= viewport[HEIGHT] - PIXELS_LEFT_ON_PAGE;
                if (newY < 0) {
                    newY = 0;
                }
                break;
            case Canvas.DOWN:
                newY += viewport[HEIGHT] - PIXELS_LEFT_ON_PAGE;
                if (newY > viewable[HEIGHT] - viewport[HEIGHT]) {
                    newY = viewable[HEIGHT] - viewport[HEIGHT];
                }
                break;
            }
            updatePopupLayer(newY);
        }

        /**
         * Perform a scrolling at the given position.
         * @param position
         */
        void uScrollAt(int position) {
            int newY = (viewable[HEIGHT] - viewport[HEIGHT]) * position / 100;
            if (newY < 0) {
                newY = 0;
            } else if (newY > viewable[HEIGHT] - viewport[HEIGHT]) {
                newY = viewable[HEIGHT] - viewport[HEIGHT];
            }
            updatePopupLayer(newY);
        }

        /**
         * This method initiate repaint of the popup layer
         *
         * @param newY
         */
        private void updatePopupLayer(int newY) {
            viewable[Y] = newY;
            
            // correct hilighted index depending on new viewport. The hilighted item
            // always has to be visible
            if (hilightedIndex >= 0) {
                // calculate y coordinates of hilighted item

                int vy1 = viewable[Y];
                int vy2 = viewable[Y] + viewport[HEIGHT];
                for (int i = 0, y = 0;
                     i <= cg.numOfEls - 1;
                     i++, y += elHeights[i]) {
                    
                    if (y >= vy1) {
                        if (y + elHeights[i] <= vy2) {
                            if (hilightedIndex <= i) {
                                hilightedIndex = i;
                                break;
                            }
                        } else {
                            if (hilightedIndex >= i) {
                                hilightedIndex = i;
                                if (i > 0 && y > vy1) {
                                    hilightedIndex--;
                                } 
                                break;
                            }                            
                        }
                    } else if (y + elHeights[i] >= vy2) {
                        hilightedIndex = i;
                        break;
                    }
                }
            } 
            
            requestRepaint();
            updateScrollIndicator();
        }

        /**
         * Updates the scroll indicator.
         */
        public void updateScrollIndicator() {
            if (scrollInd != null) {
                if (sbVisible) {
                    scrollInd.setVerticalScroll(
                          (viewable[Y] * 100 / (viewable[HEIGHT] - viewport[HEIGHT])),
                          (viewport[HEIGHT] * 100 / viewable[HEIGHT]));
                } else {
                    scrollInd.setVerticalScroll(0, 100);
                }
                super.updateScrollIndicator();
            } 
        }


        /**
         * Handle pointer events 
         * @param type pointer event type 
         * @param x x coordinate of pointer 
         * @param y y coordinate of pointer
         * @return true if the event is processed and should not be passed
         * to other layers, false - otherwise 
         */
        public boolean pointerInput(int type, int x, int y) {
            ScreenLFImpl sLF = (ScreenLFImpl)lf.item.owner.getLF();
            int transX = x + this.bounds[X] + sLF.viewable[X] - lf.bounds[X];
            int transY = y + this.bounds[Y] + sLF.viewable[Y] - lf.bounds[Y];

            boolean consume = true;
            
            if (!containsPoint(x + bounds[X], y + bounds[Y])) {
                consume = false;
            }
            
            switch (type) {
            case EventConstants.PRESSED:
                lf.uCallPointerPressed(transX, transY);
                break;
            case EventConstants.RELEASED:
                lf.uCallPointerReleased(transX, transY);
                break;
            }
            return consume;
        }

        /**
         * Update bounds of popup anf show
         */
        public void refresh() {
            // show popup
            if (popUpOpen) {
                ScreenLFImpl sLF = (ScreenLFImpl) cg.owner.getLF();
                int x = getInnerBounds(X) - sLF.viewable[X] + contentBounds[X];
                int y = getInnerBounds(Y) - sLF.viewable[Y] + contentBounds[Y];

                popupLayer.show(x, y,
                        contentBounds[WIDTH], contentBounds[HEIGHT],
                        viewable[WIDTH], viewable[HEIGHT],
                        y,
                        sLF.viewport[HEIGHT] -
                                y - contentBounds[HEIGHT]);
            }
        }

        /**
         *  Return sizeChanged flag
         * @return true if size change iccurs
         */
        public boolean isSizeChanged() {
            return sizeChanged;
        }

        /**
         *  Set sizeChanged flag
         * @param sizeChanged true if size change occurs
         */
        public void setSizeChanged(boolean sizeChanged) {
            this.sizeChanged = sizeChanged;
        }

        /**
         *  Return Popup layer flag
         * @return true if popup Layer is shown
         */
        public boolean isPopupOpen() {
            return popUpOpen;
        }

        /**
         *  Set popup Layer flag
         */
        public void setPopupOpen() {
            this.popUpOpen = true;
        }

        /** The ChoiceGroupPopupLFImpl associated with this popup */
        ChoiceGroupPopupLFImpl lf; // = null;

        /**
         * The viewport setting inside this popup (X, Y, WIDTH, HEIGHT).
         * It is set in layer's coordinate system.
         */
        private int viewport[]; // = null;

        /** True if popup is drawn below Popup button, false - otherwise */
        boolean popupDrawnDown; // = false;

        /** True if sb is present in the Popup layer, false - otherwise */
        private boolean sbVisible; // = false;

        // True if size of screen was changed
        private boolean sizeChanged;

        /** The state of the popup ChoiceGroup (false by default) */
        private boolean popUpOpen; // = false;

        /**
         * This is the number of pixels left from the previous "page"
         * when a page up or down occurs
         */
        static final int PIXELS_LEFT_ON_PAGE = 15;

    }

}

