001package jmri.util;
002
003import java.awt.Color;
004import java.awt.Component;
005import java.awt.Dimension;
006import java.awt.Graphics;
007import java.awt.event.ActionEvent;
008import java.awt.event.ActionListener;
009import java.awt.event.KeyEvent;
010import java.awt.event.MouseWheelEvent;
011import java.awt.event.MouseWheelListener;
012import javax.swing.Icon;
013import javax.swing.JComponent;
014import javax.swing.JMenu;
015import javax.swing.JMenuItem;
016import javax.swing.JPopupMenu;
017import javax.swing.MenuSelectionManager;
018import javax.swing.Timer;
019import javax.swing.event.ChangeEvent;
020import javax.swing.event.ChangeListener;
021import javax.swing.event.MenuKeyEvent;
022import javax.swing.event.MenuKeyListener;
023import javax.swing.event.PopupMenuEvent;
024import javax.swing.event.PopupMenuListener;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028/**
029 * A class that provides scrolling capabilities to a long menu dropdown or popup
030 * menu. A number of items can optionally be frozen at the top and/or bottom of
031 * the menu.
032 * <p>
033 * <b>Implementation note:</b> The default number of items to display at a time
034 * is 15, and the default scrolling interval is 125 milliseconds.
035 *
036 * @version 1.5.0 04/05/12
037 * @author Darryl
038 * @version 1.5.1 07/20/17 - added scrollwheel support
039 * @author George Warner
040 */
041// NOTE: Provided by Darryl Burke from <https://tips4java.wordpress.com/2009/02/01/menu-scroller/>
042// (Thank you DarrylB! ;-)
043public class MenuScroller
044        implements MouseWheelListener {
045
046    //private JMenu menu;
047    private JPopupMenu menu;
048    private Component[] menuItems;
049    private MenuScrollerItem upItem;
050    private MenuScrollerItem downItem;
051    private final MenuScrollerPopupMenuListener menuScrollerPopupMenuListener = new MenuScrollerPopupMenuListener();
052    private final MenuScrollerMenuKeyListener menuScrollerMenuKeyListener = new MenuScrollerMenuKeyListener();
053    private int scrollCount;
054    private int interval;
055    private int topFixedCount;
056    private int bottomFixedCount;
057    private int firstIndex = 0;
058    private int keepVisibleIndex = -1;
059
060//     private Component lastFocused = null;
061
062    /**
063     * Register a menu to be scrolled with the default number of items to
064     * display at a time and the default scrolling interval.
065     *
066     * @param menu the menu
067     * @return the MenuScroller
068     */
069    public static MenuScroller setScrollerFor(JMenu menu) {
070        return new MenuScroller(menu);
071    }
072
073    /**
074     * Register a popup menu to be scrolled with the default number of items to
075     * display at a time and the default scrolling interval.
076     *
077     * @param menu the popup menu
078     * @return the MenuScroller
079     */
080    public static MenuScroller setScrollerFor(JPopupMenu menu) {
081        return new MenuScroller(menu);
082    }
083
084    /**
085     * Register a menu to be scrolled with the default number of items to
086     * display at a time and the specified scrolling interval.
087     *
088     * @param menu        the menu
089     * @param scrollCount the number of items to display at a time
090     * @return the MenuScroller
091     * @throws IllegalArgumentException if scrollCount is 0 or negative
092     */
093    public static MenuScroller setScrollerFor(JMenu menu, int scrollCount) {
094        return new MenuScroller(menu, scrollCount);
095    }
096
097    /**
098     * Register a popup menu to be scrolled with the default number of items to
099     * display at a time and the specified scrolling interval.
100     *
101     * @param menu        the popup menu
102     * @param scrollCount the number of items to display at a time
103     * @return the MenuScroller
104     * @throws IllegalArgumentException if scrollCount is 0 or negative
105     */
106    public static MenuScroller setScrollerFor(JPopupMenu menu, int scrollCount) {
107        return new MenuScroller(menu, scrollCount);
108    }
109
110    /**
111     * Register a menu to be scrolled, with the specified number of items to
112     * display at a time and the specified scrolling interval.
113     *
114     * @param menu        the menu
115     * @param scrollCount the number of items to be displayed at a time
116     * @param interval    the scroll interval, in milliseconds
117     * @return the MenuScroller
118     * @throws IllegalArgumentException if scrollCount or interval is 0 or
119     *                                  negative
120     */
121    public static MenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval) {
122        return new MenuScroller(menu, scrollCount, interval);
123    }
124
125    /**
126     * Register a popup menu to be scrolled, with the specified number of items
127     * to display at a time and the specified scrolling interval.
128     *
129     * @param menu        the popup menu
130     * @param scrollCount the number of items to be displayed at a time
131     * @param interval    the scroll interval, in milliseconds
132     * @return the MenuScroller
133     * @throws IllegalArgumentException if scrollCount or interval is 0 or
134     *                                  negative
135     */
136    public static MenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval) {
137        return new MenuScroller(menu, scrollCount, interval);
138    }
139
140    /**
141     * Register a menu to be scrolled, with the specified number of items to
142     * display in the scrolling region, the specified scrolling interval, and
143     * the specified numbers of items fixed at the top and bottom of the menu.
144     *
145     * @param menu             the menu
146     * @param scrollCount      the number of items to display in the scrolling
147     *                         portion
148     * @param interval         the scroll interval, in milliseconds
149     * @param topFixedCount    the number of items to fix at the top. May be 0.
150     * @param bottomFixedCount the number of items to fix at the bottom. May be
151     *                         0
152     * @throws IllegalArgumentException if scrollCount or interval is 0 or
153     *                                  negative or if topFixedCount or
154     *                                  bottomFixedCount is negative
155     * @return the MenuScroller
156     */
157    public static MenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval,
158            int topFixedCount, int bottomFixedCount) {
159        return new MenuScroller(menu, scrollCount, interval,
160                topFixedCount, bottomFixedCount);
161    }
162
163    /**
164     * Register a popup menu to be scrolled, with the specified number of items
165     * to display in the scrolling region, the specified scrolling interval, and
166     * the specified numbers of items fixed at the top and bottom of the popup
167     * menu.
168     *
169     * @param menu             the popup menu
170     * @param scrollCount      the number of items to display in the scrolling
171     *                         portion
172     * @param interval         the scroll interval, in milliseconds
173     * @param topFixedCount    the number of items to fix at the top. May be 0
174     * @param bottomFixedCount the number of items to fix at the bottom. May be
175     *                         0
176     * @throws IllegalArgumentException if scrollCount or interval is 0 or
177     *                                  negative or if topFixedCount or
178     *                                  bottomFixedCount is negative
179     * @return the MenuScroller
180     */
181    public static MenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval,
182            int topFixedCount, int bottomFixedCount) {
183        return new MenuScroller(menu, scrollCount, interval,
184                topFixedCount, bottomFixedCount);
185    }
186
187    /**
188     * Construct a <code>MenuScroller</code> that scrolls a menu with the
189     * default number of items to display at a time, and default scrolling
190     * interval.
191     *
192     * @param menu the menu
193     */
194    public MenuScroller(JMenu menu) {
195        this(menu, 15);
196    }
197
198    /**
199     * Construct a <code>MenuScroller</code> that scrolls a popup menu with the
200     * default number of items to display at a time, and default scrolling
201     * interval.
202     *
203     * @param menu the popup menu
204     */
205    public MenuScroller(JPopupMenu menu) {
206        this(menu, 15);
207    }
208
209    /**
210     * Construct a <code>MenuScroller</code> that scrolls a menu with the
211     * specified number of items to display at a time, and default scrolling
212     * interval.
213     *
214     * @param menu        the menu
215     * @param scrollCount the number of items to display at a time
216     * @throws IllegalArgumentException if scrollCount is 0 or negative
217     */
218    public MenuScroller(JMenu menu, int scrollCount) {
219        this(menu, scrollCount, 150);
220    }
221
222    /**
223     * Construct a <code>MenuScroller</code> that scrolls a popup menu with the
224     * specified number of items to display at a time, and default scrolling
225     * interval.
226     *
227     * @param menu        the popup menu
228     * @param scrollCount the number of items to display at a time
229     * @throws IllegalArgumentException if scrollCount is 0 or negative
230     */
231    public MenuScroller(JPopupMenu menu, int scrollCount) {
232        this(menu, scrollCount, 150);
233    }
234
235    /**
236     * Construct a <code>MenuScroller</code> that scrolls a menu with the
237     * specified number of items to display at a time, and specified scrolling
238     * interval.
239     *
240     * @param menu        the menu
241     * @param scrollCount the number of items to display at a time
242     * @param interval    the scroll interval, in milliseconds
243     * @throws IllegalArgumentException if scrollCount or interval is 0 or
244     *                                  negative
245     */
246    public MenuScroller(JMenu menu, int scrollCount, int interval) {
247        this(menu, scrollCount, interval, 0, 0);
248    }
249
250    /**
251     * Construct a <code>MenuScroller</code> that scrolls a popup menu with the
252     * specified number of items to display at a time, and specified scrolling
253     * interval.
254     *
255     * @param menu        the popup menu
256     * @param scrollCount the number of items to display at a time
257     * @param interval    the scroll interval, in milliseconds
258     * @throws IllegalArgumentException if scrollCount or interval is 0 or
259     *                                  negative
260     */
261    public MenuScroller(JPopupMenu menu, int scrollCount, int interval) {
262        this(menu, scrollCount, interval, 0, 0);
263    }
264
265    /**
266     * Construct a <code>MenuScroller</code> that scrolls a menu with the
267     * specified number of items to display in the scrolling region, the
268     * specified scrolling interval, and the specified numbers of items fixed at
269     * the top and bottom of the menu.
270     *
271     * @param menu             the menu
272     * @param scrollCount      the number of items to display in the scrolling
273     *                         portion
274     * @param interval         the scroll interval, in milliseconds
275     * @param topFixedCount    the number of items to fix at the top. May be 0
276     * @param bottomFixedCount the number of items to fix at the bottom. May be
277     *                         0
278     * @throws IllegalArgumentException if scrollCount or interval is 0 or
279     *                                  negative or if topFixedCount or
280     *                                  bottomFixedCount is negative
281     */
282    public MenuScroller(JMenu menu, int scrollCount, int interval,
283            int topFixedCount, int bottomFixedCount) {
284        this(menu.getPopupMenu(), scrollCount, interval, topFixedCount, bottomFixedCount);
285    }
286
287    /**
288     * Construct a <code>MenuScroller</code> that scrolls a popup menu with the
289     * specified number of items to display in the scrolling region, the
290     * specified scrolling interval, and the specified numbers of items fixed at
291     * the top and bottom of the popup menu.
292     *
293     * @param menu             the popup menu
294     * @param scrollCount      the number of items to display in the scrolling
295     *                         portion
296     * @param interval         the scroll interval, in milliseconds
297     * @param topFixedCount    the number of items to fix at the top. May be 0
298     * @param bottomFixedCount the number of items to fix at the bottom. May be
299     *                         0
300     * @throws IllegalArgumentException if scrollCount or interval is 0 or
301     *                                  negative or if topFixedCount or
302     *                                  bottomFixedCount is negative
303     */
304    public MenuScroller(JPopupMenu menu, int scrollCount, int interval,
305            int topFixedCount, int bottomFixedCount) {
306        if (scrollCount <= 0 || interval <= 0) {
307            throw new IllegalArgumentException("scrollCount and interval must be greater than 0");
308        }
309        if (topFixedCount < 0 || bottomFixedCount < 0) {
310            throw new IllegalArgumentException("topFixedCount and bottomFixedCount cannot be negative");
311        }
312
313        upItem = new MenuScrollerItem(MenuScrollerIcon.UP, -1);
314        downItem = new MenuScrollerItem(MenuScrollerIcon.DOWN, +1);
315        setScrollCount(scrollCount);
316        setInterval(interval);
317        setTopFixedCount(topFixedCount);
318        setBottomFixedCount(bottomFixedCount);
319
320        this.menu = menu;
321
322        installListeners();
323    }
324
325    private void installListeners() {
326
327        // remove all menu key listeners
328        for (MenuKeyListener mkl : menu.getMenuKeyListeners()) {
329            menu.removeMenuKeyListener(mkl);
330        }
331
332        // add our menu key listener
333        menu.addMenuKeyListener(menuScrollerMenuKeyListener);
334
335// Dead code commented out 2021-12-13
336//        if (false) {    // not working
337//        KeyListenerInstaller.installKeyListenerOnAllComponents(new KeyAdapter() {
338//            @Override
339//            public void keyTyped(KeyEvent e) {
340//                int keyCode = e.getKeyCode();
341//                log.debug("keyTyped(" + keyCode + ")");
342//            }
343//        }, menu);
344//
345//             menu.addKeyListener(new KeyAdapter() {
346//                 @Override
347//                 public void keyTyped(KeyEvent e) {
348//                     int keyCode = e.getKeyCode();
349//                     log.debug("keyTyped({})", keyCode);
350//                 }
351//             });
352//
353//         }
354
355        // add a Popup Menu listener
356        menu.addPopupMenuListener(menuScrollerPopupMenuListener);
357
358        // add my mouse wheel listener
359        // (so mouseWheelMoved (below) will be called)
360        menu.addMouseWheelListener(this);
361
362//         lastFocused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
363//         if (menu.isFocusable()) {
364//             menu.requestFocus();
365//         }
366    }
367
368    private void removeListeners() {
369//         lastFocused.requestFocus();
370//            menu.removeMenuKeyListener(menuScrollerMenuKeyListener);
371        menu.removePopupMenuListener(menuScrollerPopupMenuListener);
372        menu.removeMouseWheelListener(this);
373    }
374
375    @Override
376    public void mouseWheelMoved(MouseWheelEvent e) {
377        // compute how much to scroll the menu
378        int amount = e.getScrollAmount() * e.getWheelRotation();
379        firstIndex += amount;
380        refreshMenu();
381        e.consume();
382    }
383
384    /**
385     * Return the scroll interval in milliseconds.
386     *
387     * @return the scroll interval in milliseconds
388     */
389    public int getInterval() {
390        return interval;
391    }
392
393    /**
394     * Set the scroll interval in milliseconds.
395     *
396     * @param interval the scroll interval in milliseconds
397     * @throws IllegalArgumentException if interval is 0 or negative
398     */
399    public void setInterval(int interval) {
400        if (interval <= 0) {
401            throw new IllegalArgumentException("interval must be greater than 0");
402        }
403        upItem.setInterval(interval);
404        downItem.setInterval(interval);
405        this.interval = interval;
406    }
407
408    /**
409     * Return the number of items in the scrolling portion of the menu.
410     *
411     * @return the number of items to display at a time
412     */
413    public int getscrollCount() {
414        return scrollCount;
415    }
416
417    /**
418     * Set the number of items in the scrolling portion of the menu.
419     *
420     * @param scrollCount the number of items to display at a time
421     * @throws IllegalArgumentException if scrollCount is 0 or negative
422     */
423    public void setScrollCount(int scrollCount) {
424        if (scrollCount <= 0) {
425            throw new IllegalArgumentException("scrollCount must be greater than 0");
426        }
427        this.scrollCount = scrollCount;
428        MenuSelectionManager.defaultManager().clearSelectedPath();
429    }
430
431    /**
432     * Return the number of items fixed at the top of the menu or popup menu.
433     *
434     * @return the number of items
435     */
436    public int getTopFixedCount() {
437        return topFixedCount;
438    }
439
440    /**
441     * Set the number of items to fix at the top of the menu or popup menu.
442     *
443     * @param topFixedCount the number of items
444     */
445    public void setTopFixedCount(int topFixedCount) {
446        if (firstIndex <= topFixedCount) {
447            firstIndex = topFixedCount;
448        } else {
449            firstIndex += (topFixedCount - this.topFixedCount);
450        }
451        this.topFixedCount = topFixedCount;
452    }
453
454    /**
455     * Return the number of items fixed at the bottom of the menu or popup
456     * menu.
457     *
458     * @return the number of items
459     */
460    public int getBottomFixedCount() {
461        return bottomFixedCount;
462    }
463
464    /**
465     * Set the number of items to fix at the bottom of the menu or popup menu.
466     *
467     * @param bottomFixedCount the number of items
468     */
469    public void setBottomFixedCount(int bottomFixedCount) {
470        this.bottomFixedCount = bottomFixedCount;
471    }
472
473    /**
474     * Scroll the specified item into view each time the menu is opened. Call
475     * this method with <code>null</code> to restore the default behavior, which
476     * is to show the menu as it last appeared.
477     *
478     * @param item the item to keep visible
479     * @see #keepVisible(int)
480     */
481    public void keepVisible(JMenuItem item) {
482        if (item == null) {
483            keepVisibleIndex = -1;
484        } else {
485            int index = menu.getComponentIndex(item);
486            keepVisibleIndex = index;
487        }
488    }
489
490    /**
491     * Scroll the item at the specified index into view each time the menu is
492     * opened. Call this method with <code>-1</code> to restore the default
493     * behavior, which is to show the menu as it last appeared.
494     *
495     * @param index the index of the item to keep visible
496     * @see #keepVisible(javax.swing.JMenuItem)
497     */
498    public void keepVisible(int index) {
499        keepVisibleIndex = index;
500    }
501
502    /**
503     * Remove this MenuScroller from the associated menu and restores the
504     * default behavior of the menu.
505     */
506    public void dispose() {
507        if (menu != null) {
508            removeListeners();
509            menu = null;
510        }
511    }
512
513    /**
514     * Ensure that the <code>dispose</code> method of this MenuScroller is
515     * called when there are no more refrences to it.
516     *
517     * @exception Throwable if an error occurs.
518     * @see MenuScroller#dispose()
519     */
520    @Override
521    @SuppressWarnings("deprecation") // Object.finalize
522    protected void finalize() throws Throwable {
523        dispose();
524    }
525
526    private void refreshMenu() {
527        if (menuItems != null && menuItems.length > 0) {
528            firstIndex = Math.max(topFixedCount, firstIndex);
529            firstIndex = Math.min(menuItems.length - bottomFixedCount - scrollCount, firstIndex);
530
531            upItem.setEnabled(firstIndex > topFixedCount);
532            downItem.setEnabled(firstIndex + scrollCount < menuItems.length - bottomFixedCount);
533
534            menu.removeAll();
535            for (int i = 0; i < topFixedCount; i++) {
536                menu.add(menuItems[i]);
537            }
538            if (topFixedCount > 0) {
539                menu.addSeparator();
540            }
541
542            menu.add(upItem);
543            for (int i = firstIndex; i < scrollCount + firstIndex; i++) {
544                menu.add(menuItems[i]);
545            }
546            menu.add(downItem);
547
548            if (bottomFixedCount > 0) {
549                menu.addSeparator();
550            }
551            for (int i = menuItems.length - bottomFixedCount; i < menuItems.length; i++) {
552                menu.add(menuItems[i]);
553            }
554
555            int maxPreferredWidth = 0;
556            for (Component item : menuItems) {
557                maxPreferredWidth = Math.max(maxPreferredWidth, item.getPreferredSize().width);
558            }
559            menu.setPreferredSize(new Dimension(maxPreferredWidth, menu.getPreferredSize().height));
560
561            java.awt.Container cont = upItem.getParent();
562            if (cont instanceof JComponent) {
563                ((JComponent) cont).revalidate();
564                ((JComponent) cont).repaint();
565            }
566        }
567    }
568
569    private class MenuScrollerPopupMenuListener implements PopupMenuListener {
570
571        @Override
572        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
573            setMenuItems();
574        }
575
576        @Override
577        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
578            restoreMenuItems();
579        }
580
581        @Override
582        public void popupMenuCanceled(PopupMenuEvent e) {
583            restoreMenuItems();
584        }
585
586        private void setMenuItems() {
587            menuItems = menu.getComponents();
588
589            // Dead code commented out 2021-12-13
590            // if (false) {    // not working
591            //     for (Component c : menuItems) {
592            //         JMenuItem jmi = (JMenuItem) c;
593            //         // remove all menu key listeners
594            //         for (MenuKeyListener mkl : jmi.getMenuKeyListeners()) {
595            //             jmi.removeMenuKeyListener(mkl);
596            //         }
597            //         jmi.addMenuKeyListener(menuScrollerMenuKeyListener);
598            //     }
599            // }
600
601            if (keepVisibleIndex >= topFixedCount
602                    && keepVisibleIndex <= menuItems.length - bottomFixedCount
603                    && (keepVisibleIndex > firstIndex + scrollCount
604                    || keepVisibleIndex < firstIndex)) {
605                firstIndex = Math.min(firstIndex, keepVisibleIndex);
606                firstIndex = Math.max(firstIndex, keepVisibleIndex - scrollCount + 1);
607            }
608            if (menuItems.length > topFixedCount + scrollCount + bottomFixedCount) {
609                refreshMenu();
610            }
611        }
612
613        private void restoreMenuItems() {
614            menu.removeAll();
615            for (Component c : menuItems) {
616
617                // Dead code commented out 2021-12-13
618                // if (false) {    // not working
619                //     JMenuItem jmi = (JMenuItem) c;
620                //    jmi.removeMenuKeyListener(menuScrollerMenuKeyListener);
621                // }
622
623                menu.add(c);
624            }
625        }
626    }
627
628    private class MenuScrollerTimer extends Timer {
629
630        public MenuScrollerTimer(final int increment, int interval) {
631            super(interval, new ActionListener() {
632
633                @Override
634                public void actionPerformed(ActionEvent e) {
635                    firstIndex += increment;
636                    refreshMenu();
637                }
638            });
639        }
640    }
641
642    private class MenuScrollerItem extends JMenuItem
643            implements ChangeListener {
644
645        private MenuScrollerTimer timer;
646
647        public MenuScrollerItem(MenuScrollerIcon icon, int increment) {
648            setIcon(icon);
649            setDisabledIcon(icon);
650            timer = new MenuScrollerTimer(increment, interval);
651            addChangeListener(this);
652        }
653
654        public void setInterval(int interval) {
655            timer.setDelay(interval);
656        }
657
658        @Override
659        public void stateChanged(ChangeEvent e) {
660            if (isArmed() && !timer.isRunning()) {
661                timer.start();
662            }
663            if (!isArmed() && timer.isRunning()) {
664                timer.stop();
665            }
666        }
667    }   // class MenuScrollerItem
668
669    // TODO: Determine why these methods are not being called
670    private class MenuScrollerMenuKeyListener implements MenuKeyListener {
671
672        @Override
673        public void menuKeyTyped(MenuKeyEvent e) {
674            int keyCode = e.getKeyCode();
675            log.debug("MenuScroller.keyTyped({})", keyCode);
676        }
677
678        @Override
679        public void menuKeyPressed(MenuKeyEvent e) {
680            int keyCode = e.getKeyCode();
681            log.debug("MenuScroller.keyPressed({})", keyCode);
682        }
683
684        @Override
685        public void menuKeyReleased(MenuKeyEvent e) {
686            int keyCode = e.getKeyCode();
687            switch (keyCode) {
688                case KeyEvent.VK_UP: {
689                    log.debug("MenuScroller.keyReleased(VK_UP)");
690                    firstIndex--;
691                    refreshMenu();
692                    e.consume();
693                    break;
694                }
695
696                case KeyEvent.VK_DOWN: {
697                    log.debug("MenuScroller.keyReleased(VK_DOWN)");
698                    firstIndex++;
699                    refreshMenu();
700                    e.consume();
701                    break;
702                }
703
704                default: {
705                    log.debug("MenuScroller.keyReleased({})", keyCode);
706                    break;
707                }
708            }   //switch
709        }
710    }
711
712    private static enum MenuScrollerIcon implements Icon {
713
714        UP(9, 1, 9),
715        DOWN(1, 9, 1);
716        final int[] xPoints = {1, 5, 9};
717        final int[] yPoints;
718
719        MenuScrollerIcon(int... yPoints) {
720            this.yPoints = yPoints;
721        }
722
723        @Override
724        public void paintIcon(Component c, Graphics g, int x, int y) {
725            Dimension size = c.getSize();
726            Graphics g2 = g.create(size.width / 2 - 5, size.height / 2 - 5, 10, 10);
727            g2.setColor(Color.GRAY);
728            g2.drawPolygon(xPoints, yPoints, 3);
729            if (c.isEnabled()) {
730                g2.setColor(Color.BLACK);
731                g2.fillPolygon(xPoints, yPoints, 3);
732            }
733            g2.dispose();
734        }
735
736        @Override
737        public int getIconWidth() {
738            return 0;
739        }
740
741        @Override
742        public int getIconHeight() {
743            return 10;
744        }
745    }
746
747    private final static Logger log = LoggerFactory.getLogger(MenuScroller.class);
748
749}