WIP Windows Native implementation

This commit is contained in:
nathan 2017-07-11 01:39:24 +02:00
parent 8d97b90162
commit 787e16b4e0
31 changed files with 3568 additions and 0 deletions

View File

@ -0,0 +1,29 @@
/*
* 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.jna.windows;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinNT;
public class COLORREF extends WinNT.HANDLE {
public COLORREF() {
}
public COLORREF(Pointer p) {
super(p);
}
}

View File

@ -0,0 +1,148 @@
/*
* 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.jna.windows;
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.RECT;
import static com.sun.jna.platform.win32.WinGDI.BITMAPINFO;
import static com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.win32.W32APIOptions;
import dorkbox.systemTray.jna.windows.structs.LOGFONT;
public
class GDI32 {
static {
Native.register(NativeLibrary.getInstance("GDI32", W32APIOptions.DEFAULT_OPTIONS));
}
public static final int ETO_OPAQUE = 2;
public static final int SRCCOPY = 0xCC0020;
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144938(v=vs.85).aspx
*/
public static native
boolean GetTextExtentPoint32(HDC hdc, String lpString, int c, WinUser.SIZE lpSize);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd145093(v=vs.85).aspx
*/
public static native
COLORREF SetTextColor(HDC hdc, COLORREF crColor);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162964(v=vs.85).aspx
*/
public static native
COLORREF SetBkColor(HDC hdc, COLORREF crColor);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162713(v=vs.85).aspx
*/
public static native
boolean ExtTextOut(HDC hdc, int X, int Y, int fuOptions, RECT lprc, String lpString, int cbCount, int[] lpDx);
/**
* https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd183500(v=vs.85).aspx
*/
public static native
HFONT CreateFontIndirect(LOGFONT l);
/**
* The SelectObject function selects an object into the specified device context (DC).
* The new object replaces the previous object of the same type.
*
* @param hDC Handle to the DC.
* @param hGDIObj Handle to the object to be selected.
*
* @return If the selected object is not a region and the function succeeds, the return value
* is a handle to the object being replaced. If the selected object is a region and the
* function succeeds, the return value is one of the REGION values.
*/
public static native
HANDLE SelectObject(HDC hDC, HANDLE hGDIObj);
/**
* The CreateCompatibleDC function creates a memory device context (DC) compatible with the specified device.
*
* @param hDC Handle to an existing DC. If this handle is NULL, the function creates a memory DC compatible with the
* application's current screen.
*
* @return If the function succeeds, the return value is the handle to a memory DC.
* If the function fails, the return value is NULL.
* To get extended error information, call GetLastError.
*/
public static native
HDC CreateCompatibleDC(HDC hDC);
/**
* The DeleteDC function deletes the specified device context (DC).
*
* @param hDC Handle to the device context.
*
* @return If the function succeeds, the return value is nonzero.
* If the function fails, the return value is zero.
* To get extended error information, call GetLastError.
*/
public static native
boolean DeleteDC(HDC hDC);
/**
* The DeleteObject function deletes a logical pen, brush, font, bitmap, region, or palette,
* freeing all system resources associated with the object. After the object is deleted, the
* specified handle is no longer valid.
*
* @param hObject Handle to a logical pen, brush, font, bitmap, region, or palette.
*
* @return If the function succeeds, the return value is nonzero.
* If the specified handle is not valid or is currently selected into a DC, the return value is zero.
* To get extended error information, call GetLastError.
*/
public static native
boolean DeleteObject(HANDLE hObject);
/**
* The CreateDIBSection function creates a DIB that applications can write to directly.
* The function gives you a pointer to the location of the bitmap bit values. You can supply
* a handle to a file-mapping object that the function will use to create the bitmap, or you
* can let the system allocate the memory for the bitmap.
*
* @param hDC Handle to a device context. If the value of iUsage is DIB_PAL_COLORS, the function uses this
* device context's logical palette to initialize the DIB colors.
* @param pbmi Pointer to a BITMAPINFO structure that specifies various attributes of the DIB, including
* the bitmap dimensions and colors.
* @param iUsage Specifies the type of data contained in the bmiColors array member of the BITMAPINFO structure
* pointed to by pbmi (either logical palette indexes or literal RGB values).
* @param ppvBits Pointer to a variable that receives a pointer to the location of the DIB bit values.
* @param hSection Handle to a file-mapping object that the function will use to create the DIB. This parameter can be NULL.
* @param dwOffset Specifies the offset from the beginning of the file-mapping object referenced by hSection where storage
* for the bitmap bit values is to begin.
*
* @return Specifies the offset from the beginning of the file-mapping object referenced by hSection where storage
* for the bitmap bit values is to begin.
*/
public static native
HBITMAP CreateDIBSection(HDC hDC, BITMAPINFO pbmi, int iUsage, PointerByReference ppvBits, Pointer hSection, int dwOffset);
}

View File

@ -0,0 +1,33 @@
/*
* 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.jna.windows;
public
class GetLastErrorException extends RuntimeException {
private static final long serialVersionUID = 3980497906900380359L;
private String message;
public
GetLastErrorException() {
message = Kernel32.getLastErrorMessage();
}
public
String toString() {
return message;
}
}

View File

@ -0,0 +1,104 @@
/*
* 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.jna.windows;
import static com.sun.jna.platform.win32.WinDef.HBITMAP;
import static com.sun.jna.platform.win32.WinDef.HDC;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinGDI;
import com.sun.jna.ptr.PointerByReference;
public class HBITMAPWrap extends HBITMAP {
// https://github.com/twall/jna/blob/master/contrib/alphamaskdemo/com/sun/jna/contrib/demo/AlphaMaskDemo.java
private static
HBITMAP createBitmap(BufferedImage image) {
int w = image.getWidth(null);
int h = image.getHeight(null);
HDC screenDC = User32.IMPL.GetDC(null);
HDC memDC = GDI32.CreateCompatibleDC(screenDC);
HBITMAP hBitmap = null;
try {
BufferedImage buf = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D g = (Graphics2D) buf.getGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
g.drawImage(image, 0, 0, w, h, null);
WinGDI.BITMAPINFO bmi = new WinGDI.BITMAPINFO();
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = h;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
bmi.bmiHeader.biSizeImage = w * h * 4;
PointerByReference ppbits = new PointerByReference();
hBitmap = GDI32.CreateDIBSection(memDC, bmi, WinGDI.DIB_RGB_COLORS, ppbits, null, 0);
Pointer pbits = ppbits.getValue();
Raster raster = buf.getData();
int[] pixel = new int[4];
int[] bits = new int[w * h];
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
raster.getPixel(x, h - y - 1, pixel);
int red = (pixel[2] & 0xFF) << 0;
int green = (pixel[1] & 0xFF) << 8;
int blue = (pixel[0] & 0xFF) << 16;
int alpha = (pixel[3] & 0xFF) << 24;
bits[x + y * w] = alpha | red | green | blue;
}
}
pbits.write(0, bits, 0, bits.length);
return hBitmap;
} finally {
User32.IMPL.ReleaseDC(null, screenDC);
GDI32.DeleteDC(memDC);
}
}
BufferedImage img;
public HBITMAPWrap(BufferedImage img) {
setPointer(createBitmap(img).getPointer());
this.img = img;
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
public void close() {
if (Pointer.nativeValue(getPointer()) != 0) {
GDI32.DeleteObject(this);
setPointer(new Pointer(0));
}
}
public BufferedImage getImage() {
return img;
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.jna.windows;
import static com.sun.jna.platform.win32.WinDef.HBITMAP;
import static com.sun.jna.platform.win32.WinDef.HICON;
import com.sun.jna.Pointer;
import dorkbox.systemTray.jna.windows.structs.ICONINFO;
/**
* http://www.pinvoke.net/default.aspx/user32.createiconindirect
*/
public class HICONWrap extends HICON {
static HICON createIconIndirect(HBITMAP bm) {
ICONINFO info = new ICONINFO();
info.IsIcon = true;
info.MaskBitmap = bm;
info.ColorBitmap = bm;
HICON hicon = User32.IMPL.CreateIconIndirect(info);
if (hicon == null) {
throw new GetLastErrorException();
}
return hicon;
}
private HBITMAPWrap bm;
public HICONWrap() {
}
public HICONWrap(Pointer p) {
super(p);
}
public HICONWrap(HBITMAPWrap bm) {
this.bm = bm;
setPointer(createIconIndirect(bm).getPointer());
}
public void close() {
bm.close();
if (Pointer.nativeValue(getPointer()) != 0) {
GDI32.DeleteObject(this);
setPointer(new Pointer(0));
}
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.jna.windows;
import com.sun.jna.Memory;
import com.sun.jna.ptr.PointerByReference;
public
class Kernel32 {
public static
String getLastErrorMessage() {
// has to be Kernel32.INSTANCE, otherwise it will crash the JVM
int hresult = com.sun.jna.platform.win32.Kernel32.INSTANCE.GetLastError();
if (hresult == 0) {
return "HRESULT: 0x0 [No Error]";
} else {
Memory memory = new Memory(1024);
PointerByReference reference = new PointerByReference(memory);
// Must be Kernel32.INSTANCE because of how it pulls in variety arguments.
com.sun.jna.platform.win32.Kernel32.INSTANCE.FormatMessage(com.sun.jna.platform.win32.Kernel32.FORMAT_MESSAGE_FROM_SYSTEM, null, hresult, 0, reference, (int) memory.size(), null);
String memoryMessage = reference.getPointer()
.getString(0, true);
memoryMessage = memoryMessage.trim();
return String.format("HRESULT: 0x%08x [%s]", hresult, memoryMessage);
}
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.jna.windows;
import static com.sun.jna.platform.win32.WinDef.HWND;
import static com.sun.jna.platform.win32.WinDef.LPARAM;
import static com.sun.jna.platform.win32.WinDef.WPARAM;
public
class Listener {
public
void run(HWND hWnd, WPARAM wParam, LPARAM lParam) {
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.jna.windows;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
public
class MSG extends Structure {
public Pointer hWnd;
public int message;
public Parameter wParam;
public Parameter lParam;
public int time;
public int x;
public int y;
@Override
protected
List getFieldOrder() {
return Arrays.asList("hWnd", "message", "wParam", "lParam", "time", "x", "y");
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.jna.windows;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.win32.W32APIOptions;
import dorkbox.systemTray.jna.windows.structs.BLENDFUNCTION;
public
class MsImg32 {
static {
Native.register(NativeLibrary.getInstance("Msimg32", W32APIOptions.DEFAULT_OPTIONS));
}
public static final int ETO_OPAQUE = 2;
public static final int SRCCOPY = 0xCC0020;
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183351(v=vs.85).aspx
*/
public static native
boolean AlphaBlend(WinDef.HDC hdcDest, int xoriginDest, int yoriginDest, int wDest, int hDest, WinDef.HDC hdcSrc, int xoriginSrc,
int yoriginSrc, int wSrc, int hSrc, BLENDFUNCTION.ByValue ftn);
}

View File

@ -0,0 +1,17 @@
package dorkbox.systemTray.jna.windows;
import com.sun.jna.IntegerType;
import com.sun.jna.Pointer;
public
class Parameter extends IntegerType {
public
Parameter() {
this(0);
}
public
Parameter(long value) {
super(Pointer.SIZE, value);
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.jna.windows;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.win32.W32APIOptions;
import dorkbox.systemTray.jna.windows.structs.NOTIFYICONDATA;
public
class Shell32 {
static {
Native.register(NativeLibrary.getInstance("shell32", W32APIOptions.DEFAULT_OPTIONS));
}
static public final int NIM_ADD = 0x0;
static public final int NIM_MODIFY = 0x1;
static public final int NIM_DELETE = 0x2;
public static native
boolean Shell_NotifyIcon(int dwMessage, NOTIFYICONDATA lpdata);
}

View File

@ -0,0 +1,239 @@
/*
* 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.jna.windows;
import static com.sun.jna.platform.win32.WinDef.HDC;
import static com.sun.jna.platform.win32.WinDef.HICON;
import static com.sun.jna.platform.win32.WinDef.HINSTANCE;
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.LRESULT;
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 com.sun.jna.Callback;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinNT;
import dorkbox.systemTray.jna.windows.structs.ICONINFO;
import dorkbox.systemTray.jna.windows.structs.MENUITEMINFO;
import dorkbox.systemTray.jna.windows.structs.NONCLIENTMETRICS;
import dorkbox.util.OS;
@SuppressWarnings("WeakerAccess")
public
interface User32 {
User32 IMPL = OS.is64bit() ? new User32_64() : new User32_32();
int WM_QUIT = 18;
int SPI_GETNONCLIENTMETRICS = 0x0029;
int COLOR_MENU = 4;
int COLOR_MENUTEXT = 7;
int COLOR_HIGHLIGHTTEXT = 14;
int COLOR_HIGHLIGHT = 13;
int COLOR_GRAYTEXT = 17;
int GWL_WNDPROC = -4;
int WM_LBUTTONUP = 0x202;
int WM_RBUTTONUP = 0x205;
int MF_BYPOSITION = 0x400;
/**
* This is overridden by the 64-bit version to be SetWindowLongPtr instead.
*/
int SetWindowLong(HWND hWnd, int nIndex, Callback procedure);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms647626(v=vs.85).aspx
*/
HMENU CreatePopupMenu();
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms647616(v=vs.85).aspx
*/
boolean AppendMenu(HMENU hMenu, int uFlags, int uIDNewItem, String lpNewItem);
/**
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms647629(v=vs.85).aspx
*/
boolean DeleteMenu(HMENU hMenu, int uPosition, int uFlags);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms647631(v=vs.85).aspx
*/
boolean DestroyMenu(HMENU hMenu);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms648002(v=vs.85).aspx
*/
boolean TrackPopupMenu(HMENU hMenu, int uFlags, int x, int y, int nReserved, HWND hWnd, RECT prcRect);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms648001(v=vs.85).aspx
*/
boolean SetMenuItemInfo(HMENU hMenu, int uItem, boolean fByPosition, MENUITEMINFO lpmii);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms647980(v=vs.85).aspx
*/
boolean GetMenuItemInfo(HMENU hMenu, int uItem, boolean fByPosition, MENUITEMINFO lpmii);
/**
* Brings the thread that created the specified window into the foreground
* and activates the window. Keyboard input is directed to the window, and
* various visual cues are changed for the user. The system assigns a
* slightly higher priority to the thread that created the foreground window
* than it does to other threads.
*
* @param hWnd A handle to the window that should be activated and brought to
* the foreground.
*
* @return If the window was brought to the foreground, the return value is
* nonzero.
*/
boolean SetForegroundWindow(HWND hWnd);
/**
* The GetSystemMetrics function retrieves various system metrics (widths
* and heights of display elements) and system configuration settings. All
* dimensions retrieved by GetSystemMetrics are in pixels.
*
* @param nIndex System metric or configuration setting to retrieve. This
* parameter can be one of the following values. Note that all
* SM_CX* values are widths and all SM_CY* values are heights.
* Also note that all settings designed to return Boolean data
* represent TRUE as any nonzero value, and FALSE as a zero
* value.
*
* @return If the function succeeds, the return value is the requested
* system metric or configuration setting. If the function fails,
* the return value is zero. GetLastError does not provide extended
* error information.
*/
int GetSystemMetrics(int nIndex);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms633500(v=vs.85).aspx
*/
HWND FindWindowEx(HWND hwndParent, HWND hwndChildAfter, String lpszClass, String lpszWindow);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms644950(v=vs.85).aspx
*/
LRESULT SendMessage(HWND hWnd, int Msg, WPARAM wParam, LPARAM lParam);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms648062(v=vs.85).aspx
*/
HICON CreateIconIndirect(ICONINFO piconinfo);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms632682(v=vs.85).aspx
*/
boolean DestroyWindow(HWND hWnd);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx
*/
boolean SystemParametersInfo(int uiAction, int uiParam, NONCLIENTMETRICS pvParam, int fWinIni);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms724371(v=vs.85).aspx
*/
COLORREF GetSysColor(int nIndex);
/**
* This function places a message in the message queue associated with the
* thread that created the specified window and then returns without waiting
* for the thread to process the message. Messages in a message queue are
* retrieved by calls to the GetMessage or PeekMessage function.
*
* @param hWnd Handle to the window whose window procedure is to receive the
* message.
* @param msg Specifies the message to be posted.
* @param wParam Specifies additional message-specific information.
* @param lParam Specifies additional message-specific information.
*/
void PostMessage(HWND hWnd, int msg, WPARAM wParam, LPARAM lParam);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
*/
HWND CreateWindowEx(int dwExStyle,
String lpClassName,
String lpWindowName,
int dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
WinNT.HANDLE lpParam);
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms644947(v=vs.85).aspx
*/
LRESULT DefWindowProc(HWND hWnd, int Msg, WPARAM wParam, LPARAM lParam);
boolean GetMessage(MSG lpMsg, Pointer hWnd, int wMsgFilterMin, int wMsgFilterMax);
boolean TranslateMessage(MSG lpMsg);
boolean DispatchMessage(MSG lpMsg);
int RegisterWindowMessage(WString lpString);
/**
* This function retrieves a handle to a display device context (DC) for the
* client area of the specified window. The display device context can be
* used in subsequent graphics display interface (GDI) functions to draw in
* the client area of the window.
*
* @param hWnd Handle to the window whose device context is to be retrieved.
* If this value is NULL, GetDC retrieves the device context for
* the entire screen.
*
* @return The handle the device context for the specified window's client
* area indicates success. NULL indicates failure. To get extended
* error information, call GetLastError.
*/
HDC GetDC(HWND hWnd);
/**
* This function releases a device context (DC), freeing it for use by other
* applications. The effect of ReleaseDC depends on the type of device
* context.
*
* @param hWnd Handle to the window whose device context is to be released.
* @param hDC Handle to the device context to be released.
*
* @return The return value specifies whether the device context is
* released. 1 indicates that the device context is released. Zero
* indicates that the device context is not released.
*/
int ReleaseDC(HWND hWnd, HDC hDC);
boolean GetCursorPos(POINT point);
}

View File

@ -0,0 +1,175 @@
/*
* 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.jna.windows;
import static com.sun.jna.platform.win32.WinDef.HDC;
import static com.sun.jna.platform.win32.WinDef.HICON;
import static com.sun.jna.platform.win32.WinDef.HINSTANCE;
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.LRESULT;
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 com.sun.jna.Callback;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.win32.W32APIOptions;
import dorkbox.systemTray.jna.windows.structs.ICONINFO;
import dorkbox.systemTray.jna.windows.structs.MENUITEMINFO;
import dorkbox.systemTray.jna.windows.structs.NONCLIENTMETRICS;
/**
* On first glance, this appears to be unnecessary to have a DirectMapping class implement an interface - however this is so different
* methods can be overridden by the correct 64bit versions, otherwise multiple copies of this library would have to be loaded (one for
* "normal", and another for the "special case").
*
* Doing it this way greatly simplifies the API while maintaining Direct Mapping, at the cost of a slightly more complex code hierarchy.
*/
public
class User32_32 implements User32 {
static {
Native.register(NativeLibrary.getInstance("user32", W32APIOptions.DEFAULT_OPTIONS));
}
// is replaced by the 64bit version
@Override
public native
int SetWindowLong(HWND hWnd, int nIndex, Callback procedure);
@Override
public native
HMENU CreatePopupMenu();
@Override
public native
boolean AppendMenu(final HMENU hMenu, final int uFlags, final int uIDNewItem, final String lpNewItem);
@Override
public native
boolean DeleteMenu(final HMENU hMenu, final int uPosition, final int uFlags);
@Override
public native
boolean DestroyMenu(final HMENU hMenu);
@Override
public native
boolean TrackPopupMenu(final HMENU hMenu,
final int uFlags,
final int x,
final int y,
final int nReserved,
final HWND hWnd,
final RECT prcRect);
@Override
public native
boolean SetMenuItemInfo(final HMENU hMenu, final int uItem, final boolean fByPosition, final MENUITEMINFO lpmii);
@Override
public native
boolean GetMenuItemInfo(final HMENU hMenu, final int uItem, final boolean fByPosition, final MENUITEMINFO lpmii);
@Override
public native
boolean SetForegroundWindow(final HWND hWnd);
@Override
public native
int GetSystemMetrics(final int nIndex);
@Override
public native
HWND FindWindowEx(final HWND hwndParent, final HWND hwndChildAfter, final String lpszClass, final String lpszWindow);
@Override
public native
LRESULT SendMessage(final HWND hWnd, final int Msg, final WPARAM wParam, final LPARAM lParam);
@Override
public native
HICON CreateIconIndirect(final ICONINFO piconinfo);
@Override
public native
boolean DestroyWindow(final HWND hWnd);
@Override
public native
boolean SystemParametersInfo(final int uiAction, final int uiParam, final NONCLIENTMETRICS pvParam, final int fWinIni);
@Override
public native
COLORREF GetSysColor(final int nIndex);
@Override
public native
void PostMessage(final HWND hWnd, final int msg, final WPARAM wParam, final LPARAM lParam);
@Override
public native
HWND CreateWindowEx(final int dwExStyle,
final String lpClassName,
final String lpWindowName,
final int dwStyle,
final int x,
final int y,
final int nWidth,
final int nHeight,
final HWND hWndParent,
final HMENU hMenu,
final HINSTANCE hInstance,
final WinNT.HANDLE lpParam);
@Override
public native
LRESULT DefWindowProc(final HWND hWnd, final int Msg, final WPARAM wParam, final LPARAM lParam);
@Override
public native
boolean GetMessage(final MSG lpMsg, final Pointer hWnd, final int wMsgFilterMin, final int wMsgFilterMax);
@Override
public native
boolean TranslateMessage(final MSG lpMsg);
@Override
public native
boolean DispatchMessage(final MSG lpMsg);
@Override
public native
int RegisterWindowMessage(final WString lpString);
@Override
public native
HDC GetDC(final HWND hWnd);
@Override
public native
int ReleaseDC(final HWND hWnd, final HDC hDC);
@Override
public native
boolean GetCursorPos(final POINT point);
}

View File

@ -0,0 +1,173 @@
/*
* 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.jna.windows;
import static com.sun.jna.platform.win32.WinDef.HMENU;
import static com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.Callback;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.win32.W32APIOptions;
import dorkbox.systemTray.jna.windows.structs.ICONINFO;
import dorkbox.systemTray.jna.windows.structs.MENUITEMINFO;
import dorkbox.systemTray.jna.windows.structs.NONCLIENTMETRICS;
/**
* On first glance, this appears to be unnecessary to have a DirectMapping class implement an interface - however this is so different
* methods can be overridden by the correct 64bit versions, otherwise multiple copies of this library would have to be loaded (one for
* "normal", and another for the "special case").
*
* Doing it this way greatly simplifies the API while maintaining Direct Mapping, at the cost of a slightly more complex code hierarchy.
*/
public
class User32_64 implements User32 {
static {
Native.register(NativeLibrary.getInstance("user32", W32APIOptions.DEFAULT_OPTIONS));
}
@Override
public
int SetWindowLong(HWND hWnd, int nIndex, Callback procedure) {
return SetWindowLongPtr(hWnd, nIndex, procedure);
}
// should be used instead of SetWindowLong for 64 versions
public native
int SetWindowLongPtr(HWND hWnd, int nIndex, Callback procedure);
@Override
public native
HMENU CreatePopupMenu();
@Override
public native
boolean AppendMenu(final HMENU hMenu, final int uFlags, final int uIDNewItem, final String lpNewItem);
@Override
public native
boolean DeleteMenu(final HMENU hMenu, final int uPosition, final int uFlags);
@Override
public native
boolean DestroyMenu(final HMENU hMenu);
@Override
public native
boolean TrackPopupMenu(final HMENU hMenu,
final int uFlags,
final int x,
final int y,
final int nReserved,
final HWND hWnd,
final WinDef.RECT prcRect);
@Override
public native
boolean SetMenuItemInfo(final HMENU hMenu, final int uItem, final boolean fByPosition, final MENUITEMINFO lpmii);
@Override
public native
boolean GetMenuItemInfo(final HMENU hMenu, final int uItem, final boolean fByPosition, final MENUITEMINFO lpmii);
@Override
public native
boolean SetForegroundWindow(final HWND hWnd);
@Override
public native
int GetSystemMetrics(final int nIndex);
@Override
public native
HWND FindWindowEx(final HWND hwndParent, final HWND hwndChildAfter, final String lpszClass, final String lpszWindow);
@Override
public native
WinDef.LRESULT SendMessage(final HWND hWnd, final int Msg, final WinDef.WPARAM wParam, final WinDef.LPARAM lParam);
@Override
public native
WinDef.HICON CreateIconIndirect(final ICONINFO piconinfo);
@Override
public native
boolean DestroyWindow(final HWND hWnd);
@Override
public native
boolean SystemParametersInfo(final int uiAction, final int uiParam, final NONCLIENTMETRICS pvParam, final int fWinIni);
@Override
public native
COLORREF GetSysColor(final int nIndex);
@Override
public native
void PostMessage(final HWND hWnd, final int msg, final WinDef.WPARAM wParam, final WinDef.LPARAM lParam);
@Override
public native
HWND CreateWindowEx(final int dwExStyle,
final String lpClassName,
final String lpWindowName,
final int dwStyle,
final int x,
final int y,
final int nWidth,
final int nHeight,
final HWND hWndParent,
final HMENU hMenu,
final WinDef.HINSTANCE hInstance,
final WinNT.HANDLE lpParam);
@Override
public native
WinDef.LRESULT DefWindowProc(final HWND hWnd, final int Msg, final WinDef.WPARAM wParam, final WinDef.LPARAM lParam);
@Override
public native
boolean GetMessage(final MSG lpMsg, final Pointer hWnd, final int wMsgFilterMin, final int wMsgFilterMax);
@Override
public native
boolean TranslateMessage(final MSG lpMsg);
@Override
public native
boolean DispatchMessage(final MSG lpMsg);
@Override
public native
int RegisterWindowMessage(final WString lpString);
@Override
public native
WinDef.HDC GetDC(final HWND hWnd);
@Override
public native
int ReleaseDC(final HWND hWnd, final WinDef.HDC hDC);
@Override
public native
boolean GetCursorPos(final WinDef.POINT point);
}

View File

@ -0,0 +1,28 @@
/*
* 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.jna.windows;
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.LRESULT;
import static com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.win32.StdCallLibrary;
public
interface WNDPROC extends StdCallLibrary.StdCallCallback {
LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam);
}

View File

@ -0,0 +1,207 @@
/*
* 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.jna.windows;
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.LRESULT;
import static com.sun.jna.platform.win32.WinDef.WPARAM;
import static com.sun.jna.platform.win32.WinUser.WM_USER;
import static dorkbox.systemTray.jna.windows.User32.GWL_WNDPROC;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sun.jna.WString;
import dorkbox.systemTray.SystemTray;
@SuppressWarnings({"Convert2Lambda", "UnusedAssignment", "Convert2Diamond", "FieldCanBeLocal"})
public
class WindowsEventDispatch implements Runnable {
private static final String NAME = "WindowsEventDispatch";
public static final int WM_TASKBARCREATED = User32.IMPL.RegisterWindowMessage(new WString("TaskbarCreated"));
public static final int WM_COMMAND = 0x0111;
public static final int WM_SHELLNOTIFY = WM_USER + 1;
public static final int WM_MEASUREITEM = 44;
public static final int WM_DRAWITEM = 43;
public static final int MF_POPUP = 0x00000010;
private static final WindowsEventDispatch edt = new WindowsEventDispatch();
private final static Map<Integer, List<Listener>> messageIDs = new HashMap<Integer, List<Listener>>();
private static final Object lock = new Object();
private static int referenceCount = 0;
private Thread dispatchThread;
// keep these around to prevent GC
private WNDPROC WndProc;
// used to dispatch messages
private volatile HWND hWnd;
private
WindowsEventDispatch() {
}
public static
void start() {
synchronized (lock) {
int ref = referenceCount++;
if (ref == 0) {
edt.start_();
}
try {
// wait for the dispatch thread to start
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static
void stop() {
synchronized (lock) {
if (--referenceCount == 0) {
edt.stop_();
}
}
}
public static
HWND get() {
return edt.hWnd;
}
// always from inside lock!
private
void start_() {
dispatchThread = new Thread(this, NAME);
dispatchThread.start();
}
// always from inside lock!
private void
stop_() {
WPARAM wparam = new WPARAM(0);
LPARAM lparam = new LPARAM(0);
User32.IMPL.SendMessage(hWnd, User32.WM_QUIT, wparam, lparam);
try {
// wait for the dispatch thread to quit (but only if we are not on the dispatch thread)
if (!Thread.currentThread().equals(dispatchThread)) {
dispatchThread.join();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@SuppressWarnings("Java8MapApi")
public static
void addListener(final int messageId, final Listener listener) {
synchronized (messageIDs) {
List<Listener> listeners = messageIDs.get(messageId);
if (listeners == null) {
listeners = new ArrayList<Listener>();
messageIDs.put(messageId, listeners);
}
listeners.add(listener);
}
}
public static
void removeListener(final Listener listener) {
synchronized (messageIDs) {
for (Map.Entry<Integer, List<Listener>> entry : messageIDs.entrySet()) {
List<Listener> value = entry.getValue();
if (value.remove(listener)) {
return;
}
}
}
}
@Override
public
void run() {
WndProc = new WNDPROC() {
@Override
public
LRESULT callback(HWND hWnd, int msg, WPARAM wParam, LPARAM lParam) {
List<Listener> listeners = null;
synchronized (messageIDs) {
listeners = messageIDs.get(msg);
if (listeners != null) {
// make a copy, in case a listener action modifies the message listener
listeners = new ArrayList<Listener>(listeners);
}
}
if (listeners != null) {
for (final Listener listener : listeners) {
if (listener != null) {
try {
listener.run(hWnd, wParam, lParam);
} catch (Exception e) {
SystemTray.logger.error("Error executing listener.", e);
}
}
}
}
return User32.IMPL.DefWindowProc(hWnd, msg, wParam, lParam);
}
};
hWnd = User32.IMPL.CreateWindowEx(0, "STATIC", NAME, 0, 0, 0, 0, 0, null, null, null,
null);
if (hWnd == null) {
throw new GetLastErrorException();
}
User32.IMPL.SetWindowLong(hWnd, GWL_WNDPROC, WndProc);
synchronized (lock) {
lock.notifyAll();
}
MSG msg = new MSG();
while (User32.IMPL.GetMessage(msg, null, 0, 0)) {
User32.IMPL.TranslateMessage(msg);
User32.IMPL.DispatchMessage(msg);
}
if (hWnd != null) {
if (!User32.IMPL.DestroyWindow(hWnd)) {
throw new GetLastErrorException();
}
hWnd = null;
}
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinUser;
public class BLENDFUNCTION extends Structure {
public static class ByValue extends BLENDFUNCTION implements Structure.ByValue {
}
public static class ByReference extends BLENDFUNCTION implements Structure.ByReference {
}
public byte BlendOp = WinUser.AC_SRC_OVER; // only valid value
public byte BlendFlags = 0; // only valid value
public byte SourceConstantAlpha;
public byte AlphaFormat;
public
BLENDFUNCTION() {
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("BlendOp", "BlendFlags", "SourceConstantAlpha", "AlphaFormat");
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.jna.windows.structs;
import static com.sun.jna.platform.win32.WinDef.HDC;
import static com.sun.jna.platform.win32.WinDef.HWND;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.BaseTSD;
import com.sun.jna.platform.win32.WinDef;
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb775802(v=vs.85).aspx
*/
public class DRAWITEMSTRUCT extends Structure {
public static class ByValue extends DRAWITEMSTRUCT implements Structure.ByValue {
}
public static class ByReference extends DRAWITEMSTRUCT implements Structure.ByReference {
}
public static final int ODT_BUTTON = 4;
public static final int ODT_COMBOBOX = 3;
public static final int ODT_LISTBOX = 2;
public static final int ODT_LISTVIEW = 102;
public static final int ODT_MENU = 1;
public static final int ODT_STATIC = 5;
public static final int ODT_TAB = 101;
public static final int ODS_SELECTED = 1;
public int CtlType;
public int CtlID;
public int itemID;
public int itemAction;
public int itemState;
public HWND hwndItem;
public HDC hDC;
public WinDef.RECT rcItem;
public BaseTSD.ULONG_PTR itemData;
public DRAWITEMSTRUCT() {
}
public DRAWITEMSTRUCT(Pointer p) {
super(p);
read();
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("CtlType", "CtlID", "itemID", "itemAction", "itemState", "hwndItem", "hDC", "rcItem", "itemData");
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.jna.windows.structs;
import static com.sun.jna.platform.win32.WinDef.DWORD;
import static com.sun.jna.platform.win32.WinDef.HBITMAP;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms648052(v=vs.85).aspx
*/
public
class ICONINFO extends Structure {
public boolean IsIcon;
public DWORD xHotspot;
public DWORD yHotspot;
public HBITMAP MaskBitmap;
public HBITMAP ColorBitmap;
public
ICONINFO() {
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("IsIcon", "xHotspot", "yHotspot", "MaskBitmap", "ColorBitmap");
}
public static
class ByValue extends ICONINFO implements Structure.ByValue {}
}

View File

@ -0,0 +1,77 @@
/*
* 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.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef;
/**
* https://msdn.microsoft.com/en-us/library/windows/desktop/dd145037(v=vs.85).aspx
*/
public class LOGFONT extends Structure {
public static final int LF_FACESIZE = 32;
public static class ByValue extends LOGFONT implements Structure.ByValue {
}
public WinDef.LONG lfHeight;
public WinDef.LONG lfWidth;
public WinDef.LONG lfEscapement;
public WinDef.LONG lfOrientation;
public WinDef.LONG lfWeight;
public byte lfItalic;
public byte lfUnderline;
public byte lfStrikeOut;
public byte lfCharSet;
public byte lfOutPrecision;
public byte lfClipPrecision;
public byte lfQuality;
public byte lfPitchAndFamily;
public char[] lfFaceName = new char[LF_FACESIZE];
public LOGFONT() {
}
public LOGFONT(Pointer p) {
super(p);
read();
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("lfHeight",
"lfWidth",
"lfEscapement",
"lfOrientation",
"lfWeight",
"lfItalic",
"lfUnderline",
"lfStrikeOut",
"lfCharSet",
"lfOutPrecision",
"lfClipPrecision",
"lfQuality",
"lfPitchAndFamily",
"lfFaceName");
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.BaseTSD;
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb775804(v=vs.85).aspx
*/
public class MEASUREITEMSTRUCT extends Structure {
public static class ByValue extends MEASUREITEMSTRUCT implements Structure.ByValue {
}
public static class ByReference extends MEASUREITEMSTRUCT implements Structure.ByReference {
}
public static final int ODT_MENU = 1;
public static final int ODT_LISTBOX = 2;
public static final int ODT_COMBOBOX = 3;
public static final int ODT_BUTTON = 4;
public static final int ODT_STATIC = 5;
public int CtlType;
public int CtlID;
public int itemID;
public int itemWidth;
public int itemHeight;
public BaseTSD.ULONG_PTR itemData;
public MEASUREITEMSTRUCT() {
}
public MEASUREITEMSTRUCT(Pointer p) {
super(p);
read();
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("CtlType", "CtlID", "itemID", "itemWidth", "itemHeight", "itemData");
}
}

View File

@ -0,0 +1,89 @@
/*
* 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.jna.windows.structs;
import static com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import static com.sun.jna.platform.win32.WinDef.HBITMAP;
import static com.sun.jna.platform.win32.WinDef.HMENU;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms647578(v=vs.85).aspx
*/
public
class MENUITEMINFO extends Structure {
public static final int MFS_ENABLED = 0x00000000;
public static final int MFS_DISABLED = 0x00000003;
public static final int MFS_GRAYED = 0x00000003;
public static final int MFS_DEFAULT = 0x00001000;
public static final int MFS_CHECKED = 0x00000008;
public static final int MFS_UNCHECKED= 0x00000000;
public static final int MFS_HILITE = 0x00000080;
public static final int MFS_UNHILITE = 0x00000000;
public static final int MIIM_DATA = 0x00000020;
public int cbSize;
public int fMask;
public int fType;
public int fState;
public int wID;
public HMENU hSubMenu;
public HBITMAP hbmpChecked;
public HBITMAP hbmpUnchecked;
public ULONG_PTR dwItemData;
public String dwTypeData;
public int cch;
public HBITMAP hbmpItem;
public
MENUITEMINFO() {
cbSize = size();
}
public
MENUITEMINFO(Pointer p) {
super(p);
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("cbSize",
"fMask",
"fType",
"fState",
"wID",
"hSubMenu",
"hbmpChecked",
"hbmpUnchecked",
"dwItemData",
"dwTypeData",
"cch",
"hbmpItem");
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/ff729175(v=vs.85).aspx
*/
public class NONCLIENTMETRICS extends Structure {
public static class ByValue extends NONCLIENTMETRICS implements Structure.ByValue {
}
public static class ByReference extends NONCLIENTMETRICS implements Structure.ByReference {
}
public static final int ODT_MENU = 1;
public static final int ODT_LISTBOX = 2;
public static final int ODT_COMBOBOX = 3;
public static final int ODT_BUTTON = 4;
public static final int ODT_STATIC = 5;
public int cbSize;
public int iBorderWidth;
public int iScrollWidth;
public int iScrollHeight;
public int iCaptionWidth;
public int iCaptionHeight;
public LOGFONT.ByValue lfCaptionFont;
public int iSmCaptionWidth;
public int iSmCaptionHeight;
public LOGFONT.ByValue lfSmCaptionFont;
public int iMenuWidth;
public int iMenuHeight;
public LOGFONT.ByValue lfMenuFont;
public LOGFONT.ByValue lfStatusFont;
public LOGFONT.ByValue lfMessageFont;
//public int iPaddedBorderWidth;
public NONCLIENTMETRICS() {
cbSize = size();
}
public NONCLIENTMETRICS(Pointer p) {
super(p);
read();
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("cbSize",
"iBorderWidth",
"iScrollWidth",
"iScrollHeight",
"iCaptionWidth",
"iCaptionHeight",
"lfCaptionFont",
"iSmCaptionWidth",
"iSmCaptionHeight",
"lfSmCaptionFont",
"iMenuWidth",
"iMenuHeight",
"lfMenuFont",
"lfStatusFont",
"lfMessageFont"
//"iPaddedBorderWidth"
);
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.jna.windows.structs;
import static com.sun.jna.platform.win32.WinDef.HWND;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef;
/**
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb773352(v=vs.85).aspx
*/
public class NOTIFYICONDATA extends Structure {
static public final int NIF_MESSAGE = 0x1;
static public final int NIF_ICON = 0x2;
static public final int NIF_TIP = 0x4;
static public final int NIF_STATE = 0x8;
static public final int NIF_INFO = 0x10;
static public final int NIIF_NONE = 0x0;
static public final int NIIF_INFO = 0x1;
static public final int NIIF_WARNING = 0x2;
static public final int NIIF_ERROR = 0x3;
static public final int NIIF_USER = 0x4;
public int cbSize;
public HWND hWnd;
public int uID;
public int uFlags;
public int uCallbackMessage;
public WinDef.HICON hIcon;
public char[] szTip = new char[128];
public int dwState;
public int dwStateMask;
public char[] szInfo = new char[256];
public int uTimeoutOrVersion; // {UINT uTimeout; UINT uVersion;};
public char[] szInfoTitle = new char[64];
public int dwInfoFlags;
public
NOTIFYICONDATA() {
cbSize = size();
}
public
void setTooltip(String s) {
uFlags |= NIF_TIP;
System.arraycopy(s.toCharArray(), 0, szTip, 0, Math.min(s.length(), szTip.length));
szTip[s.length()] = '\0';
}
public
void setBalloon(String title, String message, int millis, int niif) {
uFlags |= NIF_INFO;
System.arraycopy(message.toCharArray(), 0, szInfo, 0, Math.min(message.length(), szInfo.length));
szInfo[message.length()] = '\0';
uTimeoutOrVersion = millis;
System.arraycopy(title.toCharArray(), 0, szInfoTitle, 0, Math.min(title.length(), szInfoTitle.length));
szInfoTitle[title.length()] = '\0';
dwInfoFlags = niif;
}
public
void setIcon(WinDef.HICON hIcon) {
uFlags |= NIF_ICON;
this.hIcon = hIcon;
}
public void setCallback(int callback) {
uFlags |= NIF_MESSAGE;
uCallbackMessage = callback;
}
@Override
protected List<String> getFieldOrder () {
return Arrays.asList("cbSize",
"hWnd",
"uID",
"uFlags",
"uCallbackMessage",
"hIcon",
"szTip",
"dwState",
"dwStateMask",
"szInfo",
"uTimeoutOrVersion",
"szInfoTitle",
"dwInfoFlags");
}
}

View File

@ -0,0 +1,177 @@
/*
* 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.jna.windows.User32.MF_BYPOSITION;
import static dorkbox.systemTray.nativeUI.WindowsMenu.MFT_OWNERDRAW;
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.systemTray.jna.windows.GDI32;
import dorkbox.systemTray.jna.windows.GetLastErrorException;
import dorkbox.systemTray.jna.windows.HBITMAPWrap;
import dorkbox.systemTray.jna.windows.User32;
import dorkbox.systemTray.jna.windows.structs.MENUITEMINFO;
import dorkbox.util.SwingUtil;
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);
}
}
}
static
BufferedImage createBitmap(Icon icon) {
BufferedImage bi = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
icon.paintIcon(null, g, 0, 0);
g.dispose();
return bi;
}
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();
}
}
}

View File

@ -0,0 +1,744 @@
/*
* 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.systemTray.jna.windows.WindowsEventDispatch.MF_POPUP;
import static dorkbox.systemTray.jna.windows.WindowsEventDispatch.WM_COMMAND;
import static dorkbox.systemTray.jna.windows.WindowsEventDispatch.WM_DRAWITEM;
import static dorkbox.systemTray.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.jna.windows.GDI32;
import dorkbox.systemTray.jna.windows.GetLastErrorException;
import dorkbox.systemTray.jna.windows.Listener;
import dorkbox.systemTray.jna.windows.MsImg32;
import dorkbox.systemTray.jna.windows.User32;
import dorkbox.systemTray.jna.windows.WindowsEventDispatch;
import dorkbox.systemTray.jna.windows.structs.BLENDFUNCTION;
import dorkbox.systemTray.jna.windows.structs.DRAWITEMSTRUCT;
import dorkbox.systemTray.jna.windows.structs.MEASUREITEMSTRUCT;
import dorkbox.systemTray.jna.windows.structs.NONCLIENTMETRICS;
import dorkbox.systemTray.peer.MenuPeer;
// 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), null);
}
return;
} else {
throw new GetLastErrorException();
}
}
WPARAM wparam = new WPARAM(0);
LPARAM lparam = new LPARAM(0);
User32.IMPL.PostMessage(mainHwnd, WM_NULL, wparam, lparam);
}
// // 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;
}
}

View File

@ -0,0 +1,94 @@
/*
* 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();
// }
// });
}
}

View File

@ -0,0 +1,153 @@
/*
* 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();
// }
// });
}
}

View File

@ -0,0 +1,45 @@
/*
* 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);
// }
// });
}
}

View File

@ -0,0 +1,71 @@
/*
* 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);
// }
// });
}
}

View File

@ -0,0 +1,272 @@
/*
* 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 dorkbox.systemTray.jna.windows.Shell32.NIM_ADD;
import static dorkbox.systemTray.jna.windows.Shell32.NIM_DELETE;
import static dorkbox.systemTray.jna.windows.Shell32.NIM_MODIFY;
import static dorkbox.systemTray.jna.windows.Shell32.Shell_NotifyIcon;
import static dorkbox.systemTray.jna.windows.User32.WM_LBUTTONUP;
import static dorkbox.systemTray.jna.windows.User32.WM_QUIT;
import static dorkbox.systemTray.jna.windows.User32.WM_RBUTTONUP;
import static dorkbox.systemTray.jna.windows.WindowsEventDispatch.WM_SHELLNOTIFY;
import static dorkbox.systemTray.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.systemTray.jna.windows.HBITMAPWrap;
import dorkbox.systemTray.jna.windows.HICONWrap;
import dorkbox.systemTray.jna.windows.Kernel32;
import dorkbox.systemTray.jna.windows.Listener;
import dorkbox.systemTray.jna.windows.Shell32;
import dorkbox.systemTray.jna.windows.User32;
import dorkbox.systemTray.jna.windows.WindowsEventDispatch;
import dorkbox.systemTray.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();
WPARAM wparam = new WPARAM(0);
LPARAM lparam = new LPARAM(0);
User32.IMPL.PostMessage(WindowsEventDispatch.get(), WM_QUIT, wparam, lparam);
}
};
// 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(WindowsBaseMenuItem.createBitmap(imageIcon));
return new HICONWrap(hbitmapTrayIcon);
}
return null;
}
}