List.java
上传用户:haobig99
上传日期:2022-06-15
资源大小:369k
文件大小:64k
源码类别:

J2ME

开发平台:

Java

  1. /*
  2.  * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
  3.  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4.  *
  5.  * This code is free software; you can redistribute it and/or modify it
  6.  * under the terms of the GNU General Public License version 2 only, as
  7.  * published by the Free Software Foundation.  Sun designates this
  8.  * particular file as subject to the "Classpath" exception as provided
  9.  * by Sun in the LICENSE file that accompanied this code.
  10.  *
  11.  * This code is distributed in the hope that it will be useful, but WITHOUT
  12.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14.  * version 2 for more details (a copy is included in the LICENSE file that
  15.  * accompanied this code).
  16.  *
  17.  * You should have received a copy of the GNU General Public License version
  18.  * 2 along with this work; if not, write to the Free Software Foundation,
  19.  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20.  *
  21.  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22.  * CA 95054 USA or visit www.sun.com if you need additional information or
  23.  * have any questions.
  24.  */
  25. package com.sun.lwuit;
  26. import com.sun.lwuit.util.EventDispatcher;
  27. import com.sun.lwuit.animations.Motion;
  28. import com.sun.lwuit.geom.Dimension;
  29. import com.sun.lwuit.geom.Rectangle;
  30. import com.sun.lwuit.events.ActionEvent;
  31. import com.sun.lwuit.events.ActionListener;
  32. import com.sun.lwuit.events.DataChangedListener;
  33. import com.sun.lwuit.events.SelectionListener;
  34. import com.sun.lwuit.list.DefaultListCellRenderer;
  35. import com.sun.lwuit.list.DefaultListModel;
  36. import com.sun.lwuit.list.ListCellRenderer;
  37. import com.sun.lwuit.list.ListModel;
  38. import com.sun.lwuit.plaf.LookAndFeel;
  39. import com.sun.lwuit.plaf.Style;
  40. import com.sun.lwuit.plaf.UIManager;
  41. import java.util.Vector;
  42. /**
  43.  * A set of elements that is rendered using a {@link com.sun.lwuit.list.ListCellRenderer}
  44.  * and are extracted via the {@link com.sun.lwuit.list.ListModel}.
  45.  * <p>A list can represent many UI concepts ranging from a carousel to a "todo" checklist, this
  46.  * is made possible thanks to extensive use of Swing's style of MVC. Specifically a list
  47.  * component is relatively simple, it invokes the model in order to extract the displayed/selected
  48.  * information and shows it to the user by invoking the cell renderer.
  49.  * <p>The list class itself is completely decoupled from everything, thus it allows us to extract its
  50.  * content from any source (e.g. network, storage etc.) and display the information in any form
  51.  * (e.g. checkboxed elemenents, icons etc.).
  52.  *
  53.  * @see com.sun.lwuit.list
  54.  * @author Chen Fishbein
  55.  */
  56. public class List extends Component {
  57.     /**
  58.      * Indicates the list isn't fixed and that selection is movable
  59.      */
  60.     public static final int FIXED_NONE = 0;
  61.     /**
  62.      * Indicates that the list is not fixed in place but cycles its elements
  63.      */
  64.     public static final int FIXED_NONE_CYCLIC = 1;
  65.     /**
  66.      * Indicates the list selection will only reach the edge when there are no more
  67.      * elements in the list.
  68.      */
  69.     public static final int FIXED_NONE_ONE_ELEMENT_MARGIN_FROM_EDGE = 2;
  70.     /**
  71.      * Allows to test for fixed none
  72.      */
  73.     private static final int FIXED_NONE_BOUNDRY = 9;
  74.     /**
  75.      * Indicates the list selection is fixed into place at the top of the list
  76.      * or at the left of the list
  77.      */
  78.     public static final int FIXED_LEAD = 10;
  79.     /**
  80.      * Indicates the list selection is fixed into place at the bottom of the list
  81.      * or at the right of the list
  82.      */
  83.     public static final int FIXED_TRAIL = 11;
  84.     /**
  85.      * Indicates the list selection is fixed into place at the center of the list
  86.      */
  87.     public static final int FIXED_CENTER = 12;
  88.     /**
  89.      * @see setRenderingPrototype
  90.      */
  91.     private Object renderingPrototype;
  92.     /**
  93.      * Indicates whether selection is fixable to place in which case all the
  94.      * elements in the list move and selection stays in place. Fixed selection
  95.      * can be one of: FIXED_NONE, FIXED_TRAIL, FIXED_LEAD, FIXED_CENTER
  96.      */
  97.     private int fixedSelection;
  98.     private ListModel model;
  99.     private ListCellRenderer renderer = new DefaultListCellRenderer();
  100.     private int orientation = VERTICAL;
  101.     /**
  102.      * Indicates the list orientation is VERTICAL
  103.      */
  104.     public static final int VERTICAL = 0;
  105.     /**
  106.      * Indicates the list orientation is HORIZONTAL
  107.      */
  108.     public static final int HORIZONTAL = 1;
  109.     static final int COMBO = 2;
  110.     EventDispatcher dispatcher = new EventDispatcher();
  111.     Object eventSource = this;
  112.     private Dimension elemSize;
  113.     private Dimension selectedElemSize;
  114.     private boolean inputOnFocus = true;
  115.     private boolean numericKeyActions = true;
  116.     private boolean paintFocusBehindList = true;
  117.     /**
  118.      * Minimum number of elements shown in a list, this member is used to calculate
  119.      * the list preferred size. If the number of elements in the model is smaller than
  120.      * this then this value is used in the calculations.
  121.      */
  122.     private int minElementHeight = 0;
  123.     /**
  124.      * Indicates the gap between each item in the list
  125.      */
  126.     private int itemGap = 2;
  127.     /**
  128.      * Indicates the surrounding border gap
  129.      */
  130.     private int borderGap = 2;
  131.     private Listeners listener;
  132.     /**
  133.      * Indicates the position within the current animation, 0 means no animation
  134.      * is in progress
  135.      */
  136.     private int animationPosition;
  137.     private int destination;
  138.     private Motion listMotion;
  139.     private boolean fireOnClick = true;
  140.     private boolean fireOnRelease;
  141.     /**
  142.      * Initial x/y positions for the fixed mode drag
  143.      */
  144.     private int initialFixedDrag;
  145.     private int initialFixedSelection;
  146.     private boolean commandList;
  147.     /**
  148.      * Indicates whether the list should not paint the focus component if the list
  149.      * itself has no focus.
  150.      */
  151.     private boolean ignoreFocusComponentWhenUnfocused;
  152.     /**
  153.      * Used internally by the combo box
  154.      */
  155.     boolean disposeDialogOnSelection;
  156.     /**
  157.      * Indicates that the background of a cell renderer might mutate between one entry and the next,
  158.      * it is recommended that this flag remains false for performance reasons.
  159.      */
  160.     private boolean mutableRendererBackgrounds;
  161.     /**
  162.      * This flag indicates if the List should automatically scroll to the
  163.      * selected element when it's been initialized.
  164.      */
  165.     private boolean scrollToSelected = true;
  166.     /**
  167.      * Creates a new instance of List
  168.      *
  169.      * @param items set of items placed into the list model
  170.      */
  171.     public List(Vector items) {
  172.         this(new DefaultListModel(items));
  173.     }
  174.     /**
  175.      * Creates a new instance of List
  176.      *
  177.      * @param items set of items placed into the list model
  178.      */
  179.     public List(Object[] items) {
  180.         this(new DefaultListModel(items));
  181.     }
  182.     /**
  183.      * Creates a new instance of List with an empty default model
  184.      */
  185.     public List() {
  186.         this(new DefaultListModel());
  187.     }
  188.     /**
  189.      * Creates a new instance of List with the given model
  190.      *
  191.      * @param model the model instance
  192.      */
  193.     public List(ListModel model) {
  194.         setUIID("List");
  195.         setModel(model);
  196.         LookAndFeel laf = UIManager.getInstance().getLookAndFeel();
  197.         setSmoothScrolling(laf.isDefaultSmoothScrolling());
  198.     }
  199.     /**
  200.      * @inheritDoc
  201.      */
  202.     void initComponentImpl() {
  203.         dataChanged(0, 0);
  204.         // lazily bind listeners to prevent a memory leak in cases where models
  205.         // are stored separately from view
  206.         bindListeners();
  207.         super.initComponentImpl();
  208.         int index = model.getSelectedIndex();
  209.         if(index >= 0){
  210.             model.setSelectedIndex(index);
  211.         }
  212.     }
  213.     /**
  214.      * @inheritDoc
  215.      */
  216.     protected void laidOut() {
  217.         super.laidOut();
  218.         if (isScrollable() && isInitialized() && scrollToSelected) {
  219.             int index = model.getSelectedIndex();
  220.             if (index >= 0) {
  221.                 selectElement(index);
  222.             }
  223.         }
  224.     }
  225.     /**
  226.      * @inheritDoc
  227.      */
  228.     void deinitializeImpl() {
  229.         super.deinitializeImpl();
  230.         // cleanup to allow garbage collection even if the user keeps the model in
  231.         // memory which is a valid use case
  232.         if (listener != null) {
  233.             model.removeDataChangedListener(listener);
  234.             model.removeSelectionListener(listener);
  235.             listener = null;
  236.         }
  237.     }
  238.     /**
  239.      * Callback to allow subclasses to react to a change in the model
  240.      *
  241.      * @param status the type data change; REMOVED, ADDED or CHANGED
  242.      * @param index item index in a list model
  243.      */
  244.     protected void modelChanged(int status, int index) {
  245.     }
  246.     /**
  247.      * Callback to allow subclasses to react to a selectio change in the model
  248.      *
  249.      * @param oldSelected the old selection value
  250.      * @param newSelected the new selection value
  251.      */
  252.     protected void listSelectionChanged(int oldSelected, int newSelected) {
  253.     }
  254.     /**
  255.      * @inheritDoc
  256.      */
  257.     public boolean isScrollableY() {
  258.         return getScrollDimension().getHeight() > getHeight() && (fixedSelection < FIXED_NONE_BOUNDRY) &&
  259.                 orientation != HORIZONTAL;
  260.     }
  261.     /**
  262.      * @inheritDoc
  263.      */
  264.     public boolean isScrollableX() {
  265.         return getScrollDimension().getWidth() > getWidth() && (fixedSelection < FIXED_NONE_BOUNDRY) &&
  266.                 orientation == HORIZONTAL;
  267.     }
  268.     /**
  269.      * Minimum number of elements shown in a list, this member is used to calculate
  270.      * the list preferred size. If the number of elements in the model is smaller than
  271.      * this then this value is used in the calculations.
  272.      *
  273.      * @return the minimum number of elements
  274.      */
  275.     public int getMinElementHeight() {
  276.         return minElementHeight;
  277.     }
  278.     /**
  279.      * Minimum number of elements shown in a list, this member is used to calculate
  280.      * the list preferred size. If the number of elements in the model is smaller than
  281.      * this then this value is used in the calculations.
  282.      *
  283.      * @param minElementHeight the minimum number of elements
  284.      */
  285.     public void setMinElementHeight(int minElementHeight) {
  286.         this.minElementHeight = minElementHeight;
  287.     }
  288.     /**
  289.      *
  290.      * Returns the number of elements in the list, shorthand for
  291.      * getModel().getSize()
  292.      *
  293.      * @return the number of elements in the list
  294.      */
  295.     public int size() {
  296.         return model.getSize();
  297.     }
  298.     /**
  299.      * Returns the current selected offset in the list
  300.      *
  301.      * @return the current selected offset in the list
  302.      */
  303.     public int getSelectedIndex() {
  304.         return model.getSelectedIndex();
  305.     }
  306.     /**
  307.      * Sets the current selected offset in the list, by default this implementation
  308.      * will scroll the list to the selection if the selection is outside of the screen
  309.      *
  310.      * @param index the current selected offset in the list
  311.      */
  312.     public void setSelectedIndex(int index) {
  313.         setSelectedIndex(index, true);
  314.     }
  315.     /**
  316.      * @inheritDoc
  317.      */
  318.     protected Rectangle getVisibleBounds() {
  319.         Rectangle pos = new Rectangle();
  320.         Dimension rendererSize = getElementSize(false, true);
  321.         Style style = getStyle();
  322.         int width = getWidth() - style.getPadding(isRTL(), RIGHT) - style.getPadding(isRTL(), LEFT) - getSideGap();
  323.         calculateComponentPosition(getSelectedIndex(), width, pos, rendererSize, getElementSize(true, true), true);
  324.         pos.setX(pos.getX() + getX());
  325.         pos.setY(pos.getY() + getY());
  326.         return pos;
  327.     }
  328.     /**
  329.      * Sets the current selected offset in the list
  330.      *
  331.      * @param index the current selected offset in the list
  332.      * @param scrollToSelection indicates whether scrolling to selection should
  333.      * occur if the selection is outside of view
  334.      */
  335.     public void setSelectedIndex(int index, boolean scrollToSelection) {
  336.         if (index < 0) {
  337.             throw new IllegalArgumentException("Selection index is negative:" + index);
  338.         }
  339.         model.setSelectedIndex(index);
  340.         if (scrollToSelection && isInitialized()) {
  341.             selectElement(index);
  342.         }
  343.     }
  344.     /**
  345.      * Returns the current selected item in the list or null for no selection
  346.      *
  347.      * @return the current selected item in the list
  348.      */
  349.     public Object getSelectedItem() {
  350.         int idx = model.getSelectedIndex();
  351.         if (idx < model.getSize() && idx > -1) {
  352.             return model.getItemAt(idx);
  353.         }
  354.         return null;
  355.     }
  356.     /**
  357.      * Sets the current selected item in the list
  358.      *
  359.      * @param item the current selected item in the list
  360.      */
  361.     public void setSelectedItem(Object item) {
  362.         int size = model.getSize();
  363.         for (int iter = 0; iter < size; iter++) {
  364.             Object current = model.getItemAt(iter);
  365.             if (current == item || (current != null && current.equals(item))) {
  366.                 model.setSelectedIndex(iter);
  367.                 break;
  368.             }
  369.         }
  370.     }
  371.     /**
  372.      * Returns the model underlying the list
  373.      *
  374.      * @return the model underlying the list
  375.      */
  376.     public ListModel getModel() {
  377.         return model;
  378.     }
  379.     /**
  380.      * @inheritdoc
  381.      */
  382.     protected void setShouldCalcPreferredSize(boolean shouldCalcPreferredSize) {
  383.         super.setShouldCalcPreferredSize(shouldCalcPreferredSize);
  384.         elemSize = null;
  385.         selectedElemSize = null;
  386.         // we should try passing the should calcPreferredSize to the renderer so it can revalidate too
  387.         if(shouldCalcPreferredSize) {
  388.             ListCellRenderer r = getRenderer();
  389.             Object val;
  390.             if (renderingPrototype != null) {
  391.                 val = renderingPrototype;
  392.             } else {
  393.                 if (getModel().getSize() > 0) {
  394.                     val = getModel().getItemAt(0);
  395.                 } else {
  396.                     return;
  397.                 }
  398.             }
  399.             Component c = r.getListCellRendererComponent(this, val, 0, false);
  400.             c.setShouldCalcPreferredSize(shouldCalcPreferredSize);
  401.             c = r.getListCellRendererComponent(this, val, 0, true);
  402.             c.setShouldCalcPreferredSize(shouldCalcPreferredSize);
  403.         }
  404.     }
  405.     void dataChanged(int status, int index) {
  406.         setShouldCalcPreferredSize(true);
  407.         if (getSelectedIndex() >= model.getSize()) {
  408.             setSelectedIndex(Math.max(model.getSize() - 1, 0));
  409.         }
  410.         modelChanged(status, index);
  411.         repaint();
  412.     }
  413.     private void bindListeners() {
  414.         if (listener == null) {
  415.             listener = new Listeners();
  416.             model.addDataChangedListener(listener);
  417.             model.addSelectionListener(listener);
  418.         }
  419.     }
  420.     /**
  421.      * Replaces/sets the model underlying the list
  422.      *
  423.      * @param model the new model underlying the list
  424.      */
  425.     public void setModel(ListModel model) {
  426.         if (this.model != null) {
  427.             setShouldCalcPreferredSize(true);
  428.             this.model.removeDataChangedListener(listener);
  429.             this.model.removeSelectionListener(listener);
  430.             this.model = model;
  431.             listener = null;
  432.             // when replacing a model on a scrolled list reset the scrolling if necessary
  433.             if (getScrollDimension().getHeight() < getScrollY() + getHeight()) {
  434.                 setScrollY(0);
  435.             }
  436.             if (getScrollDimension().getWidth() < getScrollX() + getWidth()) {
  437.                 setScrollX(0);
  438.             }
  439.         }
  440.         this.model = model;
  441.         if (isInitialized()) {
  442.             bindListeners();
  443.         }
  444.         repaint();
  445.     }
  446.     /**
  447.      * Indicate whether pressing the number keys should trigger an action
  448.      *
  449.      * @return true if pressing the number keys should trigger an action
  450.      */
  451.     public boolean isNumericKeyActions() {
  452.         return numericKeyActions;
  453.     }
  454.     /**
  455.      * Indicate whether pressing the number keys should trigger an action
  456.      *
  457.      * @param numericKeyActions true to trigger an action on number keys
  458.      */
  459.     public void setNumericKeyActions(boolean numericKeyActions) {
  460.         this.numericKeyActions = numericKeyActions;
  461.     }
  462.     /**
  463.      * Indicates that the list should be treated as a list of commands, if the
  464.      * user "clicks" a command from the list its action performed method is invoked.
  465.      *
  466.      * @return true if the list is treated as a command list
  467.      */
  468.     public boolean isCommandList() {
  469.         return commandList;
  470.     }
  471.     /**
  472.      * Indicates that the list should be treated as a list of commands, if the
  473.      * user "clicks" a command from the list its action performed method is invoked.
  474.      *
  475.      * @param commandList true for the list to be treated as a command list
  476.      */
  477.     public void setCommandList(boolean commandList) {
  478.         this.commandList = commandList;
  479.     }
  480.     /**
  481.      * Indicates whether the list should not paint the focus component if the list
  482.      * itself has no focus.
  483.      *
  484.      * @return the ignoreFocusComponentWhenUnfocused
  485.      */
  486.     public boolean isIgnoreFocusComponentWhenUnfocused() {
  487.         return ignoreFocusComponentWhenUnfocused;
  488.     }
  489.     /**
  490.      * Indicates whether the list should not paint the focus component if the list
  491.      * itself has no focus.
  492.      *
  493.      * @param ignoreFocusComponentWhenUnfocused true to ignore the focus component false otherwise
  494.      */
  495.     public void setIgnoreFocusComponentWhenUnfocused(boolean ignoreFocusComponentWhenUnfocused) {
  496.         this.ignoreFocusComponentWhenUnfocused = ignoreFocusComponentWhenUnfocused;
  497.     }
  498.     /**
  499.      * Indicates that the background of a cell renderer might mutate between one entry and the next,
  500.      * it is recommended that this flag remains false for performance reasons.
  501.      * @return the value of the flag
  502.      */
  503.     public boolean isMutableRendererBackgrounds() {
  504.         return mutableRendererBackgrounds;
  505.     }
  506.     /**
  507.      * Indicates that the background of a cell renderer might mutate between one entry and the next,
  508.      * it is recommended that this flag remains false for performance reasons.
  509.      * @param mutableRendererBackgrounds the new value for the flag
  510.      */
  511.     public void setMutableRendererBackgrounds(boolean mutableRendererBackgrounds) {
  512.         this.mutableRendererBackgrounds = mutableRendererBackgrounds;
  513.     }
  514.     private class Listeners implements DataChangedListener, SelectionListener {
  515.         public void dataChanged(int status, int index) {
  516.             List.this.dataChanged(status, index);
  517.         }
  518.         public void selectionChanged(int oldSelected, int newSelected) {
  519.             repaint();
  520.             List.this.listSelectionChanged(oldSelected, newSelected);
  521.         }
  522.     }
  523.     /**
  524.      * Sets the renderer which is used to draw list elements
  525.      *
  526.      * @param renderer cell renderer instance
  527.      */
  528.     public void setListCellRenderer(ListCellRenderer renderer) {
  529.         if (this.renderer != null) {
  530.             //calculate the item list size and the list size.
  531.             elemSize = null;
  532.             selectedElemSize = null;
  533.             setShouldCalcPreferredSize(true);
  534.         }
  535.         this.renderer = renderer;
  536.     }
  537.     /**
  538.      * Returns the renderer which is used to draw list elements
  539.      *
  540.      * @return the renderer which is used to draw list elements
  541.      */
  542.     public ListCellRenderer getRenderer() {
  543.         return renderer;
  544.     }
  545.     /**
  546.      * Returns the list orientation
  547.      *
  548.      * @return the list orientation HORIZONTAL or VERTICAL
  549.      * @see #HORIZONTAL
  550.      * @see #VERTICAL
  551.      */
  552.     public int getOrientation() {
  553.         return orientation;
  554.     }
  555.     /**
  556.      * @inheritDoc
  557.      */
  558.     public void refreshTheme() {
  559.         ListCellRenderer r = getRenderer();
  560.         if (renderingPrototype != null) {
  561.             r.getListCellRendererComponent(this, renderingPrototype, 0, false).refreshTheme();
  562.         } else {
  563.             if (getModel().getSize() > 0) {
  564.                 r.getListCellRendererComponent(this, getModel().getItemAt(0), 0, false).refreshTheme();
  565.             } else {
  566.                 r.getListCellRendererComponent(this, null, 0, false).refreshTheme();
  567.             }
  568.         }
  569.         Component focus = r.getListFocusComponent(this);
  570.         if (focus != null) {
  571.             focus.refreshTheme();
  572.         }
  573.         super.refreshTheme();
  574.     }
  575.     /**
  576.      * Sets the list orientation HORIZONTAL or VERTICAL
  577.      *
  578.      * @param orientation the list orientation HORIZONTAL or VERTICAL
  579.      * @see #HORIZONTAL
  580.      * @see #VERTICAL
  581.      */
  582.     public void setOrientation(int orientation) {
  583.         this.orientation = orientation;
  584.     }
  585.     /**
  586.      * Makes sure the selected index is visible if it is not in the current view
  587.      * rect the list will scroll so it fits within
  588.      *
  589.      * @param rect the rectangle area to scroll to
  590.      */
  591.     public void scrollRectToVisible(Rectangle rect) {
  592.         if (fixedSelection < FIXED_NONE_BOUNDRY) {
  593.             //Dimension elemSize = getElementSize();
  594.             Rectangle toScroll;
  595.             if (orientation != HORIZONTAL) {
  596.                 toScroll = new Rectangle(getScrollX(), rect.getY(), rect.getSize().getWidth(), rect.getSize().getHeight() + itemGap);
  597.             } else {
  598.                 toScroll = new Rectangle(rect.getX(), getScrollY(), rect.getSize().getWidth() + itemGap, rect.getSize().getHeight());
  599.             }
  600.             super.scrollRectToVisible(toScroll, this);
  601.         }
  602.     }
  603.     /**
  604.      * @inheritDoc
  605.      */
  606.     public void setHandlesInput(boolean b) {
  607.         Form f = getComponentForm();
  608.         if (f != null) {
  609.             // prevent the list from losing focus if its the only element
  610.             // or when the user presses fire and there is no other component
  611.             super.setHandlesInput(b || f.isSingleFocusMode());
  612.         } else {
  613.             super.setHandlesInput(b);
  614.         }
  615.     }
  616.     void setHandlesInputParent(boolean b) {
  617.         super.setHandlesInput(b);
  618.     }
  619.     /**
  620.      * @inheritDoc
  621.      */
  622.     protected void fireClicked() {
  623.         boolean h = handlesInput();
  624.         setHandlesInput(!h);
  625.         if (h) {
  626.             fireActionEvent();
  627.         }
  628.         repaint();
  629.     }
  630.     /**
  631.      * @inheritDoc
  632.      */
  633.     protected boolean isSelectableInteraction() {
  634.         return true;
  635.     }
  636.     /**
  637.      * @inheritDoc
  638.      */
  639.     public void keyReleased(int keyCode) {
  640.         // other events are in keyReleased to prevent the next event from reaching the next form
  641.         int gameAction = Display.getInstance().getGameAction(keyCode);
  642.         if (gameAction == Display.GAME_FIRE) {
  643.             boolean h = handlesInput();
  644.             setHandlesInput(!h);
  645.             if (h) {
  646.                 fireActionEvent();
  647.             }
  648.             repaint();
  649.             return;
  650.         }
  651.         if (numericKeyActions && gameAction != Display.GAME_LEFT &&
  652.                 gameAction != Display.GAME_RIGHT && gameAction != Display.GAME_UP &&
  653.                 gameAction != Display.GAME_DOWN) {
  654.             if (keyCode >= '1' && keyCode <= '9') {
  655.                 int offset = keyCode - '1';
  656.                 if (offset < getModel().getSize()) {
  657.                     setSelectedIndex(offset);
  658.                     fireActionEvent();
  659.                 }
  660.             }
  661.         }
  662.     }
  663.     /**
  664.      * @inheritDoc
  665.      */
  666.     public void keyPressed(int keyCode) {
  667.         // scrolling events are in keyPressed to provide immediate feedback
  668.         if (!handlesInput()) {
  669.             return;
  670.         }
  671.         int gameAction = Display.getInstance().getGameAction(keyCode);
  672.         int keyFwd;
  673.         int keyBck;
  674.         if (getOrientation() != HORIZONTAL) {
  675.             keyFwd = Display.GAME_DOWN;
  676.             keyBck = Display.GAME_UP;
  677.             if (gameAction == Display.GAME_LEFT || gameAction == Display.GAME_RIGHT) {
  678.                 setHandlesInput(false);
  679.             }
  680.         } else {
  681.             if (isRTL()) {
  682.          keyFwd = Display.GAME_LEFT;
  683.             keyBck = Display.GAME_RIGHT;
  684.             } else {
  685.          keyFwd = Display.GAME_RIGHT;
  686.             keyBck = Display.GAME_LEFT;
  687.             }
  688.             if (gameAction == Display.GAME_DOWN || gameAction == Display.GAME_UP) {
  689.                 setHandlesInput(false);
  690.             }
  691.         }
  692.         int selectedIndex = model.getSelectedIndex();
  693.         if (gameAction == keyBck) {
  694.             selectedIndex--;
  695.             if (selectedIndex < 0) {
  696.                 if (fixedSelection != FIXED_NONE && fixedSelection != FIXED_NONE_ONE_ELEMENT_MARGIN_FROM_EDGE) {
  697.                     selectedIndex = size() - 1;
  698.                 } else {
  699.                     selectedIndex = 0;
  700.                     setHandlesInput(false);
  701.                 }
  702.             }
  703.         } else if (gameAction == keyFwd) {
  704.             selectedIndex++;
  705.             if (selectedIndex >= size()) {
  706.                 if (fixedSelection != FIXED_NONE && fixedSelection != FIXED_NONE_ONE_ELEMENT_MARGIN_FROM_EDGE) {
  707.                     selectedIndex = 0;
  708.                 } else {
  709.                     selectedIndex = size() - 1;
  710.                     setHandlesInput(false);
  711.                 }
  712.             }
  713.         }
  714.         if (selectedIndex != model.getSelectedIndex()) {
  715.             model.setSelectedIndex(selectedIndex);
  716.             int direction = (gameAction == keyFwd ? 1 : -1);
  717.             if ((isRTL()) && (getOrientation() == HORIZONTAL)) {
  718.              direction = -direction;
  719.             }
  720.             updateAnimationPosition(direction);
  721.             if (fixedSelection == FIXED_NONE || fixedSelection == FIXED_NONE_CYCLIC) {
  722.                 selectElement(selectedIndex);
  723.             }
  724.             if (fixedSelection == FIXED_NONE_ONE_ELEMENT_MARGIN_FROM_EDGE) {
  725.                 // are we going down?
  726.                 if (keyFwd == gameAction) {
  727.                     selectElement(Math.min(selectedIndex + 1, getModel().getSize() - 1));
  728.                 } else {
  729.                     selectElement(Math.max(selectedIndex - 1, 0));
  730.                 }
  731.             }
  732.         }
  733.         repaint();
  734.     }
  735.     private void selectElement(int selectedIndex) {
  736.         Dimension size = getElementSize(false, true);
  737.         Rectangle rect;
  738.         if (getOrientation() != HORIZONTAL) {
  739.             rect = new Rectangle(getX(), (size.getHeight() + itemGap) * selectedIndex, getElementSize(true, true));
  740.         } else {
  741.             rect = new Rectangle((size.getWidth() + itemGap) * selectedIndex, getY(), getElementSize(true, true));
  742.         }
  743.         scrollRectToVisible(rect);
  744.     }
  745.     /**
  746.      * Updates the animation constant to a new value based on a keypress
  747.      *
  748.      * @param direction direction of the animation 1 or -1
  749.      */
  750.     private void updateAnimationPosition(int direction) {
  751.         if (animationPosition != 0) {
  752.             return;
  753.         }
  754.         if (isSmoothScrolling()) {
  755.             if (orientation != HORIZONTAL) {
  756.                 animationPosition += (direction * getElementSize(false, true).getHeight());
  757.             } else {
  758.                 animationPosition += (direction * getElementSize(false, true).getWidth());
  759.             }
  760.             destination = Math.abs(animationPosition);
  761.             initListMotion();
  762.         }
  763.     }
  764.     private void initListMotion() {
  765.         Form p = getComponentForm();
  766.         if(p != null) {
  767.             p.registerAnimatedInternal(this);
  768.         }
  769.         listMotion = Motion.createSplineMotion(0, destination, getScrollAnimationSpeed());
  770.         listMotion.start();
  771.     }
  772.     /**
  773.      * Calculates the desired bounds for the component and returns them within the
  774.      * given rectangle.
  775.      */
  776.     private void calculateComponentPosition(int index, int defaultWidth, Rectangle rect, Dimension rendererSize, Dimension selectedSize, boolean beforeSelected) {
  777.         Style style = getStyle();
  778.         int initialY = style.getPadding(false, TOP);
  779.         int initialX = style.getPadding(false, LEFT);
  780.         boolean rtl = isRTL();
  781.         if (rtl) {
  782.             initialX += getSideGap();
  783.         }
  784.         int selection = getSelectedIndex();
  785.         Dimension d = rect.getSize();
  786.         int selectedDiff;
  787.         // the algorithm illustrated here is very simple despite the "mess" of code...
  788.         // The idea is that if we have a "fixed" element we just add up the amount of pixels
  789.         // to get it into its place in the screen (nothing for top obviously).
  790.         // In order to cause the list to be cyclic we just subtract the list size
  791.         // which will cause the bottom elements to "return" from the top.
  792.         if (orientation != HORIZONTAL) {
  793.             int height = rendererSize.getHeight();
  794.             selectedDiff = selectedSize.getHeight() - height;
  795.             rect.setX(initialX);
  796.             d.setHeight(height);
  797.             d.setWidth(defaultWidth);
  798.             int y = 0;
  799.             int listHeight = getHeight() - style.getPadding(false, TOP) - style.getPadding(false, BOTTOM);
  800.             int totalHeight = (height + itemGap) * getModel().getSize() + selectedDiff;
  801.             switch (fixedSelection) {
  802.                 case FIXED_CENTER:
  803.                     y = listHeight / 2 - (height + itemGap + selectedDiff) / 2 +
  804.                             (index - selection) * (height + itemGap);
  805.                     if (!beforeSelected) {
  806.                         y += selectedDiff;
  807.                     }
  808.                     y = recalcOffset(y, totalHeight, listHeight, height + itemGap);
  809.                     break;
  810.                 case FIXED_TRAIL:
  811.                     y = listHeight - (height + itemGap + selectedDiff);
  812.                 case FIXED_LEAD:
  813.                     y += (index - selection) * (height + itemGap);
  814.                     if (index - selection > 0) {
  815.                         y += selectedDiff;
  816.                     }
  817.                     y = recalcOffset(y, totalHeight, listHeight, height + itemGap);
  818.                     break;
  819.                 default:
  820.                     y = index * (height + itemGap);
  821.                     if (!beforeSelected) {
  822.                         y += selectedDiff;
  823.                     }
  824.                     break;
  825.             }
  826.             rect.setY(y + initialY);
  827.             if (index == selection) {
  828.                 d.setHeight(d.getHeight() + selectedDiff);
  829.             }
  830.         } else {
  831.             int width = rendererSize.getWidth();
  832.             selectedDiff = selectedSize.getWidth() - width;
  833.             rect.setY(initialY);
  834.             d.setHeight(getHeight() - style.getPadding(false, TOP) - style.getPadding(false, BOTTOM));
  835.             d.setWidth(width);
  836.             int x = 0;
  837.             int listWidth = getWidth() - style.getPadding(isRTL(), RIGHT) - style.getPadding(isRTL(), LEFT);
  838.             int totalWidth = (width + itemGap) * getModel().getSize() + selectedDiff;
  839.             switch (fixedSelection) {
  840.                 case FIXED_CENTER:
  841.                     x = listWidth / 2 - (width + itemGap + selectedDiff) / 2 +
  842.                             (index - selection) * (width + itemGap);
  843.                     if (!beforeSelected) {
  844.                         x += selectedDiff;
  845.                     }
  846.                     if(rtl) {
  847.                      x = listWidth - x - width;
  848.                     }
  849.                     x = recalcOffset(x, totalWidth, listWidth, width + itemGap);
  850.                     break;
  851.                 case FIXED_TRAIL:
  852.                     x = listWidth - (width + itemGap + selectedDiff);
  853.                 case FIXED_LEAD:
  854.                     x += (index - selection) * (width + itemGap);
  855.                     if (index - selection > 0) {
  856.                         x += selectedDiff;
  857.                     }
  858.                     if (rtl) {
  859.                      x = listWidth - x - width;
  860.                     }
  861.                     x = recalcOffset(x, totalWidth, listWidth, width + itemGap);
  862.                     break;
  863.                 default:
  864.                     x = index * (width + itemGap);
  865.                     if (!beforeSelected) {
  866.                         x += selectedDiff;
  867.                     }
  868.                     break;
  869.             }
  870.             int rectX=initialX + x;
  871.             if ((rtl) && (fixedSelection<FIXED_NONE_BOUNDRY)) {
  872.              rectX = initialX + totalWidth - (x - initialX) - (width + itemGap);
  873.              if(index == getSelectedIndex()) {
  874.              rectX -= selectedDiff;
  875.                 }
  876.              if(totalWidth < listWidth) {
  877.              rectX += (listWidth - totalWidth);
  878.                 }
  879.             }
  880.             rect.setX(rectX);
  881.             if (index == selection) {
  882.                 d.setWidth(d.getWidth() + selectedDiff);
  883.             }
  884.         }
  885.     }
  886.     /**
  887.      * Allows us to recalculate the bounds of a coordinate to make it "loop" back
  888.      * into view
  889.      *
  890.      * @param offset either x or y coordinate
  891.      * @param totalSize the total width or height of the list with all the elements (including scroll)
  892.      * @param viewSize the size visible to the user
  893.      * @param componentSize the size of the component
  894.      * @return offset after manipulation if such manipulation was performed
  895.      */
  896.     private int recalcOffset(int offset, int totalSize, int viewSize, int componentSize) {
  897.         if (offset + animationPosition % componentSize >= viewSize) {
  898.             offset -= totalSize;
  899.         } else {
  900.             if (offset + animationPosition % componentSize < 1 - componentSize) {
  901.                 offset += totalSize;
  902.             }
  903.         }
  904.         return offset;
  905.     }
  906.     /**
  907.      * @inheritDoc
  908.      */
  909.     public void paint(Graphics g) {
  910.         UIManager.getInstance().getLookAndFeel().drawList(g, this);
  911.         Style style = getStyle();
  912.         int width = getWidth() - style.getPadding(isRTL(), RIGHT) - style.getPadding(isRTL(), LEFT) - getSideGap();
  913.         if (isScrollableX()) {
  914.             width = Math.max(width, getScrollDimension().getWidth() - style.getPadding(isRTL(), RIGHT) - style.getPadding(isRTL(), LEFT) - getSideGap());
  915.         }
  916.         int numOfcomponents = model.getSize();
  917.         if (numOfcomponents == 0) {
  918.             return;
  919.         }
  920.         int xTranslate = getX();
  921.         int yTranslate = getY();
  922.         g.translate(xTranslate, yTranslate);
  923.         Rectangle pos = new Rectangle();
  924.         Dimension rendererSize = getElementSize(false, true);
  925.         int selection = model.getSelectedIndex();
  926.         if (animationPosition != 0 && fixedSelection > FIXED_NONE_BOUNDRY) {
  927.             // selection should never move during animation of fixed elements
  928.             selection = -1;
  929.             if (orientation != HORIZONTAL) {
  930.                 yTranslate += animationPosition;
  931.                 g.translate(0, animationPosition);
  932.             } else {
  933.                 xTranslate += animationPosition;
  934.                 g.translate(animationPosition, 0);
  935.             }
  936.         }
  937.         int clipX = g.getClipX();
  938.         int clipY = g.getClipY();
  939.         int clipWidth = g.getClipWidth();
  940.         int clipHeight = g.getClipHeight();
  941.         // this flag is for preformance improvements
  942.         // if we figured out that the list items are not visible anymore
  943.         // we should break from the List loop
  944.         boolean shouldBreak = false;
  945.         // improve performance for browsing the end of a very large list
  946.         int startingPoint = 0;
  947.         if (fixedSelection < FIXED_NONE_BOUNDRY) {
  948.             int startX = clipX + getAbsoluteX();
  949.             if (isRTL()) {
  950.                 //In RTL the start of the list is not in the left side of the viewport, but rather the right side
  951.              startX += getWidth();
  952.             }
  953.             startingPoint = Math.max(0, pointerSelect(startX, clipY + getAbsoluteY()) - 1);
  954.         }
  955.         int startOffset = 0;
  956.         int endOffset = numOfcomponents;
  957.         if(mutableRendererBackgrounds) {
  958.             for (int i = startingPoint; i < numOfcomponents; i++) {
  959.                 // skip on the selected
  960.                 if (i == getSelectedIndex() && animationPosition == 0) {
  961.                     shouldBreak = true;
  962.                     if(!shouldBreak) {
  963.                         startOffset = i;
  964.                     }
  965.                     endOffset = i;
  966.                     continue;
  967.                 }
  968.                 calculateComponentPosition(i, width, pos, rendererSize, getElementSize(true, true), i <= getSelectedIndex());
  969.                 // if the renderer is in the clipping region
  970.                 if (pos.intersects(clipX, clipY, clipWidth, clipHeight)) {
  971.                     if(!shouldBreak) {
  972.                         startOffset = i;
  973.                     }
  974.                     endOffset = i;
  975.                     Dimension size = pos.getSize();
  976.                     Component selectionCmp = renderer.getListCellRendererComponent(this, getModel().getItemAt(i), i, i == getSelectedIndex());
  977.                     renderComponentBackground(g, selectionCmp, pos.getX(), pos.getY(), size.getWidth(), size.getHeight());
  978.                     shouldBreak = true;
  979.                 } else {
  980.                     //this is relevant only if the List is not fixed.
  981.                     if (shouldBreak && (fixedSelection < FIXED_NONE_BOUNDRY)) {
  982.                         break;
  983.                     }
  984.                 }
  985.             }
  986.         } else {
  987.             Object valueAt0 = getModel().getItemAt(0);
  988.             Component selectionCmp = renderer.getListCellRendererComponent(this, valueAt0, 0, true);
  989.             Component unselectedCmp = renderer.getListCellRendererComponent(this, valueAt0, 0, false);
  990.             for (int i = startingPoint; i < numOfcomponents; i++) {
  991.                 // skip on the selected
  992.                 if (i == getSelectedIndex() && animationPosition == 0) {
  993.                     shouldBreak = true;
  994.                     if(!shouldBreak) {
  995.                         startOffset = i;
  996.                     }
  997.                     endOffset = i;
  998.                     continue;
  999.                 }
  1000.                 calculateComponentPosition(i, width, pos, rendererSize, getElementSize(true, true), i <= getSelectedIndex());
  1001.                 // if the renderer is in the clipping region
  1002.                 if (pos.intersects(clipX, clipY, clipWidth, clipHeight)) {
  1003.                     if(!shouldBreak) {
  1004.                         startOffset = i;
  1005.                     }
  1006.                     endOffset = i;
  1007.                     if(i == getSelectedIndex()) {
  1008.                         Dimension size = pos.getSize();
  1009.                         renderComponentBackground(g, selectionCmp, pos.getX(), pos.getY(), size.getWidth(), size.getHeight());
  1010.                     } else {
  1011.                         Dimension size = pos.getSize();
  1012.                         renderComponentBackground(g, unselectedCmp, pos.getX(), pos.getY(), size.getWidth(), size.getHeight());
  1013.                     }
  1014.                     shouldBreak = true;
  1015.                 } else {
  1016.                     //this is relevant only if the List is not fixed.
  1017.                     if (shouldBreak && (fixedSelection < FIXED_NONE_BOUNDRY)) {
  1018.                         break;
  1019.                     }
  1020.                 }
  1021.             }
  1022.         }
  1023.         if (paintFocusBehindList) {
  1024.             paintFocus(g, width, pos, rendererSize);
  1025.         }
  1026.         for (int i = startOffset; i <= endOffset; i++) {
  1027.             // skip on the selected
  1028.             if (i == getSelectedIndex() && animationPosition == 0) {
  1029.                 continue;
  1030.             }
  1031.             calculateComponentPosition(i, width, pos, rendererSize, getElementSize(true, true), i <= getSelectedIndex());
  1032.             Object value = model.getItemAt(i);
  1033.             Component cmp = renderer.getListCellRendererComponent(this, value, i, false);
  1034.             cmp.setCellRenderer(true);
  1035.             Dimension size = pos.getSize();
  1036.             renderComponent(g, cmp, pos.getX(), pos.getY(), size.getWidth(), size.getHeight());
  1037.         }
  1038.         calculateComponentPosition(getSelectedIndex(), width, pos, rendererSize, getElementSize(true, true), true);
  1039.         Dimension size = pos.getSize();
  1040.         //if the animation has finished draw the selected element
  1041.         if ((renderer.getListFocusComponent(this) == null && (fixedSelection < FIXED_NONE_BOUNDRY)) || animationPosition == 0 && model.getSize() > 0) {
  1042.             Component selected = renderer.getListCellRendererComponent(this, model.getItemAt(selection), selection, true);
  1043.             renderComponentBackground(g, selected, pos.getX(), pos.getY(), size.getWidth(), size.getHeight());
  1044.             renderComponent(g, selected, pos.getX(), pos.getY(), size.getWidth(), size.getHeight());
  1045.         }
  1046.         if (!paintFocusBehindList) {
  1047.             paintFocus(g, width, pos, rendererSize);
  1048.         }
  1049.         g.translate(-xTranslate, -yTranslate);
  1050.     }
  1051.     private void paintFocus(Graphics g, int width, Rectangle pos, Dimension rendererSize) {
  1052.         if(ignoreFocusComponentWhenUnfocused && !hasFocus()) {
  1053.             return;
  1054.         }
  1055.         calculateComponentPosition(getSelectedIndex(), width, pos, rendererSize, getElementSize(true, true), true);
  1056.         Dimension size = pos.getSize();
  1057.         Component cmp = renderer.getListFocusComponent(this);
  1058.         if (cmp != null) {
  1059.             cmp.setCellRenderer(true);
  1060.             int x = pos.getX();
  1061.             int y = pos.getY();
  1062.             // prevent focus animation from working during a drag operation
  1063.             if (orientation != HORIZONTAL) {
  1064.                 y -= animationPosition;
  1065.             } else {
  1066.                 x -= animationPosition;
  1067.             }
  1068.             renderComponentBackground(g, cmp, x, y, size.getWidth(), size.getHeight());
  1069.             renderComponent(g, cmp, x, y, size.getWidth(), size.getHeight());
  1070.         }
  1071.     }
  1072.     /**
  1073.      * Renders the current component on the screen
  1074.      */
  1075.     private void renderComponent(Graphics g, Component cmp, int x, int y, int width, int height) {
  1076.         Style s = cmp.getStyle();
  1077.         int left = s.getMargin(isRTL(), LEFT);
  1078.         int top =  s.getMargin(false, TOP);
  1079.         cmp.setWidth(width - left - s.getMargin(isRTL(), RIGHT));
  1080.         cmp.setHeight(height - top - s.getMargin(false, BOTTOM));
  1081.         cmp.setX(x + left);
  1082.         cmp.setY(y + top);
  1083.         int oX = g.getClipX();
  1084.         int oY = g.getClipY();
  1085.         int oWidth = g.getClipWidth();
  1086.         int oHeight = g.getClipHeight();
  1087.         g.clipRect(cmp.getX(), cmp.getY(), cmp.getWidth(), cmp.getHeight());
  1088.         cmp.paint(g);
  1089.         if (cmp.isBorderPainted()) {
  1090.             cmp.paintBorder(g);
  1091.         }
  1092.         g.setClip(oX, oY, oWidth, oHeight);
  1093.     }
  1094.     private void renderComponentBackground(Graphics g, Component cmp, int x, int y, int width, int height) {
  1095.         Style s = cmp.getStyle();
  1096.         int left = s.getMargin(isRTL(), LEFT);
  1097.         int top =  s.getMargin(false, TOP);
  1098.         cmp.setWidth(width - left - s.getMargin(isRTL(), RIGHT));
  1099.         cmp.setHeight(height - top - s.getMargin(false, BOTTOM));
  1100.         cmp.setX(x + left);
  1101.         cmp.setY(y + top);
  1102.         int cX = g.getClipX();
  1103.         int cY = g.getClipY();
  1104.         int cW = g.getClipWidth();
  1105.         int cH = g.getClipHeight();
  1106.         g.clipRect(cmp.getX(), cmp.getY(), cmp.getWidth(), cmp.getHeight());
  1107.         cmp.paintBackground(g);
  1108.         g.setClip(cX, cY, cW, cH);
  1109.     }
  1110.     /**
  1111.      * Invoked to indicate interest in future selection events
  1112.      *
  1113.      * @param l the selection listener to be added
  1114.      */
  1115.     public void addSelectionListener(SelectionListener l) {
  1116.         model.addSelectionListener(l);
  1117.     }
  1118.     /**
  1119.      * Invoked to indicate no further interest in future selection events
  1120.      *
  1121.      * @param l the selection listener to be removed
  1122.      */
  1123.     public void removeSelectionListener(SelectionListener l) {
  1124.         model.removeSelectionListener(l);
  1125.     }
  1126.     /**
  1127.      * Allows binding a listener to user selection actions
  1128.      *
  1129.      * @param l the action listener to be added
  1130.      */
  1131.     public void addActionListener(ActionListener l) {
  1132.         dispatcher.addListener(l);
  1133.     }
  1134.     /**
  1135.      * Allows binding a listener to user selection actions
  1136.      *
  1137.      * @param l the action listener to be removed
  1138.      */
  1139.     public void removeActionListener(ActionListener l) {
  1140.         dispatcher.removeListener(l);
  1141.     }
  1142.     /**
  1143.      * @inheritDoc
  1144.      */
  1145.     protected void fireActionEvent() {
  1146.         if(isEnabled()){
  1147.             if(disposeDialogOnSelection) {
  1148.                 ((Dialog)getComponentForm()).dispose();
  1149.             }
  1150.             super.fireActionEvent();
  1151.             ActionEvent a = new ActionEvent(eventSource);
  1152.             dispatcher.fireActionEvent(a);
  1153.             if(isCommandList() && !a.isConsumed()) {
  1154.                 Object i = getSelectedItem();
  1155.                 if(i != null && i instanceof Command) {
  1156.                     ((Command)i).actionPerformed(a);
  1157.                     if(!a.isConsumed()) {
  1158.                         Form f = getComponentForm();
  1159.                         if(f != null) {
  1160.                             f.actionCommandImpl((Command)i);
  1161.                         }
  1162.                     }
  1163.                 }
  1164.             }
  1165.         }
  1166.     }
  1167.     /**
  1168.      * A list can start handling input implicitly upon gaining focus, this can
  1169.      * make for a more intuitive UI when no other focus elements exist or when
  1170.      * their use case is infrequent. However, it might be odd in some cases
  1171.      * where the list "steals" focus.
  1172.      *
  1173.      * @param inputOnFocus true is a list can start handling input
  1174.      * implicitly upon gaining focus
  1175.      */
  1176.     public void setInputOnFocus(boolean inputOnFocus) {
  1177.         this.inputOnFocus = inputOnFocus;
  1178.     }
  1179.     /**
  1180.      * This method determines if the animated focus is drawn on top of the List
  1181.      * or behind the List when moving.
  1182.      *
  1183.      * @param paintFocusBehindList
  1184.      */
  1185.     public void setPaintFocusBehindList(boolean paintFocusBehindList) {
  1186.         this.paintFocusBehindList = paintFocusBehindList;
  1187.     }
  1188.     /**
  1189.      * @inheritDoc
  1190.      */
  1191.     void focusGainedInternal() {
  1192.         if (inputOnFocus) {
  1193.             setHandlesInput(true);
  1194.         }
  1195.     }
  1196.     /**
  1197.      * @inheritDoc
  1198.      */
  1199.     void focusLostInternal() {
  1200.     }
  1201.     /**
  1202.      * Returns the gap between items
  1203.      *
  1204.      * @return the gap between items
  1205.      */
  1206.     public int getItemGap() {
  1207.         return itemGap;
  1208.     }
  1209.     /**
  1210.      * Set the gap between items
  1211.      *
  1212.      * @param itemGap the gap between items
  1213.      */
  1214.     public void setItemGap(int itemGap) {
  1215.         this.itemGap = itemGap;
  1216.     }
  1217.     /**
  1218.      * The rendering prototype is optionally used in calculating the size of the
  1219.      * List and is recommended for performance reasons. You should invoke it with an object
  1220.      * representing a theoretical value in the list which will be used to calculate
  1221.      * the size required for each element in the list.
  1222.      * <p>This allows list size calculations to work across look and feels and allows
  1223.      * developers to predetermin size for list elements.
  1224.      * <p>e.g. For a list of Strings which you would like to always be 5 characters wide
  1225.      * you can use a prototype "XXXXX" which would use the preferred size of the XXXXX
  1226.      * String to determine the size of the list element. E.g. for a list of dates you can use
  1227.      * new Date(30, 12, 00) etc..
  1228.      *
  1229.      * @param renderingPrototype a value that can be passed to the renderer to indicate the preferred
  1230.      * size of a list component.
  1231.      */
  1232.     public void setRenderingPrototype(Object renderingPrototype) {
  1233.         this.renderingPrototype = renderingPrototype;
  1234.     }
  1235.     /**
  1236.      * See set rendering prototype
  1237.      *
  1238.      * @see #setRenderingPrototype(java.lang.Object)
  1239.      * @return the value of the rendering prototype
  1240.      */
  1241.     public Object getRenderingPrototype() {
  1242.         return renderingPrototype;
  1243.     }
  1244.     /**
  1245.      * Calculates the default size for elements on the list
  1246.      *
  1247.      * @return the default dimension for elements in a list
  1248.      */
  1249.     Dimension getElementSize(boolean selected, boolean addMargin) {
  1250.         if (selected) {
  1251.             if (selectedElemSize == null) {
  1252.                 // don't keep element size if there are no elements and no prototype...
  1253.                 if (renderingPrototype == null) {
  1254.                     if (model.getSize() == 0) {
  1255.                         // put a sensible value as default when there are no elements or rendering prototype
  1256.                         if(addMargin) {
  1257.                             return new Label("XXXXXX").getPreferredSizeWithMargin();
  1258.                         }
  1259.                         return new Label("XXXXXX").getPreferredSize();
  1260.                     }
  1261.                 }
  1262.                 selectedElemSize = calculateElementSize(true, addMargin);
  1263.             }
  1264.             return selectedElemSize;
  1265.         } else {
  1266.             if (elemSize == null) {
  1267.                 // don't keep element size if there are no elements and no prototype...
  1268.                 if (renderingPrototype == null) {
  1269.                     if (model.getSize() == 0) {
  1270.                         // put a sensible value as default when there are no elements or rendering prototype
  1271.                         Label l = new Label("XXXXXX");
  1272.                         if(addMargin) {
  1273.                             return l.getPreferredSizeWithMargin();
  1274.                         } else {
  1275.                             return l.getPreferredSize();
  1276.                         }
  1277.                     }
  1278.                 }
  1279.                 elemSize = calculateElementSize(false, addMargin);
  1280.             }
  1281.             return elemSize;
  1282.         }
  1283.     }
  1284.     /**
  1285.      * Calculates the size of an element based on a forumla or on rendering prototype
  1286.      */
  1287.     private Dimension calculateElementSize(boolean selected, boolean addMargin) {
  1288.         if (renderingPrototype != null) {
  1289.             Component unselected = renderer.getListCellRendererComponent(this, renderingPrototype, 0, selected);
  1290.             if(addMargin) {
  1291.                 return unselected.getPreferredSizeWithMargin();
  1292.             } else {
  1293.                 return unselected.getPreferredSize();
  1294.             }
  1295.         }
  1296.         int width = 0;
  1297.         int height = 0;
  1298.         int elements = Math.min(5, model.getSize());
  1299.         int marginY = 0;
  1300.         int marginX = 0;
  1301.         for (int iter = 0; iter < elements; iter++) {
  1302.             Component cmp = renderer.getListCellRendererComponent(this, model.getItemAt(iter), iter, selected);
  1303.             Dimension d = cmp.getPreferredSize();
  1304.             width = Math.max(width, d.getWidth());
  1305.             height = Math.max(height, d.getHeight());
  1306.             if(iter == 0) {
  1307.                 Style s = cmp.getStyle();
  1308.                 marginY = s.getMargin(TOP) + s.getMargin(BOTTOM);
  1309.                 marginX = s.getMargin(LEFT) + s.getMargin(RIGHT);
  1310.             }
  1311.         }
  1312.         return new Dimension(width + marginX, height + marginY);
  1313.     }
  1314.     /**
  1315.      * @inheritDoc
  1316.      */
  1317.     protected void longPointerPress(int x, int y) {
  1318.         int s = pointerSelect(x, y);
  1319.         if(s > -1) {
  1320.             model.setSelectedIndex(s);
  1321.         }
  1322.     }
  1323.     /**
  1324.      * @inheritDoc
  1325.      */
  1326.     public void pointerPressed(int x, int y) {
  1327.         if(fixedSelection > FIXED_NONE_BOUNDRY) {
  1328.             // for a fixed list we need to store the initial drag position
  1329.             if(isSmoothScrolling()) {
  1330.                 if(orientation != HORIZONTAL) {
  1331.                     initialFixedDrag = y;
  1332.                 } else {
  1333.                     initialFixedDrag = x;
  1334.                 }
  1335.                 initialFixedSelection = getModel().getSelectedIndex();
  1336.             }
  1337.         }
  1338.         // prevent a hover event from activating the drag in case of a click screen,
  1339.         // this is essential for the Storm device
  1340.         setDragActivated(false);
  1341.         
  1342.         int current = model.getSelectedIndex();
  1343.         int selection = pointerSelect(x, y);
  1344.         if (selection > -1) {
  1345.             model.setSelectedIndex(selection);
  1346.             fireOnRelease = current == selection;
  1347.         }
  1348.         super.pointerPressed(x, y);
  1349.     }
  1350.     /**
  1351.      * @inheritDoc
  1352.      */
  1353.     public void pointerHover(int[] x, int[] y) {
  1354.         clearDrag();
  1355.         if(!isDragActivated()) {
  1356.             int selection = pointerSelect(x[0], y[0]);
  1357.             if (selection > -1) {
  1358.                 model.setSelectedIndex(selection);
  1359.             }
  1360.         }
  1361.         pointerDraggedImpl(x[0], y[0]);
  1362.     }
  1363.     /**
  1364.      * @inheritDoc
  1365.      */
  1366.     public void pointerDragged(int x, int y) {
  1367.         pointerDraggedImpl(x, y);
  1368.     }
  1369.     private void pointerDraggedImpl(int x, int y) {
  1370.         if (isSmoothScrolling()) {
  1371.             if(fixedSelection < FIXED_NONE_BOUNDRY) {
  1372.                 super.pointerDragged(x, y);
  1373.             } else {
  1374.                 if(getModel().getSize() > 2) {
  1375.                     // a fixed list is not scrollable we need to implement dragging ourselves...
  1376.                     int offset;
  1377.                     int componentSize;
  1378.                     if(orientation != HORIZONTAL) {
  1379.                         offset = y - initialFixedDrag;
  1380.                         componentSize = getElementSize(false, true).getHeight();
  1381.                     } else {
  1382.                         offset = x - initialFixedDrag;
  1383.                         componentSize = getElementSize(false, true).getWidth();
  1384.                     }
  1385.                     if(!isDragActivated()) {
  1386.                         if(offset > 4) {
  1387.                             setDragActivated(true);
  1388.                         }
  1389.                     }
  1390.                     if(isDragActivated()) {
  1391.                         int elementSelection = initialFixedSelection + (offset / componentSize) % getModel().getSize();
  1392.                         if(elementSelection < 0) {
  1393.                             elementSelection += getModel().getSize();
  1394.                         }
  1395.                         getModel().setSelectedIndex(elementSelection);
  1396.                     }
  1397.                 }
  1398.                 int numOfcomponents = getModel().getSize();
  1399.                 Style style = getStyle();
  1400.                 int width = getWidth() - style.getPadding(false, RIGHT) - style.getPadding(false, LEFT) - getSideGap();
  1401.                 if (isScrollableX()) {
  1402.                     width = Math.max(width, getScrollDimension().getWidth() - style.getPadding(false, RIGHT) - style.getPadding(false, LEFT) - getSideGap());
  1403.                 }
  1404.                 Dimension rendererSize = getElementSize(false, true);
  1405.                 Dimension selectedSize = getElementSize(true, true);
  1406.                 Rectangle pos = new Rectangle();
  1407.                 for (int i = 0; i < numOfcomponents; i++) {
  1408.                     calculateComponentPosition(i, width, pos, rendererSize, selectedSize, true);
  1409.                     if (pos.contains(x, y)) {
  1410.                         model.setSelectedIndex(i);
  1411.                         break;
  1412.                     }
  1413.                 }
  1414.             }
  1415.         } else {
  1416.             model.setSelectedIndex(pointerSelect(x, y));
  1417.         }
  1418.     }
  1419.     private int pointerSelect(int x, int y) {
  1420.         int selectedIndex = -1;
  1421.         int numOfcomponents = getModel().getSize();
  1422.         Style style = getStyle();
  1423.         Dimension rendererSize = getElementSize(false, true);
  1424.         Dimension selectedSize = getElementSize(true, true);
  1425.         Rectangle pos = new Rectangle();
  1426.         int width = getWidth() - style.getPadding(false, RIGHT) - style.getPadding(false, LEFT) - getSideGap();
  1427.         if (isScrollableX()) {
  1428.             width = Math.max(width, getScrollDimension().getWidth() - style.getPadding(false, RIGHT) - style.getPadding(false, LEFT) - getSideGap());
  1429.         }
  1430.         y = y - getAbsoluteY();
  1431.         x = x - getAbsoluteX();
  1432.         if (fixedSelection < FIXED_NONE_BOUNDRY) {
  1433.             calculateComponentPosition(getSelectedIndex(), width, pos, rendererSize, getElementSize(true, true), true);
  1434.             if (orientation != HORIZONTAL) {
  1435.                 if(y < pos.getY()){
  1436.                     selectedIndex = y / (rendererSize.getHeight() + itemGap);
  1437.                 }else{
  1438.                     int current = getSelectedIndex();
  1439.                     if(y < pos.getY() + selectedSize.getHeight()){
  1440.                         selectedIndex = current;
  1441.                     }else{
  1442.                         selectedIndex = (current+1) + (y - (pos.getY() + selectedSize.getHeight()))/(rendererSize.getHeight() + itemGap);
  1443.                     }
  1444.                 }
  1445.             } else {
  1446.                 if (isRTL()) {
  1447.                  if (x > pos.getX()+selectedSize.getWidth()) {
  1448.                  int delta=x-(pos.getX()+selectedSize.getWidth());
  1449.                  delta /= (rendererSize.getWidth() + itemGap);
  1450.                         // should have been -1-delta, but works better like this.
  1451.                  selectedIndex = getSelectedIndex()-delta;
  1452.                  } else {
  1453.                     if(x >= pos.getX()){
  1454.                         selectedIndex = getSelectedIndex();
  1455.                     } else {
  1456.                         int delta=pos.getX()-x;
  1457.                         delta/=(rendererSize.getWidth() + itemGap);
  1458.                         selectedIndex=getSelectedIndex()+1+delta;
  1459.                     }
  1460.                  }
  1461.                 } else {
  1462.                     if(x < pos.getX()){
  1463.                         selectedIndex = x / (rendererSize.getWidth() + itemGap);
  1464.                     }else{
  1465.                         int current = getSelectedIndex();
  1466.                         if(x < pos.getX() + selectedSize.getWidth()){
  1467.                             selectedIndex = current;
  1468.                         }else{
  1469.                             selectedIndex = (current+1) + (x - (pos.getX() + selectedSize.getWidth()))/(rendererSize.getWidth() + itemGap);
  1470.                         }
  1471.                     }
  1472.                 }
  1473.             }
  1474.         } else {
  1475.             for (int i = 0; i < numOfcomponents; i++) {
  1476.                 calculateComponentPosition(i, width, pos, rendererSize, selectedSize, true);
  1477.                 if (pos.contains(x, y)) {
  1478.                     selectedIndex = i;
  1479.                     break;
  1480.                 }
  1481.             }
  1482.         }
  1483.         if (selectedIndex < 0 || selectedIndex >= size()) {
  1484.             return -1;
  1485.         }
  1486.         return selectedIndex;
  1487.     }
  1488.     /**
  1489.      * This method determines if the List fires the action event when the pointer
  1490.      * was clicked on one of the items, or only if the item was the selected item
  1491.      * By default the value is true.
  1492.      *
  1493.      * @param fireOnClick
  1494.      */
  1495.     public void setFireOnClick(boolean fireOnClick){
  1496.         this.fireOnClick = fireOnClick;
  1497.     }
  1498.     /**
  1499.      * @inheritDoc
  1500.      */
  1501.     public void pointerHoverReleased(int[] x, int[] y) {
  1502.         pointerReleasedImpl(x[0], y[0], true);
  1503.     }
  1504.     private void pointerReleasedImpl(int x, int y, boolean isHover) {
  1505.         if (isDragActivated()) {
  1506.             if(fixedSelection < FIXED_NONE_BOUNDRY) {
  1507.                 super.pointerReleased(x, y);
  1508.             } else {
  1509.                 if(isScrollableY()){
  1510.                     destination += Display.getInstance().getDragSpeed(true);
  1511.                 }else{
  1512.                     destination += Display.getInstance().getDragSpeed(false) * 1000;
  1513.                 }
  1514.                 if(destination < 0) {
  1515.                     destination += model.getSize();
  1516.                     if(destination >= model.getSize()) {
  1517.                         destination %= model.getSize();
  1518.                     }
  1519.                 } else {
  1520.                     if(destination >= model.getSize()) {
  1521.                         destination -= model.getSize();
  1522.                         if(destination >= model.getSize()) {
  1523.                             destination %= model.getSize();
  1524.                         }
  1525.                     }
  1526.                 }
  1527.                 initListMotion();
  1528.                 setDragActivated(false);
  1529.             }
  1530.             return;
  1531.         }
  1532.         
  1533.         if (!isHover) {
  1534.             if (fireOnClick || fireOnRelease) {
  1535.                 fireActionEvent();
  1536.             }
  1537.         }
  1538.     }
  1539.     /**
  1540.      * @inheritDoc
  1541.      */
  1542.     public void pointerReleased(int x, int y) {
  1543.         pointerReleasedImpl(x, y, false);
  1544.     }
  1545.     /**
  1546.      * @inheritDoc
  1547.      */
  1548.     protected Dimension calcPreferredSize() {
  1549.         return UIManager.getInstance().getLookAndFeel().getListPreferredSize(this);
  1550.     }
  1551.     /**
  1552.      * Allows adding an element to a list if the underlying model supports this, notice that
  1553.      * it is an optional operation and if the model does not support it (default list model does)
  1554.      * then this operation may failed.
  1555.      *
  1556.      * @param item the item to be added to a list model
  1557.      */
  1558.     public void addItem(Object item) {
  1559.         model.addItem(item);
  1560.     }
  1561.     /**
  1562.      * Indicates whether selection is fixable to place in which case all the
  1563.      * elements in the list move and selection stays in place.
  1564.      *
  1565.      * @return one of: FIXED_NONE, FIXED_TRAIL, FIXED_LEAD, FIXED_CENTER, FIXED_NONE_CYCLIC
  1566.      */
  1567.     public int getFixedSelection() {
  1568.         return fixedSelection;
  1569.     }
  1570.     /**
  1571.      * Indicates whether selection is fixable to place in which case all the
  1572.      * elements in the list move and selection stays in place.
  1573.      *
  1574.      * @param fixedSelection one of: FIXED_NONE, FIXED_TRAIL, FIXED_LEAD,
  1575.      * FIXED_CENTER, FIXED_NONE_CYCLIC
  1576.      */
  1577.     public void setFixedSelection(int fixedSelection) {
  1578.         this.fixedSelection = fixedSelection;
  1579.     }
  1580.     void deregisterAnimatedInternal() {
  1581.         if (animationPosition == 0) {
  1582.             super.deregisterAnimatedInternal();
  1583.         }
  1584.     }
  1585.     
  1586.     /**
  1587.      * @inheritDoc
  1588.      */
  1589.     public boolean animate() {
  1590.         // parent is performing the animation we shouldn't do anything in this case
  1591.         // this is the scrolling animation which we don't want to interfear with
  1592.         boolean parentFinished = super.animate();
  1593.         if (animationPosition != 0) {
  1594.             if (animationPosition < 0) {
  1595.                 animationPosition = Math.min(listMotion.getValue() - destination, 0);
  1596.             } else {
  1597.                 animationPosition = Math.max(destination - listMotion.getValue(), 0);
  1598.             }
  1599.             if(animationPosition == 0) {
  1600.                 deregisterAnimatedInternal();
  1601.             }
  1602.             return true;
  1603.         }
  1604.         return parentFinished;
  1605.     }
  1606.     /**
  1607.      * Setting the surrounding border gap
  1608.      *
  1609.      * @param borderGap number of pixels for the gap
  1610.      */
  1611.     public void setBorderGap(int borderGap) {
  1612.         this.borderGap = borderGap;
  1613.     }
  1614.     /**
  1615.      * Getting the surrounding border gap
  1616.      *
  1617.      * @return border gap in pixels
  1618.      */
  1619.     public int getBorderGap() {
  1620.         return borderGap;
  1621.     }
  1622.     /**
  1623.      * This flag indicates to the List if the List should scroll to the selected
  1624.      * element when it's been initialized.
  1625.      *
  1626.      * @param scrollToSelected if true the List scrolls to the selected element
  1627.      * when It's been initalized.
  1628.      */
  1629.     public void setScrollToSelected(boolean scrollToSelected) {
  1630.         this.scrollToSelected = scrollToSelected;
  1631.     }
  1632.     /**
  1633.      * @inheritDoc
  1634.      */
  1635.     protected String paramString() {
  1636.         String elemSizeStr = "element size = ";
  1637.         if (elemSize != null) {
  1638.             elemSizeStr += elemSize.toString();
  1639.         }
  1640.         return super.paramString() + ", " + elemSizeStr +
  1641.                 ", itemGap = " + itemGap +
  1642.                 ", orientation = " + orientation +
  1643.                 ", selected index = " + getSelectedIndex() +
  1644.                 ", size = " + size();
  1645.     }
  1646. }