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 = "(?"; + 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\="([^"]*)".*