package sun.lwawt.macosx;
import java.awt.AWTEvent;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.PopupMenu;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.peer.TrayIconPeer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.swing.Icon;
import javax.swing.UIManager;
import sun.awt.SunToolkit;
import static sun.awt.AWTAccessor.MenuComponentAccessor;
import static sun.awt.AWTAccessor.getMenuComponentAccessor;
public class CTrayIcon extends CFRetainedResource implements TrayIconPeer {
private TrayIcon target;
private PopupMenu ;
private final Frame dummyFrame;
IconObserver observer = new IconObserver();
private static int mouseClickButtons = 0;
private final static boolean useTemplateImages = AccessController.doPrivileged((PrivilegedAction<Boolean>)
() -> Boolean.getBoolean("apple.awt.enableTemplateImages")
);
CTrayIcon(TrayIcon target) {
super(0, true);
this.target = target;
this.popup = target.getPopupMenu();
this.dummyFrame = new Frame();
setPtr(createModel());
checkAndCreatePopupPeer();
updateImage();
}
private CPopupMenu checkAndCreatePopupPeer() {
CPopupMenu menuPeer = null;
if (popup != null) {
try {
final MenuComponentAccessor acc = getMenuComponentAccessor();
menuPeer = acc.getPeer(popup);
if (menuPeer == null) {
popup.addNotify();
menuPeer = acc.getPeer(popup);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return menuPeer;
}
private long createModel() {
return nativeCreate();
}
private native long nativeCreate();
public long () {
PopupMenu newPopup = target.getPopupMenu();
if (popup == newPopup) {
if (popup == null) {
return 0L;
}
} else {
if (newPopup != null) {
if (popup != null) {
popup.removeNotify();
popup = newPopup;
} else {
popup = newPopup;
}
} else {
return 0L;
}
}
return checkAndCreatePopupPeer().ptr;
}
public void displayMessage(final String caption, final String text,
final String messageType) {
Icon icon = getIconForMessageType(messageType);
CImage cimage = null;
if (icon != null) {
BufferedImage image = scaleIcon(icon, 0.75);
cimage = CImage.getCreator().createFromImage(image, null);
}
if (cimage != null) {
cimage.execute(imagePtr -> {
execute(ptr -> nativeShowNotification(ptr, caption, text,
imagePtr));
});
} else {
execute(ptr -> nativeShowNotification(ptr, caption, text, 0));
}
}
@Override
public void dispose() {
dummyFrame.dispose();
if (popup != null) {
popup.removeNotify();
}
LWCToolkit.targetDisposedPeer(target, this);
target = null;
super.dispose();
}
@Override
public void setToolTip(String tooltip) {
execute(ptr -> nativeSetToolTip(ptr, tooltip));
}
private native void nativeSetToolTip(long trayIconModel, String tooltip);
@Override
public void (int x, int y) {
}
@Override
public void updateImage() {
Image image = target.getImage();
if (image != null) {
updateNativeImage(image);
}
}
void updateNativeImage(Image image) {
MediaTracker tracker = new MediaTracker(new Button(""));
tracker.addImage(image, 0);
try {
tracker.waitForAll();
} catch (InterruptedException ignore) { }
if (image.getWidth(null) <= 0 ||
image.getHeight(null) <= 0)
{
return;
}
CImage cimage = CImage.getCreator().createFromImage(image, observer);
boolean imageAutoSize = target.isImageAutoSize();
if (cimage != null) {
cimage.execute(imagePtr -> {
execute(ptr -> {
setNativeImage(ptr, imagePtr, imageAutoSize, useTemplateImages);
});
});
}
}
private native void setNativeImage(final long model, final long nsimage, final boolean autosize, final boolean template);
private void postEvent(final AWTEvent event) {
SunToolkit.executeOnEventHandlerThread(target, new Runnable() {
public void run() {
SunToolkit.postEvent(SunToolkit.targetToAppContext(target), event);
}
});
}
private void handleMouseEvent(NSEvent nsEvent) {
int buttonNumber = nsEvent.getButtonNumber();
final SunToolkit tk = (SunToolkit)Toolkit.getDefaultToolkit();
if ((buttonNumber > 2 && !tk.areExtraMouseButtonsEnabled())
|| buttonNumber > tk.getNumberOfButtons() - 1) {
return;
}
int jeventType = NSEvent.nsToJavaEventType(nsEvent.getType());
int jbuttonNumber = MouseEvent.NOBUTTON;
int jclickCount = 0;
if (jeventType != MouseEvent.MOUSE_MOVED) {
jbuttonNumber = NSEvent.nsToJavaButton(buttonNumber);
jclickCount = nsEvent.getClickCount();
}
int jmodifiers = NSEvent.nsToJavaModifiers(
nsEvent.getModifierFlags());
boolean isPopupTrigger = NSEvent.isPopupTrigger(jmodifiers);
int eventButtonMask = (jbuttonNumber > 0)?
MouseEvent.getMaskForButton(jbuttonNumber) : 0;
long when = System.currentTimeMillis();
if (jeventType == MouseEvent.MOUSE_PRESSED) {
mouseClickButtons |= eventButtonMask;
} else if (jeventType == MouseEvent.MOUSE_DRAGGED) {
mouseClickButtons = 0;
}
int absX = nsEvent.getAbsX();
int absY = nsEvent.getAbsY();
MouseEvent mouseEvent = new MouseEvent(dummyFrame, jeventType, when,
jmodifiers, absX, absY, absX, absY, jclickCount, isPopupTrigger,
jbuttonNumber);
mouseEvent.setSource(target);
postEvent(mouseEvent);
if (jeventType == MouseEvent.MOUSE_PRESSED && isPopupTrigger) {
final String cmd = target.getActionCommand();
final ActionEvent event = new ActionEvent(target,
ActionEvent.ACTION_PERFORMED, cmd);
postEvent(event);
}
if (jeventType == MouseEvent.MOUSE_RELEASED) {
if ((mouseClickButtons & eventButtonMask) != 0) {
MouseEvent clickEvent = new MouseEvent(dummyFrame,
MouseEvent.MOUSE_CLICKED, when, jmodifiers, absX, absY,
absX, absY, jclickCount, isPopupTrigger, jbuttonNumber);
clickEvent.setSource(target);
postEvent(clickEvent);
}
mouseClickButtons &= ~eventButtonMask;
}
}
private native void nativeShowNotification(long trayIconModel,
String caption, String text,
long nsimage);
private native Point2D nativeGetIconLocation(long trayIconModel);
private static BufferedImage scaleIcon(Icon icon, double scaleFactor) {
if (icon == null) {
return null;
}
int w = icon.getIconWidth();
int h = icon.getIconHeight();
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
BufferedImage iconImage = gc.createCompatibleImage(w, h,
Transparency.TRANSLUCENT);
Graphics2D g = iconImage.createGraphics();
icon.paintIcon(null, g, 0, 0);
g.dispose();
int scaledW = (int) (w * scaleFactor);
int scaledH = (int) (h * scaleFactor);
BufferedImage scaledImage = gc.createCompatibleImage(scaledW, scaledH,
Transparency.TRANSLUCENT);
g = scaledImage.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(iconImage, 0, 0, scaledW, scaledH, null);
g.dispose();
return scaledImage;
}
private static Icon getIconForMessageType(String messageType) {
if (messageType.equals("ERROR")) {
return UIManager.getIcon("OptionPane.errorIcon");
} else if (messageType.equals("WARNING")) {
return UIManager.getIcon("OptionPane.warningIcon");
} else {
return UIManager.getIcon("OptionPane.informationIcon");
}
}
class IconObserver implements ImageObserver {
@Override
public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height) {
if (target == null || image != target.getImage())
{
return false;
}
if ((flags & (ImageObserver.FRAMEBITS | ImageObserver.ALLBITS |
ImageObserver.WIDTH | ImageObserver.HEIGHT)) != 0)
{
SunToolkit.executeOnEventHandlerThread(target, new Runnable() {
public void run() {
updateNativeImage(image);
}
});
}
return (flags & ImageObserver.ALLBITS) == 0;
}
}
}