Home » openjdk-7 » java » awt » [javadoc | source]

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    */
   17   
   18   package java.awt;
   19   
   20   import java.awt.event.InputEvent;
   21   import java.awt.event.KeyEvent;
   22   import java.awt.event.MouseEvent;
   23   import java.awt.event.MouseWheelEvent;
   24   import org.apache.harmony.awt.ChoiceStyle;
   25   import org.apache.harmony.awt.PeriodicTimer;
   26   import org.apache.harmony.awt.ScrollStateController;
   27   import org.apache.harmony.awt.Scrollable;
   28   import org.apache.harmony.awt.ScrollbarStateController;
   29   import org.apache.harmony.awt.gl.MultiRectArea;
   30   import org.apache.harmony.awt.state.ListState;
   31   
   32   /**
   33    * Helper class: popup window containing list of
   34    * Choice items, implements popup and scrolling list behavior
   35    * of Choice component
   36    */
   37   class ChoicePopupBox extends PopupBox {
   38       private final Choice choice;
   39   
   40       /**
   41        * how many items to scroll on PgUp/PgDn
   42        */
   43       final static int PAGE_SIZE = 8;
   44   
   45       /**
   46        * how many ticks to skip when scroll speed is minimum
   47        */
   48       final static int MAX_SKIP = 12;
   49   
   50       int selectedItem;
   51   
   52       int focusedItem;
   53   
   54       /**
   55        * fields for dragging/scrolling behavior implementation
   56        */
   57       private int scrollLocation;
   58   
   59       transient ScrollPaneAdjustable vAdj;
   60   
   61       Scrollable scrollable;
   62   
   63       ScrollStateController scrollController;
   64   
   65       ScrollbarStateController scrollbarController;
   66   
   67       boolean scroll;
   68   
   69       private boolean scrollDragged;
   70   
   71       /**
   72        * fields for scrolling animation
   73        */
   74       private final PeriodicTimer dragScrollTimer;
   75   
   76       private int dragScrollSpeed;
   77   
   78       private Runnable dragScrollHandler;
   79   
   80       private int nSkip; // scroll every nSkip ticks
   81   
   82       private final ChoiceListState listState;
   83   
   84       private final ChoiceStyle style;
   85   
   86       /**
   87        * List state implementation
   88        */
   89       class ChoiceListState extends Choice.ComponentState implements ListState {
   90           ChoiceListState() {
   91               choice.super();
   92           }
   93   
   94           public Rectangle getItemBounds(int idx) {
   95               return ChoicePopupBox.this.getItemBounds(idx);
   96           }
   97   
   98           public Rectangle getClient() {
   99               Rectangle clientRect = ChoicePopupBox.this.getBounds();
  100               if (scroll) {
  101                   clientRect.width -= scrollable.getAdjustableWidth();
  102               }
  103               clientRect.grow(-1, -1);
  104               return clientRect;
  105           }
  106   
  107           public int getItemCount() {
  108               return choice.getItemCount();
  109           }
  110   
  111           public boolean isSelected(int idx) {
  112               return (selectedItem == idx);
  113           }
  114   
  115           public String getItem(int idx) {
  116               return choice.getItem(idx);
  117           }
  118   
  119           public int getCurrentIndex() {
  120               return focusedItem;
  121           }
  122   
  123           @Override
  124           public Dimension getSize() {
  125               return ChoicePopupBox.this.getSize();
  126           }
  127       }
  128   
  129       /**
  130        * Scrolling behavior implementation
  131        */
  132       class ChoiceScrollable implements Scrollable {
  133           public Adjustable getVAdjustable() {
  134               return vAdj;
  135           }
  136   
  137           public Adjustable getHAdjustable() {
  138               return null;
  139           }
  140   
  141           public Insets getInsets() {
  142               return Choice.INSETS;
  143           }
  144   
  145           public Point getLocation() {
  146               return new Point(0, scrollLocation);
  147           }
  148   
  149           public void setLocation(Point p) {
  150               scrollLocation = p.y;
  151           }
  152   
  153           public Component getComponent() {
  154               return choice;
  155           }
  156   
  157           public Dimension getSize() {
  158               return new Dimension(getWidth(), choice.getItemHeight() * choice.getItemCount());
  159           }
  160   
  161           public void doRepaint() {
  162               repaint();
  163           }
  164   
  165           public int getAdjustableWidth() {
  166               return vAdj.getBounds().width;
  167           }
  168   
  169           public int getAdjustableHeight() {
  170               return 0;
  171           }
  172   
  173           public void setAdjustableSizes(Adjustable adj, int vis, int min, int max) {
  174               vAdj.setSizes(vis, min, max);
  175           }
  176   
  177           public int getAdjustableMode(Adjustable adj) {
  178               return Scrollable.VERTICAL_ONLY;
  179           }
  180   
  181           public void setAdjustableBounds(Adjustable adj, Rectangle r) {
  182               vAdj.setBounds(r);
  183           }
  184   
  185           public int getWidth() {
  186               return ChoicePopupBox.this.getWidth();
  187           }
  188   
  189           public int getHeight() {
  190               return ChoicePopupBox.this.getHeight();
  191           }
  192   
  193           public void doRepaint(Rectangle r) {
  194               repaint(r);
  195           }
  196       }
  197   
  198       public ChoicePopupBox(Choice choice) {
  199           scrollLocation = 0;
  200           this.choice = choice;
  201           style = choice.popupStyle;
  202           vAdj = new ScrollPaneAdjustable(choice, Adjustable.VERTICAL) {
  203               private static final long serialVersionUID = 2280561825998835980L;
  204   
  205               @Override
  206               void repaintComponent(Rectangle r) {
  207                   if (isVisible()) {
  208                       repaint(r);
  209                   }
  210               }
  211           };
  212           scrollable = new ChoiceScrollable();
  213           listState = new ChoiceListState();
  214           scrollController = new ScrollStateController(scrollable);
  215           scrollbarController = vAdj.getStateController();
  216           vAdj.addAdjustmentListener(scrollController);
  217           nSkip = MAX_SKIP;
  218           dragScrollTimer = new PeriodicTimer(25l, getAsyncHandler(getDragScrollHandler()));
  219       }
  220   
  221       public int getWidth() {
  222           return getSize().width;
  223       }
  224   
  225       /**     
  226        * @return action to be performed on scrolling by mouse drag:
  227        * action just highlights("focuses") next/previous item
  228        */
  229       private Runnable getDragScrollHandler() {
  230           if (dragScrollHandler == null) {
  231               dragScrollHandler = new Runnable() {
  232                   int n;
  233   
  234                   public void run() {
  235                       if (n++ % nSkip == 0) {
  236                           int selItem = focusedItem
  237                                   + (dragScrollSpeed / Math.abs(dragScrollSpeed));
  238                           selectItem(selItem, false);
  239                           vAdj.setValue(vAdj.getValue() + dragScrollSpeed);
  240                       }
  241                   }
  242               };
  243           }
  244           return dragScrollHandler;
  245       }
  246   
  247       /**
  248        * Makes handler asynchronous
  249        * @param handler any Runnable
  250        * @return asynchronous Runnable: just invokes <code> handler.run()</code>
  251        *  on event dispatch thread
  252        */
  253       private Runnable getAsyncHandler(final Runnable handler) {
  254           return new Runnable() {
  255               public void run() {
  256                   EventQueue.invokeLater(handler);
  257               }
  258           };
  259       }
  260   
  261       /**
  262        * Paints popup list and vertical scrollbar(if necessary)
  263        */
  264       @Override
  265       void paint(Graphics g) {
  266           toolkit.theme.drawList(g, listState, true);
  267           if (scroll) {
  268               Rectangle r = vAdj.getBounds();
  269               Rectangle oldClip = g.getClipBounds();
  270               g.setClip(r.x, r.y, r.width, r.height);
  271               vAdj.prepaint(g);
  272               g.setClip(oldClip);
  273           }
  274       }
  275   
  276       /**
  277        * Gets item bounds inside popup list
  278        * @param pos item index
  279        * @return item bounds rectangle relative to
  280        * popup list window origin
  281        */
  282       Rectangle getItemBounds(int pos) {
  283           int itemHeight = choice.getItemHeight();
  284           Point p = new Point(0, pos * itemHeight);
  285           Rectangle itemRect = new Rectangle(p, new Dimension(getWidth(), itemHeight));
  286           itemRect.translate(0, scrollLocation);
  287           return itemRect;
  288       }
  289   
  290       /**
  291        * Calculates popup window screen bounds
  292        * using choice style and location on screen
  293        * @return list popup window bounds in screen coordinates
  294        */
  295       Rectangle calcBounds() {
  296           scroll = false;
  297           int itemHeight = choice.getItemHeight();
  298           int choiceWidth = choice.getWidth();
  299           int itemWidth = style.getPopupWidth(choiceWidth);
  300           int count = choice.getItemCount();
  301           if (count > PAGE_SIZE) {
  302               count = PAGE_SIZE;
  303               scroll = true;
  304           }
  305           int height = count * itemHeight;
  306           Rectangle screenBounds = choice.getGraphicsConfiguration().getBounds();
  307           Point screenLoc = choice.getLocationOnScreen();
  308           int x = style.getPopupX(screenLoc.x, itemWidth, choiceWidth, screenBounds.width);
  309           int y = calcY(screenLoc.y, height, screenBounds.height);
  310           return new Rectangle(x, y, itemWidth, height);
  311       }
  312   
  313       /**
  314        * Places list popup window below or above Choice component
  315        * @param y Choice component screen y-coordinate
  316        * @param height list height 
  317        * @param screenHeight height of entire screen
  318        * @return y screen coordinate of list popup window
  319        */
  320       int calcY(int y, int height, int screenHeight) {
  321           int h = choice.getHeight();
  322           y += h; // popup is below choice
  323           int maxHeight = screenHeight - y;
  324           if (height > maxHeight) {
  325               // popup is above choice
  326               y -= height + h;
  327           }
  328           return y;
  329       }
  330   
  331       void show() {
  332           Rectangle r = calcBounds();
  333           selectedItem = choice.getSelectedIndex();
  334           show(r.getLocation(), r.getSize(), choice.getWindowAncestor());
  335           if (scroll) {
  336               vAdj.setUnitIncrement(choice.getItemHeight());
  337               vAdj.setBlockIncrement(vAdj.getUnitIncrement() * PAGE_SIZE);
  338               scrollController.layoutScrollbars();
  339               makeFirst(choice.selectedIndex);
  340           }
  341           getNativeWindow().setAlwaysOnTop(true);
  342       }
  343   
  344       /**
  345        * @return current popup window bounds
  346        */
  347       Rectangle getBounds() {
  348           return new Rectangle(new Point(), getSize());
  349       }
  350   
  351       @Override
  352       void onKeyEvent(int eventId, int vKey, long when, int modifiers) {
  353           if (eventId == KeyEvent.KEY_PRESSED) {
  354               switch (vKey) {
  355                   case KeyEvent.VK_ESCAPE:
  356                       hide();
  357                       break;
  358                   case KeyEvent.VK_ENTER:
  359                       hide();
  360                       choice.selectAndFire(selectedItem);
  361                       break;
  362               }
  363           }
  364       }
  365   
  366       /**
  367        * Moves selection <code>incr</code> items up/down from current selected
  368        * item, scrolls list to the new selected item
  369        */
  370       void changeSelection(int incr) {
  371           int newIndex = choice.getValidIndex(selectedItem + incr);
  372           makeFirst(newIndex);
  373           selectItem(newIndex);
  374       }
  375   
  376       /**
  377        * Scrolls list to make item the first visible item
  378        * @param idx index of item to scroll to(make visible)
  379        */
  380       void makeFirst(int idx) {
  381           if (scroll) {
  382               vAdj.setValue(getItemBounds(idx).y - scrollLocation);
  383           }
  384       }
  385   
  386       /**
  387        * Scrolls list to specified y position
  388        */
  389       void dragScroll(int y) {
  390           if (!scroll) {
  391               return;
  392           }
  393           int h = getHeight();
  394           if ((y >= 0) && (y < h)) {
  395               dragScrollTimer.stop();
  396               return;
  397           }
  398           int itemHeight = choice.getItemHeight();
  399           int dy = ((y < 0) ? -y : (y - h));
  400           nSkip = Math.max(1, MAX_SKIP - 5 * dy / itemHeight);
  401           dragScrollSpeed = itemHeight * (y / Math.abs(y));
  402           dragScrollTimer.start();
  403       }
  404   
  405       /**
  406        * Handles all mouse events on popup window
  407        */
  408       @Override
  409       void onMouseEvent(int eventId, Point where, int mouseButton, long when, int modifiers,
  410               int wheelRotation) {
  411           if (scroll && (eventId == MouseEvent.MOUSE_WHEEL)) {
  412               processMouseWheel(where, when, modifiers, wheelRotation);
  413               return;
  414           }
  415           if (scroll && (vAdj.getBounds().contains(where) || scrollDragged)) {
  416               processMouseEvent(eventId, where, mouseButton, when, modifiers);
  417               return;
  418           }
  419           boolean button1 = (mouseButton == MouseEvent.BUTTON1);
  420           int y = where.y;
  421           int index = (y - scrollLocation) / choice.getItemHeight();
  422           switch (eventId) {
  423               case MouseEvent.MOUSE_PRESSED:
  424                   break;
  425               case MouseEvent.MOUSE_DRAGGED:
  426                   // scroll on mouse drag
  427                   if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0) {
  428                       dragScroll(y);
  429                   }
  430               case MouseEvent.MOUSE_MOVED:
  431                   if ((index < 0) || (index >= choice.getItemCount()) || (index == selectedItem)) {
  432                       return;
  433                   }
  434                   boolean select = getBounds().contains(where);
  435                   if ((y >= 0) && (y < getHeight())) {
  436                       selectItem(index, select); //hot track
  437                   }
  438                   break;
  439               case MouseEvent.MOUSE_RELEASED:
  440                   if (button1) {
  441                       hide();
  442                       choice.selectAndFire(index);
  443                   }
  444                   break;
  445           }
  446       }
  447   
  448       /**
  449        * Mouse wheel event processing by vertical scrollbar
  450        */
  451       private void processMouseWheel(Point where, long when, int modifiers, int wheelRotation) {
  452           MouseWheelEvent mwe = new MouseWheelEvent(choice, MouseEvent.MOUSE_WHEEL, when,
  453                   modifiers, where.x, where.y, 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 1,
  454                   wheelRotation);
  455           scrollController.mouseWheelMoved(mwe);
  456       }
  457   
  458       /**
  459        * Mouse event processing by vertical scrollbar
  460        */
  461       private void processMouseEvent(int id, Point where, int button, long when, int modifiers) {
  462           MouseEvent me = new MouseEvent(choice, id, when, modifiers, where.x, where.y, 0, false,
  463                   button);
  464           switch (id) {
  465               case MouseEvent.MOUSE_PRESSED:
  466                   scrollDragged = true;
  467                   scrollbarController.mousePressed(me);
  468                   break;
  469               case MouseEvent.MOUSE_RELEASED:
  470                   scrollDragged = false;
  471                   scrollbarController.mouseReleased(me);
  472                   break;
  473               case MouseEvent.MOUSE_DRAGGED:
  474                   scrollbarController.mouseDragged(me);
  475                   break;
  476           }
  477       }
  478   
  479       /**
  480        * Selects/focuses item and repaints popup window
  481        * @param index item to be focused/selected
  482        * @param highlight item is highlighted(selected) if true,
  483        * only focused otherwise
  484        */
  485       private void selectItem(int index, boolean highlight) {
  486           Rectangle oldRect = listState.getItemBounds(focusedItem);
  487           if (focusedItem != selectedItem) {
  488               oldRect = oldRect.union(listState.getItemBounds(selectedItem));
  489           }
  490           focusedItem = index;
  491           selectedItem = (highlight ? focusedItem : -1);
  492           Rectangle itemRect = listState.getItemBounds(index);
  493           Rectangle paintRect = itemRect.union(oldRect);
  494           paintRect.grow(0, 2);
  495           if (scroll) {
  496               paintRect.width -= vAdj.getBounds().width;
  497           }
  498           repaint(paintRect);
  499       }
  500   
  501       private void selectItem(int index) {
  502           selectItem(index, true);
  503       }
  504   
  505       void repaint(final Rectangle r) {
  506           EventQueue.invokeLater(new Runnable() {
  507               public void run() {
  508                   paint(new MultiRectArea(r));
  509               }
  510           });
  511       }
  512   
  513       void repaint() {
  514           repaint(null);
  515       }
  516   
  517       @Override
  518       boolean closeOnUngrab(Point start, Point end) {
  519           if (!getBounds().contains(end)) {
  520               // close on mouse ungrab only
  521               // if grab started not above
  522               // the scrollbar
  523               return !vAdj.getBounds().contains(start);
  524           }
  525           return true;
  526       }
  527   
  528       @Override
  529       void hide() {
  530           // stop timer before closing:
  531           if (scroll && dragScrollTimer.isRunning()) {
  532               dragScrollTimer.stop();
  533           }
  534           super.hide();
  535       }
  536   }

Home » openjdk-7 » java » awt » [javadoc | source]