[4593] | 1 | /**
|
---|
[5927] | 2 | * @(#)MenuScroller.java 1.5.0 04/02/12
|
---|
| 3 | * License: use / modify without restrictions (see http://tips4java.wordpress.com/about/)
|
---|
[4593] | 4 | */
|
---|
| 5 | package org.openstreetmap.josm.gui;
|
---|
| 6 |
|
---|
| 7 | import java.awt.Color;
|
---|
| 8 | import java.awt.Component;
|
---|
| 9 | import java.awt.Dimension;
|
---|
| 10 | import java.awt.Graphics;
|
---|
| 11 | import java.awt.event.ActionEvent;
|
---|
| 12 | import java.awt.event.ActionListener;
|
---|
[5664] | 13 | import java.awt.event.MouseWheelEvent;
|
---|
| 14 | import java.awt.event.MouseWheelListener;
|
---|
| 15 |
|
---|
[4593] | 16 | import javax.swing.Icon;
|
---|
| 17 | import javax.swing.JComponent;
|
---|
| 18 | import javax.swing.JMenu;
|
---|
| 19 | import javax.swing.JMenuItem;
|
---|
| 20 | import javax.swing.JPopupMenu;
|
---|
| 21 | import javax.swing.MenuSelectionManager;
|
---|
| 22 | import javax.swing.Timer;
|
---|
| 23 | import javax.swing.event.ChangeEvent;
|
---|
| 24 | import javax.swing.event.ChangeListener;
|
---|
| 25 | import javax.swing.event.PopupMenuEvent;
|
---|
| 26 | import javax.swing.event.PopupMenuListener;
|
---|
| 27 |
|
---|
| 28 | /**
|
---|
| 29 | * A class that provides scrolling capabilities to a long menu dropdown or
|
---|
| 30 | * popup menu. A number of items can optionally be frozen at the top and/or
|
---|
| 31 | * bottom of the menu.
|
---|
| 32 | * <P>
|
---|
| 33 | * <B>Implementation note:</B> The default number of items to display
|
---|
| 34 | * at a time is 15, and the default scrolling interval is 125 milliseconds.
|
---|
| 35 | * <P>
|
---|
| 36 | * @author Darryl, http://tips4java.wordpress.com/2009/02/01/menu-scroller/
|
---|
| 37 | */
|
---|
| 38 | public class MenuScroller {
|
---|
| 39 |
|
---|
| 40 | //private JMenu menu;
|
---|
| 41 | private JPopupMenu menu;
|
---|
| 42 | private Component[] menuItems;
|
---|
| 43 | private MenuScrollItem upItem;
|
---|
| 44 | private MenuScrollItem downItem;
|
---|
| 45 | private final MenuScrollListener menuListener = new MenuScrollListener();
|
---|
[5664] | 46 | private final MouseWheelListener mouseWheelListener = new MouseScrollListener();
|
---|
[4593] | 47 | private int scrollCount;
|
---|
| 48 | private int interval;
|
---|
| 49 | private int topFixedCount;
|
---|
| 50 | private int bottomFixedCount;
|
---|
| 51 | private int firstIndex = 0;
|
---|
| 52 | private int keepVisibleIndex = -1;
|
---|
| 53 |
|
---|
| 54 | /**
|
---|
| 55 | * Registers a menu to be scrolled with the default number of items to
|
---|
| 56 | * display at a time and the default scrolling interval.
|
---|
| 57 | *
|
---|
| 58 | * @param menu the menu
|
---|
| 59 | * @return the MenuScroller
|
---|
| 60 | */
|
---|
| 61 | public static MenuScroller setScrollerFor(JMenu menu) {
|
---|
| 62 | return new MenuScroller(menu);
|
---|
| 63 | }
|
---|
| 64 |
|
---|
| 65 | /**
|
---|
| 66 | * Registers a popup menu to be scrolled with the default number of items to
|
---|
| 67 | * display at a time and the default scrolling interval.
|
---|
| 68 | *
|
---|
| 69 | * @param menu the popup menu
|
---|
| 70 | * @return the MenuScroller
|
---|
| 71 | */
|
---|
| 72 | public static MenuScroller setScrollerFor(JPopupMenu menu) {
|
---|
| 73 | return new MenuScroller(menu);
|
---|
| 74 | }
|
---|
| 75 |
|
---|
| 76 | /**
|
---|
| 77 | * Registers a menu to be scrolled with the default number of items to
|
---|
| 78 | * display at a time and the specified scrolling interval.
|
---|
| 79 | *
|
---|
| 80 | * @param menu the menu
|
---|
| 81 | * @param scrollCount the number of items to display at a time
|
---|
| 82 | * @return the MenuScroller
|
---|
| 83 | * @throws IllegalArgumentException if scrollCount is 0 or negative
|
---|
| 84 | */
|
---|
| 85 | public static MenuScroller setScrollerFor(JMenu menu, int scrollCount) {
|
---|
| 86 | return new MenuScroller(menu, scrollCount);
|
---|
| 87 | }
|
---|
| 88 |
|
---|
| 89 | /**
|
---|
| 90 | * Registers a popup menu to be scrolled with the default number of items to
|
---|
| 91 | * display at a time and the specified scrolling interval.
|
---|
| 92 | *
|
---|
| 93 | * @param menu the popup menu
|
---|
| 94 | * @param scrollCount the number of items to display at a time
|
---|
| 95 | * @return the MenuScroller
|
---|
| 96 | * @throws IllegalArgumentException if scrollCount is 0 or negative
|
---|
| 97 | */
|
---|
| 98 | public static MenuScroller setScrollerFor(JPopupMenu menu, int scrollCount) {
|
---|
| 99 | return new MenuScroller(menu, scrollCount);
|
---|
| 100 | }
|
---|
| 101 |
|
---|
| 102 | /**
|
---|
| 103 | * Registers a menu to be scrolled, with the specified number of items to
|
---|
| 104 | * display at a time and the specified scrolling interval.
|
---|
| 105 | *
|
---|
| 106 | * @param menu the menu
|
---|
| 107 | * @param scrollCount the number of items to be displayed at a time
|
---|
| 108 | * @param interval the scroll interval, in milliseconds
|
---|
| 109 | * @return the MenuScroller
|
---|
| 110 | * @throws IllegalArgumentException if scrollCount or interval is 0 or negative
|
---|
| 111 | */
|
---|
| 112 | public static MenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval) {
|
---|
| 113 | return new MenuScroller(menu, scrollCount, interval);
|
---|
| 114 | }
|
---|
| 115 |
|
---|
| 116 | /**
|
---|
| 117 | * Registers a popup menu to be scrolled, with the specified number of items to
|
---|
| 118 | * display at a time and the specified scrolling interval.
|
---|
| 119 | *
|
---|
| 120 | * @param menu the popup menu
|
---|
| 121 | * @param scrollCount the number of items to be displayed at a time
|
---|
| 122 | * @param interval the scroll interval, in milliseconds
|
---|
| 123 | * @return the MenuScroller
|
---|
| 124 | * @throws IllegalArgumentException if scrollCount or interval is 0 or negative
|
---|
| 125 | */
|
---|
| 126 | public static MenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval) {
|
---|
| 127 | return new MenuScroller(menu, scrollCount, interval);
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 | /**
|
---|
| 131 | * Registers a menu to be scrolled, with the specified number of items
|
---|
| 132 | * to display in the scrolling region, the specified scrolling interval,
|
---|
| 133 | * and the specified numbers of items fixed at the top and bottom of the
|
---|
| 134 | * menu.
|
---|
| 135 | *
|
---|
| 136 | * @param menu the menu
|
---|
| 137 | * @param scrollCount the number of items to display in the scrolling portion
|
---|
| 138 | * @param interval the scroll interval, in milliseconds
|
---|
| 139 | * @param topFixedCount the number of items to fix at the top. May be 0.
|
---|
| 140 | * @param bottomFixedCount the number of items to fix at the bottom. May be 0
|
---|
| 141 | * @throws IllegalArgumentException if scrollCount or interval is 0 or
|
---|
| 142 | * negative or if topFixedCount or bottomFixedCount is negative
|
---|
| 143 | * @return the MenuScroller
|
---|
| 144 | */
|
---|
| 145 | public static MenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval,
|
---|
| 146 | int topFixedCount, int bottomFixedCount) {
|
---|
| 147 | return new MenuScroller(menu, scrollCount, interval,
|
---|
| 148 | topFixedCount, bottomFixedCount);
|
---|
| 149 | }
|
---|
| 150 |
|
---|
| 151 | /**
|
---|
| 152 | * Registers a popup menu to be scrolled, with the specified number of items
|
---|
| 153 | * to display in the scrolling region, the specified scrolling interval,
|
---|
| 154 | * and the specified numbers of items fixed at the top and bottom of the
|
---|
| 155 | * popup menu.
|
---|
| 156 | *
|
---|
| 157 | * @param menu the popup menu
|
---|
| 158 | * @param scrollCount the number of items to display in the scrolling portion
|
---|
| 159 | * @param interval the scroll interval, in milliseconds
|
---|
| 160 | * @param topFixedCount the number of items to fix at the top. May be 0
|
---|
| 161 | * @param bottomFixedCount the number of items to fix at the bottom. May be 0
|
---|
| 162 | * @throws IllegalArgumentException if scrollCount or interval is 0 or
|
---|
| 163 | * negative or if topFixedCount or bottomFixedCount is negative
|
---|
| 164 | * @return the MenuScroller
|
---|
| 165 | */
|
---|
| 166 | public static MenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval,
|
---|
| 167 | int topFixedCount, int bottomFixedCount) {
|
---|
| 168 | return new MenuScroller(menu, scrollCount, interval,
|
---|
| 169 | topFixedCount, bottomFixedCount);
|
---|
| 170 | }
|
---|
| 171 |
|
---|
| 172 | /**
|
---|
| 173 | * Constructs a <code>MenuScroller</code> that scrolls a menu with the
|
---|
| 174 | * default number of items to display at a time, and default scrolling
|
---|
| 175 | * interval.
|
---|
| 176 | *
|
---|
| 177 | * @param menu the menu
|
---|
| 178 | */
|
---|
| 179 | public MenuScroller(JMenu menu) {
|
---|
| 180 | this(menu, 15);
|
---|
| 181 | }
|
---|
| 182 |
|
---|
| 183 | /**
|
---|
| 184 | * Constructs a <code>MenuScroller</code> that scrolls a popup menu with the
|
---|
| 185 | * default number of items to display at a time, and default scrolling
|
---|
| 186 | * interval.
|
---|
| 187 | *
|
---|
| 188 | * @param menu the popup menu
|
---|
| 189 | */
|
---|
| 190 | public MenuScroller(JPopupMenu menu) {
|
---|
| 191 | this(menu, 15);
|
---|
| 192 | }
|
---|
| 193 |
|
---|
| 194 | /**
|
---|
| 195 | * Constructs a <code>MenuScroller</code> that scrolls a menu with the
|
---|
| 196 | * specified number of items to display at a time, and default scrolling
|
---|
| 197 | * interval.
|
---|
| 198 | *
|
---|
| 199 | * @param menu the menu
|
---|
| 200 | * @param scrollCount the number of items to display at a time
|
---|
| 201 | * @throws IllegalArgumentException if scrollCount is 0 or negative
|
---|
| 202 | */
|
---|
| 203 | public MenuScroller(JMenu menu, int scrollCount) {
|
---|
| 204 | this(menu, scrollCount, 150);
|
---|
| 205 | }
|
---|
| 206 |
|
---|
| 207 | /**
|
---|
| 208 | * Constructs a <code>MenuScroller</code> that scrolls a popup menu with the
|
---|
| 209 | * specified number of items to display at a time, and default scrolling
|
---|
| 210 | * interval.
|
---|
| 211 | *
|
---|
| 212 | * @param menu the popup menu
|
---|
| 213 | * @param scrollCount the number of items to display at a time
|
---|
| 214 | * @throws IllegalArgumentException if scrollCount is 0 or negative
|
---|
| 215 | */
|
---|
| 216 | public MenuScroller(JPopupMenu menu, int scrollCount) {
|
---|
| 217 | this(menu, scrollCount, 150);
|
---|
| 218 | }
|
---|
| 219 |
|
---|
| 220 | /**
|
---|
| 221 | * Constructs a <code>MenuScroller</code> that scrolls a menu with the
|
---|
| 222 | * specified number of items to display at a time, and specified scrolling
|
---|
| 223 | * interval.
|
---|
| 224 | *
|
---|
| 225 | * @param menu the menu
|
---|
| 226 | * @param scrollCount the number of items to display at a time
|
---|
| 227 | * @param interval the scroll interval, in milliseconds
|
---|
| 228 | * @throws IllegalArgumentException if scrollCount or interval is 0 or negative
|
---|
| 229 | */
|
---|
| 230 | public MenuScroller(JMenu menu, int scrollCount, int interval) {
|
---|
| 231 | this(menu, scrollCount, interval, 0, 0);
|
---|
| 232 | }
|
---|
| 233 |
|
---|
| 234 | /**
|
---|
| 235 | * Constructs a <code>MenuScroller</code> that scrolls a popup menu with the
|
---|
| 236 | * specified number of items to display at a time, and specified scrolling
|
---|
| 237 | * interval.
|
---|
| 238 | *
|
---|
| 239 | * @param menu the popup menu
|
---|
| 240 | * @param scrollCount the number of items to display at a time
|
---|
| 241 | * @param interval the scroll interval, in milliseconds
|
---|
| 242 | * @throws IllegalArgumentException if scrollCount or interval is 0 or negative
|
---|
| 243 | */
|
---|
| 244 | public MenuScroller(JPopupMenu menu, int scrollCount, int interval) {
|
---|
| 245 | this(menu, scrollCount, interval, 0, 0);
|
---|
| 246 | }
|
---|
| 247 |
|
---|
| 248 | /**
|
---|
| 249 | * Constructs a <code>MenuScroller</code> that scrolls a menu with the
|
---|
| 250 | * specified number of items to display in the scrolling region, the
|
---|
| 251 | * specified scrolling interval, and the specified numbers of items fixed at
|
---|
| 252 | * the top and bottom of the menu.
|
---|
| 253 | *
|
---|
| 254 | * @param menu the menu
|
---|
| 255 | * @param scrollCount the number of items to display in the scrolling portion
|
---|
| 256 | * @param interval the scroll interval, in milliseconds
|
---|
| 257 | * @param topFixedCount the number of items to fix at the top. May be 0
|
---|
| 258 | * @param bottomFixedCount the number of items to fix at the bottom. May be 0
|
---|
| 259 | * @throws IllegalArgumentException if scrollCount or interval is 0 or
|
---|
| 260 | * negative or if topFixedCount or bottomFixedCount is negative
|
---|
| 261 | */
|
---|
| 262 | public MenuScroller(JMenu menu, int scrollCount, int interval,
|
---|
| 263 | int topFixedCount, int bottomFixedCount) {
|
---|
| 264 | this(menu.getPopupMenu(), scrollCount, interval, topFixedCount, bottomFixedCount);
|
---|
| 265 | }
|
---|
| 266 |
|
---|
| 267 | /**
|
---|
| 268 | * Constructs a <code>MenuScroller</code> that scrolls a popup menu with the
|
---|
| 269 | * specified number of items to display in the scrolling region, the
|
---|
| 270 | * specified scrolling interval, and the specified numbers of items fixed at
|
---|
| 271 | * the top and bottom of the popup menu.
|
---|
| 272 | *
|
---|
| 273 | * @param menu the popup menu
|
---|
| 274 | * @param scrollCount the number of items to display in the scrolling portion
|
---|
| 275 | * @param interval the scroll interval, in milliseconds
|
---|
| 276 | * @param topFixedCount the number of items to fix at the top. May be 0
|
---|
| 277 | * @param bottomFixedCount the number of items to fix at the bottom. May be 0
|
---|
| 278 | * @throws IllegalArgumentException if scrollCount or interval is 0 or
|
---|
| 279 | * negative or if topFixedCount or bottomFixedCount is negative
|
---|
| 280 | */
|
---|
| 281 | public MenuScroller(JPopupMenu menu, int scrollCount, int interval,
|
---|
| 282 | int topFixedCount, int bottomFixedCount) {
|
---|
| 283 | if (scrollCount <= 0 || interval <= 0) {
|
---|
| 284 | throw new IllegalArgumentException("scrollCount and interval must be greater than 0");
|
---|
| 285 | }
|
---|
| 286 | if (topFixedCount < 0 || bottomFixedCount < 0) {
|
---|
| 287 | throw new IllegalArgumentException("topFixedCount and bottomFixedCount cannot be negative");
|
---|
| 288 | }
|
---|
| 289 |
|
---|
| 290 | upItem = new MenuScrollItem(MenuIcon.UP, -1);
|
---|
| 291 | downItem = new MenuScrollItem(MenuIcon.DOWN, +1);
|
---|
| 292 | setScrollCount(scrollCount);
|
---|
| 293 | setInterval(interval);
|
---|
| 294 | setTopFixedCount(topFixedCount);
|
---|
| 295 | setBottomFixedCount(bottomFixedCount);
|
---|
| 296 |
|
---|
| 297 | this.menu = menu;
|
---|
| 298 | menu.addPopupMenuListener(menuListener);
|
---|
[5664] | 299 | menu.addMouseWheelListener(mouseWheelListener);
|
---|
[4593] | 300 | }
|
---|
| 301 |
|
---|
| 302 | /**
|
---|
| 303 | * Returns the scroll interval in milliseconds
|
---|
| 304 | *
|
---|
| 305 | * @return the scroll interval in milliseconds
|
---|
| 306 | */
|
---|
| 307 | public int getInterval() {
|
---|
| 308 | return interval;
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | /**
|
---|
| 312 | * Sets the scroll interval in milliseconds
|
---|
| 313 | *
|
---|
| 314 | * @param interval the scroll interval in milliseconds
|
---|
| 315 | * @throws IllegalArgumentException if interval is 0 or negative
|
---|
| 316 | */
|
---|
| 317 | public void setInterval(int interval) {
|
---|
| 318 | if (interval <= 0) {
|
---|
| 319 | throw new IllegalArgumentException("interval must be greater than 0");
|
---|
| 320 | }
|
---|
| 321 | upItem.setInterval(interval);
|
---|
| 322 | downItem.setInterval(interval);
|
---|
| 323 | this.interval = interval;
|
---|
| 324 | }
|
---|
| 325 |
|
---|
| 326 | /**
|
---|
| 327 | * Returns the number of items in the scrolling portion of the menu.
|
---|
| 328 | *
|
---|
| 329 | * @return the number of items to display at a time
|
---|
| 330 | */
|
---|
| 331 | public int getscrollCount() {
|
---|
| 332 | return scrollCount;
|
---|
| 333 | }
|
---|
| 334 |
|
---|
| 335 | /**
|
---|
| 336 | * Sets the number of items in the scrolling portion of the menu.
|
---|
| 337 | *
|
---|
| 338 | * @param scrollCount the number of items to display at a time
|
---|
| 339 | * @throws IllegalArgumentException if scrollCount is 0 or negative
|
---|
| 340 | */
|
---|
| 341 | public void setScrollCount(int scrollCount) {
|
---|
| 342 | if (scrollCount <= 0) {
|
---|
| 343 | throw new IllegalArgumentException("scrollCount must be greater than 0");
|
---|
| 344 | }
|
---|
| 345 | this.scrollCount = scrollCount;
|
---|
| 346 | MenuSelectionManager.defaultManager().clearSelectedPath();
|
---|
| 347 | }
|
---|
| 348 |
|
---|
| 349 | /**
|
---|
| 350 | * Returns the number of items fixed at the top of the menu or popup menu.
|
---|
| 351 | *
|
---|
| 352 | * @return the number of items
|
---|
| 353 | */
|
---|
| 354 | public int getTopFixedCount() {
|
---|
| 355 | return topFixedCount;
|
---|
| 356 | }
|
---|
| 357 |
|
---|
| 358 | /**
|
---|
| 359 | * Sets the number of items to fix at the top of the menu or popup menu.
|
---|
| 360 | *
|
---|
| 361 | * @param topFixedCount the number of items
|
---|
| 362 | */
|
---|
| 363 | public void setTopFixedCount(int topFixedCount) {
|
---|
| 364 | if (firstIndex <= topFixedCount) {
|
---|
| 365 | firstIndex = topFixedCount;
|
---|
| 366 | } else {
|
---|
| 367 | firstIndex += (topFixedCount - this.topFixedCount);
|
---|
| 368 | }
|
---|
| 369 | this.topFixedCount = topFixedCount;
|
---|
| 370 | }
|
---|
| 371 |
|
---|
| 372 | /**
|
---|
| 373 | * Returns the number of items fixed at the bottom of the menu or popup menu.
|
---|
| 374 | *
|
---|
| 375 | * @return the number of items
|
---|
| 376 | */
|
---|
| 377 | public int getBottomFixedCount() {
|
---|
| 378 | return bottomFixedCount;
|
---|
| 379 | }
|
---|
| 380 |
|
---|
| 381 | /**
|
---|
| 382 | * Sets the number of items to fix at the bottom of the menu or popup menu.
|
---|
| 383 | *
|
---|
| 384 | * @param bottomFixedCount the number of items
|
---|
| 385 | */
|
---|
| 386 | public void setBottomFixedCount(int bottomFixedCount) {
|
---|
| 387 | this.bottomFixedCount = bottomFixedCount;
|
---|
| 388 | }
|
---|
| 389 |
|
---|
| 390 | /**
|
---|
| 391 | * Scrolls the specified item into view each time the menu is opened. Call this method with
|
---|
| 392 | * <code>null</code> to restore the default behavior, which is to show the menu as it last
|
---|
| 393 | * appeared.
|
---|
| 394 | *
|
---|
| 395 | * @param item the item to keep visible
|
---|
| 396 | * @see #keepVisible(int)
|
---|
| 397 | */
|
---|
| 398 | public void keepVisible(JMenuItem item) {
|
---|
| 399 | if (item == null) {
|
---|
| 400 | keepVisibleIndex = -1;
|
---|
| 401 | } else {
|
---|
| 402 | int index = menu.getComponentIndex(item);
|
---|
| 403 | keepVisibleIndex = index;
|
---|
| 404 | }
|
---|
| 405 | }
|
---|
| 406 |
|
---|
| 407 | /**
|
---|
| 408 | * Scrolls the item at the specified index into view each time the menu is opened. Call this
|
---|
| 409 | * method with <code>-1</code> to restore the default behavior, which is to show the menu as
|
---|
| 410 | * it last appeared.
|
---|
| 411 | *
|
---|
| 412 | * @param index the index of the item to keep visible
|
---|
| 413 | * @see #keepVisible(javax.swing.JMenuItem)
|
---|
| 414 | */
|
---|
| 415 | public void keepVisible(int index) {
|
---|
| 416 | keepVisibleIndex = index;
|
---|
| 417 | }
|
---|
| 418 |
|
---|
| 419 | /**
|
---|
| 420 | * Removes this MenuScroller from the associated menu and restores the
|
---|
| 421 | * default behavior of the menu.
|
---|
| 422 | */
|
---|
| 423 | public void dispose() {
|
---|
| 424 | if (menu != null) {
|
---|
| 425 | menu.removePopupMenuListener(menuListener);
|
---|
[5664] | 426 | menu.removeMouseWheelListener(mouseWheelListener);
|
---|
| 427 | menu.setPreferredSize(null);
|
---|
[4593] | 428 | menu = null;
|
---|
| 429 | }
|
---|
| 430 | }
|
---|
| 431 |
|
---|
| 432 | /**
|
---|
| 433 | * Ensures that the <code>dispose</code> method of this MenuScroller is
|
---|
| 434 | * called when there are no more refrences to it.
|
---|
| 435 | *
|
---|
| 436 | * @exception Throwable if an error occurs.
|
---|
| 437 | * @see MenuScroller#dispose()
|
---|
| 438 | */
|
---|
| 439 | @Override
|
---|
| 440 | public void finalize() throws Throwable {
|
---|
| 441 | dispose();
|
---|
| 442 | }
|
---|
| 443 |
|
---|
| 444 | private void refreshMenu() {
|
---|
| 445 | if (menuItems != null && menuItems.length > 0) {
|
---|
| 446 | firstIndex = Math.max(topFixedCount, firstIndex);
|
---|
| 447 | firstIndex = Math.min(menuItems.length - bottomFixedCount - scrollCount, firstIndex);
|
---|
| 448 |
|
---|
| 449 | upItem.setEnabled(firstIndex > topFixedCount);
|
---|
| 450 | downItem.setEnabled(firstIndex + scrollCount < menuItems.length - bottomFixedCount);
|
---|
| 451 |
|
---|
| 452 | menu.removeAll();
|
---|
| 453 | for (int i = 0; i < topFixedCount; i++) {
|
---|
| 454 | menu.add(menuItems[i]);
|
---|
| 455 | }
|
---|
| 456 | if (topFixedCount > 0) {
|
---|
[5927] | 457 | menu.addSeparator();
|
---|
[4593] | 458 | }
|
---|
| 459 |
|
---|
| 460 | menu.add(upItem);
|
---|
| 461 | for (int i = firstIndex; i < scrollCount + firstIndex; i++) {
|
---|
| 462 | menu.add(menuItems[i]);
|
---|
| 463 | }
|
---|
| 464 | menu.add(downItem);
|
---|
| 465 |
|
---|
| 466 | if (bottomFixedCount > 0) {
|
---|
[5664] | 467 | menu.addSeparator();
|
---|
[4593] | 468 | }
|
---|
| 469 | for (int i = menuItems.length - bottomFixedCount; i < menuItems.length; i++) {
|
---|
| 470 | menu.add(menuItems[i]);
|
---|
| 471 | }
|
---|
| 472 |
|
---|
| 473 | int preferredWidth = 0;
|
---|
| 474 | for (Component item : menuItems) {
|
---|
| 475 | preferredWidth = Math.max(preferredWidth, item.getPreferredSize().width);
|
---|
| 476 | }
|
---|
| 477 | menu.setPreferredSize(new Dimension(preferredWidth, menu.getPreferredSize().height));
|
---|
| 478 |
|
---|
| 479 | JComponent parent = (JComponent) upItem.getParent();
|
---|
| 480 | parent.revalidate();
|
---|
| 481 | parent.repaint();
|
---|
| 482 | }
|
---|
| 483 | }
|
---|
[6070] | 484 |
|
---|
[4593] | 485 | private class MenuScrollListener implements PopupMenuListener {
|
---|
| 486 |
|
---|
| 487 | @Override
|
---|
| 488 | public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
|
---|
| 489 | setMenuItems();
|
---|
| 490 | }
|
---|
| 491 |
|
---|
| 492 | @Override
|
---|
| 493 | public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
|
---|
| 494 | restoreMenuItems();
|
---|
| 495 | }
|
---|
| 496 |
|
---|
| 497 | @Override
|
---|
| 498 | public void popupMenuCanceled(PopupMenuEvent e) {
|
---|
| 499 | restoreMenuItems();
|
---|
| 500 | }
|
---|
| 501 |
|
---|
| 502 | private void setMenuItems() {
|
---|
| 503 | menuItems = menu.getComponents();
|
---|
| 504 | if (keepVisibleIndex >= topFixedCount
|
---|
| 505 | && keepVisibleIndex <= menuItems.length - bottomFixedCount
|
---|
| 506 | && (keepVisibleIndex > firstIndex + scrollCount
|
---|
| 507 | || keepVisibleIndex < firstIndex)) {
|
---|
| 508 | firstIndex = Math.min(firstIndex, keepVisibleIndex);
|
---|
| 509 | firstIndex = Math.max(firstIndex, keepVisibleIndex - scrollCount + 1);
|
---|
| 510 | }
|
---|
| 511 | if (menuItems.length > topFixedCount + scrollCount + bottomFixedCount) {
|
---|
| 512 | refreshMenu();
|
---|
| 513 | }
|
---|
| 514 | }
|
---|
| 515 |
|
---|
| 516 | private void restoreMenuItems() {
|
---|
| 517 | menu.removeAll();
|
---|
| 518 | for (Component component : menuItems) {
|
---|
| 519 | menu.add(component);
|
---|
| 520 | }
|
---|
| 521 | }
|
---|
| 522 | }
|
---|
| 523 |
|
---|
| 524 | private class MenuScrollTimer extends Timer {
|
---|
| 525 |
|
---|
| 526 | public MenuScrollTimer(final int increment, int interval) {
|
---|
| 527 | super(interval, new ActionListener() {
|
---|
| 528 |
|
---|
| 529 | @Override
|
---|
| 530 | public void actionPerformed(ActionEvent e) {
|
---|
| 531 | firstIndex += increment;
|
---|
| 532 | refreshMenu();
|
---|
| 533 | }
|
---|
| 534 | });
|
---|
| 535 | }
|
---|
| 536 | }
|
---|
| 537 |
|
---|
| 538 | private class MenuScrollItem extends JMenuItem
|
---|
| 539 | implements ChangeListener {
|
---|
| 540 |
|
---|
| 541 | private MenuScrollTimer timer;
|
---|
| 542 |
|
---|
| 543 | public MenuScrollItem(MenuIcon icon, int increment) {
|
---|
| 544 | setIcon(icon);
|
---|
| 545 | setDisabledIcon(icon);
|
---|
| 546 | timer = new MenuScrollTimer(increment, interval);
|
---|
| 547 | addChangeListener(this);
|
---|
| 548 | }
|
---|
| 549 |
|
---|
| 550 | public void setInterval(int interval) {
|
---|
| 551 | timer.setDelay(interval);
|
---|
| 552 | }
|
---|
| 553 |
|
---|
| 554 | @Override
|
---|
| 555 | public void stateChanged(ChangeEvent e) {
|
---|
| 556 | if (isArmed() && !timer.isRunning()) {
|
---|
| 557 | timer.start();
|
---|
| 558 | }
|
---|
| 559 | if (!isArmed() && timer.isRunning()) {
|
---|
| 560 | timer.stop();
|
---|
| 561 | }
|
---|
| 562 | }
|
---|
| 563 | }
|
---|
| 564 |
|
---|
| 565 | private static enum MenuIcon implements Icon {
|
---|
| 566 |
|
---|
| 567 | UP(9, 1, 9),
|
---|
| 568 | DOWN(1, 9, 1);
|
---|
| 569 | final int[] xPoints = {1, 5, 9};
|
---|
| 570 | final int[] yPoints;
|
---|
| 571 |
|
---|
| 572 | MenuIcon(int... yPoints) {
|
---|
| 573 | this.yPoints = yPoints;
|
---|
| 574 | }
|
---|
| 575 |
|
---|
| 576 | @Override
|
---|
| 577 | public void paintIcon(Component c, Graphics g, int x, int y) {
|
---|
| 578 | Dimension size = c.getSize();
|
---|
| 579 | Graphics g2 = g.create(size.width / 2 - 5, size.height / 2 - 5, 10, 10);
|
---|
| 580 | g2.setColor(Color.GRAY);
|
---|
| 581 | g2.drawPolygon(xPoints, yPoints, 3);
|
---|
| 582 | if (c.isEnabled()) {
|
---|
| 583 | g2.setColor(Color.BLACK);
|
---|
| 584 | g2.fillPolygon(xPoints, yPoints, 3);
|
---|
| 585 | }
|
---|
| 586 | g2.dispose();
|
---|
| 587 | }
|
---|
| 588 |
|
---|
| 589 | @Override
|
---|
| 590 | public int getIconWidth() {
|
---|
| 591 | return 0;
|
---|
| 592 | }
|
---|
| 593 |
|
---|
| 594 | @Override
|
---|
| 595 | public int getIconHeight() {
|
---|
| 596 | return 10;
|
---|
| 597 | }
|
---|
| 598 | }
|
---|
[6070] | 599 |
|
---|
[5664] | 600 | private class MouseScrollListener implements MouseWheelListener {
|
---|
[6084] | 601 | @Override
|
---|
[5664] | 602 | public void mouseWheelMoved(MouseWheelEvent mwe) {
|
---|
| 603 | if (menu.getComponents().length > scrollCount) {
|
---|
| 604 | firstIndex += mwe.getWheelRotation();
|
---|
| 605 | refreshMenu();
|
---|
| 606 | }
|
---|
| 607 | mwe.consume(); // (Comment 16, Huw)
|
---|
| 608 | }
|
---|
| 609 | }
|
---|
[4593] | 610 | }
|
---|