Added WindowsNative system tray icon
This commit is contained in:
parent
55da0b3380
commit
2c004d2c46
294
src/dorkbox/systemTray/ui/swing/_WindowsNativeTray.java
Normal file
294
src/dorkbox/systemTray/ui/swing/_WindowsNativeTray.java
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 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.ui.swing;
|
||||||
|
|
||||||
|
import static com.sun.jna.platform.win32.WinDef.HWND;
|
||||||
|
import static com.sun.jna.platform.win32.WinDef.LPARAM;
|
||||||
|
import static com.sun.jna.platform.win32.WinDef.WPARAM;
|
||||||
|
import static com.sun.jna.platform.win32.WinUser.WM_QUIT;
|
||||||
|
import static dorkbox.util.jna.windows.Shell32.NIM_ADD;
|
||||||
|
import static dorkbox.util.jna.windows.Shell32.NIM_DELETE;
|
||||||
|
import static dorkbox.util.jna.windows.Shell32.NIM_MODIFY;
|
||||||
|
import static dorkbox.util.jna.windows.Shell32.Shell_NotifyIcon;
|
||||||
|
import static dorkbox.util.jna.windows.User32.WM_LBUTTONUP;
|
||||||
|
import static dorkbox.util.jna.windows.User32.WM_RBUTTONUP;
|
||||||
|
import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_SHELLNOTIFY;
|
||||||
|
import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_TASKBARCREATED;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
|
import com.sun.jna.platform.win32.WinDef.POINT;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.MenuItem;
|
||||||
|
import dorkbox.systemTray.SystemTray;
|
||||||
|
import dorkbox.systemTray.Tray;
|
||||||
|
import dorkbox.util.ImageUtil;
|
||||||
|
import dorkbox.util.SwingUtil;
|
||||||
|
import dorkbox.util.jna.windows.HBITMAPWrap;
|
||||||
|
import dorkbox.util.jna.windows.HICONWrap;
|
||||||
|
import dorkbox.util.jna.windows.Kernel32;
|
||||||
|
import dorkbox.util.jna.windows.Listener;
|
||||||
|
import dorkbox.util.jna.windows.Shell32;
|
||||||
|
import dorkbox.util.jna.windows.User32;
|
||||||
|
import dorkbox.util.jna.windows.WindowsEventDispatch;
|
||||||
|
import dorkbox.util.jna.windows.structs.NOTIFYICONDATA;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native implementation of a System tray on Windows with a Swing menu (the native menu is terrible)
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
class _WindowsNativeTray extends Tray {
|
||||||
|
private final Listener quitListener;
|
||||||
|
private final Listener menuListener;
|
||||||
|
private final Listener showListener;
|
||||||
|
private volatile TrayPopup popupMenu;
|
||||||
|
// is the system tray visible or not.
|
||||||
|
private volatile boolean visible = true;
|
||||||
|
private volatile File imageFile;
|
||||||
|
private volatile HICONWrap imageIcon;
|
||||||
|
private volatile String tooltipText = "";
|
||||||
|
|
||||||
|
public
|
||||||
|
_WindowsNativeTray(final SystemTray systemTray) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// we override various methods, because each tray implementation is SLIGHTLY different. This allows us customization.
|
||||||
|
final SwingMenu swingMenu = new SwingMenu(null, null) {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setImage(final MenuItem menuItem) {
|
||||||
|
imageFile = menuItem.getImage();
|
||||||
|
|
||||||
|
if (imageIcon != null) {
|
||||||
|
imageIcon.close();
|
||||||
|
}
|
||||||
|
imageIcon = convertImage(imageFile);
|
||||||
|
|
||||||
|
NOTIFYICONDATA nid = new NOTIFYICONDATA();
|
||||||
|
nid.hWnd = WindowsEventDispatch.get();
|
||||||
|
if (imageIcon != null) {
|
||||||
|
nid.setIcon(imageIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Shell32.Shell_NotifyIcon(NIM_MODIFY, nid)) {
|
||||||
|
SystemTray.logger.error("Error setting the image for the tray. {}", Kernel32.getLastErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't want to matter which (setImage/setTooltip/setEnabled) is done first, and if the image/enabled is changed, we
|
||||||
|
// want to make sure keep the tooltip text the same as before.
|
||||||
|
setTooltip_(tooltipText);
|
||||||
|
|
||||||
|
SwingUtil.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
if (popupMenu == null) {
|
||||||
|
TrayPopup popupMenu = (TrayPopup) _native;
|
||||||
|
popupMenu.pack();
|
||||||
|
popupMenu.setFocusable(true);
|
||||||
|
_WindowsNativeTray.this.popupMenu = popupMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
popupMenu.setTitleBarImage(imageFile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setEnabled(final MenuItem menuItem) {
|
||||||
|
boolean enabled = menuItem.getEnabled();
|
||||||
|
|
||||||
|
if (visible && !enabled) {
|
||||||
|
// hide
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
else if (!visible && enabled) {
|
||||||
|
// show
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setText(final MenuItem menuItem) {
|
||||||
|
// no op.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setShortcut(final MenuItem menuItem) {
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setTooltip(final MenuItem menuItem) {
|
||||||
|
final String text = menuItem.getTooltip();
|
||||||
|
|
||||||
|
setTooltip_(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void remove() {
|
||||||
|
hide();
|
||||||
|
|
||||||
|
super.remove();
|
||||||
|
|
||||||
|
User32.User32.PostMessage(WindowsEventDispatch.get(), WM_QUIT, new WPARAM(0), new LPARAM(0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// will wait until it's started up.
|
||||||
|
WindowsEventDispatch.start();
|
||||||
|
|
||||||
|
HWND hWnd = WindowsEventDispatch.get();
|
||||||
|
if (hWnd == null) {
|
||||||
|
throw new RuntimeException("The Windows System Tray is not supported! Please write an issue and include your OS type and configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
showListener = new Listener() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) {
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
quitListener = new Listener() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) {
|
||||||
|
WindowsEventDispatch.stop();
|
||||||
|
|
||||||
|
WindowsEventDispatch.removeListener(showListener);
|
||||||
|
WindowsEventDispatch.removeListener(quitListener);
|
||||||
|
WindowsEventDispatch.removeListener(menuListener);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
menuListener = new Listener() {
|
||||||
|
final POINT mousePosition = new POINT();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) {
|
||||||
|
int lp = lParam.intValue();
|
||||||
|
|
||||||
|
switch (lp) {
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
if (popupMenu != null && User32.User32.GetCursorPos(mousePosition)) {
|
||||||
|
Point point = new Point(mousePosition.x, mousePosition.y);
|
||||||
|
popupMenu.doShow(point, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WindowsEventDispatch.addListener(WM_TASKBARCREATED, showListener);
|
||||||
|
WindowsEventDispatch.addListener(WM_QUIT, quitListener);
|
||||||
|
WindowsEventDispatch.addListener(WM_SHELLNOTIFY, menuListener);
|
||||||
|
|
||||||
|
show();
|
||||||
|
|
||||||
|
bind(swingMenu, null, systemTray);
|
||||||
|
}
|
||||||
|
|
||||||
|
private
|
||||||
|
void setTooltip_(final String text) {
|
||||||
|
if (tooltipText != null && tooltipText.equals(text) || tooltipText == null && text != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltipText = text;
|
||||||
|
|
||||||
|
NOTIFYICONDATA nid = new NOTIFYICONDATA();
|
||||||
|
nid.hWnd = WindowsEventDispatch.get();
|
||||||
|
|
||||||
|
if (text != null) {
|
||||||
|
nid.setTooltip(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shell_NotifyIcon(NIM_MODIFY, nid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private
|
||||||
|
void hide() {
|
||||||
|
if (imageIcon != null) {
|
||||||
|
imageIcon.close();
|
||||||
|
imageIcon = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
NOTIFYICONDATA nid = new NOTIFYICONDATA();
|
||||||
|
nid.hWnd = WindowsEventDispatch.get();
|
||||||
|
|
||||||
|
if (!Shell32.Shell_NotifyIcon(NIM_DELETE, nid)) {
|
||||||
|
SystemTray.logger.error("Error hiding tray. {}", Kernel32.getLastErrorMessage());
|
||||||
|
}
|
||||||
|
visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private
|
||||||
|
void show() {
|
||||||
|
if (imageIcon != null) {
|
||||||
|
imageIcon.close();
|
||||||
|
}
|
||||||
|
imageIcon = convertImage(imageFile);
|
||||||
|
|
||||||
|
NOTIFYICONDATA nid = new NOTIFYICONDATA();
|
||||||
|
nid.hWnd = WindowsEventDispatch.get();
|
||||||
|
nid.setTooltip(tooltipText);
|
||||||
|
nid.setIcon(imageIcon);
|
||||||
|
nid.setCallback(WM_SHELLNOTIFY);
|
||||||
|
|
||||||
|
if (!Shell_NotifyIcon(NIM_ADD, nid)) {
|
||||||
|
SystemTray.logger.error("Error showing tray. {}", Kernel32.getLastErrorMessage());
|
||||||
|
}
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
HICONWrap convertImage(final File imageFile) {
|
||||||
|
if (imageFile != null) {
|
||||||
|
ImageIcon imageIcon = new ImageIcon(imageFile.getAbsolutePath());
|
||||||
|
// fully loads the image and returns when it's done loading the image
|
||||||
|
imageIcon = new ImageIcon(imageIcon.getImage());
|
||||||
|
|
||||||
|
HBITMAPWrap hbitmapTrayIcon = new HBITMAPWrap(ImageUtil.getBufferedImage(imageIcon));
|
||||||
|
return new HICONWrap(hbitmapTrayIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
boolean hasImage() {
|
||||||
|
return imageFile != null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user