/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package org.esa.snap.ui;
import org.esa.snap.core.util.Guardian;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Arrays;
/**
* A handler which can be registered on components as a mouse listener.
* <p>This handler pops-up a popup menu if the corresponding event is a popup menu trigger on a given platform. The
* popup-menu is created by the <code>PopupMenuFactory</code> instance passed to the constructor of this class.
*
* @author Norman Fomferra
* @see PopupMenuFactory
*/
public class PopupMenuHandler implements MouseListener, KeyListener {
private PopupMenuFactory popupMenuFactory;
/**
* Constructs a new pop-up menu handler for th given pop-up menu factory.
*
* @param popupMenuFactory the factory for the menu, must not be <code>null</code>
*/
public PopupMenuHandler(PopupMenuFactory popupMenuFactory) {
Guardian.assertNotNull("popupMenuFactory", popupMenuFactory);
this.popupMenuFactory = popupMenuFactory;
}
/**
* Invoked when the mouse has been clicked on a component.
*/
public void mouseClicked(MouseEvent event) {
maybeShowPopupMenu(event);
}
/**
* Invoked when a mouse button has been pressed on a component.
*/
public void mousePressed(MouseEvent event) {
maybeShowPopupMenu(event);
}
/**
* Invoked when a mouse button has been released on a component.
*/
public void mouseReleased(MouseEvent event) {
maybeShowPopupMenu(event);
}
/**
* Invoked when the mouse enters a component.
*/
public void mouseEntered(MouseEvent event) {
}
/**
* Invoked when the mouse exits a component.
*/
public void mouseExited(MouseEvent event) {
}
/**
* Invoked when a key has been pressed.
*/
public void keyPressed(KeyEvent event) {
}
/**
* Invoked when a key has been released.
*/
public void keyReleased(KeyEvent event) {
}
/**
* Invoked when a key has been typed. This event occurs when a key press is followed by a key release.
*/
public void keyTyped(KeyEvent event) {
}
private void maybeShowPopupMenu(MouseEvent event) {
if (event.isPopupTrigger()) {
if (event.getComponent().isVisible()) {
showPopupMenu(event);
}
}
}
private void showPopupMenu(MouseEvent event) {
JPopupMenu popupMenu = popupMenuFactory.createPopupMenu(event.getComponent());
if (popupMenu == null) {
popupMenu = popupMenuFactory.createPopupMenu(event);
}
if (popupMenu != null) {
rearrangeMenuItems(popupMenu);
UIUtils.showPopup(popupMenu, event);
}
}
private void rearrangeMenuItems(JPopupMenu popupMenu) {
Component[] components = popupMenu.getComponents();
Arrays.sort(components, (o1, o2) -> getGroupName(o1).compareToIgnoreCase(getGroupName(o2)));
popupMenu.removeAll();
Component lastComponent = null;
String lastGroupName = null;
for (Component component : components) {
String groupName = getGroupName(component);
if (lastGroupName != null
&& !lastGroupName.equals(groupName)
&& !(lastComponent instanceof JSeparator)) {
popupMenu.addSeparator();
}
lastGroupName = groupName;
lastComponent = component;
if (component instanceof JMenuItem) {
popupMenu.add((JMenuItem) component);
} else if (component instanceof Action) {
popupMenu.add((Action) component);
} else {
popupMenu.add(component);
}
}
}
private String getGroupName(Component component) {
Action action = null;
if (component instanceof AbstractButton) {
action = ((AbstractButton) component).getAction();
} else if (component instanceof Action) {
action = (Action) component;
}
if (action != null) {
Object parent = action.getValue("parent");
if (parent != null) {
return parent.toString();
}
}
return "";
}
}