SystemTray/src/dorkbox/systemTray/swing/SwingMenuEntry.java

289 lines
8.6 KiB
Java
Raw Normal View History

/*
* Copyright 2014 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.systemTray.swing;
import dorkbox.systemTray.ImageUtil;
import dorkbox.systemTray.MenuEntry;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.SystemTrayMenuAction;
import dorkbox.util.SwingUtil;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import javax.swing.UIManager;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
class SwingMenuEntry implements MenuEntry {
private static final String tempDirPath = ImageUtil.TEMP_DIR.getAbsolutePath();
private final SwingSystemTrayMenuPopup parent;
private final SystemTray systemTray;
private final JMenuItem menuItem;
private final ActionListener swingCallback;
private volatile String text;
private volatile SystemTrayMenuAction callback;
private int iconHeight = -1;
SwingMenuEntry(final SwingSystemTrayMenuPopup parentMenu, final String label, final String imagePath, final SystemTrayMenuAction callback,
final SystemTray systemTray) {
this.parent = parentMenu;
this.text = label;
this.callback = callback;
this.systemTray = systemTray;
swingCallback = new ActionListener() {
@Override
public
void actionPerformed(ActionEvent e) {
// we want it to run on the EDT
handle();
}
};
menuItem = new JMenuItem(label);
menuItem.addActionListener(swingCallback);
if (imagePath != null && !imagePath.isEmpty()) {
setImageIcon(imagePath);
}
parentMenu.add(menuItem);
}
private
void handle() {
SystemTrayMenuAction cb = this.callback;
if (cb != null) {
cb.onClick(systemTray, this);
}
}
@Override
public
String getText() {
return text;
}
@Override
public
void setText(final String newText) {
this.text = newText;
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
menuItem.setText(newText);
}
});
}
private
void setImage_(final String imagePath) {
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
setImageIcon(imagePath);
}
});
}
private
void setImageIcon(final String imagePath) {
if (imagePath != null && !imagePath.isEmpty()) {
if (iconHeight != 0) {
// this will (and should) be the correct size for the system. On the systems tested, it was 16
// see: http://en-human-begin.blogspot.de/2007/11/javas-icons-by-default.html
Icon icon = UIManager.getIcon("FileView.fileIcon");
iconHeight = icon.getIconHeight();
}
ImageIcon origIcon = new ImageIcon(imagePath);
int origIconHeight = origIcon.getIconHeight();
int origIconWidth = origIcon.getIconWidth();
int savedIconHeight = this.iconHeight;
// it is necessary to resize this icon, so that it matches what our preferred size is for icons
if (origIconHeight != savedIconHeight && savedIconHeight != 0) {
//noinspection SuspiciousNameCombination
Dimension scaledDimension = getScaledDimension(origIconWidth, origIconHeight, savedIconHeight, savedIconHeight);
Image image = origIcon.getImage();
// scale it the smoothly
Image newImage = image.getScaledInstance(scaledDimension.width, scaledDimension.height, java.awt.Image.SCALE_SMOOTH);
origIcon = new ImageIcon(newImage);
// save it to temp spot on disk (so we don't have to KEEP on doing this). (but it MUST be the temp location, otherwise
// it's always 'on the fly')
if (imagePath.startsWith(tempDirPath)) {
// have to delete the old one
File file = new File(imagePath);
boolean delete = file.delete();
if (delete) {
// now write out the new one
String extension = ImageUtil.getExtension(imagePath);
if (extension.equals("")) {
extension = "png"; // made up
}
BufferedImage bufferedImage = getBufferedImage(image);
try {
ImageIO.write(bufferedImage, extension, file);
} catch (IOException e) {
// this shouldn't happen, but you never know...
e.printStackTrace();
}
}
}
}
menuItem.setIcon(origIcon);
}
else {
menuItem.setIcon(null);
}
}
@Override
public
void setImage(final String imagePath) {
if (imagePath == null) {
setImage_(null);
}
else {
setImage_(ImageUtil.iconPath(imagePath));
}
}
@Override
public
void setImage(final URL imageUrl) {
if (imageUrl == null) {
setImage_(null);
}
else {
setImage_(ImageUtil.iconPath(imageUrl));
}
}
@Override
public
void setImage(final String cacheName, final InputStream imageStream) {
if (imageStream == null) {
setImage_(null);
}
else {
setImage_(ImageUtil.iconPath(cacheName, imageStream));
}
}
@Override
@Deprecated
public
void setImage(final InputStream imageStream) {
if (imageStream == null) {
setImage_(null);
}
else {
setImage_(ImageUtil.iconPathNoCache(imageStream));
}
}
@Override
public
void setCallback(final SystemTrayMenuAction callback) {
this.callback = callback;
}
@Override
public
void remove() {
SwingUtil.invokeAndWait(new Runnable() {
@Override
public
void run() {
menuItem.removeActionListener(swingCallback);
parent.remove(menuItem);
}
});
}
private static
Dimension getScaledDimension(int originalWidth, int originalHeight, int boundWidth, int boundHeight) {
//this function comes from http://stackoverflow.com/questions/10245220/java-image-resize-maintain-aspect-ratio
int newWidth = originalWidth;
int newHeight = originalHeight;
// first check if we need to scale width
if (originalWidth > boundWidth) {
//scale width to fit
newWidth = boundWidth;
//scale height to maintain aspect ratio
newHeight = (newWidth * originalHeight) / originalWidth;
}
// then check if we need to scale even with the new height
if (newHeight > boundHeight) {
//scale height to fit instead
newHeight = boundHeight;
//scale width to maintain aspect ratio
newWidth = (newHeight * originalWidth) / originalHeight;
}
return new Dimension(newWidth, newHeight);
}
private static
BufferedImage getBufferedImage(Image image) {
if (image instanceof BufferedImage) {
return (BufferedImage) image;
}
BufferedImage bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(image, 0, 0, null);
bGr.dispose();
// Return the buffered image
return bimage;
}
}