package org.rr.commons.swing.components.button;
import java.awt.Point;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.Popup;
import javax.swing.PopupFactory;
/**
* This is a workaround for the JPopupMenu for using heavy weight popups
* only. Ubunutu 8.10 and 9.04 did not show light weight popups.
*/
public class JMediumWeightPopupMenu extends JPopupMenu {
private static final long serialVersionUID = 3651666432810622912L;
private int desiredLocationX;
private int desiredLocationY;
/**
* Key used to indicate a light weight popup should be used.
*/
static final int LIGHT_WEIGHT_POPUP = 0;
/**
* Key used to indicate a medium weight Popup should be used.
*/
static final int MEDIUM_WEIGHT_POPUP = 1;
/*
* Key used to indicate a heavy weight Popup should be used.
*/
static final int HEAVY_WEIGHT_POPUP = 2;
/**
* Sets the location of the upper left corner of the
* popup menu using x, y coordinates.
*
* @param x the x coordinate of the popup's new position
* in the screen's coordinate space
* @param y the y coordinate of the popup's new position
* in the screen's coordinate space
* @beaninfo
* description: The location of the popup menu.
*/
public void setLocation(int x, int y) {
desiredLocationX = x;
desiredLocationY = y;
super.setLocation(x, y);
}
/**
* Sets the visibility of the popup menu.
*
* @param b true to make the popup visible, or false to
* hide it
* @beaninfo
* bound: true
* description: Makes the popup visible
*/
public void setVisible(boolean b) {
// Is it a no-op?
if (b == isVisible())
return;
// if closing, first close all Submenus
if (b == false) {
// 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is
// a protected method and cannot be called from BasicPopupMenuUI directly
// The real solution could be to make
// firePopupMenuCanceled public and call it directly.
Boolean doCanceled = (Boolean)getClientProperty("JPopupMenu.firePopupMenuCanceled");
if (doCanceled != null && doCanceled == Boolean.TRUE) {
putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE);
firePopupMenuCanceled();
}
getSelectionModel().clearSelection();
} else {
// This is a popup menu with MenuElement children,
// set selection path before popping up!
if (isPopupMenu()) {
MenuElement me[] = new MenuElement[1];
me[0] = (MenuElement) this;
MenuSelectionManager.defaultManager().setSelectedPath(me);
}
}
if(b) {
firePopupMenuWillBecomeVisible();
Popup popup = getPopup();
setPopupField("popup", popup);
firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE);
} else {
super.setVisible(b);
}
}
private void setPopupField(String fieldName, Popup popup) {
try {
Field field = JPopupMenu.class.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(this, popup);
}catch (Exception e) {
e.printStackTrace();
}
}
private Popup getPopupField(String fieldName) {
try {
Field field = JPopupMenu.class.getDeclaredField(fieldName);
field.setAccessible(true);
field.get(this);
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Returns true if the popup menu is a standalone popup menu
* rather than the submenu of a <code>JMenu</code>.
*
* @return true if this menu is a standalone popup menu, otherwise false
*/
private boolean isPopupMenu() {
try {
Method method = JPopupMenu.class.getDeclaredMethod("isPopupMenu", new Class[0]);
method.setAccessible(true);
return ((Boolean)method.invoke(this, new Object[0])).booleanValue();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* Returns an point which has been adjusted to take into account of the
* desktop bounds, taskbar and multi-monitor configuration.
* <p>
* This adustment may be cancelled by invoking the application with
* -Djavax.swing.adjustPopupLocationToFit=false
*/
Point adjustPopupLocationToFitScreen2(int xposition, int yposition) {
try {
Method method = JPopupMenu.class.getDeclaredMethod("adjustPopupLocationToFitScreen", new Class[] {int.class, int.class});
method.setAccessible(true);
return (Point)method.invoke(this, new Object[] {Integer.valueOf(xposition), Integer.valueOf(yposition)});
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Provides a hint as to the type of <code>Popup</code> that should
* be created.
*/
void setPopupType(int type, PopupFactory popupFactory) {
try {
Method method = PopupFactory.class.getDeclaredMethod("setPopupType", new Class[] {int.class});
method.setAccessible(true);
method.invoke(popupFactory, new Object[] {Integer.valueOf(type)});
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns a <code>Popup</code> instance from the
* <code>PopupMenuUI</code> that has had <code>show</code> invoked on
* it. If the current <code>popup</code> is non-null,
* this will invoke <code>dispose</code> of it, and then
* <code>show</code> the new one.
* <p>
* This does NOT fire any events, it is up the caller to dispatch
* the necessary events.
*/
private Popup getPopup() {
Popup oldPopup = getPopupField("popup");
if (oldPopup != null) {
oldPopup.hide();
}
PopupFactory popupFactory = PopupFactory.getSharedInstance();
setPopupType(HEAVY_WEIGHT_POPUP, popupFactory);
// adjust the location of the popup
Point p = adjustPopupLocationToFitScreen2(desiredLocationX,desiredLocationY);
desiredLocationX = p.x;
desiredLocationY = p.y;
Popup newPopup = getUI().getPopup(this, desiredLocationX,
desiredLocationY);
// popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
newPopup.show();
return newPopup;
}
}