diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..b7181ef
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.project b/.project
new file mode 100644
index 0000000..9a0c119
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ Synthuse
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..8000cd6
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/lib/jna-3.4.0.jar b/lib/jna-3.4.0.jar
new file mode 100644
index 0000000..cd906f9
Binary files /dev/null and b/lib/jna-3.4.0.jar differ
diff --git a/lib/platform-3.4.0.jar b/lib/platform-3.4.0.jar
new file mode 100644
index 0000000..d9e9c70
Binary files /dev/null and b/lib/platform-3.4.0.jar differ
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..b65e7c1
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,30 @@
+Synthuse
+
+Version 1.0.5 released on 1-30-2014
+By Edward Jakubowski ejakubowski7@gmail.com
+
+Description:
+ Synthuse is a portable java base software testing framework for desktop windows
+applications. This framework has a built-in object spy and automation Test IDE.
+What makes this different from other frameworks is that it shares similar command
+syntax as Selenium. It also utilizes Xpath statements to locate target windows/objects
+The familar syntaxes makes it easier to train automation tester in this frameworks.
+
+
+Configurations:
+ All configurable settings are stored in the following file: synthuse.properties
+Changes to this file will not take affect until you restart the application.
+Example synthuse.properties file below:
+urlList=
+xpathList=//win[@class\='Notepad']\u00BA
+xpathHightlight=.*process\="([^"]*)".*
+
+
+Software Requirements:
+ - Java 1.6 or greater
+
+
+Release Notes:
+ 1-30-2014 version 1.0.5
+ - Base version
+
diff --git a/src/org/synthuse/Api.java b/src/org/synthuse/Api.java
new file mode 100644
index 0000000..574b0dd
--- /dev/null
+++ b/src/org/synthuse/Api.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.awt.Point;
+
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.platform.win32.WinDef.HDC;
+import com.sun.jna.platform.win32.WinDef.HPEN;
+import com.sun.jna.platform.win32.WinNT.HANDLE;
+import com.sun.jna.platform.win32.WinUser;
+import com.sun.jna.platform.win32.WinDef.HWND;
+import com.sun.jna.platform.win32.WinDef.LPARAM;
+import com.sun.jna.platform.win32.WinDef.LRESULT;
+import com.sun.jna.platform.win32.WinDef.RECT;
+import com.sun.jna.platform.win32.WinDef.WPARAM;
+import com.sun.jna.platform.win32.WinNT.LARGE_INTEGER;
+import com.sun.jna.platform.win32.WinUser.WNDENUMPROC;
+import com.sun.jna.ptr.PointerByReference;
+import com.sun.jna.win32.W32APIOptions;
+
+public class Api {
+
+ public static int WM_SETTEXT = 0x000c;
+ public static int WM_GETTEXT = 0x000D;
+ public static int WM_GETTEXTLENGTH = 0x000E;
+ public static int WM_MOUSEMOVE = 0x200;
+ public static int WM_LBUTTONDOWN = 0x0201;
+ public static int WM_LBUTTONUP = 0x0202;
+ public static int WM_LBUTTONDBLCLK = 0x203;
+ public static int WM_RBUTTONDOWN = 0x0204;
+ public static int WM_RBUTTONUP = 0x0205;
+ public static int WM_RBUTTONDBLCLK = 0x206;
+ public static int WM_MBUTTONDOWN = 0x207;
+ public static int WM_MBUTTONUP = 0x208;
+ public static int WM_MBUTTONDBLCLK = 0x209;
+ public static int WM_MOUSEWHEEL = 0x20A;
+ public static int WM_MOUSEHWHEEL = 0x20E;
+ public static int WM_MOUSEHOVER = 0x2A1;
+ public static int WM_NCMOUSELEAVE = 0x2A2;
+ public static int WM_MOUSELEAVE = 0x2A3;
+
+ public static int WM_CLOSE = 0x10;
+ public static int WM_DESTROY = 0x0002;
+ public static int WM_NCDESTROY = 0x0082;
+ public static int WM_QUIT = 0x12;
+
+ public static int WM_SETFOCUS = 0x0007;
+ public static int WM_NEXTDLGCTL = 0x0028;
+ public static int WM_ENABLE = 0x000A;
+ public static int WM_KEYFIRST = 0x100;
+ public static int WM_KEYDOWN = 0x100;
+ public static int WM_KEYUP = 0x101;
+ public static int WM_CHAR = 0x102;
+ public static int WM_DEADCHAR = 0x103;
+ public static int WM_SYSKEYDOWN = 0x104;
+ public static int WM_SYSKEYUP = 0x105;
+ public static int WM_SYSCHAR = 0x106;
+
+ public static int WM_CUT = 0x300;
+ public static int WM_COPY = 0x301;
+ public static int WM_PASTE = 0x302;
+ public static int WM_CLEAR = 0x303;
+ public static int WM_UNDO = 0x304;
+
+
+ public static int PROCESS_QUERY_INFORMATION = 0x0400;
+ public static int PROCESS_VM_READ = 0x0010;
+
+ public static int PS_SOLID = 0x0;
+ public static int HOLLOW_BRUSH = 0x5;
+
+ public static int WM_PAINT = 0x0F;
+ public static int WM_SETREDRAW = 0x0B;
+ public static int WM_ERASEBKGND = 0x14;
+
+ public static int RDW_FRAME = 0x0400;
+ public static int RDW_INVALIDATE = 0x0001;
+ public static int RDW_UPDATENOW = 0x0100;
+ public static int RDW_ALLCHILDREN = 0x0080;
+
+ public static int VK_SHIFT = 16;
+ public static int VK_LSHIFT = 0xA0;
+ public static int VK_RSHIFT = 0xA1;
+ public static int VK_CONTROL = 17;
+ public static int VK_LCONTROL = 0xA2;
+ public static int VK_RCONTROL = 0xA3;
+ public static int VK_MENU = 18;
+ public static int VK_LMENU = 0xA4;
+ public static int VK_RMENU = 0xA5;
+
+ public static int CWP_ALL = 0x0000; // Does not skip any child windows
+
+ public User32 user32;
+ public Psapi psapi;
+ public Kernel32 kernel32;
+
+ public static final int POINT_Y(long i)
+ {
+ return (int) (i >> 32);
+ }
+
+ public static final int POINT_X(long i)
+ {
+ return (int) (i & 0xFFFF);
+ }
+
+ public interface User32 extends W32APIOptions {
+ User32 instance = (User32) Native.loadLibrary("user32", User32.class, DEFAULT_OPTIONS);
+
+ boolean ShowWindow(HWND hWnd, int nCmdShow);
+ boolean SetForegroundWindow(HWND hWnd);
+ void SwitchToThisWindow(HWND hWnd, boolean fAltTab);
+ HWND SetFocus(HWND hWnd);
+
+ HWND FindWindow(String winClass, String title);
+ LRESULT SendMessage(HWND hWnd, int Msg, WPARAM wParam, LPARAM lParam);
+ LRESULT SendMessageA(HWND editHwnd, int wmGettext, long l, byte[] lParamStr);
+ boolean DestroyWindow(HWND hWnd);
+
+ boolean EnumWindows (WNDENUMPROC wndenumproc, int lParam);
+ boolean EnumChildWindows(HWND hWnd, WNDENUMPROC lpEnumFunc, Pointer data);
+ HWND GetParent(HWND hWnd);
+ boolean IsWindowVisible(HWND hWnd);
+
+ int GetWindowRect(HWND hWnd, RECT r);
+ HDC GetWindowDC(HWND hWnd);
+ int ReleaseDC(HWND hWnd, HDC hDC);
+ boolean InvalidateRect(HWND hWnd, long lpRect, boolean bErase);
+ boolean UpdateWindow(HWND hWnd);
+ boolean RedrawWindow(HWND hWnd, long lprcUpdate, long hrgnUpdate, int flags);
+
+ void GetWindowTextA(HWND hWnd, byte[] buffer, int buflen);
+ int GetTopWindow(HWND hWnd);
+ int GetWindow(HWND hWnd, int flag);
+ final int GW_HWNDNEXT = 2;
+ int GetClassName(HWND hWnd, char[] buffer2, int i);
+ int GetWindowModuleFileName(HWND hWnd, char[] buffer2, int i);
+ int GetWindowThreadProcessId(HWND hWnd, PointerByReference pref);
+
+ boolean GetCursorPos(long[] lpPoint); //use macros POINT_X() and POINT_Y() on long lpPoint[0]
+
+ HWND WindowFromPoint(long point);
+ HWND ChildWindowFromPointEx(HWND hwndParent, long point, int uFlags);
+ boolean ClientToScreen(HWND hWnd, long[] lpPoint);//use macros POINT_X() and POINT_Y() on long lpPoint[0]
+ boolean ScreenToClient(HWND hWnd, long[] lpPoint);//use macros POINT_X() and POINT_Y() on long lpPoint[0]
+ //HWND WindowFromPoint(int xPoint, int yPoint);
+ //HWND WindowFromPoint(POINT point);
+ }
+
+ public interface Gdi32 extends W32APIOptions {
+ Gdi32 instance = (Gdi32) Native.loadLibrary("gdi32", Gdi32.class, DEFAULT_OPTIONS);
+ HANDLE SelectObject(HDC hdc, HANDLE hgdiobj);
+ HANDLE GetStockObject(int fnObject);
+ boolean Rectangle(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
+ HPEN CreatePen(int fnPenStyle, int nWidth, int crColor);
+ }
+
+ public interface Psapi extends W32APIOptions {
+ Psapi instance = (Psapi) Native.loadLibrary("psapi", Psapi.class, DEFAULT_OPTIONS);
+ int GetModuleBaseNameW(Pointer hProcess, Pointer hmodule, char[] lpBaseName, int size);
+ }
+
+ public interface Kernel32 extends W32APIOptions {
+ Kernel32 instance = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class, DEFAULT_OPTIONS);
+ boolean GetDiskFreeSpaceEx(String lpDirectoryName, LARGE_INTEGER.ByReference lpFreeBytesAvailable, LARGE_INTEGER.ByReference lpTotalNumberOfBytes, LARGE_INTEGER.ByReference lpTotalNumberOfFreeBytes);
+ int GetLastError();
+ Pointer OpenProcess(int dwDesiredAccess, boolean bInheritHandle, Pointer pointer);
+ }
+
+
+ public Api() {
+ user32 = User32.instance;
+ psapi = Psapi.instance;
+ kernel32 = Kernel32.instance;
+ }
+
+ public static String GetHandleAsString(HWND hWnd) {
+ if (hWnd == null)
+ return "0";
+ //String longHexStr = hWnd.toString().substring("native@".length());
+ String longHexStr = hWnd.getPointer().toString().substring("native@".length());
+ Long l = Long.decode(longHexStr);
+ return l.toString();
+ }
+
+ public static HWND GetHandleFromString(String hWnd) {
+ if (hWnd == null)
+ return null;
+ if (hWnd.isEmpty())
+ return null;
+ String cleanNumericHandle = hWnd.replaceAll("[^\\d.]", "");
+ try {
+ return (new HWND(new Pointer(Long.parseLong(cleanNumericHandle))));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String GetWindowClassName(HWND hWnd) {
+ char[] buffer = new char[1026];
+ User32.instance.GetClassName(hWnd, buffer, 1026);
+ return Native.toString(buffer);
+ }
+
+ public static String GetWindowText(HWND hWnd) {
+ String text = "";
+ byte[] buffer = new byte[1024];
+ User32.instance.GetWindowTextA(hWnd, buffer, buffer.length);
+ text = Native.toString(buffer);
+ if (text.isEmpty())
+ text = new Api().sendWmGetText(hWnd);
+ return text;
+ }
+
+ public static HWND GetWindowFromPoint(Point p) {
+
+ long[] getPos = new long [1];
+ User32.instance.GetCursorPos(getPos);
+ HWND hwnd = User32.instance.WindowFromPoint(getPos[0]);
+ HWND childHwnd = GetHiddenChildWindowFromPoint(hwnd, getPos[0]);
+ hwnd = childHwnd;
+ //System.out.println(getPos[0] + "," + getPos[1] + " int: " + x + ", " + y);
+ //System.out.println("child: " + GetHandleAsString(childHwnd) + " " + POINT_X(getPos[0]) +", " + POINT_Y(getPos[0]));
+ return hwnd;
+ }
+
+ public static HWND GetHiddenChildWindowFromPoint(HWND inHwnd, long point)
+ {
+ //int x = POINT_X(point);int y = POINT_Y(point);
+
+ long[] getPos = new long [1];
+ getPos[0] = point;
+ //if (!User32.instance.ClientToScreen(inHwnd, getPos)) return lHWND;
+ //x = POINT_X(getPos[0]);y = POINT_Y(getPos[0]);
+ //System.out.println("ClientToScreen " + GetHandleAsString(inHwnd) + ", " + x + ", " + y);
+
+ if (!User32.instance.ScreenToClient(inHwnd, getPos)) return inHwnd; // if point is not correct use original hwnd.
+ //x = POINT_X(getPos[0]);y = POINT_Y(getPos[0]);
+ //System.out.println("ScreenToClient " + GetHandleAsString(inHwnd) + ", " + x + ", " + y);
+
+ HWND childHwnd = User32.instance.ChildWindowFromPointEx(inHwnd, getPos[0], CWP_ALL);
+ //System.out.println("ChildWindowFromPointEx2 " + GetHandleAsString(inHwnd) + ", " + x + ", " + y + " = " + GetHandleAsString(childHwnd));
+
+ if (childHwnd == null) // if childHwnd is not correct use original hwnd.
+ return inHwnd;
+
+ return childHwnd;
+ }
+
+ public HWND findWindowByTitle(String title) {
+ HWND handle = user32.FindWindow(null, title);
+ return handle;
+ }
+
+ public boolean activateWindow(HWND handle) {
+ boolean result = user32.SetForegroundWindow(handle);
+ user32.SetFocus(handle);
+ return result;
+ }
+
+ public void SetDialogFocus(HWND hdlg, HWND hwndControl) {
+ WPARAM wp = new WPARAM(hwndControl.getPointer().getLong(0));
+ LPARAM lp = new LPARAM(1);
+ user32.SendMessage(hdlg, WM_NEXTDLGCTL, wp, lp);
+ }
+
+ public boolean showWindow(HWND handle) {
+ return user32.ShowWindow(handle, WinUser.SW_SHOW);
+ }
+
+ public boolean hideWindow(HWND handle) {
+ return user32.ShowWindow(handle, WinUser.SW_HIDE);
+ }
+
+ public boolean minimizeWindow(HWND handle) {
+ return user32.ShowWindow(handle, WinUser.SW_MINIMIZE);
+ }
+
+ public boolean maximizeWindow(HWND handle) {
+ return user32.ShowWindow(handle, WinUser.SW_MAXIMIZE);
+ }
+
+ public boolean restoreWindow(HWND handle) {
+ return user32.ShowWindow(handle, WinUser.SW_RESTORE);
+ }
+
+ public boolean closeWindow(HWND handle) {
+ //return user32.DestroyWindow(handle);
+ user32.SendMessage(handle, WM_CLOSE , null, null);
+ //user32.SendMessage(handle, WM_NCDESTROY , null, null);
+ return true;
+ }
+
+ public void switchToThisWindow(HWND handle, boolean fAltTab) {
+ user32.SwitchToThisWindow(handle, fAltTab);
+ }
+
+ public String sendWmGetText(HWND handle) {
+ int bufSize = 8192;
+ byte[] lParamStr = new byte[bufSize];
+ user32.SendMessageA(handle, WM_GETTEXT, bufSize, lParamStr);
+ return (Native.toString(lParamStr));
+ }
+
+ public void sendWmSetText(HWND handle, String text) {
+ user32.SendMessageA(handle, WM_SETTEXT, 0, Native.toByteArray(text));
+ }
+
+ public void sendClick(HWND handle) {
+ user32.SendMessageA(handle, WM_LBUTTONDOWN, 0, null);
+ user32.SendMessageA(handle, WM_LBUTTONUP, 0, null);
+ }
+
+ public void sendDoubleClick(HWND handle) {
+ user32.SendMessageA(handle, WM_LBUTTONDBLCLK, 0, null);
+ //user32.SendMessageA(handle, WM_LBUTTONUP, 0, null);
+ }
+
+ public void sendRightClick(HWND handle) {
+ user32.SendMessageA(handle, WM_RBUTTONDOWN, 0, null);
+ user32.SendMessageA(handle, WM_RBUTTONUP, 0, null);
+ }
+
+ public void sendKeyDown(HWND handle, int keyCode) {
+ user32.SendMessageA(handle, WM_KEYDOWN, keyCode, null);
+ //user32.SendMessageA(handle, WM_KEYUP, keyCode, null);
+ }
+
+ public void sendKeyUp(HWND handle, int keyCode) {
+ //user32.SendMessageA(handle, WM_KEYDOWN, keyCode, null);
+ user32.SendMessageA(handle, WM_KEYUP, keyCode, null);
+ }
+
+ public int getDiskUsedPercentage() {
+ return getDiskUsedPercentage(null);
+ }
+
+ public int getDiskUsedPercentage(String target) {
+ LARGE_INTEGER.ByReference lpFreeBytesAvailable = new LARGE_INTEGER.ByReference();
+ LARGE_INTEGER.ByReference lpTotalNumberOfBytes = new LARGE_INTEGER.ByReference();
+ LARGE_INTEGER.ByReference lpTotalNumberOfFreeBytes = new LARGE_INTEGER.ByReference();
+ Kernel32.instance.GetDiskFreeSpaceEx(target, lpFreeBytesAvailable, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes);
+ double freeBytes = lpTotalNumberOfFreeBytes.getValue();
+ double totalBytes = lpTotalNumberOfBytes.getValue();
+ //System.out.println("freespace " + humanReadableByteCount(freeBytes) + "/ totalspace " + humanReadableByteCount(totalBytes));
+ return (int)(((totalBytes-freeBytes)/totalBytes) * 100.0);
+ }
+
+ public static String humanReadableByteCount(double bytes) {
+ boolean si = true;
+ int unit = si ? 1000 : 1024;
+ if (bytes < unit) return bytes + " B";
+ int exp = (int) (Math.log(bytes) / Math.log(unit));
+ String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
+ return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
+ }
+
+ // creates highlight around selected window
+ public static void highlightWindow(HWND hwnd){
+ //COLORREF i.e. 0x00804070 Red = 0x70 green = 0x40 blue = 0x80
+ //g_hRectanglePen = CreatePen (PS_SOLID, 3, RGB(256, 0, 0));
+ HPEN rectPen = Gdi32.instance.CreatePen(PS_SOLID, 3, 0x00000099); //RGB(255, 0, 0)
+ RECT rect = new RECT();
+ User32.instance.GetWindowRect(hwnd, rect);
+ HDC dc = User32.instance.GetWindowDC(hwnd);
+ if (dc != null) {
+ // Select our created pen into the DC and backup the previous pen.
+ HANDLE prevPen = Gdi32.instance.SelectObject(dc, rectPen);
+
+ // Select a transparent brush into the DC and backup the previous brush.
+ HANDLE prevBrush = Gdi32.instance.SelectObject(dc, Gdi32.instance.GetStockObject(HOLLOW_BRUSH));
+
+ // Draw a rectangle in the DC covering the entire window area of the found window.
+ Gdi32.instance.Rectangle (dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top);
+
+ // Reinsert the previous pen and brush into the found window's DC.
+ Gdi32.instance.SelectObject(dc, prevPen);
+ Gdi32.instance.SelectObject(dc, prevBrush);
+
+ // Finally release the DC.
+ User32.instance.ReleaseDC(hwnd, dc);
+ }
+ }
+
+ public static void refreshWindow(HWND hwnd) {
+ User32.instance.InvalidateRect(hwnd, 0, true);
+ User32.instance.UpdateWindow(hwnd);
+ User32.instance.RedrawWindow(hwnd, 0, 0, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ }
+
+}
diff --git a/src/org/synthuse/CommandPopupMenu.java b/src/org/synthuse/CommandPopupMenu.java
new file mode 100644
index 0000000..e8db402
--- /dev/null
+++ b/src/org/synthuse/CommandPopupMenu.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
+import javax.swing.JMenu;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+@SuppressWarnings("serial")
+public class CommandPopupMenu extends JPopupMenu {
+
+ public static interface menuEvents {
+ void menuItemClicked(String command, int paramCount, boolean useXpath, ActionEvent e);
+ }
+ public menuEvents events = new menuEvents() {
+ public void menuItemClicked(String command, int paramCount, boolean useXpath, ActionEvent e) {
+ JOptionPane.showMessageDialog(null, command);
+ }
+ };
+
+ public CommandPopupMenu() {
+
+ JMenu mnKeyboard = new JMenu("Keyboard");
+ add(mnKeyboard);
+
+ CommandMenuItem mntmSendkeys = new CommandMenuItem("sendKeys", 2, false);
+ mnKeyboard.add(mntmSendkeys);
+
+ CommandMenuItem mntmKeydown = new CommandMenuItem("keyDown", 2, false);
+ mnKeyboard.add(mntmKeydown);
+
+ CommandMenuItem mntmKeyup = new CommandMenuItem("keyUp", 2, false);
+ mnKeyboard.add(mntmKeyup);
+
+ CommandMenuItem mntmKeycopy = new CommandMenuItem("keyCopy", 1);
+ mnKeyboard.add(mntmKeycopy);
+
+ CommandMenuItem mntmKeypaste = new CommandMenuItem("keyPaste", 1);
+ mnKeyboard.add(mntmKeypaste);
+
+ CommandMenuItem mntmKeyEsc = new CommandMenuItem("keyEscape", 1);
+ mnKeyboard.add(mntmKeyEsc);
+
+ CommandMenuItem mntmKeyFunc = new CommandMenuItem("keyFunctionX", 2, false);
+ mnKeyboard.add(mntmKeyFunc);
+
+ JMenu mnMouse = new JMenu("Mouse");
+ add(mnMouse);
+
+ CommandMenuItem mntmClick = new CommandMenuItem("click", 1, false);
+ mnMouse.add(mntmClick);
+
+ CommandMenuItem mntmDoubleclick = new CommandMenuItem("doubleClick", 1, false);
+ mnMouse.add(mntmDoubleclick);
+
+ CommandMenuItem mntmRightclick = new CommandMenuItem("rightClick", 1, false);
+ mnMouse.add(mntmRightclick);
+
+ CommandMenuItem mntmwinClick = new CommandMenuItem("winClick", 2);
+ mnMouse.add(mntmwinClick);
+
+ CommandMenuItem mntmwinDoubleClick = new CommandMenuItem("winDoubleClick", 2);
+ mnMouse.add(mntmwinDoubleClick);
+
+ CommandMenuItem mntmwinRightClick = new CommandMenuItem("winRightClick", 2);
+ mnMouse.add(mntmwinRightClick);
+
+ CommandMenuItem mntmDraganddrop = new CommandMenuItem("dragAndDrop", 3);
+ mnMouse.add(mntmDraganddrop);
+
+ CommandMenuItem mntmMousedown = new CommandMenuItem("mouseDown", 1, false);
+ mnMouse.add(mntmMousedown);
+
+ CommandMenuItem mntmMouseup = new CommandMenuItem("mouseUp", 1, false);
+ mnMouse.add(mntmMouseup);
+
+ CommandMenuItem mntmMousedownright = new CommandMenuItem("mouseDownRight", 1, false);
+ mnMouse.add(mntmMousedownright);
+
+ CommandMenuItem mntmMouseupright = new CommandMenuItem("mouseUpRight", 1, false);
+ mnMouse.add(mntmMouseupright);
+
+ CommandMenuItem mntmMousemove = new CommandMenuItem("mouseMove", 3, false);
+ mnMouse.add(mntmMousemove);
+
+ JMenu mnWinMessages = new JMenu("Win Messages");
+ add(mnWinMessages);
+
+ CommandMenuItem mntmWindowfocus = new CommandMenuItem("windowFocus", 2);
+ mnWinMessages.add(mntmWindowfocus);
+
+ CommandMenuItem mntmWindowSwitchToThis = new CommandMenuItem("windowSwitchToThis", 2);
+ mnWinMessages.add(mntmWindowSwitchToThis);
+
+ CommandMenuItem mntmSetcursorposition = new CommandMenuItem("setCursorPosition", 3);
+ mnWinMessages.add(mntmSetcursorposition);
+
+ CommandMenuItem mntmWindowminimize = new CommandMenuItem("windowMinimize", 2);
+ mnWinMessages.add(mntmWindowminimize);
+
+ CommandMenuItem mntmWindowmaximize = new CommandMenuItem("windowMaximize", 2);
+ mnWinMessages.add(mntmWindowmaximize);
+
+ CommandMenuItem mntmWindowrestore = new CommandMenuItem("windowRestore", 2);
+ mnWinMessages.add(mntmWindowrestore);
+
+ CommandMenuItem mntmWindowhide = new CommandMenuItem("windowHide", 2);
+ mnWinMessages.add(mntmWindowhide);
+
+ CommandMenuItem mntmWindowshow = new CommandMenuItem("windowShow", 2);
+ mnWinMessages.add(mntmWindowshow);
+
+ CommandMenuItem mntmWindowclose = new CommandMenuItem("windowClose", 2);
+ mnWinMessages.add(mntmWindowclose);
+
+ CommandMenuItem mntmSetwindowtext = new CommandMenuItem("setWindowText", 3);
+ mnWinMessages.add(mntmSetwindowtext);
+
+ CommandMenuItem mntmGetwindowtext = new CommandMenuItem("getWindowText", 2);
+ mnWinMessages.add(mntmGetwindowtext);
+
+ CommandMenuItem mntmGetwindowclass = new CommandMenuItem("getWindowClass", 2);
+ mnWinMessages.add(mntmGetwindowclass);
+
+ CommandMenuItem mntmPause = new CommandMenuItem("pause", 2, false);
+ add(mntmPause);
+
+ CommandMenuItem mntmWaitfortitle = new CommandMenuItem("waitForTitle", 2, false);
+ add(mntmWaitfortitle);
+
+ CommandMenuItem mntmWaitfortext = new CommandMenuItem("waitForText", 2, false);
+ add(mntmWaitfortext);
+
+ CommandMenuItem mntmWaitforclass = new CommandMenuItem("waitForClass", 2, false);
+ add(mntmWaitforclass);
+
+ CommandMenuItem mntmWaitforvisible = new CommandMenuItem("waitForVisible", 2);
+ add(mntmWaitforvisible);
+
+ CommandMenuItem mntmSettimeout = new CommandMenuItem("setTimeout", 2, false);
+ add(mntmSettimeout);
+
+ CommandMenuItem mntmSetspeed = new CommandMenuItem("setSpeed", 2, false);
+ add(mntmSetspeed);
+ }
+
+ class CommandMenuItem extends JMenuItem {
+ public String commandText = "";
+ public int paramCount = 2;
+ public boolean useXpath = true;
+
+ public CommandMenuItem(String arg0, int paramCount) {
+ super(arg0);
+ init(arg0, paramCount, true);
+ }
+
+ public CommandMenuItem(String arg0, int paramCount, boolean useXpath) {
+ super(arg0);
+ init(arg0, paramCount, useXpath);
+ }
+
+ public void init(String arg0, int paramCount, boolean useXpath) {
+ this.commandText = arg0;
+ this.paramCount = paramCount;
+ this.useXpath = useXpath;
+ this.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ events.menuItemClicked(commandText, CommandMenuItem.this.paramCount, CommandMenuItem.this.useXpath, e);
+ }
+ });
+ }
+ }
+
+ public static String buildSkeletonCommand(String command, int paramCount, String xpathStr, boolean useXpath) {
+ String xpathItem = xpathStr;
+ if (!useXpath)
+ xpathItem = "";
+ String actionStr = "| do | " + command + " | ";
+ if (paramCount > 1)
+ actionStr += "on | " + xpathItem + " | ";
+ if (paramCount > 2)
+ actionStr += "with | |";
+ return actionStr;
+ }
+}
diff --git a/src/org/synthuse/CommandProcessor.java b/src/org/synthuse/CommandProcessor.java
new file mode 100644
index 0000000..474d41d
--- /dev/null
+++ b/src/org/synthuse/CommandProcessor.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.sql.Timestamp;
+import java.text.DecimalFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.swing.JOptionPane;
+
+import com.sun.jna.platform.win32.WinDef.HWND;
+
+public class CommandProcessor implements Runnable{
+
+ private static String WIN_XML = "";
+ public static long LAST_UPDATED_XML = 0;
+ public static long SPEED = 1000; // ms
+ public static double XML_UPDATE_THRESHOLD = 5.0; // seconds
+ public static long WAIT_TIMEOUT_THRESHOLD = 30000; //ms
+ public static AtomicBoolean STOP_PROCESSOR = new AtomicBoolean(false);
+
+ public String lastError = "";
+ public String scriptStr = "";
+ private Api api = new Api();
+
+ private int executeErrorCount = 0;
+ private String currentCommandText = "";
+
+ public static interface Events {
+ void statusChanged(String status);
+ void executionCompleted();
+ }
+ public Events events = new Events() {
+ public void statusChanged(String status){
+ System.out.println(status);
+ }
+ public void executionCompleted(){
+
+ }
+ };
+
+ public CommandProcessor () {
+ }
+
+ public CommandProcessor (String scriptStr, Events customEvents) { //multithreading support
+ this.scriptStr = scriptStr;
+ this.events = customEvents;
+ }
+
+ @Override
+ public void run() { //multithreading support
+ executeAllScriptCommands(scriptStr);
+ }
+
+ public static void executeThreaded(String scriptStr, Events customEvents) { //multithreading support
+ Thread t = new Thread(new CommandProcessor(scriptStr, customEvents));
+ t.start();
+ }
+
+ public void executeAllScriptCommands(String scriptStr) {
+ events.statusChanged("Executing Test Script...");
+ //CommandProcessor cmdProcessor = new CommandProcessor();
+ int errors = 0;
+ long startTime = System.nanoTime();
+ String[] lines = scriptStr.split("\n");
+ for (String line : lines) {
+
+ if (!line.trim().startsWith("|"))
+ continue; //skip if it doesn't start with bar
+ String[] parsed = line.split("\\|");
+ //System.out.println("line: " + line);
+ //System.out.println("parsed len = " + parsed.length);
+ //System.out.println("parsed 2 = " + parsed[2]);
+ //System.out.println("parsed 4 = " + parsed[4]);
+ events.statusChanged("Executing line: " + line);
+ if (parsed.length == 4 || parsed.length == 3)
+ execute(parsed[2].trim(), new String[] {});
+ if (parsed.length == 6)
+ execute(parsed[2].trim(), new String[] {parsed[4].trim()});
+ if (parsed.length == 7)
+ execute(parsed[2].trim(), new String[] {parsed[4].trim(), parsed[6].trim()});
+
+ if (executeErrorCount > 0) //check if any errors occurred
+ ++errors;
+
+ if (STOP_PROCESSOR.get())
+ break;
+ }
+ double seconds = ((double)(System.nanoTime() - startTime) / 1000000000);
+ String forcedStr = "Completed";
+ if (STOP_PROCESSOR.get())
+ forcedStr = "Stopped";
+ events.statusChanged("Script Execution " + forcedStr + " with " + errors + " error(s) in " + new DecimalFormat("#.###").format(seconds) + " seconds");
+ events.executionCompleted();
+ if (errors > 0)
+ JOptionPane.showMessageDialog(null, lastError);
+ }
+
+ public Object execute(String command, String[] args) {
+ executeErrorCount = 0;
+ currentCommandText = command;
+ String joinedArgs = "";
+ for (String arg : args)
+ joinedArgs += arg + " | ";
+ if (joinedArgs.endsWith("| "))
+ joinedArgs = joinedArgs.substring(0, joinedArgs.length()-2);
+ StatusWindow sw = new StatusWindow(command + " " + joinedArgs, -1);
+
+ Object result = executeCommandSwitch(command, args);
+ if (SPEED > 0)
+ try { Thread.sleep(SPEED); } catch (Exception e) {e.printStackTrace();}
+ sw.dispose();
+ return result;
+ }
+
+ private Object executeCommandSwitch(String command, String[] args) {
+ try {
+
+ //Key commands
+ if (command.equals("sendKeys"))
+ return cmdSendKeys(args);
+ if (command.equals("keyDown"))
+ return cmdKeyDown(args);
+ if (command.equals("keyUp"))
+ return cmdKeyUp(args);
+ if (command.equals("keyCopy"))
+ return cmdKeyCopy(args);
+ if (command.equals("keyPaste"))
+ return cmdKeyPaste(args);
+ if (command.equals("keyEscape"))
+ return cmdKeyEscape(args);
+ if (command.equals("keyFunctionX"))
+ return cmdKeyFunc(args);
+
+
+ //Mouse commands
+ if (command.equals("click"))
+ return cmdClick(args);
+ if (command.equals("doubleClick"))
+ return cmdDoubleClick(args);
+ if (command.equals("rightClick"))
+ return cmdRightClick(args);
+ if (command.equals("winClick"))
+ return cmdWinClick(args);
+ if (command.equals("winDoubleClick"))
+ return cmdWinDoubleClick(args);
+ if (command.equals("winRightClick"))
+ return cmdWinRightClick(args);
+ if (command.equals("dragAndDrop"))
+ return cmdRightClick(args);
+ if (command.equals("mouseDown"))
+ return cmdMouseDown(args);
+ if (command.equals("mouseUp"))
+ return cmdMouseUp(args);
+ if (command.equals("mouseDownRight"))
+ return cmdMouseDownRight(args);
+ if (command.equals("mouseUpRight"))
+ return cmdMouseUpRight(args);
+ if (command.equals("mouseMove"))
+ return cmdMouseMove(args);
+
+ //Windows Api Commands
+ if (command.equals("windowFocus"))
+ return cmdWindowFocus(args);
+ if (command.equals("windowMinimize"))
+ return cmdWindowMinimize(args);
+ if (command.equals("windowMaximize"))
+ return cmdWindowMaximize(args);
+ if (command.equals("windowRestore"))
+ return cmdWindowRestore(args);
+ if (command.equals("windowShow"))
+ return cmdWindowShow(args);
+ if (command.equals("windowHide"))
+ return cmdWindowHide(args);
+ if (command.equals("windowSwitchToThis"))
+ return cmdWindowSwitchToThis(args);
+ if (command.equals("windowClose"))
+ return cmdWindowClose(args);
+ if (command.equals("setWindowText"))
+ return cmdSetText(args);
+ if (command.equals("getWindowText"))
+ return cmdGetText(args);
+
+ // Misc Commands
+ if (command.equals("delay") || command.equals("pause")) {
+ Thread.sleep(Long.parseLong(args[0]));
+ return true;
+ }
+
+ if (command.equals("setSpeed"))
+ return cmdSetSpeed(args);
+ if (command.equals("setTimeout"))
+ return cmdSetTimeout(args);
+ if (command.equals("waitForTitle"))
+ return cmdWaitForTitle(args);
+ if (command.equals("waitForText"))
+ return cmdWaitForText(args);
+ if (command.equals("waitForClass"))
+ return cmdWaitForClass(args);
+ if (command.equals("waitForVisible"))
+ return cmdWaitForVisible(args);
+
+ }
+ catch (Exception e) {
+ appendError(e);
+ return false;
+ }
+ appendError("Error: Command '" + command + "' not found.");
+ return false;
+ }
+
+ private void appendError(Exception e) {
+ ++executeErrorCount;
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ lastError += new Timestamp((new Date()).getTime()) + " - " + sw.toString() + "\n";
+ try {
+ sw.close();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ private void appendError(String msg) {
+ ++executeErrorCount;
+ lastError += new Timestamp((new Date()).getTime()) + " - " + msg + "\n";
+ }
+
+ private boolean checkArgumentLength(String[] args, int expectedLength) {
+ if (args.length < expectedLength) {
+ appendError("Error: expected at least " + expectedLength + " arguments (" + currentCommandText + "[" + args.length + "])");
+ return false;
+ }
+ return true;
+ }
+
+ private boolean whenFalseAppendError(boolean cmdResult) {
+ if (!cmdResult)
+ appendError("Error: command '" + currentCommandText + "' failed");
+ return cmdResult;
+ }
+
+ private HWND findHandleWithXpath(String xpath) {
+ return findHandleWithXpath(xpath, false);
+ }
+
+ private HWND findHandleWithXpath(String xpath, boolean ignoreFailedFind) {
+ HWND result = null;
+ double secondsFromLastUpdate = ((double)(System.nanoTime() - LAST_UPDATED_XML) / 1000000000);
+ if (secondsFromLastUpdate > XML_UPDATE_THRESHOLD) { //default 5 second threshold
+ WIN_XML = WindowsEnumeratedXml.getXml();
+ LAST_UPDATED_XML = System.nanoTime();
+ }
+ WindowsEnumeratedXml.evaluateXpathGetValues(WIN_XML, xpath);
+ String resultStr = "";
+ List resultList = WindowsEnumeratedXml.evaluateXpathGetValues(WIN_XML, xpath);
+ for(String item: resultList) {
+ if (item.contains("hwnd=")) {
+ List hwndList = WindowsEnumeratedXml.evaluateXpathGetValues(item, "//@hwnd");
+ resultStr = hwndList.get(0);
+ }
+ else
+ resultStr = item;
+ break;
+ }
+ result = Api.GetHandleFromString(resultStr);
+ if (result == null && !ignoreFailedFind)
+ appendError("Error: Failed to find window handle matching: " + xpath);
+ return result;
+ }
+
+ private boolean cmdSendKeys(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ return whenFalseAppendError(RobotMacro.sendKeys(args[0]));
+ }
+
+ private boolean cmdKeyDown(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ if (args[0].length() < 1){
+ appendError("Error: command '" + currentCommandText + "' failed, expected first argument length > 0");
+ return false;
+ }
+ char keyChar = args[0].charAt(0);
+ return RobotMacro.keyDown(keyChar);
+ }
+
+ private boolean cmdKeyUp(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ if (args[0].length() < 1){
+ appendError("Error: command '" + currentCommandText + "' failed, expected first argument length > 0");
+ return false;
+ }
+ char keyChar = args[0].charAt(0);
+ return RobotMacro.keyUp(keyChar);
+ }
+
+ private boolean cmdKeyCopy(String[] args) {
+ RobotMacro.copyKey();
+ return true;
+ }
+
+ private boolean cmdKeyPaste(String[] args) {
+ RobotMacro.pasteKey();
+ return true;
+ }
+
+ private boolean cmdKeyEscape(String[] args) {
+ RobotMacro.escapeKey();
+ return true;
+ }
+
+ private boolean cmdKeyFunc(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ if (args[0].length() < 1){
+ appendError("Error: command '" + currentCommandText + "' failed, expected first argument length > 0");
+ return false;
+ }
+ int fNum = Integer.parseInt(args[0]);
+ RobotMacro.functionKey(fNum);
+ return true;
+ }
+
+ private boolean cmdClick(String[] args) {
+ RobotMacro.leftClickMouse();
+ return true;
+ }
+
+ private boolean cmdDoubleClick(String[] args) {
+ RobotMacro.doubleClickMouse();
+ return true;
+ }
+
+ private boolean cmdRightClick(String[] args) {
+ RobotMacro.rightClickMouse();
+ return true;
+ }
+
+ private boolean cmdMouseDown(String[] args) {
+ RobotMacro.leftMouseDown();
+ return true;
+ }
+
+ private boolean cmdMouseUp(String[] args) {
+ RobotMacro.leftMouseUp();
+ return true;
+ }
+
+ private boolean cmdMouseDownRight(String[] args) {
+ RobotMacro.rightMouseDown();
+ return true;
+ }
+
+ private boolean cmdMouseUpRight(String[] args) {
+ RobotMacro.rightMouseUp();
+ return true;
+ }
+
+ private boolean cmdMouseMove(String[] args) {
+ if (!checkArgumentLength(args, 2))
+ return false;
+ int x = Integer.parseInt(args[0]);
+ int y = Integer.parseInt(args[1]);
+ RobotMacro.mouseMove(x, y);
+ return true;
+ }
+
+ private boolean cmdWinClick(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.sendClick(handle);
+ return true;
+ }
+
+ private boolean cmdWinDoubleClick(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.sendDoubleClick(handle);
+ return true;
+ }
+
+ private boolean cmdWinRightClick(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.sendRightClick(handle);
+ return true;
+ }
+
+ private boolean cmdWindowFocus(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.activateWindow(handle);
+ //api.showWindow(handle);
+ return true;
+ }
+
+ private boolean cmdWindowMinimize(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.minimizeWindow(handle);
+ return true;
+ }
+
+ private boolean cmdWindowMaximize(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.maximizeWindow(handle);
+ return true;
+ }
+
+ private boolean cmdWindowRestore(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.restoreWindow(handle);
+ return true;
+ }
+
+ private boolean cmdWindowHide(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.hideWindow(handle);
+ return true;
+ }
+
+ private boolean cmdWindowShow(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.showWindow(handle);
+ return true;
+ }
+
+ private boolean cmdWindowSwitchToThis(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.switchToThisWindow(handle, true);
+ return true;
+ }
+
+
+ private boolean cmdWindowClose(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.closeWindow(handle);
+ return true;
+ }
+
+ private boolean cmdSetText(String[] args) {
+ if (!checkArgumentLength(args, 2))
+ return false;
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return false;
+ api.sendWmSetText(handle, args[1]);
+ return true;
+ }
+
+ private String cmdGetText(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return "";
+ HWND handle = findHandleWithXpath(args[0]);
+ if (handle == null)
+ return "";
+ return api.sendWmGetText(handle);
+ }
+
+ private boolean cmdSetSpeed(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ long speed = Long.parseLong(args[0]);
+ SPEED = speed;
+ return true;
+ }
+
+ private boolean cmdSetTimeout(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ long timeout = Long.parseLong(args[0]);
+ WAIT_TIMEOUT_THRESHOLD = timeout;
+ return true;
+ }
+
+ private boolean cmdWaitForTitle(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ long totalAttempts = (long) (WAIT_TIMEOUT_THRESHOLD / (XML_UPDATE_THRESHOLD * 1000));
+ long attemptCount = 0;
+ String xpath = "/EnumeratedWindows/win[@TEXT='" + WindowsEnumeratedXml.escapeXmlAttributeValue(args[0].toUpperCase()) + "']";
+ HWND handle = findHandleWithXpath(xpath, true);
+ if (handle != null)// first test without a timeout
+ return true;
+ while (attemptCount < totalAttempts) {
+ handle = findHandleWithXpath(xpath, true);
+ if (handle != null)
+ break;
+ try {Thread.sleep((long)(XML_UPDATE_THRESHOLD * 1000));} catch (Exception e) {e.printStackTrace();}
+ ++attemptCount;
+ if (STOP_PROCESSOR.get())
+ break;
+ }
+ return handle != null;
+ }
+
+ private boolean cmdWaitForText(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ long totalAttempts = (long) (WAIT_TIMEOUT_THRESHOLD / (XML_UPDATE_THRESHOLD * 1000));
+ long attemptCount = 0;
+ String xpath = "//[@TEXT='" + WindowsEnumeratedXml.escapeXmlAttributeValue(args[0].toUpperCase()) + "']";
+ HWND handle = findHandleWithXpath(xpath, true);
+ if (handle != null)// first test without a timeout
+ return true;
+ while (attemptCount < totalAttempts) {
+ handle = findHandleWithXpath(xpath, true);
+ if (handle != null)
+ break;
+ try {Thread.sleep((long)(XML_UPDATE_THRESHOLD * 1000));} catch (Exception e) {e.printStackTrace();}
+ ++attemptCount;
+ if (STOP_PROCESSOR.get())
+ break;
+ }
+ return handle != null;
+ }
+
+ private boolean cmdWaitForClass(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ long totalAttempts = (long) (WAIT_TIMEOUT_THRESHOLD / (XML_UPDATE_THRESHOLD * 1000));
+ long attemptCount = 0;
+ String xpath = "//win[@CLASS='" + WindowsEnumeratedXml.escapeXmlAttributeValue(args[0].toUpperCase()) + "']";
+ HWND handle = findHandleWithXpath(xpath, true);
+ if (handle != null)// first test without a timeout
+ return true;
+ while (attemptCount < totalAttempts) {
+ handle = findHandleWithXpath(xpath, true);
+ if (handle != null)
+ break;
+ try {Thread.sleep((long)(XML_UPDATE_THRESHOLD * 1000));} catch (Exception e) {e.printStackTrace();}
+ ++attemptCount;
+ if (STOP_PROCESSOR.get())
+ break;
+ }
+ return handle != null;
+ }
+
+ private boolean cmdWaitForVisible(String[] args) {
+ if (!checkArgumentLength(args, 1))
+ return false;
+ long totalAttempts = (long) (WAIT_TIMEOUT_THRESHOLD / (XML_UPDATE_THRESHOLD * 1000));
+ long attemptCount = 0;
+ HWND handle = findHandleWithXpath(args[0], true);
+ if (handle != null)// first test without a timeout
+ return true;
+ while (attemptCount < totalAttempts) {
+ handle = findHandleWithXpath(args[0], true);
+ if (handle != null)
+ break;
+ try {Thread.sleep((long)(XML_UPDATE_THRESHOLD * 1000));} catch (Exception e) {e.printStackTrace();}
+ ++attemptCount;
+ if (STOP_PROCESSOR.get())
+ break;
+ }
+ return handle != null;
+ }
+
+
+}
diff --git a/src/org/synthuse/Config.java b/src/org/synthuse/Config.java
new file mode 100644
index 0000000..35dc2fc
--- /dev/null
+++ b/src/org/synthuse/Config.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+public class Config extends PropertiesSerializer {
+
+ public static String DEFAULT_PROP_FILENAME = "synthuse.properties";
+
+ public String urlList = "";
+ public String xpathList = "";
+ public String xpathHightlight = ".*process=\"([^\"]*)\".*";
+
+ public Config() //needed for cloning
+ {
+ }
+
+ public Config(String propertyFilename)
+ {
+ super(propertyFilename);
+ load(propertyFilename);
+ }
+}
diff --git a/src/org/synthuse/DragTarget.java b/src/org/synthuse/DragTarget.java
new file mode 100644
index 0000000..ce94497
--- /dev/null
+++ b/src/org/synthuse/DragTarget.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.awt.Cursor;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceAdapter;
+import java.awt.dnd.DragSourceDragEvent;
+import java.awt.dnd.DragSourceMotionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.image.BufferedImage;
+
+import javax.activation.ActivationDataFlavor;
+import javax.activation.DataHandler;
+import javax.imageio.ImageIO;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.TransferHandler;
+
+
+public class DragTarget extends JLabel {
+
+ public static String CUSTOM_ICON_RESOURCE = "/org/synthuse/img/bullseye-logo-th32x32.png";
+
+ private static final long serialVersionUID = 1L;
+
+ public static interface dragEvents {
+ void dragStarted(JComponent c);
+ void dragMouseMoved(int x, int y);
+ void dropped(JComponent c);
+ }
+ public dragEvents events = new dragEvents() {
+ public void dragStarted(JComponent c) {
+
+ }
+ public void dragMouseMoved(int x, int y) {
+
+ }
+ public void dropped(JComponent c) {
+
+ }
+ };
+
+ public DragTarget() {
+
+ this.setTransferHandler(new CustomTransferHandler());
+
+ this.addMouseListener(new MouseAdapter(){
+ public void mousePressed(MouseEvent e)
+ {
+ JComponent comp = (JComponent)e.getSource();
+ TransferHandler handler = comp.getTransferHandler();
+ //c.setOpaque(true);
+ handler.exportAsDrag(comp, e, TransferHandler.MOVE);
+ }
+ });
+ }
+
+ @SuppressWarnings("serial")
+ class CustomTransferHandler extends TransferHandler {
+ //private final JWindow window = new JWindow();
+ private final DataFlavor localObjectFlavor;
+ private final JLabel label = new JLabel();
+ //public Cursor blankCursor = null;
+ public Cursor customCursor = null;
+
+ public CustomTransferHandler () {
+ //System.out.println("CustomTransferHandler");
+ localObjectFlavor = new ActivationDataFlavor(DragTarget.class, DataFlavor.javaJVMLocalObjectMimeType, "JLabel");
+ //label = DragTarget.this;
+ //window.add(label);
+ //window.setAlwaysOnTop(true);
+ //window.setBackground(new Color(0,true));
+ // Create a new blank cursor.
+ //BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
+ //blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(0, 0), "blank cursor");
+ try {
+ BufferedImage image = ImageIO.read(SynthuseDlg.class.getResourceAsStream(CUSTOM_ICON_RESOURCE));
+ customCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(15, 16), "custom cursor");
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+
+
+ DragSource.getDefaultDragSource().addDragSourceMotionListener(new DragSourceMotionListener() {
+ @Override
+ public void dragMouseMoved(DragSourceDragEvent e) {
+ events.dragMouseMoved(e.getLocation().x, e.getLocation().y);
+ //Point pt = e.getLocation();
+ //pt.translate(-9, -9); // offset
+ //window.setLocation(pt);
+ //e.getDragSourceContext().setCursor(customCursor);//DragSource.DefaultLinkDrop
+ }
+ });
+
+ DragSource.getDefaultDragSource().addDragSourceListener(new DragSourceAdapter() {
+ @Override
+ public void dragEnter(DragSourceDragEvent e) {
+ e.getDragSourceContext().setCursor(customCursor);//DragSource.DefaultLinkDrop
+ //System.out.println("dragEnter");
+ }
+ });
+
+ }
+
+ @Override
+ protected Transferable createTransferable(JComponent c) {
+ //System.out.println("createTransferable");
+ //JLabel l = (JLabel)c;
+ //String text = l.getText();
+ final DataHandler dh = new DataHandler(c, localObjectFlavor.getMimeType());
+ return dh;
+ //return new StringSelection("");
+ }
+
+ @Override
+ public boolean canImport(TransferSupport support) {
+ return true;
+ }
+
+ @Override
+ public boolean importData(TransferSupport support) {
+ System.out.println("importData");
+ return true;
+ }
+
+ @Override
+ public int getSourceActions(JComponent c) {
+ //System.out.println("getSourceActions");
+ events.dragStarted(c);
+ JLabel p = (JLabel)c;
+ label.setIcon(p.getIcon());
+ //label.setText(p.draggingLabel.getText());
+ //window.pack();
+ //Point pt = p.getLocation();
+ //SwingUtilities.convertPointToScreen(pt, p);
+ //window.setLocation(pt);
+ //window.setVisible(true);
+ return MOVE;
+ }
+
+ @Override
+ protected void exportDone(JComponent c, Transferable data, int action) {
+ //System.out.println("exportDone");
+ events.dropped(c);
+ JLabel src = (JLabel)c;
+ if(action == TransferHandler.MOVE) {
+ src.remove(src);
+ src.revalidate();
+ src.repaint();
+ }
+ src = null;
+ //window.setVisible(false);
+ //window.dispose();
+ }
+ }
+}
diff --git a/src/org/synthuse/PropertiesSerializer.java b/src/org/synthuse/PropertiesSerializer.java
new file mode 100644
index 0000000..e318544
--- /dev/null
+++ b/src/org/synthuse/PropertiesSerializer.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.io.*;
+import java.util.Properties;
+import java.lang.reflect.Field;
+
+
+/*
+
+// example class for PropertiesSerializer
+public class Configuration extends PropertiesSerializer{
+
+ public static final String DEFAULT_PROP_FILENAME = "./ctf.properties";
+
+ // General Settings
+ public String tests_dir = "./tests";
+ public String logs_dir = "./logs";
+ public int statusTimer = 2000;
+
+ public Configuration() //needed for cloning
+ {
+ }
+
+ public Configuration(String propertyFilename)
+ {
+ super(propertyFilename);
+ load(propertyFilename);
+ }
+}
+
+ */
+
+public class PropertiesSerializer {
+
+ protected Properties prop = new Properties();
+ protected String propertyFilename = null;
+
+ public PropertiesSerializer()
+ {
+ }
+
+ public PropertiesSerializer(String propertyFilename)
+ {
+ this.propertyFilename = propertyFilename;
+ }
+
+ public void load(String propertyFilename)
+ {
+ try
+ {
+ prop.load(new FileInputStream(propertyFilename));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ Field[] fields = this.getClass().getFields();
+ for (int i = 0 ; i < fields.length; i++)
+ {
+ //fields[i].get(this);
+ String pName = fields[i].getName();
+ String pType = "String";
+ try
+ {
+ pType = fields[i].get(this).getClass().getSimpleName();
+ }
+ catch (Exception e)
+ {
+ //e.printStackTrace();
+ }
+ try
+ {
+ if (pType.equalsIgnoreCase("integer"))
+ fields[i].set(this, Integer.parseInt(prop.get(pName) + ""));
+ if (pType.equalsIgnoreCase("boolean"))
+ fields[i].set(this, Boolean.parseBoolean(prop.get(pName) + ""));
+ else
+ fields[i].set(this, prop.get(pName));
+ }
+ catch (Exception e)
+ {
+ //e.printStackTrace();
+ }
+ }
+ }
+
+ public void save()
+ {
+ Field[] fields = this.getClass().getFields();
+ for (int i = 0 ; i < fields.length; i++)
+ {
+ //fields[i].get(this);
+ try {
+ String pName = fields[i].getName();
+ //String pType = fields[i].get(this).getClass().getSimpleName();
+ if (fields[i].get(this) == null)
+ prop.setProperty(pName, "");
+ else
+ prop.setProperty(pName, fields[i].get(this) + "");
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ }
+ try
+ {
+ FileOutputStream fos = new FileOutputStream(propertyFilename);
+ prop.store(fos, "");
+ fos.flush();
+ fos.close();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public Object clone()
+ {
+ Object newObject = null;
+ try {
+ newObject = (Object)this.getClass().newInstance();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ Field[] fields = this.getClass().getFields();
+ for (int i = 0 ; i < fields.length; i++)
+ {
+ try {
+ //fields[i].get(this);
+ //String pName = fields[i].getName();
+ fields[i].set(newObject, fields[i].get(this));
+ }
+ catch (Exception e)
+ {
+ //e.printStackTrace();
+ }
+ }
+ return newObject;
+ }
+
+ public boolean hasChanged()
+ {
+ boolean changes = false;
+ Field[] fields = this.getClass().getFields();
+ for (int i = 0 ; i < fields.length; i++)
+ {
+ //fields[i].get(this);
+ try {
+ String pName = fields[i].getName();
+ //String pType = fields[i].get(this).getClass().getSimpleName();
+ if (prop.getProperty(pName).compareTo(fields[i].get(this)+"") != 0)
+ changes = true;
+
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ }
+ return changes;
+ }
+
+ public String getPropertyFilename()
+ {
+ return this.propertyFilename;
+ }
+
+ public void setPropertyFilename(String filename)
+ {
+ this.propertyFilename = filename;
+ }
+
+ public String readValue(String propertyName)
+ {
+ String val = "";
+ val = prop.getProperty(propertyName);
+ return val;
+ }
+
+ public void writeValue(String propertyName, String propertValue)
+ {
+ prop.setProperty(propertyName, propertValue);
+ try {
+ prop.store(new FileOutputStream(propertyFilename), null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
+
diff --git a/src/org/synthuse/RobotMacro.java b/src/org/synthuse/RobotMacro.java
new file mode 100644
index 0000000..ed86901
--- /dev/null
+++ b/src/org/synthuse/RobotMacro.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.awt.*;
+import java.awt.event.*;
+
+public class RobotMacro {
+
+ public static Exception lastException = null;
+
+ public static boolean executeCommand(String cmd) {
+ Runtime runtime = Runtime.getRuntime();
+ try {
+ runtime.exec(cmd);
+ } catch (Exception e) {
+ lastException = e;
+ return false;
+ }
+ return true;
+ }
+
+ public static void delay(int time) {
+ try {
+ Robot robot = new Robot();
+ robot.delay(time);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void moveMouse(int x, int y) {
+ try {
+ Robot robot = new Robot();
+ robot.mouseMove(x, y);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void leftClickMouse() {
+ try {
+ Robot robot = new Robot();
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void doubleClickMouse() {
+ try {
+ Robot robot = new Robot();
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ robot.delay(100);
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void rightClickMouse() {
+ try {
+ System.out.println("rightClickMouse");
+ Robot robot = new Robot();
+ //robot.mouseMove(200, 200);
+ //robot.delay(1000);
+ robot.mousePress(InputEvent.BUTTON3_MASK);
+ robot.mouseRelease(InputEvent.BUTTON3_MASK);
+ //System.out.println("rightClickMouse good");
+ } catch (Exception e) {
+ lastException = e;
+ e.printStackTrace();
+ }
+ }
+
+ public static void leftMouseDown() {
+ try {
+ Robot robot = new Robot();
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void leftMouseUp() {
+ try {
+ Robot robot = new Robot();
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void rightMouseDown() {
+ try {
+ Robot robot = new Robot();
+ robot.mousePress(InputEvent.BUTTON3_MASK);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void rightMouseUp() {
+ try {
+ Robot robot = new Robot();
+ robot.mouseRelease(InputEvent.BUTTON3_MASK);
+ } catch (Exception e) {
+ lastException = e;
+ e.printStackTrace();
+ }
+ }
+
+ public static void mouseMove(int x, int y) {
+ try {
+ Robot robot = new Robot();
+ robot.mouseMove(x, y);
+ } catch (Exception e) {
+ lastException = e;
+ e.printStackTrace();
+ }
+ }
+
+ public static void copyKey() {
+ try {
+ Robot robot = new Robot();
+ robot.keyPress(KeyEvent.VK_CONTROL);
+ robot.keyPress(KeyEvent.VK_C);
+ robot.keyRelease(KeyEvent.VK_C);
+ robot.keyRelease(KeyEvent.VK_CONTROL);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void pasteKey() {
+ try {
+ Robot robot = new Robot();
+ robot.keyPress(KeyEvent.VK_CONTROL);
+ robot.keyPress(KeyEvent.VK_V);
+ robot.keyRelease(KeyEvent.VK_V);
+ robot.keyRelease(KeyEvent.VK_CONTROL);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void escapeKey() {
+ try {
+ Robot robot = new Robot();
+ robot.keyPress(KeyEvent.VK_ESCAPE);
+ robot.keyRelease(KeyEvent.VK_ESCAPE);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void functionKey(int functionNum) {
+ try {
+ Robot robot = new Robot();
+ int keyCode = 0;
+ switch (functionNum) {
+ case 1: keyCode = KeyEvent.VK_F1; break;
+ case 2: keyCode = KeyEvent.VK_F2; break;
+ case 3: keyCode = KeyEvent.VK_F3; break;
+ case 4: keyCode = KeyEvent.VK_F4; break;
+ case 5: keyCode = KeyEvent.VK_F5; break;
+ case 6: keyCode = KeyEvent.VK_F6; break;
+ case 7: keyCode = KeyEvent.VK_F7; break;
+ case 8: keyCode = KeyEvent.VK_F8; break;
+ case 9: keyCode = KeyEvent.VK_F9; break;
+ case 10: keyCode = KeyEvent.VK_F10; break;
+ case 11: keyCode = KeyEvent.VK_F11; break;
+ case 12: keyCode = KeyEvent.VK_F12; break;
+ }
+ robot.keyPress(keyCode);
+ robot.keyRelease(keyCode);
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static void tildeKey() {
+ try {
+ Robot robot = new Robot();
+ pressKeyCodes(robot, new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_QUOTE});
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+
+ public static boolean sendKeys(String keyCommands) {
+ try {
+ Robot robot = new Robot();
+ for (int i = 0; i < keyCommands.length(); i++) {
+ char key = keyCommands.charAt(i);
+ int[] keyCode = getKeyCode(key);
+ pressKeyCodes(robot, keyCode);
+ }
+ } catch (Exception e) {
+ lastException = e;
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean pressKey(char key) {
+ try {
+ Robot robot = new Robot();
+ int[] keyCode = getKeyCode(key);
+ pressKeyCodes(robot, keyCode);
+ } catch (Exception e) {
+ lastException = e;
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean keyDown(char key) {
+ try {
+ Robot robot = new Robot();
+ int[] keyCodes = getKeyCode(key);
+ for (int i = 0; i < keyCodes.length; i++) {
+ robot.keyPress(keyCodes[i]);
+ //System.out.println("pressed: " + keyCodes[i]);
+ }
+ } catch (Exception e) {
+ lastException = e;
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean keyUp(char key) {
+ try {
+ Robot robot = new Robot();
+ int[] keyCodes = getKeyCode(key);
+ for (int i = keyCodes.length - 1; i >= 0; i--) {
+ robot.keyRelease(keyCodes[i]);
+ //System.out.println("released: " + keyCodes[i]);
+ }
+ } catch (Exception e) {
+ lastException = e;
+ return false;
+ }
+ return true;
+ }
+
+ public static void pressKeyCodes(Robot robot, int[] keyCodes) {
+ for (int i = 0; i < keyCodes.length; i++) {
+ robot.keyPress(keyCodes[i]);
+ //System.out.println("pressed: " + keyCodes[i]);
+ }
+ //robot.delay(50);
+ for (int i = keyCodes.length - 1; i >= 0; i--) {
+ robot.keyRelease(keyCodes[i]);
+ //System.out.println("released: " + keyCodes[i]);
+ }
+ }
+
+ public static int[] getKeyCode(char key) {
+ switch (key) {
+ case 'a': return(new int[]{KeyEvent.VK_A});
+ case 'b': return(new int[]{KeyEvent.VK_B});
+ case 'c': return(new int[]{KeyEvent.VK_C});
+ case 'd': return(new int[]{KeyEvent.VK_D});
+ case 'e': return(new int[]{KeyEvent.VK_E});
+ case 'f': return(new int[]{KeyEvent.VK_F});
+ case 'g': return(new int[]{KeyEvent.VK_G});
+ case 'h': return(new int[]{KeyEvent.VK_H});
+ case 'i': return(new int[]{KeyEvent.VK_I});
+ case 'j': return(new int[]{KeyEvent.VK_J});
+ case 'k': return(new int[]{KeyEvent.VK_K});
+ case 'l': return(new int[]{KeyEvent.VK_L});
+ case 'm': return(new int[]{KeyEvent.VK_M});
+ case 'n': return(new int[]{KeyEvent.VK_N});
+ case 'o': return(new int[]{KeyEvent.VK_O});
+ case 'p': return(new int[]{KeyEvent.VK_P});
+ case 'q': return(new int[]{KeyEvent.VK_Q});
+ case 'r': return(new int[]{KeyEvent.VK_R});
+ case 's': return(new int[]{KeyEvent.VK_S});
+ case 't': return(new int[]{KeyEvent.VK_T});
+ case 'u': return(new int[]{KeyEvent.VK_U});
+ case 'v': return(new int[]{KeyEvent.VK_V});
+ case 'w': return(new int[]{KeyEvent.VK_W});
+ case 'x': return(new int[]{KeyEvent.VK_X});
+ case 'y': return(new int[]{KeyEvent.VK_Y});
+ case 'z': return(new int[]{KeyEvent.VK_Z});
+ case 'A': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_A});
+ case 'B': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_B});
+ case 'C': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_C});
+ case 'D': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_D});
+ case 'E': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_E});
+ case 'F': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_F});
+ case 'G': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_G});
+ case 'H': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_H});
+ case 'I': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_I});
+ case 'J': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_J});
+ case 'K': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_K});
+ case 'L': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_L});
+ case 'M': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_M});
+ case 'N': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_N});
+ case 'O': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_O});
+ case 'P': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_P});
+ case 'Q': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_Q});
+ case 'R': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_R});
+ case 'S': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_S});
+ case 'T': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_T});
+ case 'U': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_U});
+ case 'V': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_V});
+ case 'W': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_W});
+ case 'X': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_X});
+ case 'Y': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_Y});
+ case 'Z': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_Z});
+ case '`': return(new int[]{KeyEvent.VK_BACK_QUOTE});
+ case '0': return(new int[]{KeyEvent.VK_0});
+ case '1': return(new int[]{KeyEvent.VK_1});
+ case '2': return(new int[]{KeyEvent.VK_2});
+ case '3': return(new int[]{KeyEvent.VK_3});
+ case '4': return(new int[]{KeyEvent.VK_4});
+ case '5': return(new int[]{KeyEvent.VK_5});
+ case '6': return(new int[]{KeyEvent.VK_6});
+ case '7': return(new int[]{KeyEvent.VK_7});
+ case '8': return(new int[]{KeyEvent.VK_8});
+ case '9': return(new int[]{KeyEvent.VK_9});
+ case '-': return(new int[]{KeyEvent.VK_MINUS});
+ case '=': return(new int[]{KeyEvent.VK_EQUALS});
+ case '~': return(new int[]{KeyEvent.VK_ENTER});//return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_QUOTE});
+ case '!': return(new int[]{KeyEvent.VK_EXCLAMATION_MARK});
+ case '@': return(new int[]{KeyEvent.VK_AT});
+ case '#': return(new int[]{KeyEvent.VK_NUMBER_SIGN});
+ case '$': return(new int[]{KeyEvent.VK_DOLLAR});
+ case '%': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_5});
+ case '^': return(new int[]{KeyEvent.VK_CIRCUMFLEX});
+ case '&': return(new int[]{KeyEvent.VK_AMPERSAND});
+ case '*': return(new int[]{KeyEvent.VK_ASTERISK});
+ case '(': return(new int[]{KeyEvent.VK_LEFT_PARENTHESIS});
+ case ')': return(new int[]{KeyEvent.VK_RIGHT_PARENTHESIS});
+ case '_': return(new int[]{KeyEvent.VK_UNDERSCORE});
+ case '+': return(new int[]{KeyEvent.VK_PLUS});
+ case '\t': return(new int[]{KeyEvent.VK_TAB});
+ case '\n': return(new int[]{KeyEvent.VK_ENTER});
+ case '[': return(new int[]{KeyEvent.VK_OPEN_BRACKET});
+ case ']': return(new int[]{KeyEvent.VK_CLOSE_BRACKET});
+ case '\\': return(new int[]{KeyEvent.VK_BACK_SLASH});
+ case '{': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_OPEN_BRACKET});
+ case '}': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_CLOSE_BRACKET});
+ case '|': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_SLASH});
+ case ';': return(new int[]{KeyEvent.VK_SEMICOLON});
+ case ':': return(new int[]{KeyEvent.VK_COLON});
+ case '\'': return(new int[]{KeyEvent.VK_QUOTE});
+ case '"': return(new int[]{KeyEvent.VK_QUOTEDBL});
+ case ',': return(new int[]{KeyEvent.VK_COMMA});
+ case '<': return(new int[]{KeyEvent.VK_LESS});
+ case '.': return(new int[]{KeyEvent.VK_PERIOD});
+ case '>': return(new int[]{KeyEvent.VK_GREATER});
+ case '/': return(new int[]{KeyEvent.VK_SLASH});
+ case '?': return(new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_SLASH}); // needs Shift
+ case ' ': return(new int[]{KeyEvent.VK_SPACE});
+ default:
+ throw new IllegalArgumentException("Cannot find Key Code for character " + key);
+ }
+ }
+}
diff --git a/src/org/synthuse/StatusWindow.java b/src/org/synthuse/StatusWindow.java
new file mode 100644
index 0000000..1623ef8
--- /dev/null
+++ b/src/org/synthuse/StatusWindow.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.JLabel;
+import javax.swing.JWindow;
+
+
+public class StatusWindow extends JWindow {
+
+ private static final long serialVersionUID = 1L;
+ public static int FONT_SIZE = 14;
+ public static int FONT_BOLD = Font.BOLD; //Font.PLAIN
+ public static int Y_BOTTOM_OFFSET = -100;
+ public static Color BACKGROUND_COLOR = Color.yellow;
+ public static Color FOREGROUND_COLOR = Color.black;
+ //private int displayTime = -1;
+ //private String displayText = "";
+ public StatusWindow(String lblText, int displayTime) {
+ super();
+ //this.displayTime = displayTime;
+ //this.displayText = lblText;
+ //this.setLayout(new FlowLayout());
+ JLabel lbl = new JLabel(lblText);
+ lbl.setFont(new Font(lbl.getName(), FONT_BOLD, FONT_SIZE));
+ lbl.setOpaque(true); //background isn't painted without this
+ lbl.setBackground(BACKGROUND_COLOR);
+ lbl.setForeground(FOREGROUND_COLOR);
+ this.getContentPane().setLayout(new FlowLayout());
+ this.getContentPane().add(lbl);
+ this.pack();
+ this.setVisible(true);
+ //
+ if (displayTime > 0) {
+ Timer timer = new Timer(true);
+ timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ StatusWindow.this.dispose();
+ }
+ }, displayTime);
+ }
+ }
+
+ @Override
+ public void setVisible(final boolean visible) {
+ super.setVisible(visible);
+ // ...and bring window to the front.. in a strange and weird way
+ if (visible) {
+ super.setAlwaysOnTop(true);
+ super.toFront();
+ super.requestFocus();
+ Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+ //this.setLocation(dim.width/2-this.getSize().width/2, dim.height/2-this.getSize().height/2);
+ this.setLocation(dim.width/2-this.getSize().width/2, dim.height + Y_BOTTOM_OFFSET );
+ }
+ }
+
+}
diff --git a/src/org/synthuse/SynthuseDlg.java b/src/org/synthuse/SynthuseDlg.java
new file mode 100644
index 0000000..f91c734
--- /dev/null
+++ b/src/org/synthuse/SynthuseDlg.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.awt.BorderLayout;
+import java.awt.EventQueue;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Toolkit;
+
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.JToolBar;
+import javax.swing.JSplitPane;
+import javax.swing.JRadioButton;
+import javax.swing.WindowConstants;
+
+import java.awt.Component;
+
+import javax.swing.Box;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+
+import java.awt.Dimension;
+
+import javax.swing.JComboBox;
+import javax.swing.JScrollPane;
+
+/*
+import com.jgoodies.forms.layout.FormLayout;
+import com.jgoodies.forms.layout.ColumnSpec;
+import com.jgoodies.forms.layout.RowSpec;
+import com.jgoodies.forms.factories.FormFactory;
+*/
+import com.sun.jna.platform.win32.WinDef.HWND;
+
+import javax.swing.JLabel;
+import javax.swing.SwingConstants;
+
+import java.awt.FlowLayout;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JTextPane;
+
+import org.synthuse.Api.User32;
+import org.synthuse.DragTarget.dragEvents;
+
+
+
+//public class SynthuseDlg extends JDialog {
+public class SynthuseDlg extends JFrame {
+ /**
+ *
+ */
+ public static String VERSION_STR = "1.0.5";
+
+ public static String RES_STR_MAIN_ICON = "/org/synthuse/img/gnome-robots.png";
+ public static String RES_STR_REFRESH_IMG = "/org/synthuse/img/rapidsvn.png";
+ public static String RES_STR_TESTIDE_IMG = "/org/synthuse/img/applications-education.png";
+ public static String RES_STR_HELP_IMG = "/org/synthuse/img/help-3.png";
+ public static String RES_STR_TARGET_IMG = "/org/synthuse/img/bullseye-logo-th18x18.png";
+ public static String RES_STR_FIND_IMG = "/org/synthuse/img/edit-find-3.png";
+ public static String RES_STR_SETACTION_IMG = "/org/synthuse/img/document-new-5.png";
+ public static String RES_STR_CANCEL_IMG = "/org/synthuse/img/document-close-2.png";
+
+ public static List actionListQueue = new ArrayList();
+ private static final long serialVersionUID = 1L;
+ private XpathManager.Events xpathEvents = null;
+ private JPanel contentPane;
+ private JLabel lblStatus;
+ private JButton btnRefresh;
+ private JTextPane textPane;
+ private JButton btnSetAction;
+ private JButton btnCancel;
+ private JButton btnFind;
+ public static Config config = new Config(Config.DEFAULT_PROP_FILENAME);
+ private String dialogResult = "";
+ private String lastDragHwnd = "";
+ private JComboBox cmbXpath;
+ private JButton btnTestIde;
+
+ private TestIdeFrame testIde = null;
+ private int targetX;
+ private int targetY;
+
+ /**
+ * Launch the application.
+ */
+ public static void main(String[] args) {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ SynthuseDlg frame = new SynthuseDlg();
+ frame.setVisible(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ /**
+ * Create the frame.
+ */
+ public SynthuseDlg() {
+ setIconImage(Toolkit.getDefaultToolkit().getImage(SynthuseDlg.class.getResource(RES_STR_MAIN_ICON)));
+ //setModal(true);
+ setTitle("Synthuse");
+ setBounds(100, 100, 827, 420);
+ contentPane = new JPanel();
+ contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
+ contentPane.setLayout(new BorderLayout(0, 0));
+ setContentPane(contentPane);
+
+ JToolBar toolBar = new JToolBar();
+ contentPane.add(toolBar, BorderLayout.NORTH);
+
+ JRadioButton rdbtnWindows = new JRadioButton("Windows Enumerated Xml");
+ rdbtnWindows.setSelected(true);
+ toolBar.add(rdbtnWindows);
+
+ Component horizontalStrut = Box.createHorizontalStrut(20);
+ toolBar.add(horizontalStrut);
+
+ JRadioButton rdbtnUrl = new JRadioButton("Url: ");
+ rdbtnUrl.setEnabled(false);
+ toolBar.add(rdbtnUrl);
+
+ // Group the radio buttons.
+ ButtonGroup group = new ButtonGroup();
+ group.add(rdbtnWindows);
+ group.add(rdbtnUrl);
+
+ JComboBox cmbUrl = new JComboBox();
+ cmbUrl.setEnabled(false);
+ cmbUrl.setEditable(true);
+ toolBar.add(cmbUrl);
+
+ Component horizontalStrut_1 = Box.createHorizontalStrut(20);
+ toolBar.add(horizontalStrut_1);
+
+ btnRefresh = new JButton(" Refresh ");
+ btnRefresh.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ //lblStatus.setText("Loading Windows Enumerated Xml...");
+ WindowsEnumeratedXml.getXmlThreaded(textPane, lblStatus);
+ //appendToPane(WindowsEnumeratedXml.getXml());
+ //lblStatus.setText("Windows Enumerated Xml loaded");
+ }
+ });
+ btnRefresh.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_REFRESH_IMG)));
+ toolBar.add(btnRefresh);
+
+ Component horizontalStrut_3 = Box.createHorizontalStrut(20);
+ toolBar.add(horizontalStrut_3);
+
+ btnTestIde = new JButton("Test IDE");
+ btnTestIde.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ if (testIde == null) {
+ testIde = new TestIdeFrame();
+ if (SynthuseDlg.actionListQueue.size() > 0) { // if stuff is already in the queue add it to the test ide
+ for (String action : SynthuseDlg.actionListQueue) {
+ testIde.txtTest.append(action + "\n");
+ }
+ }
+ }
+ testIde.setVisible(true);
+ }
+ });
+ btnTestIde.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_TESTIDE_IMG)));
+ toolBar.add(btnTestIde);
+
+ Component horizontalStrut_2 = Box.createHorizontalStrut(20);
+ toolBar.add(horizontalStrut_2);
+
+ JButton helpBtn = new JButton("Help");
+ helpBtn.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_HELP_IMG)));
+ helpBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ JOptionPane.showMessageDialog(null, "Version " + VERSION_STR + " create by Edward Jakubowski ejakubowski7@gmail.com", "About", JOptionPane.QUESTION_MESSAGE);
+ }
+ });
+ toolBar.add(helpBtn);
+
+ JSplitPane splitPane = new JSplitPane();
+ splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
+ contentPane.add(splitPane, BorderLayout.CENTER);
+
+ JPanel panel = new JPanel();
+ panel.setMinimumSize(new Dimension(20, 35));
+ splitPane.setLeftComponent(panel);
+ /*
+ panel.setLayout(new FormLayout(new ColumnSpec[] {
+ FormFactory.RELATED_GAP_COLSPEC,
+ ColumnSpec.decode("left:max(11dlu;default)"),
+ FormFactory.RELATED_GAP_COLSPEC,
+ ColumnSpec.decode("left:default"),
+ FormFactory.RELATED_GAP_COLSPEC,
+ FormFactory.DEFAULT_COLSPEC,
+ FormFactory.RELATED_GAP_COLSPEC,
+ FormFactory.DEFAULT_COLSPEC,
+ FormFactory.RELATED_GAP_COLSPEC,
+ FormFactory.DEFAULT_COLSPEC,},
+ new RowSpec[] {
+ FormFactory.RELATED_GAP_ROWSPEC,
+ RowSpec.decode("default:grow"),}));
+ */
+ panel.setLayout(new GridBagLayout());
+
+ GridBagConstraints c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 0.5;
+ c.gridwidth = 1;
+ c.gridx = 0;
+ c.gridy = 0;
+ c.insets = new Insets(3,3,3,3); // add padding around objects
+
+ DragTarget lblTarget = new DragTarget();
+
+ lblTarget.setHorizontalAlignment(SwingConstants.CENTER);
+ lblTarget.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_TARGET_IMG)));
+ panel.add(lblTarget, c);
+
+ btnFind = new JButton("Find");
+ btnFind.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ String xpathItem = cmbXpath.getSelectedItem().toString();
+ int matches = XpathManager.nextXpathMatch(xpathItem, textPane, lblStatus, false);
+ if (matches < 0) //check for an error
+ return; //don't save bad xpath to combobox
+ if (config.xpathList == null)
+ config.xpathList = "";
+ if (!config.xpathList.contains(xpathItem + "º")){
+ config.xpathList += xpathItem + "º";
+ refreshDatabinding();
+ cmbXpath.setSelectedItem(xpathItem);
+ }
+ }
+ });
+
+ cmbXpath = new JComboBox();
+ cmbXpath.setPreferredSize(new Dimension(440, 20));//fix the width of the combobox
+ cmbXpath.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");//fix the width of the combobox
+ cmbXpath.setEditable(true);
+ cmbXpath.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent event) {
+ if (event.getKeyChar() == KeyEvent.VK_ENTER) {
+ btnFind.doClick();
+ }
+ }
+ });
+ c.gridwidth = 3;
+ c.gridx = 1;
+ panel.add(cmbXpath, c);
+ btnFind.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_FIND_IMG)));
+ c.gridwidth = 1;
+ c.gridx = 4;
+ panel.add(btnFind, c);
+
+
+ btnSetAction = new JButton("Set Action");
+ btnSetAction.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ CommandPopupMenu menu = new CommandPopupMenu();
+ final String xpathItem = cmbXpath.getSelectedItem().toString();
+ menu.events = new CommandPopupMenu.menuEvents() {
+ @Override
+ public void menuItemClicked(String command, int paramCount, boolean useXpath, ActionEvent e) {
+ String actionStr = CommandPopupMenu.buildSkeletonCommand(command, paramCount, xpathItem, useXpath);
+ SynthuseDlg.actionListQueue.add(actionStr);
+ lblStatus.setText("Setting Action: " + actionStr);
+ if (testIde != null)
+ testIde.txtTest.append(actionStr + "\n");
+ }
+ };
+ Component c = (Component) e.getSource();
+ menu.show(c, -1, c.getHeight());
+ }
+ });
+ btnSetAction.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_SETACTION_IMG)));
+ c.gridwidth = 1;
+ c.gridx = 5;
+ panel.add(btnSetAction, c);
+
+ btnCancel = new JButton("Cancel");
+ btnCancel.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ dialogResult = "";
+ SynthuseDlg.this.dispose();
+ }
+ });
+ btnCancel.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_CANCEL_IMG)));
+ c.gridwidth = 1;
+ c.gridx = 6;
+ panel.add(btnCancel, c);
+
+ JScrollPane scrollPane = new JScrollPane();
+ splitPane.setRightComponent(scrollPane);
+
+ textPane = new JTextPane();
+ textPane.setEditable(false);
+ textPane.setEditorKitForContentType("text/xml", new XmlEditorKit());
+ textPane.setContentType("text/xml");
+
+ scrollPane.setViewportView(textPane);
+
+ JPanel panel_1 = new JPanel();
+ FlowLayout flowLayout = (FlowLayout) panel_1.getLayout();
+ flowLayout.setAlignment(FlowLayout.LEFT);
+ panel_1.setMinimumSize(new Dimension(20, 20));
+ contentPane.add(panel_1, BorderLayout.SOUTH);
+
+ lblStatus = new JLabel("Status:");
+ lblStatus.setHorizontalAlignment(SwingConstants.LEFT);
+ lblStatus.setVerticalAlignment(SwingConstants.TOP);
+ panel_1.add(lblStatus);
+ xpathEvents = new XpathManager.Events() {
+ @Override
+ public void statusChanged(String status) {
+ lblStatus.setText(status);
+ }
+ @Override
+ public void executionCompleted(Object input, String results) { //buildXpathStatementThreaded finished, with results being the xpath statement
+ if (input instanceof HWND) { // in case thread takes long time to process
+ lastDragHwnd = Api.GetHandleAsString((HWND)input);
+ }
+ XpathManager.nextXpathMatch(results, textPane, lblStatus, true);
+ cmbXpath.setSelectedItem(results);
+ }
+ };
+
+ lblTarget.events = new dragEvents() {
+ public void dragStarted(JComponent c) {
+ //might be nice to minimize this window, if we weren't displaying realtime information about each window
+ }
+ public void dragMouseMoved(int x, int y) {
+ targetX = x;
+ targetY = y;
+ targetDragged();
+ }
+ public void dropped(JComponent c) {
+ lastDragHwnd = ""; //sometimes with multithreaded the order becomes incorrect, so we may want to refresh this on dropped.
+ targetDragged();
+ }
+ };
+
+ this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ this.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent arg0) {
+
+ config.save();
+ SynthuseDlg.this.dispose(); // force app to close
+ }
+ });
+
+ btnRefresh.doClick();
+ refreshDatabinding();
+ }
+
+ /*
+ @Override
+ public void setVisible(final boolean visible) {
+ // make sure that frame is marked as not disposed if it is asked to be visible
+ if (visible) {
+ //setDisposed(false);
+ }
+ // let's handle visibility...
+ if (!visible || !isVisible()) { // have to check this condition simply because super.setVisible(true) invokes toFront if frame was already visible
+ super.setVisible(visible);
+ }
+ // ...and bring frame to the front.. in a strange and weird way
+ if (visible) {
+ int state = super.getExtendedState();
+ state &= ~JFrame.ICONIFIED;
+ super.setExtendedState(state);
+ super.setAlwaysOnTop(true);
+ super.toFront();
+ super.requestFocus();
+ super.setAlwaysOnTop(false);
+ }
+ }
+ */
+
+ public String showDialog() {
+ //this.setModal(true);
+ Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+ this.setLocation(dim.width/2-this.getSize().width/2, dim.height/2-this.getSize().height/2);
+ this.setVisible(true);
+ return dialogResult;
+ }
+
+ public void refreshDatabinding() {
+ if (config.xpathList != null)
+ cmbXpath.setModel(new DefaultComboBoxModel(config.xpathList.split("º")));
+ if (config.xpathHightlight != null)
+ XmlEditorKit.TAG_HIGHLIGHTED = config.xpathHightlight;
+ }
+
+ public void targetDragged() {
+ HWND hwnd = Api.GetWindowFromPoint(new Point(targetX,targetY));
+ String handleStr = Api.GetHandleAsString(hwnd);
+ String classStr = WindowsEnumeratedXml.escapeXmlAttributeValue(Api.GetWindowClassName(hwnd));
+ String parentStr = Api.GetHandleAsString(User32.instance.GetParent(hwnd));
+
+ lblStatus.setText("class: " + classStr + " hWnd: " + handleStr + " parent: " + parentStr + " X,Y: " + targetX + ", " + targetY);
+ if (!lastDragHwnd.equals(handleStr)) {
+ if (!lastDragHwnd.isEmpty()) {
+ Api.refreshWindow(Api.GetHandleFromString(lastDragHwnd));
+ }
+ lastDragHwnd = handleStr;
+ //lastDragHwnd = (hwnd + "");
+ Api.highlightWindow(hwnd);
+ XpathManager.buildXpathStatementThreaded(hwnd, textPane, xpathEvents);
+ }
+ }
+}
diff --git a/src/org/synthuse/TestIdeFrame.java b/src/org/synthuse/TestIdeFrame.java
new file mode 100644
index 0000000..9a36f81
--- /dev/null
+++ b/src/org/synthuse/TestIdeFrame.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.awt.BorderLayout;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+import javax.swing.ImageIcon;
+import javax.swing.JToolBar;
+import javax.swing.JButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.WindowConstants;
+
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.Toolkit;
+
+import javax.swing.JLabel;
+
+public class TestIdeFrame extends JFrame {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private JPanel contentPane;
+ public JTextArea txtTest;
+ private JButton btnRun;
+ private JButton btnClear;
+ private JButton btnCopy;
+ private JLabel lblStatus;
+
+ /**
+ * Create the frame.
+ */
+
+ public TestIdeFrame() {
+ setTitle("Test IDE - Synthuse");
+ setIconImage(Toolkit.getDefaultToolkit().getImage(TestIdeFrame.class.getResource("/org/qedsys/synthuse/applications-education.png")));
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setBounds(100, 100, 700, 367);
+ contentPane = new JPanel();
+ contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
+ contentPane.setLayout(new BorderLayout(0, 0));
+ setContentPane(contentPane);
+
+ JToolBar toolBar = new JToolBar();
+ contentPane.add(toolBar, BorderLayout.NORTH);
+
+ btnRun = new JButton("Run");
+ btnRun.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ if (btnRun.getText().equals("Run")) {
+ btnRun.setText("Stop");
+ btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource("/org/qedsys/synthuse/dialog-close.png")));
+ CommandProcessor.STOP_PROCESSOR.set(false);
+ CommandProcessor.executeThreaded(txtTest.getText(), new CommandProcessor.Events() {
+ @Override
+ public void statusChanged(String status) {
+ lblStatus.setText(status);
+ }
+ @Override
+ public void executionCompleted() {
+ btnRun.setText("Run");
+ btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource("/org/qedsys/synthuse/arrow-right-3.png")));
+ }
+ });
+ }
+ else {
+ CommandProcessor.STOP_PROCESSOR.set(true);
+ //btnRun.setText("Run");
+ //btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource("/org/qedsys/synthuse/arrow-right-3.png")));
+ }
+ }
+ });
+ btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource("/org/qedsys/synthuse/arrow-right-3.png")));
+ toolBar.add(btnRun);
+
+ btnClear = new JButton("Clear");
+ btnClear.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ txtTest.setText("");
+ }
+ });
+ btnClear.setIcon(new ImageIcon(SynthuseDlg.class.getResource("/org/qedsys/synthuse/user-trash-2.png")));
+ toolBar.add(btnClear);
+
+ btnCopy = new JButton("Copy Script");
+ btnCopy.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ StringSelection stringSelection = new StringSelection(txtTest.getText());
+ Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard();
+ clpbrd.setContents(stringSelection, null);
+ //StatusWindow sw = new StatusWindow("this is a test BLAH really long string goes here to test status of stuff yaya!!!!123123 123 123", 4000);
+ }
+ });
+ btnCopy.setIcon(new ImageIcon(SynthuseDlg.class.getResource("/org/qedsys/synthuse/edit-copy-7.png")));
+ toolBar.add(btnCopy);
+
+ JScrollPane scrollPane = new JScrollPane();
+ contentPane.add(scrollPane, BorderLayout.CENTER);
+
+ txtTest = new JTextArea();
+ txtTest.setText("Click the Run button above to test the script below...\r\n\r\n");
+ scrollPane.setViewportView(txtTest);
+
+ lblStatus = new JLabel(" ");
+ contentPane.add(lblStatus, BorderLayout.SOUTH);
+
+ this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+ this.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ //TestIdeFrame.this.setVisible(false);
+ TestIdeFrame.this.dispose();
+ }
+ });
+ }
+
+}
diff --git a/src/org/synthuse/WindowInfo.java b/src/org/synthuse/WindowInfo.java
new file mode 100644
index 0000000..e36f989
--- /dev/null
+++ b/src/org/synthuse/WindowInfo.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import org.synthuse.Api.*;
+
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.platform.win32.WinDef.HWND;
+import com.sun.jna.platform.win32.WinDef.RECT;
+import com.sun.jna.ptr.PointerByReference;
+
+public class WindowInfo {
+
+ public HWND hwnd;
+ public String hwndStr = "";
+ public HWND parent = null;
+ public String parentStr = "";
+ public RECT rect;
+ public String text;
+ public String className = "";
+ public boolean isChild = false;
+ public String processName = "";
+ public long pid = 0;
+ public Object xmlObj = null;
+
+ public WindowInfo(HWND hWnd, boolean isChild) {
+ byte[] buffer = new byte[1024];
+ User32.instance.GetWindowTextA(hWnd, buffer, buffer.length);
+ text = Native.toString(buffer);
+ if (text.isEmpty())
+ text = new Api().sendWmGetText(hWnd);
+
+ char[] buffer2 = new char[1026];
+ User32.instance.GetClassName(hWnd, buffer2, 1026);
+ className = Native.toString(buffer2);
+
+ rect = new RECT();
+ User32.instance.GetWindowRect(hWnd, rect);
+
+ this.isChild = isChild;
+ if (isChild) {
+ parent = User32.instance.GetParent(hWnd);
+ parentStr = Api.GetHandleAsString(parent);
+ }
+ else {
+ //User32.instance.GetWindowModuleFileName(hWnd, path, 512);
+ PointerByReference pointer = new PointerByReference();
+ User32.instance.GetWindowThreadProcessId(hWnd, pointer);
+ Pointer p = pointer.getPointer();
+ pid = p.getLong(0);
+ Pointer process = Kernel32.instance.OpenProcess(Api.PROCESS_QUERY_INFORMATION | Api.PROCESS_VM_READ, false, pointer.getValue());
+ Psapi.instance.GetModuleBaseNameW(process, null, buffer2, 512);
+ processName = Native.toString(buffer2);
+ //processName = Native.toString(path);
+ }
+ this.hwnd = hWnd;
+ hwndStr = Api.GetHandleAsString(hWnd);
+
+ }
+
+ public String toString() {
+ return String.format("(%d,%d)-(%d,%d) : \"%s\" [%s] {%s}", rect.left,rect.top,rect.right,rect.bottom,text,className,hwnd.toString());
+ }
+
+}
diff --git a/src/org/synthuse/WindowsEnumeratedXml.java b/src/org/synthuse/WindowsEnumeratedXml.java
new file mode 100644
index 0000000..30f02c0
--- /dev/null
+++ b/src/org/synthuse/WindowsEnumeratedXml.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.sql.Timestamp;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JLabel;
+import javax.swing.JTextPane;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import com.sun.jna.Pointer;
+import com.sun.jna.platform.win32.WinUser;
+import com.sun.jna.platform.win32.WinDef.HWND;
+
+public class WindowsEnumeratedXml implements Runnable{
+ public static Exception lastException = null;
+
+ public JTextPane outputPane = null;
+ public JLabel lblStatus = null;
+ public WindowsEnumeratedXml() {
+ }
+
+ public WindowsEnumeratedXml(JTextPane outputPane, JLabel lblStatus) {
+ this.outputPane = outputPane;
+ this.lblStatus = lblStatus;
+ }
+
+ @Override
+ public void run() {
+ lblStatus.setText("Loading Windows Enumerated Xml...");
+ long startTime = System.nanoTime();
+ outputPane.setText(getXml());
+ outputPane.setCaretPosition(0);
+ double seconds = ((double)(System.nanoTime() - startTime) / 1000000000);
+ lblStatus.setText("Windows Enumerated Xml loaded in " + new DecimalFormat("#.###").format(seconds) + " seconds");
+ }
+
+ public static void getXmlThreaded(JTextPane outputPane, JLabel lblStatus) {
+ Thread t = new Thread(new WindowsEnumeratedXml(outputPane, lblStatus));
+ t.start();
+ }
+
+ public static String getXml() {
+ final Map infoList = new LinkedHashMap();
+ final Map processList = new LinkedHashMap();
+
+ class ChildWindowCallback implements WinUser.WNDENUMPROC {
+ @Override
+ public boolean callback(HWND hWnd, Pointer lParam) {
+ infoList.put(Api.GetHandleAsString(hWnd), new WindowInfo(hWnd, true));
+ return true;
+ }
+ }
+
+ class ParentWindowCallback implements WinUser.WNDENUMPROC {
+ @Override
+ public boolean callback(HWND hWnd, Pointer lParam) {
+ infoList.put(Api.GetHandleAsString(hWnd), new WindowInfo(hWnd, false));
+ Api.User32.instance.EnumChildWindows(hWnd, new ChildWindowCallback(), new Pointer(0));
+ return true;
+ }
+ }
+ Api.User32.instance.EnumWindows(new ParentWindowCallback(), 0);
+
+ // convert window info list to xml dom
+ try {
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
+
+ // root elements
+ Document doc = docBuilder.newDocument();
+ Element rootElement = doc.createElement("EnumeratedWindows");
+ doc.appendChild(rootElement);
+
+ long parentCount = 0;
+ long childCount = 0;
+ for (String handle : infoList.keySet()) {
+ WindowInfo w = infoList.get(handle);
+ //System.out.println(w);
+ // create new win xml element
+ Element win = doc.createElement("win");
+ win.setAttribute("hwnd", w.hwndStr);
+ win.setAttribute("text", w.text);
+ win.setAttribute("TEXT", w.text.toUpperCase());
+ win.setAttribute("class", w.className);
+ win.setAttribute("CLASS", w.className.toUpperCase());
+ if (!w.isChild) {
+ parentCount++;
+ if (w.processName != null && !w.processName.isEmpty()) {
+ if (!processList.containsKey(w.pid+""))
+ processList.put(w.pid+"", w.hwndStr);
+ win.setAttribute("process", w.processName);
+ win.setAttribute("PROCESS", w.processName.toUpperCase());
+ win.setAttribute("pid", w.pid+"");
+ }
+ }
+ //else
+ //win.setAttribute("parent", w.parent + ""); // not really needed since child node is append to parent node
+
+ if (w.isChild && infoList.containsKey(w.parentStr)) {
+ childCount++;
+ WindowInfo parentWi = infoList.get(w.parentStr);
+ if (parentWi.xmlObj != null)
+ ((Element)parentWi.xmlObj).appendChild(win);
+ else
+ rootElement.appendChild(win);
+ }
+ else
+ rootElement.appendChild(win);
+ w.xmlObj = win;
+
+ }
+
+ // calculate totals on various windows.
+ Element totals = doc.createElement("totals");
+ totals.setAttribute("parentCount", parentCount+"");
+ totals.setAttribute("childCount", childCount+"");
+ totals.setAttribute("windowCount", infoList.size()+"");
+ totals.setAttribute("processCount", processList.size()+"");
+ totals.setAttribute("updatedLast", new Timestamp((new Date()).getTime()) + "");
+ rootElement.appendChild(totals);
+ String output = nodeToString(rootElement);
+ //System.out.println("count - " + infoList.size() + "\r\n");
+ return output;
+ } catch (Exception e) {
+ e.printStackTrace();
+ lastException = e;
+ }
+ return "";
+ }
+
+ public static String escapeXmlAttributeValue(String unescapedStr) {
+ String result = "";
+ try {
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
+ // root elements
+ Document doc = docBuilder.newDocument();
+ Element rootElement = doc.createElement("temp");
+ doc.appendChild(rootElement);
+ Attr attribute = doc.createAttribute("attrib");
+ attribute.setNodeValue(unescapedStr);
+ rootElement.setAttributeNode(attribute);
+ result = nodeToString(rootElement);
+ result = result.replaceAll("[^\"]*\"([^\"]*)\"[^\"]*", "$1"); // keep the string within quotes.
+ } catch (Exception e) {
+ e.printStackTrace();
+ //lastException = e;
+ }
+ return result;
+ }
+
+ private static String nodeToString(Node node) {
+ StringWriter sw = new StringWriter();
+ try {
+ Transformer t = TransformerFactory.newInstance().newTransformer();
+ t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ t.setOutputProperty(OutputKeys.INDENT, "yes");
+ t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+ t.transform(new DOMSource(node), new StreamResult(sw));
+ } catch (Exception e) {
+ e.printStackTrace();
+ lastException = e;
+ }
+ return sw.toString();
+ }
+
+ public static String queryWindowInfoXml(String xpathExpr) {
+ String nl = System.getProperty("line.separator");
+ String result = "" + nl;
+ String xml = getXml();
+ List resultList = evaluateXpathGetValues(xml, xpathExpr);
+ for(String item: resultList) {
+ result += " " + item;
+ }
+ result += "" + nl;
+ return result;
+ }
+
+ public static HWND queryHandleWindowInfoXml(String xpathExpr) {
+ String xml = getXml();
+ String result = "";
+ List resultList = evaluateXpathGetValues(xml, xpathExpr);
+ for(String item: resultList) {
+ if (item.contains("hwnd")) {
+ List hwndList = evaluateXpathGetValues(item, "//@hwnd");
+ result = hwndList.get(0);
+ }
+ else
+ result = item;
+ break;
+ }
+ return Api.GetHandleFromString(result);
+ }
+
+
+ public static List evaluateXpathGetValues(String xml, String xpathExpr) {
+ List resultLst = new ArrayList();
+ try {
+ InputSource inSource = new InputSource(new StringReader(xml));
+ XPathFactory factory = XPathFactory.newInstance();
+ XPath xpath = factory.newXPath();
+ XPathExpression expr = xpath.compile(xpathExpr);
+
+ Object result = expr.evaluate(inSource, XPathConstants.NODESET);
+ NodeList nodes = (NodeList) result;
+ for (int i = 0; i < nodes.getLength(); i++) {
+ String val = nodes.item(i).getNodeValue();
+ if (val == null) // if we can't get a string value try to transform the xml node to a string
+ val = nodeToString(nodes.item(i));
+ else
+ val += System.getProperty("line.separator");
+ resultLst.add(val);
+ //System.out.println("match " + i + ": " + val);
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ lastException = e;
+ }
+ return resultLst;
+ }
+
+}
diff --git a/src/org/synthuse/XmlEditorKit.java b/src/org/synthuse/XmlEditorKit.java
new file mode 100644
index 0000000..4e01166
--- /dev/null
+++ b/src/org/synthuse/XmlEditorKit.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.util.*;
+import java.util.regex.*;
+import javax.swing.text.*;
+
+/* Example:
+
+ // Set editor kit
+ jtextpane.setEditorKitForContentType("text/xml", new XmlEditorKit());
+ jtextpane.setContentType("text/xml");
+
+ */
+
+public class XmlEditorKit extends StyledEditorKit {
+
+ private static final long serialVersionUID = 2969169649596107757L;
+ private ViewFactory xmlViewFactory;
+
+ private static HashMap patternColors;
+ private static String TAG_PATTERN = "(?[a-z]*)\\s?>?";
+ private static String TAG_END_PATTERN = "(/>)";
+ private static String TAG_ATTRIBUTE_PATTERN = "\\s([a-zA-Z0-9]*)\\s*?\\=\\s*?\\\"";
+ private static String TAG_ATTRIBUTE_VALUE = "[a-zA-Z0-9]*\\=(\"[^\"]*\")";
+ private static String TAG_COMMENT = "()";
+ private static String TAG_CDATA_START = "(\\)";
+
+ //public static String TAG_HIGHLIGHTED = ".*hwnd=\"([^\"]*)\".*";
+ public static String TAG_HIGHLIGHTED = "";
+ public static Color TAG_HIGHLIGHTED_COLOR = new Color(170, 255, 48);
+
+ public static int HIGHLIGHTED_START = 0;
+ public static int HIGHLIGHTED_END = 0;
+ public static Color HIGHLIGHTED_COLOR = new Color(240, 255, 112);
+
+ static {
+ // NOTE: the order is important!
+ patternColors = new HashMap();
+ patternColors.put(Pattern.compile(TAG_CDATA_START), new Color(128, 128, 128));
+ patternColors.put(Pattern.compile(TAG_CDATA_END), new Color(128, 128, 128));
+ patternColors.put(Pattern.compile(TAG_PATTERN), new Color(63, 127, 127));
+ patternColors.put(Pattern.compile(TAG_ATTRIBUTE_PATTERN), new Color(127, 0, 127));
+ patternColors.put(Pattern.compile(TAG_END_PATTERN), new Color(63, 127, 127));
+ patternColors.put(Pattern.compile(TAG_ATTRIBUTE_VALUE), new Color(42, 0, 255));
+ patternColors.put(Pattern.compile(TAG_COMMENT), new Color(63, 95, 191));
+ }
+
+
+ class XmlView extends PlainView {
+
+
+ public XmlView(Element element) {
+
+ super(element);
+
+ // Set tabsize to 4 (instead of the default 8)
+ getDocument().putProperty(PlainDocument.tabSizeAttribute, 4);
+ }
+
+ @Override
+ protected int drawUnselectedText(Graphics graphics, int x, int y, int p0, int p1) throws BadLocationException {
+ try {
+
+ Document doc = getDocument();
+ String text = doc.getText(p0, p1 - p0);
+
+ Segment segment = getLineBuffer();
+ int initialXpos = x;
+
+ SortedMap startMap = new TreeMap();
+ SortedMap colorMap = new TreeMap();
+
+ // Match all regexes on this snippet, store positions
+ for (Map.Entry entry : patternColors.entrySet()) {
+
+ Matcher matcher = entry.getKey().matcher(text);
+
+ while (matcher.find()) {
+ startMap.put(matcher.start(1), matcher.end(1));
+ colorMap.put(matcher.start(1), entry.getValue());
+ }
+ }
+
+ // TODO: check the map for overlapping parts
+
+ int i = 0;
+ //add tag highlighted background colors
+ if (!TAG_HIGHLIGHTED.isEmpty()) {
+ Matcher highlightMatcher = Pattern.compile(TAG_HIGHLIGHTED).matcher(text);
+ while(highlightMatcher.find()) {
+ int start = highlightMatcher.start(1);
+ int end = highlightMatcher.end(1);
+ if (i < start) {
+ graphics.setColor(Color.black);
+ doc.getText(p0 + i, start - i, segment);
+ x = Utilities.drawTabbedText(segment, x, y, graphics, this, i);
+ }
+ graphics.setColor(TAG_HIGHLIGHTED_COLOR);
+ i = end;
+ doc.getText(p0 + start, i - start, segment);
+ int width = Utilities.getTabbedTextWidth(segment, graphics.getFontMetrics(), x, this, p0 + start);
+ //graphics.drawLine(x, y, width, y);graphics.getFontMetrics()
+ graphics.fillRect(x, y - graphics.getFontMetrics().getHeight() + 2, width, graphics.getFontMetrics().getHeight());
+ graphics.setColor(Color.black);
+ doc.getText(p0 + start, i - start, segment);
+ x = Utilities.drawTabbedText(segment, x, y, graphics, this, start);
+ }
+ }
+ x = initialXpos;
+ i=0;
+ //add highlighted background colors based on position
+ //String textx = doc.getText(p0, p1 - p0);
+ if ((HIGHLIGHTED_START < p1 && HIGHLIGHTED_START >= p0) || (HIGHLIGHTED_END <= p1 && HIGHLIGHTED_END > p0) || ( HIGHLIGHTED_START < p1 && HIGHLIGHTED_END > p0)) {
+ //select whole line
+ int start = 0;
+ int end = text.length();
+ // test to see if only partial line is needed.
+ if (HIGHLIGHTED_START > p0)
+ start = HIGHLIGHTED_START - p0;
+ if (HIGHLIGHTED_END < p1)
+ end -= p1 - HIGHLIGHTED_END;
+ if (i < start) { // fill in normal color if start highlight isn't at the beginning
+ graphics.setColor(Color.black);
+ doc.getText(p0 + i, start - i, segment);
+ x = Utilities.drawTabbedText(segment, x, y, graphics, this, i);
+ }
+ graphics.setColor(HIGHLIGHTED_COLOR);// fill in the highlight color
+ i = end;
+ if (i - start > 0) {
+ doc.getText(p0 + start, i - start, segment);
+ int width = Utilities.getTabbedTextWidth(segment, graphics.getFontMetrics(), x, this, p0 + start);
+ //graphics.drawLine(x, y, width, y);graphics.getFontMetrics()
+ graphics.fillRect(x, y - graphics.getFontMetrics().getHeight() + 2, width, graphics.getFontMetrics().getHeight());
+ graphics.setColor(Color.black);
+ doc.getText(p0 + start, i - start, segment);
+ x = Utilities.drawTabbedText(segment, x, y, graphics, this, start);
+ }
+ //else
+ // System.out.println("invalid highlighting " + (i - start) + " is <= 0 (" + p0 + "-" + p1 + "=" + (p1 - p0) +") " + start + ", " + end + " len=" + text.length());
+ }
+
+ x = initialXpos;
+ i=0;
+ // Color the parts of xml foreground font
+ for (Map.Entry entry : startMap.entrySet()) {
+ int start = entry.getKey();
+ int end = entry.getValue();
+
+ if (i < start) {
+ graphics.setColor(Color.black);
+ doc.getText(p0 + i, start - i, segment);
+ x = Utilities.drawTabbedText(segment, x, y, graphics, this, i);
+ }
+
+ graphics.setColor(colorMap.get(start));
+ i = end;
+ doc.getText(p0 + start, i - start, segment);
+ x = Utilities.drawTabbedText(segment, x, y, graphics, this, start);
+ }
+
+
+ // Paint possible remaining text black
+ if (i < text.length()) {
+ graphics.setColor(Color.black);
+ doc.getText(p0 + i, text.length() - i, segment);
+ x = Utilities.drawTabbedText(segment, x, y, graphics, this, i);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return x;
+ }
+ }
+
+
+ class XmlViewFactory extends Object implements ViewFactory {
+ /**
+ * @see javax.swing.text.ViewFactory#create(javax.swing.text.Element)
+ */
+ public View create(Element element) {
+
+ return new XmlView(element);
+ }
+ }
+
+ public XmlEditorKit() {
+ xmlViewFactory = new XmlViewFactory();
+ }
+
+ @Override
+ public ViewFactory getViewFactory() {
+ return xmlViewFactory;
+ }
+
+ @Override
+ public String getContentType() {
+ return "text/xml";
+ }
+}
diff --git a/src/org/synthuse/XpathManager.java b/src/org/synthuse/XpathManager.java
new file mode 100644
index 0000000..330d61e
--- /dev/null
+++ b/src/org/synthuse/XpathManager.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2014, Synthuse.org
+ * Released under the Apache Version 2.0 License.
+ *
+ * last modified by ejakubowski
+*/
+
+package org.synthuse;
+
+import java.awt.EventQueue;
+import java.util.List;
+
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JTextPane;
+
+import org.synthuse.Api.User32;
+
+import com.sun.jna.platform.win32.WinDef.HWND;
+
+public class XpathManager implements Runnable{
+
+ private HWND hwnd = null;
+ private JTextPane windowsXmlTextPane = null;
+
+ public static interface Events {
+ void statusChanged(String status);
+ void executionCompleted(Object input, String results);
+ }
+ public Events events = new Events() {
+ public void statusChanged(String status){
+ System.out.println(status);
+ }
+ public void executionCompleted(Object input, String results){
+
+ }
+ };
+
+ public XpathManager(HWND hwnd, JTextPane windowsXmlTextPane, Events events) {
+ this.events = events;
+ this.hwnd = hwnd;
+ this.windowsXmlTextPane = windowsXmlTextPane;
+ }
+
+ @Override
+ public void run() {
+ String results = buildXpathStatement();
+ events.executionCompleted(hwnd, results);
+ }
+
+ public static void buildXpathStatementThreaded(HWND hwnd, JTextPane windowsXmlTextPane, Events events) {
+ Thread t = new Thread(new XpathManager(hwnd, windowsXmlTextPane, events));
+ t.start();
+ }
+
+ public String buildXpathStatement() {
+ String builtXpath = "";
+ try {
+ String classStr = WindowsEnumeratedXml.escapeXmlAttributeValue(Api.GetWindowClassName(hwnd));
+ String handleStr = Api.GetHandleAsString(hwnd);
+ String txtOrig = Api.GetWindowText(hwnd);
+ String txtStr = WindowsEnumeratedXml.escapeXmlAttributeValue(txtOrig);
+ String xml = this.windowsXmlTextPane.getText();
+ builtXpath = "//win[@class='" + classStr + "']";
+ List resultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath);
+ //int matches = nextXpathMatch(builtXpath, textPane, true);
+ if (resultList.size() > 1) { // if there are multiple results with the simple xpath then include parent class and text with the xpath statement.
+ HWND parent = User32.instance.GetParent(hwnd);
+ String parentClassStr = WindowsEnumeratedXml.escapeXmlAttributeValue(Api.GetWindowClassName(parent));
+ String parentTxtOrig = Api.GetWindowText(parent);
+ String parentTxtStr = WindowsEnumeratedXml.escapeXmlAttributeValue(parentTxtOrig);
+ if (!parentTxtStr.isEmpty()) {
+ if (parentTxtOrig.length() > 20) {// if the parent text is too long only test the first 20 characters
+ parentTxtStr = WindowsEnumeratedXml.escapeXmlAttributeValue(parentTxtOrig.substring(0, 20));
+ parentTxtStr = " and starts-with(@text,'" + parentTxtStr + "')";
+ }
+ else
+ parentTxtStr = " and @text='" + parentTxtStr + "'";
+ }
+ if (!parentClassStr.isEmpty())
+ builtXpath = "//win[@class='" + parentClassStr + "'" + parentTxtStr + "]/win[@class='" + classStr + "']";
+ resultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, builtXpath);
+ if (resultList.size() > 1) { // if there are still multiple results add position to the xpath
+ int position = 1;
+ for (String result : resultList) {
+ if (result.contains(handleStr)) {
+ builtXpath += "[" + position + "]";
+ break;
+ }
+ ++position;
+ }
+ }
+ if (resultList.size() == 0) { //some reason a window might have a parent window that is not associated with it's child (orphans!!)
+ if (!txtStr.isEmpty()) {
+ if (parentTxtOrig.length() > 30) {// if the text is too long only test the first 20 characters
+ txtStr = WindowsEnumeratedXml.escapeXmlAttributeValue(txtStr.substring(0, 30));
+ txtStr = " and starts-with(@text,'" + txtStr + "')";
+ }
+ else
+ txtStr = " and @text='" + txtStr + "'";
+ }
+ builtXpath = "//win[@class='" + classStr + "'" + txtStr + "]";
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return builtXpath;
+ }
+
+ public static int nextXpathMatch(String xpathExpr, JTextPane targetText, JLabel lblStatus, boolean alwaysFromTop) {
+ int results = 0;
+ try {
+ if (xpathExpr.length() == 0)
+ return results;
+
+ if (targetText instanceof JTextPane) {
+ final JTextPane target = (JTextPane)targetText;
+ target.requestFocus();
+ int cPos = target.getCaretPosition();
+ if (alwaysFromTop)
+ cPos = 0;
+ int len = target.getStyledDocument().getLength();
+ String xml = target.getText(0, len);
+ WindowsEnumeratedXml.lastException = null;
+ List resultList = WindowsEnumeratedXml.evaluateXpathGetValues(xml, xpathExpr);
+ if (resultList.size() == 0 && WindowsEnumeratedXml.lastException != null) {
+ String errMsg = WindowsEnumeratedXml.lastException.getCause().getMessage();
+ JOptionPane.showMessageDialog(null, "Exception: " + errMsg, "Error", JOptionPane.ERROR_MESSAGE);
+ return -1;
+ }
+ results = resultList.size();
+ String txt = "";
+ String targetStr = target.getText(cPos, (len - cPos));
+ int matches = 0;
+ int mPos = 0;
+ target.select(cPos, cPos); //clear selection
+ for (int i = 0; i < resultList.size(); i++) {
+ txt = resultList.get(i).trim();
+ if (txt.length() == 0)
+ continue;
+ //if (txt.endsWith("\r\n"))
+ // txt = txt.substring(0, txt.length() - 2).trim();
+ txt = txt.replaceAll("\r\n", "\n").trim();
+ while ((mPos = targetStr.indexOf(txt)) != -1) {
+ if (matches == 0){
+ if (!alwaysFromTop)
+ flashMatchingWindow(txt);
+ //target.setCaretPosition(cPos + mPos);
+ //target.select(cPos + mPos, cPos + mPos + txt.length());
+ XmlEditorKit.HIGHLIGHTED_START = cPos + mPos;
+ XmlEditorKit.HIGHLIGHTED_END = cPos + mPos + txt.length();
+ //System.out.println("HIGHLIGHTED_START = " + (cPos + mPos));
+ //System.out.println("HIGHLIGHTED_END = " + (cPos + mPos + txt.length()));
+ final int cpos = cPos + mPos +2;
+ EventQueue.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ target.updateUI();
+ target.setCaretPosition(cpos);
+ }
+ });
+ }
+ targetStr = targetStr.substring(mPos + txt.length());
+ ++matches;
+ }
+ }
+ lblStatus.setText(results + " matches");
+ if (cPos > 0 && matches == 0 && !alwaysFromTop) { //ask if user wants to search from top
+ int result = JOptionPane.showConfirmDialog(null, "No more matches found. Do you want to search from the top of the document?", "Find", JOptionPane.YES_NO_OPTION);
+ if (result == JOptionPane.YES_OPTION) {
+ target.setCaretPosition(0);
+ nextXpathMatch(xpathExpr, targetText, lblStatus, alwaysFromTop);
+ }
+ }
+ }
+ } catch (Exception e) {
+ XmlEditorKit.HIGHLIGHTED_START = 0;
+ XmlEditorKit.HIGHLIGHTED_END = 0;
+ e.printStackTrace();
+ }
+ return results;
+ }
+
+ public static void flashMatchingWindow(String txtXml) {
+ if (txtXml.contains("hwnd")) {
+ List hwndList = WindowsEnumeratedXml.evaluateXpathGetValues(txtXml, "//@hwnd");
+ if (hwndList.size() > 0) {
+ String hwndStr = hwndList.get(0);
+ HWND tHwnd = Api.GetHandleFromString(hwndStr);
+ Api.highlightWindow(tHwnd);
+ try { Thread.sleep(100); } catch (Exception e) {e.printStackTrace();}
+ Api.refreshWindow(tHwnd);
+ try { Thread.sleep(100); } catch (Exception e) {e.printStackTrace();}
+ Api.highlightWindow(tHwnd);
+ try { Thread.sleep(100); } catch (Exception e) {e.printStackTrace();}
+ Api.refreshWindow(tHwnd);
+ }
+ }
+ }
+
+}
diff --git a/src/org/synthuse/img/applications-education.png b/src/org/synthuse/img/applications-education.png
new file mode 100644
index 0000000..957faf7
Binary files /dev/null and b/src/org/synthuse/img/applications-education.png differ
diff --git a/src/org/synthuse/img/arrow-right-3.png b/src/org/synthuse/img/arrow-right-3.png
new file mode 100644
index 0000000..d46a76e
Binary files /dev/null and b/src/org/synthuse/img/arrow-right-3.png differ
diff --git a/src/org/synthuse/img/bullseye-logo-th18x18.png b/src/org/synthuse/img/bullseye-logo-th18x18.png
new file mode 100644
index 0000000..c93bc0c
Binary files /dev/null and b/src/org/synthuse/img/bullseye-logo-th18x18.png differ
diff --git a/src/org/synthuse/img/bullseye-logo-th32x32.png b/src/org/synthuse/img/bullseye-logo-th32x32.png
new file mode 100644
index 0000000..71aa4c1
Binary files /dev/null and b/src/org/synthuse/img/bullseye-logo-th32x32.png differ
diff --git a/src/org/synthuse/img/dialog-close.png b/src/org/synthuse/img/dialog-close.png
new file mode 100644
index 0000000..5efd283
Binary files /dev/null and b/src/org/synthuse/img/dialog-close.png differ
diff --git a/src/org/synthuse/img/document-close-2.png b/src/org/synthuse/img/document-close-2.png
new file mode 100644
index 0000000..5364649
Binary files /dev/null and b/src/org/synthuse/img/document-close-2.png differ
diff --git a/src/org/synthuse/img/document-new-5.png b/src/org/synthuse/img/document-new-5.png
new file mode 100644
index 0000000..e7d4847
Binary files /dev/null and b/src/org/synthuse/img/document-new-5.png differ
diff --git a/src/org/synthuse/img/edit-copy-7.png b/src/org/synthuse/img/edit-copy-7.png
new file mode 100644
index 0000000..d2f21bd
Binary files /dev/null and b/src/org/synthuse/img/edit-copy-7.png differ
diff --git a/src/org/synthuse/img/edit-find-3.png b/src/org/synthuse/img/edit-find-3.png
new file mode 100644
index 0000000..9a10550
Binary files /dev/null and b/src/org/synthuse/img/edit-find-3.png differ
diff --git a/src/org/synthuse/img/gnome-robots.png b/src/org/synthuse/img/gnome-robots.png
new file mode 100644
index 0000000..c34a56e
Binary files /dev/null and b/src/org/synthuse/img/gnome-robots.png differ
diff --git a/src/org/synthuse/img/help-3.png b/src/org/synthuse/img/help-3.png
new file mode 100644
index 0000000..884f6c1
Binary files /dev/null and b/src/org/synthuse/img/help-3.png differ
diff --git a/src/org/synthuse/img/rapidsvn.png b/src/org/synthuse/img/rapidsvn.png
new file mode 100644
index 0000000..8f5e892
Binary files /dev/null and b/src/org/synthuse/img/rapidsvn.png differ
diff --git a/src/org/synthuse/img/run-rebuild-2.png b/src/org/synthuse/img/run-rebuild-2.png
new file mode 100644
index 0000000..da123fa
Binary files /dev/null and b/src/org/synthuse/img/run-rebuild-2.png differ
diff --git a/src/org/synthuse/img/user-trash-2.png b/src/org/synthuse/img/user-trash-2.png
new file mode 100644
index 0000000..f676164
Binary files /dev/null and b/src/org/synthuse/img/user-trash-2.png differ
diff --git a/synthuse.properties b/synthuse.properties
new file mode 100644
index 0000000..86a84e3
--- /dev/null
+++ b/synthuse.properties
@@ -0,0 +1,6 @@
+#
+#Sun Feb 16 21:34:31 EST 2014
+DEFAULT_PROP_FILENAME=
+urlList=
+xpathList=
+xpathHightlight=.*process\="([^"]*)".*