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