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