From 7ff9b7947f272e8cfec48bb6330bba37e620a34d Mon Sep 17 00:00:00 2001 From: Rik Veenboer Date: Sun, 18 Oct 2015 16:05:54 +0100 Subject: [PATCH] Expand api to allow easier interaction with windows en menus as exemplified by Lightroom --- src/Test.java | 13 ++ src/main/java/org/synthuse/Api.java | 99 +++++++++++++++- src/main/java/test/Amount.java | 13 ++ src/main/java/test/Slider.java | 20 ++++ src/main/java/test/Test.java | 178 ++++++++++++++++++++++++++++ 5 files changed, 321 insertions(+), 2 deletions(-) create mode 100644 src/Test.java create mode 100644 src/main/java/test/Amount.java create mode 100644 src/main/java/test/Slider.java create mode 100644 src/main/java/test/Test.java diff --git a/src/Test.java b/src/Test.java new file mode 100644 index 0000000..95ad8ff --- /dev/null +++ b/src/Test.java @@ -0,0 +1,13 @@ +import main.java.Test.CTest; + +import com.sun.jna.Native; + +public class Test { + + public static void main(String[] args) { + CTest ctest = (CTest) Native.loadLibrary("ctest", CTest.class); + ctest.helloFromC(); + + } + +} diff --git a/src/main/java/org/synthuse/Api.java b/src/main/java/org/synthuse/Api.java index 9a86fd7..323ffa7 100644 --- a/src/main/java/org/synthuse/Api.java +++ b/src/main/java/org/synthuse/Api.java @@ -26,6 +26,7 @@ import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR; import com.sun.jna.platform.win32.WinBase.SYSTEM_INFO; import com.sun.jna.platform.win32.WinDef.HMENU; import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.WinDef.WPARAM; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinReg; import com.sun.jna.platform.win32.WinUser; @@ -36,8 +37,9 @@ import com.sun.jna.ptr.PointerByReference; import com.sun.jna.win32.StdCallLibrary.StdCallCallback; import com.sun.jna.win32.W32APIOptions; -public class Api { - +public class Api { + public static boolean EXACT = false; + // Constants public static int WM_SETTEXT = 0x000c; @@ -165,6 +167,8 @@ public class Api { public PsapiEx psapi; public Kernel32Ex kernel32; + protected HWND hWndFound; + public static final int POINT_Y(long i) { return (int) (i >> 32); @@ -807,4 +811,95 @@ public class Api { //JOptionPane.showMessageDialog(null, "lvitem size: " + lvitem.size()); } + + public WPARAM loadMenuItem(HWND hWnd, HMENU hMenu, boolean exact, String... path) throws Exception { + boolean found = false; + int itemID = -1; + int pathLength = path.length; + for (int i = 0; i < pathLength; ++i) { + String search = path[i]; + int count = user32.GetMenuItemCount(hMenu); + found = false; + for (int position = 0; position < count; ++position) { + String menuItemText = GetMenuItemText(hMenu, position); + if (exact ? menuItemText.equals(search) : menuItemText.contains(search)) { + found = true; + if (i < pathLength - 1) { + hMenu = user32.GetSubMenu(hMenu, position); + } else { + itemID = user32.GetMenuItemID(hMenu, position); + } + break; + } + } + if (!found) { + throw new Exception("Menu item not found!"); + } + } + return new WPARAM(itemID); + } + + + public HWND findTopWindow(String text, String className) { + hWndFound = null; + user32.EnumWindows(new WinUser.WNDENUMPROC() { + public boolean callback(HWND hWnd, Pointer lParam) { + if (Api.getWindowText(hWnd).contains(text)) { + hWndFound = hWnd; + return false; + } else { + return true; + } + } + }, 0); + return hWndFound; + } + + protected HWND findChildWindow(HWND hWnd, String text) { + return findChildWindow(hWnd, EXACT, text); + } + + protected HWND findChildWindow(HWND hWnd, boolean exact, String text) { + hWndFound = null; + user32.EnumChildWindows(hWnd, new WinUser.WNDENUMPROC() { + public boolean callback(HWND hWnd, Pointer lParam) { + if (exact ? Api.getWindowText(hWnd).equals(text) : Api.getWindowText(hWnd).contains(text)) { + hWndFound = hWnd; + return false; + } else { + return true; + } + } + }, null); + return hWndFound; + } + + public HWND findChildWindow(HWND hWnd, String... path) { + return findChildWindow(hWnd, EXACT, path); + } + + protected HWND findChildWindow(HWND hWnd, boolean exact, String... path) { + for (String search : path) { + hWnd = findChildWindow(hWnd, exact, search); + } + return hWnd; + } + + public HWND[] findAllChildWindow(HWND hWnd, String search) { + return findAllChildWindow(hWnd, EXACT, search); + } + + protected HWND[] findAllChildWindow(HWND hWnd, boolean exact, String search) { + ArrayList hwndList = new ArrayList(); + user32.EnumChildWindows(hWnd, new WinUser.WNDENUMPROC() { + public boolean callback(HWND hWnd, Pointer lParam) { + String text = Api.getWindowText(hWnd); + if (exact ? text.equals(search) : text.contains(search)) { + hwndList.add(hWnd); + } + return true; + } + }, null); + return hwndList.toArray(new HWND[0]); + } } diff --git a/src/main/java/test/Amount.java b/src/main/java/test/Amount.java new file mode 100644 index 0000000..3e4300a --- /dev/null +++ b/src/main/java/test/Amount.java @@ -0,0 +1,13 @@ +package test; +public enum Amount { + DECREASE_MUCH ("--"), + DECREASE_LITTLE ("-"), + INCREASE_LITTLE ("+"), + INCREASE_MUCH ("++"); + + public String value; + + Amount(String value) { + this.value = value; + } +} diff --git a/src/main/java/test/Slider.java b/src/main/java/test/Slider.java new file mode 100644 index 0000000..1f8bb6b --- /dev/null +++ b/src/main/java/test/Slider.java @@ -0,0 +1,20 @@ +package test; +public enum Slider { + EXPOSURE, + CONTRAST, + HIGHLIGHTS, + SHADOWS, + BLACKS, + WHITES, + CLARITY, + VIBRANCE; + + public String getLabel() { + String name = this.name(); + return String.format("%s%s", name.substring(0, 1), name.toLowerCase().substring(1)); + } + + public String getLabel(Amount amount) { + return String.format("%s %s", getLabel(), amount.value); + } +} diff --git a/src/main/java/test/Test.java b/src/main/java/test/Test.java new file mode 100644 index 0000000..c31e8fb --- /dev/null +++ b/src/main/java/test/Test.java @@ -0,0 +1,178 @@ +package test; +import java.util.HashMap; +import java.util.Random; + +import org.synthuse.Api; + +import com.sun.jna.platform.win32.WinDef.HMENU; +import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.WinDef.WPARAM; + +public class Test { + + protected Api api; + protected HWND hWndFound; + protected HashMap windowMap; + protected HashMap menuItemMap; + protected HashMap> sliderMap; + protected HashMap valueMap; + + class MenuItem { + public static final boolean EXACT = false; + + public HWND hWnd; + public HMENU hMenu; + public String[] path; + public boolean exact; + + public MenuItem(HWND hWnd, HMENU hMenu, String... path) { + this(hWnd, hMenu, EXACT, path); + } + + public MenuItem(HWND hWnd, HMENU hMenu, boolean exact, String... path) { + this.hWnd = hWnd; + this.hMenu = hMenu; + this.exact = exact; + this.path = path; + } + } + + public Test() { + api = new Api(); + windowMap = new HashMap(); + menuItemMap = new HashMap(); + sliderMap = new HashMap>(); + valueMap = new HashMap(); + } + + public static void main(String[] args) throws Exception { + Test test = new Test(); + test.start(); + + //test.moveSlider(Slider.CONTRAST, Amount.INCREASE_LITTLE); + + for (int k = 0; k < 5; ++k) { + Slider slider = Slider.values()[new Random().nextInt(Slider.values().length)]; + for (int j = 0; j < 5; ++j) { + for (int i = 0; i < 20; ++i) { + test.moveSlider(slider, Amount.DECREASE_MUCH); + System.out.println(test.getValue(slider)); + Thread.sleep(100); + } + Thread.sleep(200); + for (int i = 0; i < 20; ++i) { + test.moveSlider(slider, Amount.INCREASE_MUCH); + Thread.sleep(100); + } + } + } + } + + protected void moveSlider(Slider slider, Amount amount) throws Exception { + MenuItem menuItem = sliderMap.get(slider).get(amount); + activateItem(menuItem); + } + + protected float getValue(Slider slider) { + if (valueMap.containsKey(slider)) { + HWND hWnd = valueMap.get(slider); + String text = Api.getWindowText(hWnd); + return Float.valueOf(text.replace(" ", "")); + } else { + return 0; + } + } + + public void start() throws Exception { + // Find Lightroom window + HWND hWndTopWindow = api.findTopWindow("Lightroom", "AgWinMainFrame"); + if (hWndTopWindow == null) { + throw new Exception("Can't find top window"); + } + + // Find menu options from Keyboard Tamer + String[] path = {"&File", "Pl&ug-in Extras", ""}; + for (Slider slider : Slider.values()) { + HashMap amountMap = new HashMap(); + for (Amount amount : Amount.values()) { + String label = slider.getLabel(amount); + path[2] = String.format(" %s", label); + MenuItem menuItem = loadMenuItem(hWndTopWindow, true, path); + amountMap.put(amount, menuItem); + } + sliderMap.put(slider, amountMap); + } + + // Find develop sliders + path = new String[]{"", "Top", "Main", "Panel", "Last Panel", "View", "ClipView", "Accordion", "Accordion"}; + HWND hWnd = api.findChildWindow(hWndTopWindow, path); + if (hWnd == null) { + throw new Exception("Can't find window"); + } + for (HWND hWndLoop : api.findAllChildWindow(hWnd, "Collapsible")) { + path = new String[]{"Basic", "View"}; + hWnd = api.findChildWindow(hWndLoop, path); + if (hWnd != null) { + Slider slider = null; + for (HWND hWndSubLoop : api.findAllChildWindow(hWnd, "")) { + //String className = Api.getWindowClassName(hWndSubLoop); + String text = Api.getWindowText(hWndSubLoop); + if (!text.contains("Bridge") && !text.contains("View") && text.length() > 0) { + if (slider != null) { + System.out.printf("%s = %s (%.2f)\n", slider.getLabel(), text, Float.valueOf(text.replace(" ", ""))); + valueMap.put(slider, hWndSubLoop); + slider = null; + } else { + for (Slider sliderLoop : Slider.values()) { + if (sliderLoop.getLabel().equals(text)) { + slider = sliderLoop; + } + } + } + } + } + } + } + } + + public boolean activateItem(MenuItem menuItem) throws Exception { + HWND hWnd = menuItem.hWnd; + HMENU hMenu = menuItem.hMenu; + String[] path = menuItem.path; + boolean exact = menuItem.exact; + WPARAM wParam; + if (menuItemMap.containsKey(menuItem)) { + wParam = menuItemMap.get(menuItem); + } else { + wParam = api.loadMenuItem(hWnd, hMenu, exact, path); + } + return api.user32.PostMessage(hWnd, Api.WM_COMMAND, wParam, null).intValue() > 0; + } + + public boolean activateItem(HWND hWnd, boolean exact, String... path) throws Exception { + MenuItem menuItem = loadMenuItem(hWnd, exact, path); + return activateItem(menuItem); + } + + protected MenuItem loadMenuItem(HWND hWnd, boolean exact, String... path) throws Exception { + HMENU hMenu; + if (windowMap.containsKey(hWnd)) { + hMenu = windowMap.get(hWnd); + } else { + hMenu = api.user32.GetMenu(hWnd); + windowMap.put(hWnd, hMenu); + } + MenuItem menuItem = new MenuItem(hWnd, hMenu, path); + WPARAM wParam = api.loadMenuItem(hWnd, hMenu, exact, path); + menuItemMap.put(menuItem, wParam); + return menuItem; + } + + protected WPARAM loadMenuItem(MenuItem menuItem) throws Exception { + HWND hWnd = menuItem.hWnd; + HMENU hMenu = menuItem.hMenu; + String[] path = menuItem.path; + boolean exact = menuItem.exact; + return api.loadMenuItem(hWnd, hMenu, exact, path); + } +} \ No newline at end of file