Source for javax.swing.plaf.basic.BasicPopupMenuUI

   1: /* BasicPopupMenuUI.java
   2:    Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package javax.swing.plaf.basic;
  39: 
  40: import java.awt.AWTEvent;
  41: import java.awt.Component;
  42: import java.awt.Container;
  43: import java.awt.Cursor;
  44: import java.awt.Dimension;
  45: import java.awt.Point;
  46: import java.awt.event.ComponentEvent;
  47: import java.awt.event.ComponentListener;
  48: import java.awt.event.MouseEvent;
  49: 
  50: import javax.swing.BoxLayout;
  51: import javax.swing.JComponent;
  52: import javax.swing.JLayeredPane;
  53: import javax.swing.JMenu;
  54: import javax.swing.JMenuItem;
  55: import javax.swing.JPopupMenu;
  56: import javax.swing.LookAndFeel;
  57: import javax.swing.MenuElement;
  58: import javax.swing.MenuSelectionManager;
  59: import javax.swing.RootPaneContainer;
  60: import javax.swing.SwingUtilities;
  61: import javax.swing.event.MouseInputListener;
  62: import javax.swing.event.PopupMenuEvent;
  63: import javax.swing.event.PopupMenuListener;
  64: import javax.swing.plaf.ComponentUI;
  65: import javax.swing.plaf.PopupMenuUI;
  66: 
  67: 
  68: /**
  69:  * UI Delegate for JPopupMenu
  70:  */
  71: public class BasicPopupMenuUI extends PopupMenuUI
  72: {
  73:   /* popupMenu for which this UI delegate is for*/
  74:   protected JPopupMenu popupMenu;
  75: 
  76:   /* MouseInputListener listens to mouse events. Package private for inner classes. */
  77:   static transient MouseInputListener mouseInputListener;
  78: 
  79:   /* PopupMenuListener listens to popup menu events fired by JPopupMenu*/
  80:   private transient PopupMenuListener popupMenuListener;
  81: 
  82:   /* ComponentListener listening to popupMenu's invoker.
  83:    * This is package-private to avoid an accessor method.  */
  84:   TopWindowListener topWindowListener;
  85: 
  86:   /**
  87:    * Creates a new BasicPopupMenuUI object.
  88:    */
  89:   public BasicPopupMenuUI()
  90:   {
  91:     popupMenuListener = new PopupMenuHandler();
  92:     topWindowListener = new TopWindowListener();
  93:   }
  94: 
  95:   /**
  96:    * Factory method to create a BasicPopupMenuUI for the given {@link
  97:    * JComponent}, which should be a {@link JMenuItem}.
  98:    *
  99:    * @param x The {@link JComponent} a UI is being created for.
 100:    *
 101:    * @return A BasicPopupMenuUI for the {@link JComponent}.
 102:    */
 103:   public static ComponentUI createUI(JComponent x)
 104:   {
 105:     return new BasicPopupMenuUI();
 106:   }
 107: 
 108:   /**
 109:    * Installs and initializes all fields for this UI delegate. Any properties
 110:    * of the UI that need to be initialized and/or set to defaults will be
 111:    * done now. It will also install any listeners necessary.
 112:    *
 113:    * @param c The {@link JComponent} that is having this UI installed.
 114:    */
 115:   public void installUI(JComponent c)
 116:   {
 117:     super.installUI(c);
 118:     popupMenu = (JPopupMenu) c;
 119:     popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS));
 120:     popupMenu.setBorderPainted(true);
 121:     JPopupMenu.setDefaultLightWeightPopupEnabled(true);
 122: 
 123:     installDefaults();
 124:     installListeners();
 125:   }
 126: 
 127:   /**
 128:    * This method installs the defaults that are defined in  the Basic look
 129:    * and feel for this {@link JPopupMenu}.
 130:    */
 131:   public void installDefaults()
 132:   {
 133:     LookAndFeel.installColorsAndFont(popupMenu, "PopupMenu.background",
 134:                                      "PopupMenu.foreground", "PopupMenu.font");
 135:     LookAndFeel.installBorder(popupMenu, "PopupMenu.border");
 136:     popupMenu.setOpaque(true);
 137:   }
 138: 
 139:   /**
 140:    * This method installs the listeners for the {@link JMenuItem}.
 141:    */
 142:   protected void installListeners()
 143:   {
 144:     popupMenu.addPopupMenuListener(popupMenuListener);
 145:   }
 146: 
 147:   /**
 148:    * This method installs the keyboard actions for this {@link JPopupMenu}.
 149:    */
 150:   protected void installKeyboardActions()
 151:   {
 152:     // FIXME: Need to implement
 153:   }
 154: 
 155:   /**
 156:    * Performs the opposite of installUI. Any properties or resources that need
 157:    * to be cleaned up will be done now. It will also uninstall any listeners
 158:    * it has. In addition, any properties of this UI will be nulled.
 159:    *
 160:    * @param c The {@link JComponent} that is having this UI uninstalled.
 161:    */
 162:   public void uninstallUI(JComponent c)
 163:   {
 164:     uninstallListeners();
 165:     uninstallDefaults();
 166:     popupMenu = null;
 167:   }
 168: 
 169:   /**
 170:    * This method uninstalls the defaults and sets any objects created during
 171:    * install to null
 172:    */
 173:   protected void uninstallDefaults()
 174:   {
 175:     popupMenu.setBackground(null);
 176:     popupMenu.setBorder(null);
 177:     popupMenu.setFont(null);
 178:     popupMenu.setForeground(null);
 179:   }
 180: 
 181:   /**
 182:    * Unregisters all the listeners that this UI delegate was using.
 183:    */
 184:   protected void uninstallListeners()
 185:   {
 186:     popupMenu.removePopupMenuListener(popupMenuListener);
 187:   }
 188: 
 189:   /**
 190:    * Uninstalls any keyboard actions.
 191:    */
 192:   protected void uninstallKeyboardActions()
 193:   {
 194:     // FIXME: Need to implement
 195:   }
 196: 
 197:   /**
 198:    * This method returns the minimum size of the JPopupMenu.
 199:    *
 200:    * @param c The JComponent to find a size for.
 201:    *
 202:    * @return The minimum size.
 203:    */
 204:   public Dimension getMinimumSize(JComponent c)
 205:   {
 206:     return null;
 207:   }
 208: 
 209:   /**
 210:    * This method returns the preferred size of the JPopupMenu.
 211:    *
 212:    * @param c The JComponent to find a size for.
 213:    *
 214:    * @return The preferred size.
 215:    */
 216:   public Dimension getPreferredSize(JComponent c)
 217:   {
 218:     return null;
 219:   }
 220: 
 221:   /**
 222:    * This method returns the minimum size of the JPopupMenu.
 223:    *
 224:    * @param c The JComponent to find a size for.
 225:    *
 226:    * @return The minimum size.
 227:    */
 228:   public Dimension getMaximumSize(JComponent c)
 229:   {
 230:     return null;
 231:   }
 232: 
 233:   /**
 234:    * Return true if given mouse event is a platform popup trigger, and false
 235:    * otherwise
 236:    *
 237:    * @param e MouseEvent that is to be checked for popup trigger event
 238:    *
 239:    * @return true if given mouse event is a platform popup trigger, and false
 240:    *         otherwise
 241:    */
 242:   public boolean isPopupTrigger(MouseEvent e)
 243:   {
 244:     return false;
 245:   }
 246: 
 247:   /**
 248:    * This listener handles PopupMenuEvents fired by JPopupMenu
 249:    */
 250:   private class PopupMenuHandler implements PopupMenuListener
 251:   {
 252:     /**
 253:      * This method is invoked when JPopupMenu is cancelled.
 254:      *
 255:      * @param event the PopupMenuEvent
 256:      */
 257:     public void popupMenuCanceled(PopupMenuEvent event)
 258:     {
 259:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 260:       manager.clearSelectedPath();
 261:     }
 262: 
 263:     /**
 264:      * This method is invoked when JPopupMenu becomes invisible
 265:      *
 266:      * @param event the PopupMenuEvent
 267:      */
 268:     public void popupMenuWillBecomeInvisible(PopupMenuEvent event)
 269:     {
 270:       // remove listener that listens to component events fired 
 271:       // by the top - level window that this popup belongs to.
 272:       Component invoker = popupMenu.getInvoker();
 273: 
 274:       RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities
 275:                                         .getRoot(invoker);
 276:       ((Container) rootContainer).removeComponentListener(topWindowListener);
 277: 
 278:       // If this popup menu is the last popup menu visible on the screen, then
 279:       // stop interrupting mouse events in the glass pane before hiding this 
 280:       // last popup menu.
 281:       boolean topLevelMenu = (popupMenu.getInvoker() instanceof JMenu)
 282:                              && ((JMenu) popupMenu.getInvoker())
 283:                                 .isTopLevelMenu();
 284: 
 285:       if (topLevelMenu || ! (popupMenu.getInvoker() instanceof MenuElement))
 286:         {
 287:           // set glass pane not to interrupt mouse events and remove
 288:       // mouseInputListener
 289:       Container glassPane = (Container) rootContainer.getGlassPane();
 290:       glassPane.setVisible(false);
 291:       glassPane.removeMouseListener(mouseInputListener);
 292:       mouseInputListener = null;
 293:         }
 294:     }
 295: 
 296:     /**
 297:      * This method is invoked when JPopupMenu becomes visible
 298:      *
 299:      * @param event the PopupMenuEvent
 300:      */
 301:     public void popupMenuWillBecomeVisible(PopupMenuEvent event)
 302:     {
 303:       // Adds topWindowListener to top-level window to listener to 
 304:       // ComponentEvents fired by it. We need to cancel this popup menu
 305:       // if topWindow to which this popup belongs was resized or moved.
 306:       Component invoker = popupMenu.getInvoker();
 307:       RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities
 308:                                         .getRoot(invoker);
 309:       ((Container) rootContainer).addComponentListener(topWindowListener);
 310: 
 311:       // Set the glass pane to interrupt all mouse events originating in root 
 312:       // container
 313:       if (mouseInputListener == null)
 314:         {
 315:       Container glassPane = (Container) rootContainer.getGlassPane();
 316:       glassPane.setVisible(true);
 317:       mouseInputListener = new MouseInputHandler(rootContainer);
 318:       glassPane.addMouseListener(mouseInputListener);
 319:       glassPane.addMouseMotionListener(mouseInputListener);
 320:         }
 321: 
 322:       // if this popup menu is a free floating popup menu,
 323:       // then by default its first element should be always selected when
 324:       // this popup menu becomes visible. 
 325:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 326: 
 327:       if (manager.getSelectedPath().length == 0)
 328:         {
 329:       // Set selected path to point to the first item in the popup menu
 330:       MenuElement[] path = new MenuElement[2];
 331:       path[0] = popupMenu;
 332:       Component[] comps = popupMenu.getComponents();
 333:       if (comps.length != 0 && comps[0] instanceof MenuElement)
 334:         {
 335:           path[1] = (MenuElement) comps[0];
 336:           manager.setSelectedPath(path);
 337:         }
 338:         }
 339:     }
 340:   }
 341: 
 342:   /**
 343:    * ComponentListener that listens to Component Events fired by the top -
 344:    * level window to which popup menu belongs. If top-level window was
 345:    * resized, moved or hidded then popup menu will be hidded and selected
 346:    * path of current menu hierarchy will be set to null.
 347:    */
 348:   private class TopWindowListener implements ComponentListener
 349:   {
 350:     /**
 351:      * This method is invoked when top-level window is resized. This method
 352:      * closes current menu hierarchy.
 353:      *
 354:      * @param e The ComponentEvent
 355:      */
 356:     public void componentResized(ComponentEvent e)
 357:     {
 358:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 359:       manager.clearSelectedPath();
 360:     }
 361: 
 362:     /**
 363:      * This method is invoked when top-level window is moved. This method
 364:      * closes current menu hierarchy.
 365:      *
 366:      * @param e The ComponentEvent
 367:      */
 368:     public void componentMoved(ComponentEvent e)
 369:     {
 370:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 371:       manager.clearSelectedPath();
 372:     }
 373: 
 374:     /**
 375:      * This method is invoked when top-level window is shown This method
 376:      * does nothing by default.
 377:      *
 378:      * @param e The ComponentEvent
 379:      */
 380:     public void componentShown(ComponentEvent e)
 381:     {
 382:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 383:       manager.clearSelectedPath();
 384:     }
 385: 
 386:     /**
 387:      * This method is invoked when top-level window is hidden This method
 388:      * closes current menu hierarchy.
 389:      *
 390:      * @param e The ComponentEvent
 391:      */
 392:     public void componentHidden(ComponentEvent e)
 393:     {
 394:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 395:       manager.clearSelectedPath();
 396:     }
 397:   }
 398: 
 399:   /**
 400:    * MouseInputHandler listens to all mouse events originated in the root
 401:    * container. This class is responsible for closing menu hierarchy when the
 402:    * user presses mouse over any component that do not belong to the current 
 403:    * menu hierarchy. This is acomplished by interrupting all mouse event in 
 404:    * the glass pane and checking if other component was pressed while menu 
 405:    * was open, before redestributing events further to intended components
 406:    */
 407:   private class MouseInputHandler implements MouseInputListener
 408:   {
 409:     private JLayeredPane layeredPane;
 410:     private Container glassPane;
 411:     private Cursor nativeCursor;
 412:     private transient Component mouseEventTarget;
 413:     private transient Component pressedComponent;
 414:     private transient Component lastComponentEntered;
 415:     private transient Component tempComponent;
 416:     private transient int pressCount;
 417: 
 418:     /**
 419:      * Creates a new MouseInputHandler object.
 420:      *
 421:      * @param c the top most root container
 422:      */
 423:     public MouseInputHandler(RootPaneContainer c)
 424:     {
 425:       layeredPane = c.getLayeredPane();
 426:       glassPane = (Container) c.getGlassPane();
 427:     }
 428: 
 429:     /**
 430:      * Handles mouse clicked event
 431:      *
 432:      * @param e Mouse event
 433:      */
 434:     public void mouseClicked(MouseEvent e)
 435:     {
 436:       handleEvent(e);
 437:     }
 438: 
 439:     /**
 440:      * Handles mouseDragged event
 441:      *
 442:      * @param e MouseEvent
 443:      */
 444:     public void mouseDragged(MouseEvent e)
 445:     {
 446:       handleEvent(e);
 447:     }
 448: 
 449:     /**
 450:      * Handles mouseEntered event
 451:      *
 452:      * @param e MouseEvent
 453:      */
 454:     public void mouseEntered(MouseEvent e)
 455:     {
 456:       handleEvent(e);
 457:     }
 458: 
 459:     /**
 460:      * Handles mouseExited event
 461:      *
 462:      * @param e MouseEvent
 463:      */
 464:     public void mouseExited(MouseEvent e)
 465:     {
 466:       handleEvent(e);
 467:     }
 468: 
 469:     /**
 470:      * Handles mouse moved event
 471:      *
 472:      * @param e MouseEvent
 473:      */
 474:     public void mouseMoved(MouseEvent e)
 475:     {
 476:       handleEvent(e);
 477:     }
 478: 
 479:     /**
 480:      * Handles mouse pressed event
 481:      *
 482:      * @param e MouseEvent
 483:      */
 484:     public void mousePressed(MouseEvent e)
 485:     {
 486:       handleEvent(e);
 487:     }
 488: 
 489:     /**
 490:      * Handles mouse released event
 491:      *
 492:      * @param e MouseEvent
 493:      */
 494:     public void mouseReleased(MouseEvent e)
 495:     {
 496:       handleEvent(e);
 497:     }
 498: 
 499:     /*
 500:      * This method determines component that was intended to received mouse
 501:      * event, before it was interrupted within the glass pane. This method
 502:      * also redispatches mouse entered and mouse exited events to the
 503:      * appropriate components. This code is slightly modified code from
 504:      * Container.LightweightDispatcher class, which is private inside
 505:      * Container class and cannot be used here.
 506:      */
 507:     public void acquireComponentForMouseEvent(MouseEvent me)
 508:     {
 509:       int x = me.getX();
 510:       int y = me.getY();
 511: 
 512:       // Find the candidate which should receive this event.
 513:       Component parent = layeredPane;
 514:       Component candidate = null;
 515:       Point p = me.getPoint();
 516:       while ((candidate == null) && (parent != null))
 517:         {
 518:       p = SwingUtilities.convertPoint(glassPane, p.x, p.y, parent);
 519:       candidate = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y);
 520: 
 521:       if (candidate == null)
 522:         {
 523:           p = SwingUtilities.convertPoint(parent, p.x, p.y,
 524:                                           parent.getParent());
 525:           parent = parent.getParent();
 526:         }
 527:         }
 528: 
 529:       // If the only candidate we found was the native container itself,
 530:       // don't dispatch any event at all.  We only care about the lightweight
 531:       // children here.
 532:       if (candidate == layeredPane)
 533:     candidate = null;
 534: 
 535:       // If our candidate is new, inform the old target we're leaving.
 536:       if ((lastComponentEntered != null) && lastComponentEntered.isShowing()
 537:           && (lastComponentEntered != candidate))
 538:         {
 539:       // Old candidate could have been removed from 
 540:       // the layeredPane so we check first.
 541:       if (SwingUtilities.isDescendingFrom(lastComponentEntered, layeredPane))
 542:         {
 543:           Point tp = SwingUtilities.convertPoint(layeredPane, x, y,
 544:                                                  lastComponentEntered);
 545:           MouseEvent exited = new MouseEvent(lastComponentEntered,
 546:                                              MouseEvent.MOUSE_EXITED,
 547:                                              me.getWhen(),
 548:                                              me.getModifiersEx(), tp.x,
 549:                                              tp.y, me.getClickCount(),
 550:                                              me.isPopupTrigger(),
 551:                                              me.getButton());
 552: 
 553:               tempComponent = lastComponentEntered;
 554:               lastComponentEntered = null;
 555:           tempComponent.dispatchEvent(exited);
 556:         }
 557: 
 558:       lastComponentEntered = null;
 559:         }
 560: 
 561:       // If we have a candidate, maybe enter it.
 562:       if (candidate != null)
 563:         {
 564:       mouseEventTarget = candidate;
 565: 
 566:       if (candidate.isLightweight() && candidate.isShowing()
 567:           && (candidate != layeredPane)
 568:           && (candidate != lastComponentEntered))
 569:         {
 570:           lastComponentEntered = mouseEventTarget;
 571: 
 572:           Point cp = SwingUtilities.convertPoint(layeredPane, x, y,
 573:                                                  lastComponentEntered);
 574:           MouseEvent entered = new MouseEvent(lastComponentEntered,
 575:                                               MouseEvent.MOUSE_ENTERED,
 576:                                               me.getWhen(),
 577:                                               me.getModifiersEx(), cp.x,
 578:                                               cp.y, me.getClickCount(),
 579:                                               me.isPopupTrigger(),
 580:                                               me.getButton());
 581:           lastComponentEntered.dispatchEvent(entered);
 582:         }
 583:         }
 584: 
 585:       if ((me.getID() == MouseEvent.MOUSE_RELEASED)
 586:           || ((me.getID() == MouseEvent.MOUSE_PRESSED) && (pressCount > 0))
 587:           || (me.getID() == MouseEvent.MOUSE_DRAGGED))
 588:         {
 589:       // If any of the following events occur while a button is held down,
 590:       // they should be dispatched to the same component to which the
 591:       // original MOUSE_PRESSED event was dispatched:
 592:       //   - MOUSE_RELEASED
 593:       //   - MOUSE_PRESSED: another button pressed while the first is held down
 594:       //   - MOUSE_DRAGGED
 595:       if (SwingUtilities.isDescendingFrom(pressedComponent, layeredPane))
 596:         mouseEventTarget = pressedComponent;
 597:       else if (me.getID() == MouseEvent.MOUSE_CLICKED)
 598:         {
 599:           // Don't dispatch CLICKED events whose target is not the same as the
 600:           // target for the original PRESSED event.
 601:           if (candidate != pressedComponent)
 602:         mouseEventTarget = null;
 603:           else if (pressCount == 0)
 604:         pressedComponent = null;
 605:         }
 606:         }
 607:     }
 608: 
 609:     /*
 610:      * This method handles mouse events interrupted by glassPane. It
 611:      * redispatches the mouse events appropriately to the intended components.
 612:      * The code in this method is also taken from
 613:      * Container.LightweightDispatcher class. The code is slightly modified
 614:      * to handle the case when mouse is released over non-menu component. In
 615:      * this case this method closes current menu hierarchy before 
 616:      * redispatching the event further.
 617:      */
 618:     public void handleEvent(AWTEvent e)
 619:     {
 620:       if (e instanceof MouseEvent)
 621:         {
 622:       MouseEvent me = (MouseEvent) e;
 623: 
 624:       acquireComponentForMouseEvent(me);
 625: 
 626:       // Avoid dispatching ENTERED and EXITED events twice.
 627:       if (mouseEventTarget != null && mouseEventTarget.isShowing()
 628:           && (e.getID() != MouseEvent.MOUSE_ENTERED)
 629:           && (e.getID() != MouseEvent.MOUSE_EXITED))
 630:         {
 631:           MouseEvent newEvt = SwingUtilities.convertMouseEvent(glassPane,
 632:                                                                me,
 633:                                                                mouseEventTarget);
 634: 
 635:           mouseEventTarget.dispatchEvent(newEvt);
 636: 
 637:           // If mouse was clicked over the component that is not part 
 638:           // of menu hierarchy,then must close the menu hierarchy */
 639:           if (e.getID() == MouseEvent.MOUSE_RELEASED)
 640:             {
 641:           boolean partOfMenuHierarchy = false;
 642:           MenuSelectionManager manager = MenuSelectionManager
 643:                                          .defaultManager();
 644: 
 645:           partOfMenuHierarchy = manager.isComponentPartOfCurrentMenu(mouseEventTarget);
 646: 
 647:           if (! partOfMenuHierarchy)
 648:             manager.clearSelectedPath();
 649:             }
 650: 
 651:           switch (e.getID())
 652:             {
 653:         case MouseEvent.MOUSE_PRESSED:
 654:           if (pressCount++ == 0)
 655:             pressedComponent = mouseEventTarget;
 656:           break;
 657:         case MouseEvent.MOUSE_RELEASED:
 658:           // Clear our memory of the original PRESSED event, only if
 659:           // we're not expecting a CLICKED event after this. If
 660:           // there is a CLICKED event after this, it will do clean up.
 661:           if ((--pressCount == 0)
 662:               && (mouseEventTarget != pressedComponent))
 663:             pressedComponent = null;
 664:           break;
 665:             }
 666:         }
 667:         }
 668:     }
 669:   }
 670: }