Removed Windows Native Tray +menu WIP
This commit is contained in:
parent
2c004d2c46
commit
4b1eb95601
@ -1,167 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 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.nativeUI;
|
|
||||||
|
|
||||||
import static com.sun.jna.platform.win32.WinDef.HMENU;
|
|
||||||
import static dorkbox.systemTray.nativeUI.WindowsMenu.MFT_OWNERDRAW;
|
|
||||||
import static dorkbox.util.jna.windows.User32.MF_BYPOSITION;
|
|
||||||
|
|
||||||
import java.awt.AlphaComposite;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.RenderingHints;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import javax.swing.Icon;
|
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
|
|
||||||
import com.sun.jna.platform.win32.BaseTSD;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.SystemTray;
|
|
||||||
import dorkbox.util.SwingUtil;
|
|
||||||
import dorkbox.util.jna.windows.GDI32;
|
|
||||||
import dorkbox.util.jna.windows.GetLastErrorException;
|
|
||||||
import dorkbox.util.jna.windows.HBITMAPWrap;
|
|
||||||
import dorkbox.util.jna.windows.User32;
|
|
||||||
import dorkbox.util.jna.windows.structs.MENUITEMINFO;
|
|
||||||
|
|
||||||
public class WindowsBaseMenuItem {
|
|
||||||
|
|
||||||
private final int position;
|
|
||||||
|
|
||||||
volatile HBITMAPWrap hbitmapWrapImage;
|
|
||||||
volatile String text = "";
|
|
||||||
volatile boolean enabled = true; // default is enabled
|
|
||||||
|
|
||||||
// these have to be volatile, because they can be changed from any thread
|
|
||||||
private volatile ActionListener callback;
|
|
||||||
private volatile ActionEvent actionEvent;
|
|
||||||
|
|
||||||
public
|
|
||||||
WindowsBaseMenuItem(int position) {
|
|
||||||
this.position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setText(final String text) {
|
|
||||||
if (text != null) {
|
|
||||||
this.text = text;
|
|
||||||
} else {
|
|
||||||
this.text = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEnabled(final boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setImage(final File imageFile) {
|
|
||||||
if (imageFile != null) {
|
|
||||||
SwingUtil.invokeAndWaitQuietly(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
// has to run on swing EDT.
|
|
||||||
ImageIcon imageIcon = new ImageIcon(imageFile.getAbsolutePath());
|
|
||||||
// fully loads the image and returns when it's done loading the image
|
|
||||||
imageIcon = new ImageIcon(imageIcon.getImage());
|
|
||||||
|
|
||||||
hbitmapWrapImage = convertMenuImage(imageIcon);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hbitmapWrapImage = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCallback(final ActionListener callback, final ActionEvent actionEvent) {
|
|
||||||
this.callback = callback;
|
|
||||||
this.actionEvent = actionEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fireCallback() {
|
|
||||||
ActionEvent actionEvent = this.actionEvent;
|
|
||||||
ActionListener callback = this.callback;
|
|
||||||
|
|
||||||
if (callback != null) {
|
|
||||||
try {
|
|
||||||
callback.actionPerformed(actionEvent);
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
SystemTray.logger.error("Error calling menu entry {} click event.", this.text, throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HBITMAPWrap convertMenuImage(Icon icon) {
|
|
||||||
// BufferedImage img = createBitmap(icon);
|
|
||||||
|
|
||||||
int menubarHeight = WindowsMenu.getSystemMenuImageSize();
|
|
||||||
|
|
||||||
BufferedImage scaledImage = new BufferedImage(menubarHeight, menubarHeight, BufferedImage.TYPE_INT_ARGB);
|
|
||||||
Graphics2D g = scaledImage.createGraphics();
|
|
||||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
|
||||||
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
|
||||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
||||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
|
|
||||||
|
|
||||||
icon.paintIcon(null, g, 0, 0);
|
|
||||||
// g.drawImage(img, 0, 0, menubarHeight, menubarHeight, null);
|
|
||||||
g.dispose();
|
|
||||||
|
|
||||||
return new HBITMAPWrap(scaledImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove() {
|
|
||||||
if (hbitmapWrapImage != null) {
|
|
||||||
GDI32.DeleteObject(hbitmapWrapImage);
|
|
||||||
hbitmapWrapImage = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasImage() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onCreateMenu(final HMENU parentNative, final boolean hasImages) {
|
|
||||||
// setSpacerImage(hasImagesInMenu);
|
|
||||||
|
|
||||||
if (!User32.IMPL.AppendMenu(parentNative, MFT_OWNERDRAW, position, null)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
|
|
||||||
MENUITEMINFO mmi = new MENUITEMINFO();
|
|
||||||
if (!User32.IMPL.GetMenuItemInfo(parentNative, position, false, mmi)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
|
|
||||||
mmi.dwItemData = new BaseTSD.ULONG_PTR(position);
|
|
||||||
mmi.fMask |= MENUITEMINFO.MIIM_DATA;
|
|
||||||
|
|
||||||
if (!User32.IMPL.SetMenuItemInfo(parentNative, position, false, mmi)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onDeleteMenu(final HMENU parentNative) {
|
|
||||||
remove();
|
|
||||||
|
|
||||||
if (!User32.IMPL.DeleteMenu(parentNative, position, MF_BYPOSITION)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,741 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 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.nativeUI;
|
|
||||||
|
|
||||||
import static com.sun.jna.platform.win32.WinDef.HBITMAP;
|
|
||||||
import static com.sun.jna.platform.win32.WinDef.HDC;
|
|
||||||
import static com.sun.jna.platform.win32.WinDef.HFONT;
|
|
||||||
import static com.sun.jna.platform.win32.WinDef.HMENU;
|
|
||||||
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.POINT;
|
|
||||||
import static com.sun.jna.platform.win32.WinDef.RECT;
|
|
||||||
import static com.sun.jna.platform.win32.WinDef.WPARAM;
|
|
||||||
import static com.sun.jna.platform.win32.WinNT.HANDLE;
|
|
||||||
import static com.sun.jna.platform.win32.WinUser.AC_SRC_ALPHA;
|
|
||||||
import static com.sun.jna.platform.win32.WinUser.AC_SRC_OVER;
|
|
||||||
import static com.sun.jna.platform.win32.WinUser.SIZE;
|
|
||||||
import static com.sun.jna.platform.win32.WinUser.SM_CYMENUCHECK;
|
|
||||||
import static dorkbox.util.jna.windows.WindowsEventDispatch.MF_POPUP;
|
|
||||||
import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_COMMAND;
|
|
||||||
import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_DRAWITEM;
|
|
||||||
import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_MEASUREITEM;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import com.sun.jna.Pointer;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.Checkbox;
|
|
||||||
import dorkbox.systemTray.Entry;
|
|
||||||
import dorkbox.systemTray.Menu;
|
|
||||||
import dorkbox.systemTray.MenuItem;
|
|
||||||
import dorkbox.systemTray.Separator;
|
|
||||||
import dorkbox.systemTray.Status;
|
|
||||||
import dorkbox.systemTray.peer.MenuPeer;
|
|
||||||
import dorkbox.util.jna.windows.GDI32;
|
|
||||||
import dorkbox.util.jna.windows.GetLastErrorException;
|
|
||||||
import dorkbox.util.jna.windows.Listener;
|
|
||||||
import dorkbox.util.jna.windows.MsImg32;
|
|
||||||
import dorkbox.util.jna.windows.User32;
|
|
||||||
import dorkbox.util.jna.windows.WindowsEventDispatch;
|
|
||||||
import dorkbox.util.jna.windows.structs.BLENDFUNCTION;
|
|
||||||
import dorkbox.util.jna.windows.structs.DRAWITEMSTRUCT;
|
|
||||||
import dorkbox.util.jna.windows.structs.MEASUREITEMSTRUCT;
|
|
||||||
import dorkbox.util.jna.windows.structs.NONCLIENTMETRICS;
|
|
||||||
|
|
||||||
// this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both
|
|
||||||
@SuppressWarnings("ForLoopReplaceableByForEach")
|
|
||||||
class WindowsMenu extends WindowsBaseMenuItem implements MenuPeer {
|
|
||||||
|
|
||||||
volatile HMENU _nativeMenu; // must ONLY be created at the end of delete!
|
|
||||||
private final WindowsMenu parent;
|
|
||||||
|
|
||||||
private final Listener menuItemListener;
|
|
||||||
private final Listener menuItemMeasureListener;
|
|
||||||
private final Listener menuItemDrawListener;
|
|
||||||
|
|
||||||
public static final int WM_NULL = 0x0000;
|
|
||||||
|
|
||||||
public static final int VK_ESCAPE = 0x1B;
|
|
||||||
public static final int WM_KEYDOWN = 0x0100;
|
|
||||||
|
|
||||||
public static final int TPM_RECURSE = 0x0001;
|
|
||||||
public static final int TPM_RIGHTBUTTON = 0x0002;
|
|
||||||
|
|
||||||
public static final int MFT_OWNERDRAW = 256;
|
|
||||||
|
|
||||||
|
|
||||||
private static final int SPACE_ICONS = 2;
|
|
||||||
|
|
||||||
// have to make sure no other methods can call obliterate, delete, or create menu once it's already started
|
|
||||||
private AtomicBoolean obliterateInProgress = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
// this is a list (that mirrors the actual list) BECAUSE we have to create/delete the entire menu in Windows every time something is changed
|
|
||||||
private final List<WindowsBaseMenuItem> menuEntries = new ArrayList<WindowsBaseMenuItem>();
|
|
||||||
|
|
||||||
|
|
||||||
// called by the system tray constructors
|
|
||||||
// This is NOT a copy constructor!
|
|
||||||
@SuppressWarnings("IncompleteCopyConstructor")
|
|
||||||
WindowsMenu() {
|
|
||||||
super(0);
|
|
||||||
this.parent = null;
|
|
||||||
|
|
||||||
// Register drawing menu items, etc
|
|
||||||
menuItemListener = new Listener() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) {
|
|
||||||
int position = wParam.intValue() & 0xff;
|
|
||||||
WindowsBaseMenuItem item = menuEntries.get(position);
|
|
||||||
item.fireCallback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
menuItemMeasureListener = new Listener() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) {
|
|
||||||
MEASUREITEMSTRUCT ms = new MEASUREITEMSTRUCT(new Pointer(lParam.longValue()));
|
|
||||||
|
|
||||||
int position = ms.itemData.intValue();
|
|
||||||
WindowsBaseMenuItem item = menuEntries.get(position);
|
|
||||||
|
|
||||||
SIZE size = measureItem(hWnd, item);
|
|
||||||
ms.itemWidth = size.cx;
|
|
||||||
ms.itemHeight = size.cy;
|
|
||||||
ms.write();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
menuItemDrawListener = new Listener() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) {
|
|
||||||
DRAWITEMSTRUCT di = new DRAWITEMSTRUCT(new Pointer(lParam.longValue()));
|
|
||||||
|
|
||||||
int position = di.itemData.intValue();
|
|
||||||
WindowsBaseMenuItem item = menuEntries.get(position);
|
|
||||||
|
|
||||||
drawItem(item, di.hDC, di.rcItem, di.itemState);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WindowsEventDispatch.addListener(WM_COMMAND, menuItemListener);
|
|
||||||
WindowsEventDispatch.addListener(WM_MEASUREITEM, menuItemMeasureListener);
|
|
||||||
WindowsEventDispatch.addListener(WM_DRAWITEM, menuItemDrawListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is NOT a copy constructor!
|
|
||||||
@SuppressWarnings("IncompleteCopyConstructor")
|
|
||||||
private
|
|
||||||
WindowsMenu(final WindowsMenu parent, final int index) {
|
|
||||||
super(index); // is what is added to the parent menu (so images work)
|
|
||||||
this.parent = parent;
|
|
||||||
|
|
||||||
// Register drawing menu items, etc
|
|
||||||
menuItemListener = new Listener() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) {
|
|
||||||
int position = wParam.intValue() & 0xff;
|
|
||||||
WindowsBaseMenuItem item = menuEntries.get(position);
|
|
||||||
item.fireCallback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
menuItemMeasureListener = new Listener() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) {
|
|
||||||
MEASUREITEMSTRUCT ms = new MEASUREITEMSTRUCT(new Pointer(lParam.longValue()));
|
|
||||||
|
|
||||||
int position = ms.itemData.intValue();
|
|
||||||
WindowsBaseMenuItem item = menuEntries.get(position);
|
|
||||||
|
|
||||||
SIZE size = measureItem(hWnd, item);
|
|
||||||
ms.itemWidth = size.cx;
|
|
||||||
ms.itemHeight = size.cy;
|
|
||||||
ms.write();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
menuItemDrawListener = new Listener() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) {
|
|
||||||
DRAWITEMSTRUCT di = new DRAWITEMSTRUCT(new Pointer(lParam.longValue()));
|
|
||||||
|
|
||||||
int position = di.itemData.intValue();
|
|
||||||
WindowsBaseMenuItem item = menuEntries.get(position);
|
|
||||||
|
|
||||||
drawItem(item, di.hDC, di.rcItem, di.itemState);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WindowsEventDispatch.addListener(WM_COMMAND, menuItemListener);
|
|
||||||
WindowsEventDispatch.addListener(WM_MEASUREITEM, menuItemMeasureListener);
|
|
||||||
WindowsEventDispatch.addListener(WM_DRAWITEM, menuItemDrawListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WindowsMenu getParent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the menu, and unreferences everything in it. ALSO recreates ONLY the menu object.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("ForLoopReplaceableByForEach")
|
|
||||||
private
|
|
||||||
void deleteMenu() {
|
|
||||||
if (obliterateInProgress.get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_nativeMenu != null) {
|
|
||||||
// have to work in reverse so the index is preserved
|
|
||||||
for (int i = menuEntries.size()-1; i >= 0; i--) {
|
|
||||||
final WindowsBaseMenuItem menuEntry__ = menuEntries.get(i);
|
|
||||||
menuEntry__.onDeleteMenu(_nativeMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!User32.IMPL.DestroyMenu(_nativeMenu)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent != null) {
|
|
||||||
parent.deleteMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
// makes a new one
|
|
||||||
this._nativeMenu = User32.IMPL.CreatePopupMenu();
|
|
||||||
|
|
||||||
// binds sub-menu to entry (if it exists! it does not for the root menu)
|
|
||||||
if (parent != null) {
|
|
||||||
// get around windows crap (transfer handle to decimal)
|
|
||||||
int handle = (int) Pointer.nativeValue(_nativeMenu.getPointer());
|
|
||||||
|
|
||||||
if (!User32.IMPL.AppendMenu(getParent()._nativeMenu, MF_POPUP | MFT_OWNERDRAW, handle, null)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
|
||||||
*
|
|
||||||
* To work around this issue, we destroy then recreate the menu every time something is changed.
|
|
||||||
*
|
|
||||||
* ALWAYS CALLED ON THE EDT
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("ForLoopReplaceableByForEach")
|
|
||||||
private
|
|
||||||
void createMenu() {
|
|
||||||
if (obliterateInProgress.get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent != null) {
|
|
||||||
parent.createMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
// now add back other menu entries
|
|
||||||
boolean hasImages = false;
|
|
||||||
|
|
||||||
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
|
|
||||||
final WindowsBaseMenuItem menuEntry__ = menuEntries.get(i);
|
|
||||||
hasImages |= menuEntry__.hasImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
|
|
||||||
// the menu entry looks FUNKY when there are a mis-match of entries WITH and WITHOUT images
|
|
||||||
final WindowsBaseMenuItem menuEntry__ = menuEntries.get(i);
|
|
||||||
menuEntry__.onCreateMenu(_nativeMenu, hasImages);
|
|
||||||
|
|
||||||
if (menuEntry__ instanceof WindowsMenu) {
|
|
||||||
WindowsMenu subMenu = (WindowsMenu) menuEntry__;
|
|
||||||
if (subMenu.getParent() != WindowsMenu.this) {
|
|
||||||
// we don't want to "createMenu" on our sub-menu that is assigned to us directly, as they are already doing it
|
|
||||||
subMenu.createMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gtk.gtk_widget_show_all(_nativeMenu); // necessary to guarantee widget is visible (doesn't always show_all for all children)
|
|
||||||
// onMenuAdded(_nativeMenu); // not needed for windows
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Completely obliterates the menu, no possible way to reconstruct it.
|
|
||||||
*
|
|
||||||
* ALWAYS CALLED ON THE EDT
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("ForLoopReplaceableByForEach")
|
|
||||||
private
|
|
||||||
void obliterateMenu() {
|
|
||||||
if (_nativeMenu != null && !obliterateInProgress.get()) {
|
|
||||||
obliterateInProgress.set(true);
|
|
||||||
|
|
||||||
// have to remove all other menu entries
|
|
||||||
|
|
||||||
// a copy is made because sub-menus remove themselves from parents when .remove() is called. If we don't
|
|
||||||
// do this, errors will be had because indices don't line up anymore.
|
|
||||||
ArrayList<WindowsBaseMenuItem> menuEntriesCopy = new ArrayList<WindowsBaseMenuItem>(menuEntries);
|
|
||||||
menuEntries.clear();
|
|
||||||
|
|
||||||
// have to work in reverse so the index is preserved
|
|
||||||
for (int i = menuEntriesCopy.size()-1; i >= 0; i--) {
|
|
||||||
final WindowsBaseMenuItem menuEntry__ = menuEntriesCopy.get(i);
|
|
||||||
menuEntry__.onDeleteMenu(_nativeMenu);
|
|
||||||
}
|
|
||||||
menuEntriesCopy.clear();
|
|
||||||
|
|
||||||
if (!User32.IMPL.DestroyMenu(_nativeMenu)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
|
|
||||||
_nativeMenu = null;
|
|
||||||
|
|
||||||
obliterateInProgress.set(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void add(final Menu parentMenu, final Entry entry, int index) {
|
|
||||||
deleteMenu();
|
|
||||||
|
|
||||||
if (entry instanceof Menu) {
|
|
||||||
// WindowsMenu item = new WindowsMenu(WindowsMenu.this, index);
|
|
||||||
// menuEntries.add(index, item);
|
|
||||||
// ((Menu) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
|
||||||
}
|
|
||||||
else if (entry instanceof Separator) {
|
|
||||||
// WindowsMenuItemSeparator item = new WindowsMenuItemSeparator(WindowsMenu.this);
|
|
||||||
// menuEntries.add(index, item);
|
|
||||||
// entry.bind(item, parentMenu, parentMenu.getSystemTray());
|
|
||||||
}
|
|
||||||
else if (entry instanceof Checkbox) {
|
|
||||||
// WindowsMenuItemCheckbox item = new WindowsMenuItemCheckbox(WindowsMenu.this);
|
|
||||||
// menuEntries.add(index, item);
|
|
||||||
// ((Checkbox) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
|
||||||
}
|
|
||||||
else if (entry instanceof Status) {
|
|
||||||
// WindowsMenuItemStatus item = new WindowsMenuItemStatus(WindowsMenu.this);
|
|
||||||
// menuEntries.add(index, item);
|
|
||||||
// ((Status) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
|
||||||
}
|
|
||||||
else if (entry instanceof MenuItem) {
|
|
||||||
WindowsMenuItem item = new WindowsMenuItem(WindowsMenu.this, index);
|
|
||||||
menuEntries.add(index, item);
|
|
||||||
((MenuItem) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
|
||||||
}
|
|
||||||
|
|
||||||
createMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
// is overridden in tray impl
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setImage(final MenuItem menuItem) {
|
|
||||||
super.setImage(menuItem.getImage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// is overridden in tray impl
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setEnabled(final MenuItem menuItem) {
|
|
||||||
super.setEnabled(menuItem.getEnabled());
|
|
||||||
}
|
|
||||||
|
|
||||||
// is overridden in tray impl
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setText(final MenuItem menuItem) {
|
|
||||||
super.setText(menuItem.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setCallback(final MenuItem menuItem) {
|
|
||||||
// can't have a callback for menus!
|
|
||||||
}
|
|
||||||
|
|
||||||
// is overridden in tray impl
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setShortcut(final MenuItem menuItem) {
|
|
||||||
// // yikes...
|
|
||||||
// final int vKey = SwingUtil.getVirtualKey(menuItem.getShortcut());
|
|
||||||
//
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// _native.setShortcut(new MenuShortcut(vKey));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called when a child removes itself from the parent menu. Does not work for sub-menus
|
|
||||||
*
|
|
||||||
* ALWAYS CALLED ON THE EDT
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
void remove(final WindowsBaseMenuItem item) {
|
|
||||||
menuEntries.remove(item);
|
|
||||||
|
|
||||||
// have to rebuild the menu now...
|
|
||||||
deleteMenu(); // must be on EDT
|
|
||||||
createMenu(); // must be on EDT
|
|
||||||
}
|
|
||||||
|
|
||||||
// a child will always remove itself from the parent.
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void remove() {
|
|
||||||
WindowsMenu parent = getParent();
|
|
||||||
|
|
||||||
if (parent != null) {
|
|
||||||
// have to remove from the parent.menuEntries first
|
|
||||||
parent.menuEntries.remove(WindowsMenu.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete all of the children of this submenu (must happen before the menuEntry is removed)
|
|
||||||
obliterateMenu();
|
|
||||||
|
|
||||||
if (parent != null) {
|
|
||||||
// have to rebuild the menu now...
|
|
||||||
parent.deleteMenu(); // must be on EDT
|
|
||||||
parent.createMenu(); // must be on EDT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void showContextMenu(final POINT position) {
|
|
||||||
HWND mainHwnd = WindowsEventDispatch.get();
|
|
||||||
User32.IMPL.SetForegroundWindow(mainHwnd);
|
|
||||||
|
|
||||||
// TrackPopupMenu blocks until popup menu is closed
|
|
||||||
if (!User32.IMPL.TrackPopupMenu(_nativeMenu, TPM_RIGHTBUTTON, position.x, position.y, 0, mainHwnd, null)) {
|
|
||||||
// Popup menu already active
|
|
||||||
if (com.sun.jna.platform.win32.Kernel32.INSTANCE.GetLastError() == 0x000005a6) {
|
|
||||||
HWND hWnd = null;
|
|
||||||
while (true) {
|
|
||||||
// "#32768" - Name of the popup menu class
|
|
||||||
hWnd = User32.IMPL.FindWindowEx(null, hWnd, "#32768", null);
|
|
||||||
|
|
||||||
if (hWnd == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// close the previous popup menu
|
|
||||||
User32.IMPL.SendMessage(hWnd, WM_KEYDOWN, new WPARAM(VK_ESCAPE), new LPARAM(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
User32.IMPL.PostMessage(mainHwnd, WM_NULL, new WPARAM(0), new LPARAM(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// // menus have to be cleared before each render.
|
|
||||||
// void clearMenus() {
|
|
||||||
// if (_native != null) {
|
|
||||||
// if (!User32.DestroyMenu(_native)) {
|
|
||||||
// System.err.println("PROBLEM");
|
|
||||||
//// throw new GetLastErrorException();
|
|
||||||
// }
|
|
||||||
// _native = null;
|
|
||||||
// }
|
|
||||||
// for (WindowsBaseMenuItem m : menuEntries) {
|
|
||||||
// m.close();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// hMenusIDs.clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void updateMenus() {
|
|
||||||
// clearMenus();
|
|
||||||
//
|
|
||||||
// HMENU hmenu = User32.CreatePopupMenu();
|
|
||||||
// this._native = hmenu;
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < menu.getComponentCount(); i++) {
|
|
||||||
// Component e = menu.getComponent(i);
|
|
||||||
//
|
|
||||||
// if (e instanceof JMenu) {
|
|
||||||
// JMenu sub = (JMenu) e;
|
|
||||||
// HMENU hsub = createSubmenu(sub);
|
|
||||||
//
|
|
||||||
// int nID = menuEntries.size();
|
|
||||||
// menuEntries.add(new WindowsBaseMenuItem(sub));
|
|
||||||
//
|
|
||||||
// // you know, the usual windows tricks (transfer handle to
|
|
||||||
// // decimal)
|
|
||||||
// int handle = (int) Pointer.nativeValue(hsub.getPointer());
|
|
||||||
//
|
|
||||||
// if (!User32.AppendMenu(hmenu, MF_POPUP | MFT_OWNERDRAW, handle, null))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
//
|
|
||||||
// MENUITEMINFO mi = new MENUITEMINFO();
|
|
||||||
// if (!User32.GetMenuItemInfo(hmenu, handle, false, mi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
//
|
|
||||||
// mi.dwItemData = new ULONG_PTR(nID);
|
|
||||||
// mi.fMask |= MENUITEMINFO.MIIM_DATA;
|
|
||||||
// if (!User32.SetMenuItemInfo(hmenu, handle, false, mi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// } else if (e instanceof JCheckBoxMenuItem) {
|
|
||||||
// JCheckBoxMenuItem ch = (JCheckBoxMenuItem) e;
|
|
||||||
//
|
|
||||||
// int nID = menuEntries.size();
|
|
||||||
// menuEntries.add(new WindowsBaseMenuItem(ch));
|
|
||||||
//
|
|
||||||
// if (!User32.AppendMenu(hmenu, MFT_OWNERDRAW, nID, null))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
//
|
|
||||||
// MENUITEMINFO mmi = new MENUITEMINFO();
|
|
||||||
// if (!User32.GetMenuItemInfo(hmenu, nID, false, mmi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// mmi.dwItemData = new ULONG_PTR(nID);
|
|
||||||
// mmi.fMask |= MENUITEMINFO.MIIM_DATA;
|
|
||||||
// if (!User32.SetMenuItemInfo(hmenu, nID, false, mmi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// } else if (e instanceof JMenuItem) {
|
|
||||||
// JMenuItem mi = (JMenuItem) e;
|
|
||||||
//
|
|
||||||
// int nID = menuEntries.size();
|
|
||||||
// menuEntries.add(new WindowsBaseMenuItem(mi));
|
|
||||||
//
|
|
||||||
// if (!User32.AppendMenu(hmenu, MFT_OWNERDRAW, nID, null))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
//
|
|
||||||
// MENUITEMINFO mmi = new MENUITEMINFO();
|
|
||||||
// if (!User32.GetMenuItemInfo(hmenu, nID, false, mmi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// mmi.dwItemData = new ULONG_PTR(nID);
|
|
||||||
// mmi.fMask |= MENUITEMINFO.MIIM_DATA;
|
|
||||||
// if (!User32.SetMenuItemInfo(hmenu, nID, false, mmi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (e instanceof JPopupMenu.Separator) {
|
|
||||||
// if (!User32.AppendMenu(hmenu, MF_SEPARATOR, 0, null))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// HMENU createSubmenu(JMenu menu) {
|
|
||||||
// HMENU hmenu = User32.CreatePopupMenu();
|
|
||||||
// // seems like you dont have to free this menu, since it already attached
|
|
||||||
// // to main HMENU handler
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < menu.getMenuComponentCount(); i++) {
|
|
||||||
// Component e = menu.getMenuComponent(i);
|
|
||||||
//
|
|
||||||
// if (e instanceof JMenu) {
|
|
||||||
// JMenu sub = (JMenu) e;
|
|
||||||
// HMENU hsub = createSubmenu(sub);
|
|
||||||
//
|
|
||||||
// // you know, the usual windows tricks (transfer handle to
|
|
||||||
// // decimal)
|
|
||||||
// int handle = (int) Pointer.nativeValue(hsub.getPointer());
|
|
||||||
//
|
|
||||||
// int nID = menuEntries.size();
|
|
||||||
// menuEntries.add(new WindowsBaseMenuItem(sub));
|
|
||||||
//
|
|
||||||
// if (!User32.AppendMenu(hmenu, MF_POPUP | MFT_OWNERDRAW, handle, null))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
//
|
|
||||||
// MENUITEMINFO mi = new MENUITEMINFO();
|
|
||||||
// if (!User32.GetMenuItemInfo(hmenu, handle, false, mi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// mi.dwItemData = new ULONG_PTR(nID);
|
|
||||||
// mi.fMask |= MENUITEMINFO.MIIM_DATA;
|
|
||||||
// if (!User32.SetMenuItemInfo(hmenu, handle, false, mi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// } else if (e instanceof JCheckBoxMenuItem) {
|
|
||||||
// JCheckBoxMenuItem ch = (JCheckBoxMenuItem) e;
|
|
||||||
//
|
|
||||||
// int nID = menuEntries.size();
|
|
||||||
// menuEntries.add(new WindowsBaseMenuItem(ch));
|
|
||||||
//
|
|
||||||
// if (!User32.AppendMenu(hmenu, MFT_OWNERDRAW, nID, null))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
//
|
|
||||||
// MENUITEMINFO mi = new MENUITEMINFO();
|
|
||||||
// if (!User32.GetMenuItemInfo(hmenu, nID, false, mi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// mi.dwItemData = new ULONG_PTR(nID);
|
|
||||||
// mi.fMask |= MENUITEMINFO.MIIM_DATA;
|
|
||||||
// if (!User32.SetMenuItemInfo(hmenu, nID, false, mi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// } else if (e instanceof JMenuItem) {
|
|
||||||
// JMenuItem mi = (JMenuItem) e;
|
|
||||||
//
|
|
||||||
// int nID = menuEntries.size();
|
|
||||||
// menuEntries.add(new WindowsBaseMenuItem(mi));
|
|
||||||
//
|
|
||||||
// if (!User32.AppendMenu(hmenu, MFT_OWNERDRAW, nID, null))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
//
|
|
||||||
// MENUITEMINFO mmi = new MENUITEMINFO();
|
|
||||||
// if (!User32.GetMenuItemInfo(hmenu, nID, false, mmi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// mmi.dwItemData = new ULONG_PTR(nID);
|
|
||||||
// mmi.fMask |= MENUITEMINFO.MIIM_DATA;
|
|
||||||
// if (!User32.SetMenuItemInfo(hmenu, nID, false, mmi))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (e instanceof JPopupMenu.Separator) {
|
|
||||||
// if (!User32.AppendMenu(hmenu, MF_SEPARATOR, 0, null))
|
|
||||||
// throw new GetLastErrorException();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return hmenu;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
private static
|
|
||||||
void drawItem(WindowsBaseMenuItem item, HDC hDC, RECT rcItem, int itemState) {
|
|
||||||
if (!item.enabled) {
|
|
||||||
GDI32.SetTextColor(hDC, User32.IMPL.GetSysColor(User32.COLOR_GRAYTEXT));
|
|
||||||
GDI32.SetBkColor(hDC, User32.IMPL.GetSysColor(User32.COLOR_MENU));
|
|
||||||
}
|
|
||||||
else if ((itemState & DRAWITEMSTRUCT.ODS_SELECTED) == DRAWITEMSTRUCT.ODS_SELECTED) {
|
|
||||||
GDI32.SetTextColor(hDC, User32.IMPL.GetSysColor(User32.COLOR_HIGHLIGHTTEXT));
|
|
||||||
GDI32.SetBkColor(hDC, User32.IMPL.GetSysColor(User32.COLOR_HIGHLIGHT));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GDI32.SetTextColor(hDC, User32.IMPL.GetSysColor(User32.COLOR_MENUTEXT));
|
|
||||||
GDI32.SetBkColor(hDC, User32.IMPL.GetSysColor(User32.COLOR_MENU));
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = rcItem.left;
|
|
||||||
int y = rcItem.top;
|
|
||||||
|
|
||||||
x += (getSystemMenuImageSize() + SPACE_ICONS) * 2;
|
|
||||||
|
|
||||||
GDI32.SelectObject(hDC, createSystemMenuFont());
|
|
||||||
GDI32.ExtTextOut(hDC,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
GDI32.ETO_OPAQUE,
|
|
||||||
rcItem,
|
|
||||||
item.text,
|
|
||||||
item.text.length(),
|
|
||||||
null);
|
|
||||||
|
|
||||||
x = rcItem.left;
|
|
||||||
|
|
||||||
// if (item.item instanceof JCheckBoxMenuItem) {
|
|
||||||
// JCheckBoxMenuItem cc = (JCheckBoxMenuItem) item.item;
|
|
||||||
// if (cc.getState()) {
|
|
||||||
// // draw checkmark image
|
|
||||||
//// drawHBITMAP(hbitmapChecked, x, y, hbitmapChecked.getImage().getWidth(),
|
|
||||||
//// hbitmapChecked.getImage().getHeight(), hDC);
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// // draw blank checkmark image
|
|
||||||
//// drawHBITMAP(hbitmapUnchecked, x, y, hbitmapUnchecked.getImage().getWidth(),
|
|
||||||
//// hbitmapUnchecked.getImage().getHeight(), hDC);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
x += getSystemMenuImageSize() + SPACE_ICONS;
|
|
||||||
|
|
||||||
if (item.hbitmapWrapImage != null) {
|
|
||||||
drawHBITMAP(item.hbitmapWrapImage,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
item.hbitmapWrapImage.getImage().getWidth(),
|
|
||||||
item.hbitmapWrapImage.getImage().getHeight(),
|
|
||||||
hDC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static
|
|
||||||
int getSystemMenuImageSize() {
|
|
||||||
// get's the height of the default (small) checkmark
|
|
||||||
return User32.IMPL.GetSystemMetrics(SM_CYMENUCHECK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
HFONT createSystemMenuFont() {
|
|
||||||
NONCLIENTMETRICS nm = new NONCLIENTMETRICS();
|
|
||||||
|
|
||||||
User32.IMPL.SystemParametersInfo(User32.SPI_GETNONCLIENTMETRICS, 0, nm, 0);
|
|
||||||
return GDI32.CreateFontIndirect(nm.lfMenuFont);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static
|
|
||||||
void drawHBITMAP(HBITMAP hbm, int x, int y, int cx, int cy, HDC hdcDst) {
|
|
||||||
HDC hdcSrc = GDI32.CreateCompatibleDC(hdcDst);
|
|
||||||
HANDLE old = GDI32.SelectObject(hdcSrc, hbm);
|
|
||||||
|
|
||||||
BLENDFUNCTION.ByValue bld = new BLENDFUNCTION.ByValue();
|
|
||||||
bld.BlendOp = AC_SRC_OVER;
|
|
||||||
bld.BlendFlags = 0;
|
|
||||||
bld.SourceConstantAlpha = (byte) 255;
|
|
||||||
bld.AlphaFormat = AC_SRC_ALPHA;
|
|
||||||
|
|
||||||
if (!MsImg32.AlphaBlend(hdcDst, x, y, cx, cy, hdcSrc, 0, 0, cx, cy, bld)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
|
|
||||||
GDI32.SelectObject(hdcSrc, old);
|
|
||||||
|
|
||||||
if (!GDI32.DeleteDC(hdcSrc)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static
|
|
||||||
SIZE measureItem(HWND hWnd, WindowsBaseMenuItem item) {
|
|
||||||
HDC hdc = User32.IMPL.GetDC(hWnd);
|
|
||||||
HANDLE hfntOld = GDI32.SelectObject(hdc, createSystemMenuFont());
|
|
||||||
SIZE size = new SIZE();
|
|
||||||
if (!GDI32.GetTextExtentPoint32(hdc,
|
|
||||||
item.text,
|
|
||||||
item.text.length(),
|
|
||||||
size)) {
|
|
||||||
throw new GetLastErrorException();
|
|
||||||
}
|
|
||||||
GDI32.SelectObject(hdc, hfntOld);
|
|
||||||
User32.IMPL.ReleaseDC(hWnd, hdc);
|
|
||||||
|
|
||||||
size.cx += (getSystemMenuImageSize() + SPACE_ICONS) * 2;
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 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.nativeUI;
|
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.peer.MenuItemPeer;
|
|
||||||
|
|
||||||
class WindowsMenuItem extends WindowsBaseMenuItem implements MenuItemPeer {
|
|
||||||
|
|
||||||
private final WindowsMenu parent;
|
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
|
||||||
WindowsMenuItem(final WindowsMenu parent, final int index) {
|
|
||||||
super(index);
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setImage(final dorkbox.systemTray.MenuItem menuItem) {
|
|
||||||
super.setImage(menuItem.getImage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setEnabled(final dorkbox.systemTray.MenuItem menuItem) {
|
|
||||||
super.setEnabled(menuItem.getEnabled());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setText(final dorkbox.systemTray.MenuItem menuItem) {
|
|
||||||
super.setText(menuItem.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setCallback(final dorkbox.systemTray.MenuItem menuItem) {
|
|
||||||
super.setCallback(menuItem.getCallback(), new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setShortcut(final dorkbox.systemTray.MenuItem menuItem) {
|
|
||||||
// char shortcut = menuItem.getShortcut();
|
|
||||||
// // yikes...
|
|
||||||
// final int vKey = SwingUtil.getVirtualKey(shortcut);
|
|
||||||
//
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// _native.setShortcut(new MenuShortcut(vKey));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void remove() {
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// _native.deleteShortcut();
|
|
||||||
// _native.setEnabled(false);
|
|
||||||
//
|
|
||||||
// if (swingCallback != null) {
|
|
||||||
// _native.removeActionListener(swingCallback);
|
|
||||||
// swingCallback = null;
|
|
||||||
// }
|
|
||||||
// parent._native.remove(_native);
|
|
||||||
//
|
|
||||||
// _native.removeNotify();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,153 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 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.nativeUI;
|
|
||||||
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.Checkbox;
|
|
||||||
import dorkbox.systemTray.peer.CheckboxPeer;
|
|
||||||
|
|
||||||
class WindowsMenuItemCheckbox extends WindowsBaseMenuItem implements CheckboxPeer {
|
|
||||||
|
|
||||||
private final WindowsMenu parent;
|
|
||||||
private final java.awt.CheckboxMenuItem _native = new java.awt.CheckboxMenuItem();
|
|
||||||
|
|
||||||
// these have to be volatile, because they can be changed from any thread
|
|
||||||
private volatile ActionListener callback;
|
|
||||||
private volatile boolean isChecked = false;
|
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
|
||||||
WindowsMenuItemCheckbox(final WindowsMenu parent) {
|
|
||||||
super(0);
|
|
||||||
this.parent = parent;
|
|
||||||
// parent._native.add(_native);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setEnabled(final Checkbox menuItem) {
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// _native.setEnabled(menuItem.getEnabled());
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setText(final Checkbox menuItem) {
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// _native.setLabel(menuItem.getText());
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setCallback(final Checkbox menuItem) {
|
|
||||||
// if (callback != null) {
|
|
||||||
// _native.removeActionListener(callback);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// callback = menuItem.getCallback(); // can be set to null
|
|
||||||
//
|
|
||||||
// if (callback != null) {
|
|
||||||
// callback = new ActionListener() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void actionPerformed(ActionEvent e) {
|
|
||||||
// // this will run on the EDT, since we are calling it from the EDT
|
|
||||||
// menuItem.setChecked(!isChecked);
|
|
||||||
//
|
|
||||||
// // we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
|
|
||||||
// ActionListener cb = menuItem.getCallback();
|
|
||||||
// if (cb != null) {
|
|
||||||
// try {
|
|
||||||
// cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
|
|
||||||
// } catch (Throwable throwable) {
|
|
||||||
// SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// _native.addActionListener(callback);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setShortcut(final Checkbox menuItem) {
|
|
||||||
// char shortcut = menuItem.getShortcut();
|
|
||||||
// // yikes...
|
|
||||||
// final int vKey = SwingUtil.getVirtualKey(shortcut);
|
|
||||||
//
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// _native.setShortcut(new MenuShortcut(vKey));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setChecked(final Checkbox menuItem) {
|
|
||||||
// boolean checked = menuItem.getChecked();
|
|
||||||
//
|
|
||||||
// // only dispatch if it's actually different
|
|
||||||
// if (checked != this.isChecked) {
|
|
||||||
// this.isChecked = checked;
|
|
||||||
//
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// _native.setState(isChecked);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void remove() {
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// _native.deleteShortcut();
|
|
||||||
// _native.setEnabled(false);
|
|
||||||
//
|
|
||||||
// if (callback != null) {
|
|
||||||
// _native.removeActionListener(callback);
|
|
||||||
// callback = null;
|
|
||||||
// }
|
|
||||||
// parent._native.remove(_native);
|
|
||||||
//
|
|
||||||
// _native.removeNotify();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 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.nativeUI;
|
|
||||||
|
|
||||||
|
|
||||||
import dorkbox.systemTray.peer.EntryPeer;
|
|
||||||
|
|
||||||
class WindowsMenuItemSeparator extends WindowsBaseMenuItem implements EntryPeer {
|
|
||||||
|
|
||||||
private final WindowsMenu parent;
|
|
||||||
private final java.awt.MenuItem _native = new java.awt.MenuItem("-");
|
|
||||||
|
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
|
||||||
WindowsMenuItemSeparator(final WindowsMenu parent) {
|
|
||||||
super(0);
|
|
||||||
this.parent = parent;
|
|
||||||
// parent._native.add(_native);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void remove() {
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// parent._native.remove(_native);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 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.nativeUI;
|
|
||||||
|
|
||||||
import java.awt.MenuItem;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.Status;
|
|
||||||
import dorkbox.systemTray.peer.StatusPeer;
|
|
||||||
|
|
||||||
class WindowsMenuItemStatus extends WindowsBaseMenuItem implements StatusPeer {
|
|
||||||
|
|
||||||
private final WindowsMenu parent;
|
|
||||||
private final MenuItem _native = new MenuItem();
|
|
||||||
|
|
||||||
WindowsMenuItemStatus(final WindowsMenu parent) {
|
|
||||||
super(0);
|
|
||||||
this.parent = parent;
|
|
||||||
|
|
||||||
// status is ALWAYS at 0 index...
|
|
||||||
// parent._native.insert(_native, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setText(final Status menuItem) {
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// Font font = _native.getFont();
|
|
||||||
// if (font == null) {
|
|
||||||
// font = new Font(DIALOG, Font.BOLD, 12); // the default font used for dialogs.
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// font = font.deriveFont(Font.BOLD);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// _native.setFont(font);
|
|
||||||
// _native.setLabel(menuItem.getText());
|
|
||||||
//
|
|
||||||
// // this makes sure it can't be selected
|
|
||||||
// _native.setEnabled(false);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void remove() {
|
|
||||||
// SwingUtil.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public
|
|
||||||
// void run() {
|
|
||||||
// parent._native.remove(_native);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,271 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 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.nativeUI;
|
|
||||||
|
|
||||||
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.POINT;
|
|
||||||
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.io.File;
|
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.MenuItem;
|
|
||||||
import dorkbox.systemTray.SystemTray;
|
|
||||||
import dorkbox.systemTray.Tray;
|
|
||||||
import dorkbox.util.ImageUtil;
|
|
||||||
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, derivative of the original implementation by Nathan Sweet (BSD License).
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
class _WindowsNativeTray extends Tray implements NativeUI {
|
|
||||||
private final Listener quitListener;
|
|
||||||
private final Listener menuListener;
|
|
||||||
|
|
||||||
// is the system tray visible or not.
|
|
||||||
private volatile boolean visible = true;
|
|
||||||
|
|
||||||
private volatile File imageFile;
|
|
||||||
private volatile HICONWrap imageIcon;
|
|
||||||
|
|
||||||
private volatile String tooltipText = "";
|
|
||||||
private final Listener showListener;
|
|
||||||
|
|
||||||
public _WindowsNativeTray (final dorkbox.systemTray.SystemTray systemTray) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
// we override various methods, because each tray implementation is SLIGHTLY different. This allows us customization.
|
|
||||||
final WindowsMenu windowsMenu = new WindowsMenu() {
|
|
||||||
@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 setImage(final MenuItem menuItem) {
|
|
||||||
imageFile = menuItem.getImage();
|
|
||||||
|
|
||||||
if (imageIcon != null) {
|
|
||||||
imageIcon.close();
|
|
||||||
}
|
|
||||||
imageIcon = convertImage(imageFile);
|
|
||||||
|
|
||||||
NOTIFYICONDATA nid = new NOTIFYICONDATA();
|
|
||||||
nid.hWnd = WindowsEventDispatch.get();
|
|
||||||
nid.setIcon(imageIcon);
|
|
||||||
|
|
||||||
if (!Shell32.Shell_NotifyIcon(NIM_MODIFY, nid)) {
|
|
||||||
SystemTray.logger.error("Error setting the image for the tray. {}", Kernel32.getLastErrorMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setText(final MenuItem menuItem) {
|
|
||||||
// no op.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setShortcut(final MenuItem menuItem) {
|
|
||||||
// no op
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void remove() {
|
|
||||||
hide();
|
|
||||||
|
|
||||||
super.remove();
|
|
||||||
|
|
||||||
User32.IMPL.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) {
|
|
||||||
System.err.println("quit listener");
|
|
||||||
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:
|
|
||||||
if (User32.IMPL.GetCursorPos(mousePosition)) {
|
|
||||||
windowsMenu.showContextMenu(mousePosition);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WM_RBUTTONUP:
|
|
||||||
if (User32.IMPL.GetCursorPos(mousePosition)) {
|
|
||||||
windowsMenu.showContextMenu(mousePosition);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WindowsEventDispatch.addListener(WM_TASKBARCREATED, showListener);
|
|
||||||
WindowsEventDispatch.addListener(WM_QUIT, quitListener);
|
|
||||||
WindowsEventDispatch.addListener(WM_SHELLNOTIFY, menuListener);
|
|
||||||
|
|
||||||
show();
|
|
||||||
|
|
||||||
// Runtime.getRuntime().addShutdownHook(new Thread() {
|
|
||||||
// @Override
|
|
||||||
// public void run () {
|
|
||||||
// Shell_NotifyIcon(NIM_DELETE, windowNotifyIconData);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
bind(windowsMenu, null, systemTray);
|
|
||||||
}
|
|
||||||
|
|
||||||
// public synchronized void balloon (String title, String message, int millis) {
|
|
||||||
// balloonNotifyIconData.hWnd = this.windowNotifyIconData.hWnd;
|
|
||||||
// balloonNotifyIconData.uID = this.windowNotifyIconData.uID;
|
|
||||||
// balloonNotifyIconData.setBalloon(title, message, millis, NIIF_NONE);
|
|
||||||
// Shell_NotifyIcon(NIM_MODIFY, balloonNotifyIconData);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
private void hide() {
|
|
||||||
if (imageIcon != null) {
|
|
||||||
imageIcon.close();
|
|
||||||
imageIcon = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
void setTooltip_(final String tooltipText) {
|
|
||||||
if (this.tooltipText.equals(tooltipText)){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.tooltipText = tooltipText;
|
|
||||||
|
|
||||||
NOTIFYICONDATA nid = new NOTIFYICONDATA();
|
|
||||||
nid.hWnd = WindowsEventDispatch.get();
|
|
||||||
nid.setTooltip(tooltipText);
|
|
||||||
|
|
||||||
Shell_NotifyIcon(NIM_MODIFY, nid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
boolean hasImage() {
|
|
||||||
return imageFile != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user