package com.revolsys.swing.menu;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import com.revolsys.beans.ClassRegistry;
import com.revolsys.properties.BaseObjectWithProperties;
import com.revolsys.swing.Icons;
import com.revolsys.swing.action.AbstractAction;
import com.revolsys.swing.action.RunnableAction;
import com.revolsys.swing.action.enablecheck.EnableCheck;
import com.revolsys.swing.component.ComponentFactory;
import com.revolsys.swing.field.Field;
import com.revolsys.util.Property;
public class MenuFactory extends BaseObjectWithProperties implements ComponentFactory<JMenuItem> {
private static final ClassRegistry<MenuFactory> CLASS_MENUS = new ClassRegistry<>();
private static final String KEY_POPUP_MENU = MenuFactory.class.getName() + ".popup";
private static Object menuSource;
public static void addToComponent(final JComponent component, final MenuFactory menuFactory) {
component.putClientProperty(KEY_POPUP_MENU, menuFactory);
ShowMenuMouseListener.addListener(component, menuFactory::newJPopupMenu, true);
}
public static MenuFactory findMenu(final Class<?> clazz) {
synchronized (CLASS_MENUS) {
return CLASS_MENUS.find(clazz);
}
}
public static MenuFactory findMenu(final Object object) {
if (object == null) {
return null;
} else {
final Class<?> clazz = object.getClass();
return findMenu(clazz);
}
}
public static MenuFactory getMenu(final Class<?> clazz) {
if (clazz == null) {
return new MenuFactory();
} else {
synchronized (CLASS_MENUS) {
MenuFactory menu = CLASS_MENUS.get(clazz);
if (menu == null) {
final Class<?> superClass = clazz.getSuperclass();
final MenuFactory parentMenu = getMenu(superClass);
menu = new MenuFactory(clazz.getName(), parentMenu);
CLASS_MENUS.put(clazz, menu);
}
return menu;
}
}
}
public static MenuFactory getMenu(final Object object) {
final Class<?> clazz = object.getClass();
return getMenu(clazz);
}
@SuppressWarnings("unchecked")
public static <V> V getMenuSource() {
return (V)menuSource;
}
public static MenuFactory getPopupMenuFactory(final JComponent component) {
synchronized (component) {
MenuFactory menuFactory = (MenuFactory)component.getClientProperty(KEY_POPUP_MENU);
if (menuFactory == null) {
String name = "Field";
if (component instanceof Field) {
final Field field = (Field)component;
name += " " + field.getFieldName();
}
menuFactory = new MenuFactory(name);
addToComponent(component, menuFactory);
}
return menuFactory;
}
}
public static RunnableAction newMenuItem(final CharSequence name, final String toolTip,
final Icon icon, final EnableCheck enableCheck, final Runnable runnable) {
final RunnableAction action = new RunnableAction(name, toolTip, icon, runnable);
action.setEnableCheck(enableCheck);
return action;
}
public static void setMenuSource(final Object menuSource) {
MenuFactory.menuSource = menuSource;
}
public static void showMenu(final JPopupMenu menu, final Component component, final int x,
final int y) {
if (menu != null) {
final int numItems = menu.getSubElements().length;
if (menu != null && numItems > 0) {
final Window window = SwingUtilities.windowForComponent(component);
if (window != null) {
if (window.isAlwaysOnTop()) {
window.setAlwaysOnTop(true);
window.setAlwaysOnTop(false);
}
window.toFront();
}
menu.validate();
menu.show(component, x, y);
}
}
}
private EnableCheck enableCheck;
private final List<String> groupNames = new ArrayList<>();
private final Map<String, List<ComponentFactory<?>>> groups = new HashMap<>();
private String name;
public MenuFactory() {
}
public MenuFactory(final MenuFactory menuFactory) {
this(null, menuFactory);
}
public MenuFactory(final String name) {
this(name, null);
}
public MenuFactory(final String name, final MenuFactory menuFactory) {
this.name = name;
if (menuFactory != null) {
for (final String groupName : menuFactory.getGroupNames()) {
for (final ComponentFactory<?> factory : menuFactory.getGroup(groupName)) {
final ComponentFactory<?> cloneFactory = factory.clone();
addComponentFactory(groupName, cloneFactory);
}
}
}
}
public void addCheckboxMenuItem(final String groupName, final AbstractAction action,
final EnableCheck itemChecked) {
final ActionMainMenuItemFactory factory = new ActionMainMenuItemFactory(itemChecked, action);
addComponentFactory(groupName, factory);
}
public void addComponent(final Component component) {
addComponent("default", component);
}
public void addComponent(final String groupName, final Component component) {
addComponentFactory(groupName, new ComponentComponentFactory(component));
}
public void addComponentFactory(final String groupName, final ComponentFactory<?> factory) {
final List<ComponentFactory<?>> factories = getGroup(groupName);
factories.add(factory);
}
public void addComponentFactory(final String groupName, int index,
final ComponentFactory<?> factory) {
final List<ComponentFactory<?>> factories = getGroup(groupName);
if (index < 0) {
index = factories.size();
}
factories.add(index, factory);
}
public void addGroup(final int index, final String groupName) {
if (!this.groupNames.contains(groupName)) {
this.groupNames.add(index, groupName);
}
}
public void addGroup(final String groupName) {
getGroup(groupName);
}
public void addMenuItem(final AbstractAction action) {
addMenuItem("default", action);
}
public JMenuItem addMenuItem(final JMenuItem menuItem) {
addComponent(menuItem);
return menuItem;
}
public JMenuItem addMenuItem(final String title) {
final JMenuItem menuItem = new JMenuItem(title);
addComponent("default", menuItem);
return menuItem;
}
public void addMenuItem(final String groupName, final AbstractAction action) {
final ActionMainMenuItemFactory factory = new ActionMainMenuItemFactory(action);
addComponentFactory(groupName, factory);
}
public void addMenuItem(final String groupName, final int index, final AbstractAction action) {
final ActionMainMenuItemFactory factory = new ActionMainMenuItemFactory(action);
addComponentFactory(groupName, index, factory);
}
public void addMenuItem(final String groupName, final int index, final String title,
final String iconName, final Runnable runnable) {
final Icon icon = Icons.getIcon(iconName);
final RunnableAction menuItem = newMenuItem(title, title, icon, null, runnable);
addComponentFactory(groupName, index, menuItem);
}
public void addMenuItem(final String groupName, final String title, final String iconName,
final EnableCheck enableCheck, final Runnable runnable) {
final Icon icon = Icons.getIcon(iconName);
final RunnableAction menuItem = newMenuItem(title, title, icon, enableCheck, runnable);
addComponentFactory(groupName, menuItem);
}
public void addMenuItem(final String groupName, final String title, final String iconName,
final Runnable runnable) {
final Icon icon = Icons.getIcon(iconName);
final RunnableAction menuItem = newMenuItem(title, title, icon, null, runnable);
addComponentFactory(groupName, menuItem);
}
public void addMenuItem(final String groupName, final String title, final String toolTip,
final String iconName, final Runnable runnable) {
final Icon icon = Icons.getIcon(iconName);
final RunnableAction menuItem = newMenuItem(title, toolTip, icon, null, runnable);
addComponentFactory(groupName, menuItem);
}
public RunnableAction addMenuItemTitleIcon(final String groupName, final CharSequence name,
final String iconName, final EnableCheck enableCheck, final Runnable runnable) {
final Icon icon = Icons.getIcon(iconName);
final RunnableAction action = newMenuItem(name, null, icon, enableCheck, runnable);
addComponentFactory(groupName, action);
return action;
}
public RunnableAction addMenuItemTitleIcon(final String groupName, final CharSequence name,
final String iconName, final Runnable runnable) {
final Icon icon = Icons.getIcon(iconName);
final RunnableAction action = newMenuItem(name, null, icon, null, runnable);
addComponentFactory(groupName, action);
return action;
}
public RunnableAction addMenuItemTitleIcon(final String groupName, final int index,
final CharSequence name, final String iconName, final EnableCheck enableCheck,
final Runnable runnable) {
final Icon icon = Icons.getIcon(iconName);
final RunnableAction action = newMenuItem(name, null, icon, enableCheck, runnable);
addComponentFactory(groupName, index, action);
return action;
}
public void clear() {
this.groups.clear();
}
@Override
public MenuFactory clone() {
final MenuFactory clone = new MenuFactory(this);
return clone;
}
@Override
public void close(final Component component) {
}
public void deleteGroup(final String groupName) {
this.groupNames.remove(groupName);
this.groups.remove(groupName);
}
public void deleteMenuItem(final String groupName, final String menuTitle) {
final List<ComponentFactory<?>> items = this.groups.get(groupName);
if (items != null) {
for (final ComponentFactory<?> item : items) {
boolean delete = false;
if (item instanceof MenuFactory) {
final MenuFactory menuFactory = (MenuFactory)item;
if (menuTitle.equals(menuFactory.getName())) {
delete = true;
}
} else if (item instanceof ActionMainMenuItemFactory) {
final ActionMainMenuItemFactory actionFactory = (ActionMainMenuItemFactory)item;
if (menuTitle.equals(actionFactory.getName())) {
delete = true;
}
} else if (item instanceof Action) {
final Action action = (Action)item;
if (menuTitle.equals(action.getValue(Action.NAME))) {
delete = true;
}
}
if (delete) {
items.remove(item);
return;
}
}
}
}
public EnableCheck getEnableCheck() {
return this.enableCheck;
}
public MenuFactory getFactory(final String name) {
for (final List<ComponentFactory<?>> group : this.groups.values()) {
for (final ComponentFactory<?> factory : group) {
if (factory instanceof MenuFactory) {
final MenuFactory menuFactory = (MenuFactory)factory;
final String factoryName = menuFactory.getName();
if (name.equals(factoryName)) {
return menuFactory;
}
}
}
}
return null;
}
public List<ComponentFactory<?>> getGroup(final String groupName) {
List<ComponentFactory<?>> factories = this.groups.get(groupName);
if (factories == null) {
factories = new ArrayList<>();
this.groups.put(groupName, factories);
if (!this.groupNames.contains(groupName)) {
this.groupNames.add(groupName);
}
}
return factories;
}
public List<String> getGroupNames() {
return this.groupNames;
}
/*
* public void setGroupEnabled(final String groupName, final boolean enabled) { final
* List<Component> components = getGroup(groupName); for (final Component component : components)
* { component.setEnabled(enabled); } }
*/
public Map<String, List<ComponentFactory<?>>> getGroups() {
return this.groups;
}
@Override
public Icon getIcon() {
return null;
}
@Override
public String getIconName() {
return null;
}
public int getItemCount() {
int count = 0;
for (final List<ComponentFactory<?>> factories : this.groups.values()) {
count += factories.size();
}
return count;
}
@Override
public String getName() {
return this.name;
}
@Override
public String getToolTip() {
return null;
}
@Override
public JMenu newComponent() {
return newJMenu();
}
public JMenu newJMenu() {
return newJMenu(false);
}
public JMenu newJMenu(final boolean forceEnable) {
final String name = this.name;
return newJMenu(name, forceEnable);
}
public JMenu newJMenu(final String name, final boolean forceEnable) {
final JMenu menu = new JMenu(name);
if (this.enableCheck != null) {
final boolean enabled = this.enableCheck.isEnabled();
menu.setEnabled(enabled);
}
boolean first = true;
for (final String groupName : this.groupNames) {
final List<ComponentFactory<?>> factories = this.groups.get(groupName);
if (!factories.isEmpty()) {
if (first) {
first = false;
} else {
menu.addSeparator();
}
for (final ComponentFactory<?> factory : factories) {
final Component component = factory.newComponent();
if (component != null) {
if (forceEnable) {
component.setEnabled(true);
}
menu.add(component);
}
}
}
}
return menu;
}
public BaseJPopupMenu newJPopupMenu() {
return newJPopupMenu(false);
}
public BaseJPopupMenu newJPopupMenu(final boolean forceEnable) {
final BaseJPopupMenu menu = new BaseJPopupMenu(this.name);
if (this.enableCheck != null) {
final boolean enabled = this.enableCheck.isEnabled();
menu.setEnabled(enabled);
}
boolean first = true;
for (final String groupName : this.groupNames) {
boolean groupHasItem = false;
final List<ComponentFactory<?>> factories = this.groups.get(groupName);
if (!Property.isEmpty(factories)) {
for (final ComponentFactory<?> factory : factories) {
final Component component = factory.newComponent();
if (component != null) {
if (forceEnable) {
component.setEnabled(true);
}
if (!groupHasItem) {
groupHasItem = true;
if (first) {
first = false;
} else {
menu.addSeparator();
}
}
menu.add(component);
if (component instanceof JMenu) {
final JMenu childMenu = (JMenu)component;
if (childMenu.getMenuComponentCount() == 0) {
childMenu.setEnabled(false);
}
}
}
}
}
}
return menu;
}
public void setEnableCheck(final EnableCheck enableCheck) {
this.enableCheck = enableCheck;
}
public void setName(final String name) {
this.name = name;
}
public void showMenu(final Component component, final int x, final int y) {
final JPopupMenu menu = newJPopupMenu();
showMenu(menu, component, x, y);
}
public void showMenu(final Object source, final Component component, final int x, final int y) {
setMenuSource(source);
showMenu(component, x, y);
}
public boolean showMenu(final Object source, final MouseEvent e) {
if (e.isPopupTrigger() && !e.isConsumed()) {
final Component component = e.getComponent();
final int x = e.getX();
final int y = e.getY();
showMenu(source, component, x + 5, y);
return true;
} else {
return false;
}
}
@Override
public String toString() {
if (Property.hasValue(this.name)) {
return this.name;
} else {
return super.toString();
}
}
}