org.tinymediamanager.ui.components.PopupMenuScroller Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2012 - 2019 Manuel Laggner
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.tinymediamanager.ui.components;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.MenuSelectionManager;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import org.tinymediamanager.ui.IconManager;
public class PopupMenuScroller {
private JPopupMenu menu;
private Component[] menuItems;
private MenuScrollItem upItem;
private MenuScrollItem downItem;
private final MenuScrollListener menuListener = new MenuScrollListener();
private int scrollCount;
private int interval;
private int topFixedCount;
private int bottomFixedCount;
private int firstIndex = 0;
private int keepVisibleIndex = -1;
/**
* Registers a popup menu to be scrolled with the default number of items to display at a time and the default scrolling interval.
*
* @param menu
* the popup menu
* @return the MenuScroller
*/
public static PopupMenuScroller setScrollerFor(JPopupMenu menu) {
return new PopupMenuScroller(menu);
}
/**
* Registers a popup menu to be scrolled with the default number of items to display at a time and the specified scrolling interval.
*
* @param menu
* the popup menu
* @param scrollCount
* the number of items to display at a time
* @return the MenuScroller
* @throws IllegalArgumentException
* if scrollCount is 0 or negative
*/
public static PopupMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount) {
return new PopupMenuScroller(menu, scrollCount);
}
/**
* Registers a popup menu to be scrolled, with the specified number of items to display at a time and the specified scrolling interval.
*
* @param menu
* the popup menu
* @param scrollCount
* the number of items to be displayed at a time
* @param interval
* the scroll interval, in milliseconds
* @return the MenuScroller
* @throws IllegalArgumentException
* if scrollCount or interval is 0 or negative
*/
public static PopupMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval) {
return new PopupMenuScroller(menu, scrollCount, interval);
}
/**
* Registers a popup menu to be scrolled, with the specified number of items to display in the scrolling region, the specified scrolling interval,
* and the specified numbers of items fixed at the top and bottom of the popup menu.
*
* @param menu
* the popup menu
* @param scrollCount
* the number of items to display in the scrolling portion
* @param interval
* the scroll interval, in milliseconds
* @param topFixedCount
* the number of items to fix at the top. May be 0
* @param bottomFixedCount
* the number of items to fix at the bottom. May be 0
* @throws IllegalArgumentException
* if scrollCount or interval is 0 or negative or if topFixedCount or bottomFixedCount is negative
* @return the MenuScroller
*/
public static PopupMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) {
return new PopupMenuScroller(menu, scrollCount, interval, topFixedCount, bottomFixedCount);
}
/**
* Constructs a MenuScroller
that scrolls a popup menu with the default number of items to display at a time, and default scrolling
* interval.
*
* @param menu
* the popup menu
*/
public PopupMenuScroller(JPopupMenu menu) {
this(menu, 15);
}
/**
* Constructs a MenuScroller
that scrolls a popup menu with the specified number of items to display at a time, and default scrolling
* interval.
*
* @param menu
* the popup menu
* @param scrollCount
* the number of items to display at a time
* @throws IllegalArgumentException
* if scrollCount is 0 or negative
*/
public PopupMenuScroller(JPopupMenu menu, int scrollCount) {
this(menu, scrollCount, 150);
}
/**
* Constructs a MenuScroller
that scrolls a popup menu with the specified number of items to display at a time, and specified scrolling
* interval.
*
* @param menu
* the popup menu
* @param scrollCount
* the number of items to display at a time
* @param interval
* the scroll interval, in milliseconds
* @throws IllegalArgumentException
* if scrollCount or interval is 0 or negative
*/
public PopupMenuScroller(JPopupMenu menu, int scrollCount, int interval) {
this(menu, scrollCount, interval, 0, 0);
}
/**
* Constructs a MenuScroller
that scrolls a popup menu with the specified number of items to display in the scrolling region, the
* specified scrolling interval, and the specified numbers of items fixed at the top and bottom of the popup menu.
*
* @param menu
* the popup menu
* @param scrollCount
* the number of items to display in the scrolling portion
* @param interval
* the scroll interval, in milliseconds
* @param topFixedCount
* the number of items to fix at the top. May be 0
* @param bottomFixedCount
* the number of items to fix at the bottom. May be 0
* @throws IllegalArgumentException
* if scrollCount or interval is 0 or negative or if topFixedCount or bottomFixedCount is negative
*/
public PopupMenuScroller(JPopupMenu menu, int scrollCount, int interval, int topFixedCount, int bottomFixedCount) {
if (scrollCount <= 0 || interval <= 0) {
throw new IllegalArgumentException("scrollCount and interval must be greater than 0");
}
if (topFixedCount < 0 || bottomFixedCount < 0) {
throw new IllegalArgumentException("topFixedCount and bottomFixedCount cannot be negative");
}
upItem = new MenuScrollItem(IconManager.CARET_UP, -1);
downItem = new MenuScrollItem(IconManager.CARET_DOWN, +1);
setScrollCount(scrollCount);
setInterval(interval);
setTopFixedCount(topFixedCount);
setBottomFixedCount(bottomFixedCount);
this.menu = menu;
menu.addPopupMenuListener(menuListener);
}
/**
* Returns the scroll interval in milliseconds
*
* @return the scroll interval in milliseconds
*/
public int getInterval() {
return interval;
}
/**
* Sets the scroll interval in milliseconds
*
* @param interval
* the scroll interval in milliseconds
* @throws IllegalArgumentException
* if interval is 0 or negative
*/
public void setInterval(int interval) {
if (interval <= 0) {
throw new IllegalArgumentException("interval must be greater than 0");
}
upItem.setInterval(interval);
downItem.setInterval(interval);
this.interval = interval;
}
/**
* Returns the number of items in the scrolling portion of the menu.
*
* @return the number of items to display at a time
*/
public int getscrollCount() {
return scrollCount;
}
/**
* Sets the number of items in the scrolling portion of the menu.
*
* @param scrollCount
* the number of items to display at a time
* @throws IllegalArgumentException
* if scrollCount is 0 or negative
*/
public void setScrollCount(int scrollCount) {
if (scrollCount <= 0) {
throw new IllegalArgumentException("scrollCount must be greater than 0");
}
this.scrollCount = scrollCount;
MenuSelectionManager.defaultManager().clearSelectedPath();
}
/**
* Returns the number of items fixed at the top of the menu or popup menu.
*
* @return the number of items
*/
public int getTopFixedCount() {
return topFixedCount;
}
/**
* Sets the number of items to fix at the top of the menu or popup menu.
*
* @param topFixedCount
* the number of items
*/
public void setTopFixedCount(int topFixedCount) {
if (firstIndex <= topFixedCount) {
firstIndex = topFixedCount;
}
else {
firstIndex += (topFixedCount - this.topFixedCount);
}
this.topFixedCount = topFixedCount;
}
/**
* Returns the number of items fixed at the bottom of the menu or popup menu.
*
* @return the number of items
*/
public int getBottomFixedCount() {
return bottomFixedCount;
}
/**
* Sets the number of items to fix at the bottom of the menu or popup menu.
*
* @param bottomFixedCount
* the number of items
*/
public void setBottomFixedCount(int bottomFixedCount) {
this.bottomFixedCount = bottomFixedCount;
}
/**
* Scrolls the specified item into view each time the menu is opened. Call this method with null
to restore the default behavior, which
* is to show the menu as it last appeared.
*
* @param item
* the item to keep visible
* @see #keepVisible(int)
*/
public void keepVisible(JMenuItem item) {
if (item == null) {
keepVisibleIndex = -1;
}
else {
int index = menu.getComponentIndex(item);
keepVisibleIndex = index;
}
}
/**
* Scrolls the item at the specified index into view each time the menu is opened. Call this method with -1
to restore the default
* behavior, which is to show the menu as it last appeared.
*
* @param index
* the index of the item to keep visible
* @see #keepVisible(javax.swing.JMenuItem)
*/
public void keepVisible(int index) {
keepVisibleIndex = index;
}
/**
* Removes this MenuScroller from the associated menu and restores the default behavior of the menu.
*/
public void dispose() {
if (menu != null) {
menu.removePopupMenuListener(menuListener);
menu = null;
}
}
/**
* Ensures that the dispose
method of this MenuScroller is called when there are no more refrences to it.
*
* @see PopupMenuScroller#dispose()
*/
@Override
public void finalize() {
dispose();
}
private void refreshMenu() {
if (menuItems != null && menuItems.length > 0) {
firstIndex = Math.max(topFixedCount, firstIndex);
firstIndex = Math.min(menuItems.length - bottomFixedCount - scrollCount, firstIndex);
upItem.setEnabled(firstIndex > topFixedCount);
downItem.setEnabled(firstIndex + scrollCount < menuItems.length - bottomFixedCount);
menu.removeAll();
for (int i = 0; i < topFixedCount; i++) {
menu.add(menuItems[i]);
}
if (topFixedCount > 0) {
menu.addSeparator();
}
menu.add(upItem);
for (int i = firstIndex; i < scrollCount + firstIndex; i++) {
menu.add(menuItems[i]);
}
menu.add(downItem);
if (bottomFixedCount > 0) {
menu.addSeparator();
}
for (int i = menuItems.length - bottomFixedCount; i < menuItems.length; i++) {
menu.add(menuItems[i]);
}
JComponent parent = (JComponent) upItem.getParent();
parent.revalidate();
parent.repaint();
}
}
private class MenuScrollListener implements PopupMenuListener {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
setMenuItems();
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
restoreMenuItems();
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
restoreMenuItems();
}
private void setMenuItems() {
menuItems = menu.getComponents();
if (keepVisibleIndex >= topFixedCount && keepVisibleIndex <= menuItems.length - bottomFixedCount
&& (keepVisibleIndex > firstIndex + scrollCount || keepVisibleIndex < firstIndex)) {
firstIndex = Math.min(firstIndex, keepVisibleIndex);
firstIndex = Math.max(firstIndex, keepVisibleIndex - scrollCount + 1);
}
if (menuItems.length > topFixedCount + scrollCount + bottomFixedCount) {
refreshMenu();
}
}
private void restoreMenuItems() {
menu.removeAll();
for (Component component : menuItems) {
menu.add(component);
}
}
}
private class MenuScrollTimer extends Timer {
MenuScrollTimer(final int increment, int interval) {
super(interval, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
firstIndex += increment;
refreshMenu();
}
});
}
}
private class MenuScrollItem extends JMenuItem implements ChangeListener {
private MenuScrollTimer timer;
MenuScrollItem(ImageIcon icon, int increment) {
setIcon(icon);
setDisabledIcon(icon);
setHorizontalAlignment(JMenuItem.CENTER);
timer = new MenuScrollTimer(increment, interval);
addChangeListener(this);
}
public void setInterval(int interval) {
timer.setDelay(interval);
}
@Override
public void stateChanged(ChangeEvent e) {
if (isArmed() && !timer.isRunning()) {
timer.start();
}
if (!isArmed() && timer.isRunning()) {
timer.stop();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy