package org.limewire.ui.swing.util;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import org.limewire.util.OSUtils;
import com.google.inject.internal.cglib.proxy.Enhancer;
import com.google.inject.internal.cglib.proxy.MethodInterceptor;
import com.google.inject.internal.cglib.proxy.MethodProxy;
/**
* Hacks for some specific Swing vs OS issues.
*/
public class SwingHacks {
private static PopupMenuListener menuHackListener = null;
/**
* LWC-3970 : Popup menus grey at times (Windows)
*
* <p>Toggles popup visibility a few mills after shown.
* This is the only thing I have found to fix the problem since
* everything seems to function properly within java. The paints are
* called properly but sometimes the window just does not render on screen.
*/
public static void fixPopupMenuForWindows(JPopupMenu menu) {
if (!OSUtils.isWindows()) {
return;
}
if (menuHackListener == null) {
menuHackListener = new PopupMenuListener() {
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
final JPopupMenu menu = (JPopupMenu)e.getSource();
Timer flashTimer = new Timer(20, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!menu.isVisible()) {
return;
}
menu.removePopupMenuListener(menuHackListener);
menu.setVisible(false);
menu.setVisible(true);
menu.addPopupMenuListener(menuHackListener);
}
});
flashTimer.setRepeats(false);
flashTimer.start();
}
};
}
menu.addPopupMenuListener(menuHackListener);
}
/**
* LWC-3622 : Text field scrolling problem when using large insets
*
* <p> Hack to mitigate the problem where when using a text field with
* large horizontal insets you can't properly scroll back to the start
* with the cursor on certain input lengths. This hack does not completely
* correct the problem but will ensure the text field is still usuable in those
* strange cases. In the case where two characters are rendered unviewable (ie. skinny
* like i,l...) the first scroll back will remain stuck but the second will fix the problem.
*/
public static void fixTextFieldScrollClippingWithNonDefaultInsets(JTextField textComponent) {
textComponent.addCaretListener(new CaretListener() {
@Override
public void caretUpdate(CaretEvent e) {
JTextField component = (JTextField)e.getSource();
if (component.getScrollOffset() > 0 && component.getCaretPosition() == 0) {
component.setScrollOffset(0);
}
}
});
}
/**
* LWC-3706 : DnD on KDE broken (KDE wm)
*
* <p> Hack to fix one of the many Swing DnD + KDE bugs. Prevents
* drag and drop from being disabled after a cancelled drop by
* resetting the internal state after the initial null pointer.
*/
public static void fixDnDforKDE(final JComponent c) {
// TODO: should be KDE only, but is safe for Gnome in the meantime.
if (!OSUtils.isUnix()) {
return;
}
final DropTarget originalTarget = c.getDropTarget();
if (originalTarget == null ||
!originalTarget.getClass().getName()
.equals("javax.swing.TransferHandler$SwingDropTarget")) {
return;
}
// Create a proxy to the original SwingDropTarget that will catch NPE's,
// from dragOver() and reset the DropTarget.
Enhancer e = new Enhancer();
e.setSuperclass(DropTarget.class);
e.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
try {
Method originalMethod = originalTarget.getClass().getMethod(method.getName(), method.getParameterTypes());
originalMethod.setAccessible(true);
return originalMethod.invoke(originalTarget, args);
}
catch (NoSuchMethodException e) {
return null;
} catch (InvocationTargetException e) {
if (method.getName().equals("dragOver")) {
DropTargetDragEvent event = (DropTargetDragEvent)args[0];
originalTarget.dragExit(event);
originalTarget.dragEnter(event);
originalTarget.dragOver(event);
return null;
}
throw e;
}
}
});
c.setDropTarget((DropTarget)e.create());
}
}