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

339 lines
9.4 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 java.io.File;
2016-09-29 03:00:29 +02:00
import java.io.InputStream;
import java.net.URL;
2016-10-03 20:13:00 +02:00
import java.util.concurrent.atomic.AtomicReference;
2016-09-29 03:00:29 +02:00
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuEntry;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.SystemTrayMenuAction;
2016-09-29 03:00:29 +02:00
import dorkbox.systemTray.util.ImageUtils;
2016-10-03 20:13:00 +02:00
import dorkbox.util.OS;
import dorkbox.util.SwingUtil;
2016-09-29 03:00:29 +02:00
// this is a weird composite class, because it must be a Menu, but ALSO a MenuEntry -- so it has both
public
2016-09-29 03:00:29 +02:00
class SwingMenu extends Menu implements MenuEntry {
volatile JComponent _native;
2016-09-29 03:00:29 +02:00
// this have to be volatile, because they can be changed from any thread
private volatile String text;
private volatile boolean hasLegitIcon = false;
/**
* @param systemTray
* the system tray (which is the object that sits in the system tray)
* @param parent
* the parent of this menu, null if the parent is the system tray
*/
public
SwingMenu(final SystemTray systemTray, final Menu parent) {
super(systemTray, parent);
SwingUtil.invokeAndWait(new Runnable() {
@Override
public
void run() {
2016-09-29 03:00:29 +02:00
if (parent != null) {
if (OS.isLinux()) {
_native = new AdjustedJMenu((SwingSystemTrayLinuxMenuPopup)((SwingMenu) systemTray.getMenu())._native);
2016-10-03 20:13:00 +02:00
}
else {
_native = new AdjustedJMenu(null);
2016-10-03 20:13:00 +02:00
}
2016-09-29 03:00:29 +02:00
((SwingMenu) parent)._native.add(_native);
} else {
// when we are the system tray
2016-10-03 20:13:00 +02:00
if (OS.isLinux()) {
_native = new SwingSystemTrayLinuxMenuPopup();
} else {
_native = new SwingSystemTrayMenuPopup();
}
2016-09-29 03:00:29 +02:00
}
}
});
}
protected
void dispatch(final Runnable runnable) {
// this will properly check if we are running on the EDT
SwingUtil.invokeLater(runnable);
}
2016-10-03 20:13:00 +02:00
protected
void dispatchAndWait(final Runnable runnable) {
// this will properly check if we are running on the EDT
SwingUtil.invokeAndWait(runnable);
}
@Override
public
2016-10-03 20:13:00 +02:00
void addSeparator() {
dispatch(new Runnable() {
@Override
public
void run() {
synchronized (menuEntries) {
synchronized (menuEntries) {
2016-10-03 20:13:00 +02:00
MenuEntry menuEntry = new SwingEntrySeparator(SwingMenu.this);
menuEntries.add(menuEntry);
}
}
}
});
}
/**
* Enables, or disables the sub-menu entry.
*/
@Override
public
void setEnabled(final boolean enabled) {
_native.setEnabled(enabled);
}
/**
* Will add a new menu entry, or update one if it already exists
*/
protected
2016-10-03 20:13:00 +02:00
MenuEntry addEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) {
if (menuText == null) {
throw new NullPointerException("Menu text cannot be null");
}
2016-10-03 20:13:00 +02:00
final AtomicReference<MenuEntry> value = new AtomicReference<MenuEntry>();
dispatchAndWait(new Runnable() {
@Override
public
void run() {
synchronized (menuEntries) {
2016-10-03 20:13:00 +02:00
MenuEntry menuEntry = get(menuText);
2016-10-03 20:13:00 +02:00
if (menuEntry == null) {
// must always be called on the EDT
2016-10-03 20:13:00 +02:00
menuEntry = new SwingEntryItem(SwingMenu.this, callback);
menuEntry.setText(menuText);
menuEntry.setImage(imagePath);
menuEntries.add(menuEntry);
2016-10-03 20:13:00 +02:00
} else if (menuEntry instanceof SwingEntryItem) {
menuEntry.setText(menuText);
menuEntry.setImage(imagePath);
}
2016-10-03 20:13:00 +02:00
value.set(menuEntry);
}
}
});
2016-10-03 20:13:00 +02:00
return value.get();
}
2016-09-29 03:00:29 +02:00
2016-10-03 20:13:00 +02:00
/**
* Will add a new sub-menu entry, or update one if it already exists
*/
protected
Menu addMenu_(final String menuText, final File imagePath) {
if (menuText == null) {
throw new NullPointerException("Menu text cannot be null");
}
final AtomicReference<Menu> value = new AtomicReference<Menu>();
2016-09-29 03:00:29 +02:00
2016-10-03 20:13:00 +02:00
dispatchAndWait(new Runnable() {
@Override
public
void run() {
synchronized (menuEntries) {
MenuEntry menuEntry = get(menuText);
2016-09-29 03:00:29 +02:00
2016-10-03 20:13:00 +02:00
if (menuEntry == null) {
// must always be called on the EDT
menuEntry = new SwingMenu(getSystemTray(), SwingMenu.this);
menuEntry.setText(menuText);
menuEntry.setImage(imagePath);
value.set((Menu)menuEntry);
} else if (menuEntry instanceof SwingMenu) {
menuEntry.setText(menuText);
menuEntry.setImage(imagePath);
}
menuEntries.add(menuEntry);
}
}
});
return value.get();
}
2016-09-29 03:00:29 +02:00
// always called in the EDT
void renderText(final String text) {
((JMenuItem) _native).setText(text);
}
@SuppressWarnings("Duplicates")
void setImage_(final File imageFile) {
hasLegitIcon = imageFile != null;
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
if (imageFile != null) {
ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath());
((JMenuItem) _native).setIcon(origIcon);
}
else {
((JMenuItem) _native).setIcon(null);
}
}
});
}
@Override
public
String getText() {
return text;
}
@Override
public
void setText(final String newText) {
text = newText;
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
renderText(newText);
}
});
}
@Override
public
void setImage(final File imageFile) {
if (imageFile == null) {
setImage_(null);
}
else {
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageFile));
}
}
@Override
public final
void setImage(final String imagePath) {
if (imagePath == null) {
setImage_(null);
}
else {
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath));
}
}
@Override
public final
void setImage(final URL imageUrl) {
if (imageUrl == null) {
setImage_(null);
}
else {
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl));
}
}
@Override
public final
void setImage(final String cacheName, final InputStream imageStream) {
if (imageStream == null) {
setImage_(null);
}
else {
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream));
}
}
@Override
public final
void setImage(final InputStream imageStream) {
if (imageStream == null) {
setImage_(null);
}
else {
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream));
}
}
@Override
public
boolean hasImage() {
return hasLegitIcon;
}
@Override
public
void setCallback(final SystemTrayMenuAction callback) {
}
@Override
public final
void remove() {
SwingUtil.invokeAndWait(new Runnable() {
@Override
public
void run() {
_native.setVisible(false);
if (_native instanceof SwingSystemTrayMenuPopup) {
((SwingSystemTrayMenuPopup) _native).close();
}
else if (_native instanceof SwingSystemTrayLinuxMenuPopup) {
((SwingSystemTrayLinuxMenuPopup) _native).close();
}
SwingMenu parent = (SwingMenu) getParent();
if (parent != null) {
parent._native.remove(_native);
}
2016-09-29 03:00:29 +02:00
}
});
}
/*
* Called when this menu is removed from it's parent menu
*/
protected
void removePrivate() {
remove();
}
}