2015-11-09 16:13:57 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2016-02-12 01:51:15 +01:00
|
|
|
package dorkbox.systemTray.swing;
|
2015-11-09 16:13:57 +01:00
|
|
|
|
2016-02-12 01:51:15 +01:00
|
|
|
import dorkbox.systemTray.ImageUtil;
|
|
|
|
import dorkbox.systemTray.MenuEntry;
|
|
|
|
import dorkbox.systemTray.SystemTray;
|
|
|
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
2015-11-09 16:13:57 +01:00
|
|
|
import dorkbox.util.SwingUtil;
|
|
|
|
|
2016-02-21 00:56:30 +01:00
|
|
|
import javax.imageio.ImageIO;
|
|
|
|
import javax.swing.Icon;
|
2015-11-09 16:13:57 +01:00
|
|
|
import javax.swing.ImageIcon;
|
|
|
|
import javax.swing.JMenuItem;
|
2016-02-21 00:56:30 +01:00
|
|
|
import javax.swing.UIManager;
|
|
|
|
import java.awt.Dimension;
|
|
|
|
import java.awt.Graphics2D;
|
|
|
|
import java.awt.Image;
|
2015-11-09 16:13:57 +01:00
|
|
|
import java.awt.event.ActionEvent;
|
|
|
|
import java.awt.event.ActionListener;
|
2016-02-21 00:56:30 +01:00
|
|
|
import java.awt.image.BufferedImage;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
2016-02-12 01:51:15 +01:00
|
|
|
import java.io.InputStream;
|
|
|
|
import java.net.URL;
|
2015-11-09 16:13:57 +01:00
|
|
|
|
|
|
|
class SwingMenuEntry implements MenuEntry {
|
2016-02-21 00:56:30 +01:00
|
|
|
private static final String tempDirPath = ImageUtil.TEMP_DIR.getAbsolutePath();
|
|
|
|
|
2016-02-13 15:06:19 +01:00
|
|
|
private final SwingSystemTrayMenuPopup parent;
|
2015-11-09 16:13:57 +01:00
|
|
|
private final SystemTray systemTray;
|
|
|
|
private final JMenuItem menuItem;
|
|
|
|
private final ActionListener swingCallback;
|
|
|
|
|
|
|
|
private volatile String text;
|
|
|
|
private volatile SystemTrayMenuAction callback;
|
|
|
|
|
2016-02-21 00:56:30 +01:00
|
|
|
private int iconHeight = -1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-13 15:06:19 +01:00
|
|
|
SwingMenuEntry(final SwingSystemTrayMenuPopup parentMenu, final String label, final String imagePath, final SystemTrayMenuAction callback,
|
2015-11-09 16:13:57 +01:00
|
|
|
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()) {
|
2016-02-21 00:56:30 +01:00
|
|
|
setImageIcon(imagePath);
|
2015-11-09 16:13:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-02-12 01:51:15 +01:00
|
|
|
private
|
|
|
|
void setImage_(final String imagePath) {
|
2015-11-09 16:13:57 +01:00
|
|
|
SwingUtil.invokeLater(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public
|
|
|
|
void run() {
|
2016-02-21 00:56:30 +01:00
|
|
|
setImageIcon(imagePath);
|
2015-11-09 16:13:57 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:56:30 +01:00
|
|
|
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
|
2016-02-21 01:52:55 +01:00
|
|
|
String extension = ImageUtil.getExtension(imagePath);
|
|
|
|
if (extension.equals("")) {
|
|
|
|
extension = "png"; // made up
|
2016-02-21 00:56:30 +01:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-12 01:51:15 +01:00
|
|
|
@Override
|
|
|
|
public
|
2016-02-14 17:49:02 +01:00
|
|
|
void setImage(final String imagePath) {
|
2016-02-12 01:51:15 +01:00
|
|
|
if (imagePath == null) {
|
|
|
|
setImage_(null);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setImage_(ImageUtil.iconPath(imagePath));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public
|
2016-02-14 17:49:02 +01:00
|
|
|
void setImage(final URL imageUrl) {
|
2016-02-12 01:51:15 +01:00
|
|
|
if (imageUrl == null) {
|
|
|
|
setImage_(null);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setImage_(ImageUtil.iconPath(imageUrl));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public
|
2016-02-14 17:49:02 +01:00
|
|
|
void setImage(final String cacheName, final InputStream imageStream) {
|
2016-02-12 01:51:15 +01:00
|
|
|
if (imageStream == null) {
|
|
|
|
setImage_(null);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setImage_(ImageUtil.iconPath(cacheName, imageStream));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Deprecated
|
|
|
|
public
|
2016-02-14 17:49:02 +01:00
|
|
|
void setImage(final InputStream imageStream) {
|
2016-02-12 01:51:15 +01:00
|
|
|
if (imageStream == null) {
|
|
|
|
setImage_(null);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setImage_(ImageUtil.iconPathNoCache(imageStream));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-09 16:13:57 +01:00
|
|
|
@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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2016-02-21 00:56:30 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-11-09 16:13:57 +01:00
|
|
|
}
|