pax_global_header00006660000000000000000000000064131572613040014514gustar00rootroot0000000000000052 comment=6624a544973a97570f1afb82af402834cb4e3735 sikulix-1.1.1/000077500000000000000000000000001315726130400132045ustar00rootroot00000000000000sikulix-1.1.1/.gitignore000066400000000000000000000002451315726130400151750ustar00rootroot00000000000000target*/ .idea nb* *.iml stuff/ *.versionsBackup *.log *.o *.o.d *.a *.def opencvsrc/ .settings/ .project .classpath *$py.class *.pyc .exe original/ *.sikuli.robot/ sikulix-1.1.1/API/000077500000000000000000000000001315726130400136155ustar00rootroot00000000000000sikulix-1.1.1/API/README.md000066400000000000000000000011471315726130400150770ustar00rootroot00000000000000Sikuli API 2014 (version 1.1.x) === The Java implementation comprising the API to access the top elements (Screen, Region, Pattern, Match, Image, ...) and their methods allowing to search for images and to act on points and matches simulating mouse and keyboard. The ready-to-use package `sikulixapi.jar` provides this API for Java programming and any Java aware scripting languages. When using Jython or JRuby, this module provides special support to conveniently use the SikuliX features in your scripts. They are exported additionally on the fly to the folder `Lib` in the folder containing `sikulixapi.jar`. sikulix-1.1.1/API/pom.xml000077500000000000000000000115401315726130400151360ustar00rootroot00000000000000 com.sikulix sikulix1 1.1.1 ../ 4.0.0 com.sikulix sikulixapi 1.1.1 Windows windows sikulixlibswin Unix unix sikulixlibslux Mac mac sikulixlibsmac sikulix-local file://${project.basedir}/lib commons-cli commons-cli 1.2 org.apache.commons commons-exec 1.3 commons-net commons-net 3.4 com.melloware jintellitype 1.3.9 com.nativelibs4java bridj 0.7.0 com.github.vidstige jadb -v1.0-g94ebf38-23 com.sikulix ${sikulix.libs} ${project.version} com.tigervnc vncviewer 1.7.1 src/main/resources true Settings/* src/main/resources false Settings/* ../JRubyGem/lib/sikulix sikulix* Lib maven-jar-plugin 2.4 org.sikuli.script.Sikulix sikulix-1.1.1/API/scripts/000077500000000000000000000000001315726130400153045ustar00rootroot00000000000000sikulix-1.1.1/API/scripts/testjs.sikuli/000077500000000000000000000000001315726130400201175ustar00rootroot00000000000000sikulix-1.1.1/API/scripts/testjs.sikuli/testjs.js000066400000000000000000000000271315726130400217700ustar00rootroot00000000000000Sikulix.popup("hello");sikulix-1.1.1/API/scripts/testpy.sikuli/000077500000000000000000000000001315726130400201335ustar00rootroot00000000000000sikulix-1.1.1/API/scripts/testpy.sikuli/testpy.py000066400000000000000000000002751315726130400220410ustar00rootroot00000000000000popup("hello") print "********* sys.path" for e in sys.path: print e print "********* sys.argv", len(sys.argv) for e in sys.argv: print "|" + e + "|" print "********* end sys.argv" exit(3) sikulix-1.1.1/API/src/000077500000000000000000000000001315726130400144045ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/000077500000000000000000000000001315726130400153305ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/000077500000000000000000000000001315726130400162515ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/000077500000000000000000000000001315726130400170405ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/000077500000000000000000000000001315726130400203405ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/android/000077500000000000000000000000001315726130400217605ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/android/ADBClient.java000077500000000000000000000052411315726130400243550ustar00rootroot00000000000000package org.sikuli.android; import org.sikuli.basics.Debug; import se.vidstige.jadb.AdbServerLauncher; import se.vidstige.jadb.JadbConnection; import se.vidstige.jadb.JadbDevice; import java.io.IOException; import java.util.List; /** * Created by tg44 on 2016. 06. 26.. * Modified by RaiMan */ public class ADBClient { private static JadbConnection jadb = null; private static boolean shouldStopServer = false; private static JadbDevice device = null; public static boolean isAdbAvailable = true; private static void init() { getConnection(true); if (jadb == null) { try { new AdbServerLauncher().launch(); Debug.log(3, "ADBClient: ADBServer started"); getConnection(false); if (jadb != null) { shouldStopServer = true; } } catch (Exception e) { //Cannot run program "adb": error=2, No such file or directory if (e.getMessage().startsWith("Cannot run program")) { isAdbAvailable = false; Debug.error("ADBClient: package adb not available. need to be installed"); } else { Debug.error("ADBClient: ADBServer problem: %s", e.getMessage()); } } } String serial = null; if (jadb != null) { List devices = null; try { devices = jadb.getDevices(); } catch (Exception e) { } if (devices != null && devices.size() > 0) { device = devices.get(0); serial = device.getSerial(); } else { device = null; Debug.error("ADBClient: init: no devices attached"); } } if (device != null) { Debug.log(3, "ADBClient: init: attached device: serial(%s)", serial); } } public static void reset() { device = null; jadb = null; Process p = null; if (!shouldStopServer) { return; } try { p = Runtime.getRuntime().exec(new String[] {"adb", "kill-server"}); p.waitFor(); } catch (Exception e) { Debug.error("ADBClient: reset: kill-server did not work"); } } private static void getConnection(boolean quiet) { if (jadb == null) { try { jadb = new JadbConnection(); jadb.getHostVersion(); Debug.log(3, "ADBClient: ADBServer connection established"); } catch (Exception e) { if (!quiet) { Debug.error("ADBClient: ADBServer connection not possible: %s", e.getMessage()); } jadb = null; } } } public static JadbDevice getDevice() { init(); return device; } //TODO: get device by id public boolean isValid() { return jadb != null; } public boolean hasDevices() { return device != null; } } sikulix-1.1.1/API/src/main/java/org/sikuli/android/ADBDevice.java000066400000000000000000000272641315726130400243440ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.android; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.imgproc.Imgproc; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.script.RunTime; import org.sikuli.script.ScreenImage; import se.vidstige.jadb.JadbDevice; import se.vidstige.jadb.JadbException; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ADBDevice { private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, "ADBDevice: " + message, args); } private JadbDevice device = null; private int devW = -1; private int devH = -1; private ADBRobot robot = null; private ADBScreen screen = null; private List deviceProps = new ArrayList<>(); private int deviceVersion = -1; private String sDeviceVersion = "???"; private static ADBDevice adbDevice = null; public static int KEY_HOME = 3; public static int KEY_BACK = 4; public static int KEY_MENU = 82; public static int KEY_POWER = 26; private ADBDevice() { } public static ADBDevice init() { if (adbDevice == null) { adbDevice = new ADBDevice(); adbDevice.device = ADBClient.getDevice(); if (adbDevice.device == null) { adbDevice = null; } else { adbDevice.deviceProps = Arrays.asList(adbDevice.exec("getprop").split("\n")); //[ro.build.version.release]: [6.0.1] //[ro.product.brand]: [google] //[ro.product.manufacturer]: [asus] //[ro.product.model]: [Nexus 7] //[ro.product.name]: [razor] //[ro.serialno]: [094da986] Pattern pProp = Pattern.compile("\\[(.*?)\\]:.*?\\[(.*)\\]"); Matcher mProp = null; String val = ""; String key = ""; for (String prop : adbDevice.deviceProps) { if (!prop.startsWith("[ro.")) continue; mProp = pProp.matcher(prop); if (mProp.find()) { key = mProp.group(1); if (key.contains("build.version.release")) { val = mProp.group(2); try { adbDevice.deviceVersion = Integer.parseInt(val.split("\\.")[0]); adbDevice.sDeviceVersion = val; } catch (Exception e) { } } } } log(lvl, "init: %s", adbDevice.toString()); } } return adbDevice; } public static void reset() { adbDevice = null; ADBClient.reset(); } public String toString() { return String.format("attached device: serial(%s) display(%dx%d) version(%s)", getDeviceSerial(), getBounds().width, getBounds().height, sDeviceVersion); } public ADBRobot getRobot(ADBScreen screen) { if (robot == null) { this.screen = screen; robot = new ADBRobot(screen, this); } return robot; } public String getDeviceSerial() { return device.getSerial(); } public Rectangle getBounds() { if (devW < 0) { Dimension dim = getDisplayDimension(); devW = (int) dim.getWidth(); devH = (int) dim.getHeight(); } return new Rectangle(0, 0, devW, devH); } public ScreenImage captureScreen() { BufferedImage bimg = captureDeviceScreen(); return new ScreenImage(getBounds(), bimg); } public ScreenImage captureScreen(Rectangle rect) { BufferedImage bimg = captureDeviceScreen(rect.x, rect.y, rect.width, rect.height); return new ScreenImage(rect, bimg); } public BufferedImage captureDeviceScreen() { return captureDeviceScreen(0, 0, devW, devH); } public BufferedImage captureDeviceScreen(int y, int _h) { return captureDeviceScreen(0, y, devW, _h); } public BufferedImage captureDeviceScreen(int x, int y, int w, int h) { Mat matImage = captureDeviceScreenMat(x, y, w, h); BufferedImage bImage = null; if (matImage != null) { bImage = new BufferedImage(matImage.width(), matImage.height(), BufferedImage.TYPE_3BYTE_BGR); byte[] bImageData = ((DataBufferByte) bImage.getRaster().getDataBuffer()).getData(); matImage.get(0, 0, bImageData); } return bImage; } public Mat captureDeviceScreenMat(int x, int y, int w, int h) { byte[] imagePrefix = new byte[12]; byte[] image = new byte[0]; int actW = w; if (x + w > devW) { actW = devW - x; } int actH = h; if (y + h > devH) { actH = devH - y; } Debug timer = Debug.startTimer(); try { InputStream stdout = device.executeShell("screencap"); stdout.read(imagePrefix); if (imagePrefix[8] != 0x01) { log(-1, "captureDeviceScreenMat: image type not RGBA"); return null; } if (byte2int(imagePrefix, 0, 4) != devW || byte2int(imagePrefix, 4, 4) != devH) { log(-1, "captureDeviceScreenMat: width or height differ from device values"); return null; } image = new byte[actW * actH * 4]; int lenRow = devW * 4; byte[] row = new byte[lenRow]; for (int count = 0; count < y; count++) { stdout.read(row); } boolean shortRow = x + actW < devW; for (int count = 0; count < actH; count++) { if (shortRow) { stdout.read(row); System.arraycopy(row, x * 4, image, count * actW * 4, actW * 4); } else { stdout.read(image, count * actW * 4, actW * 4); } } long duration = timer.end(); log(lvl, "captureDeviceScreenMat:[%d,%d %dx%d] %d", x, y, actW, actH, duration); } catch (IOException | JadbException e) { log(-1, "captureDeviceScreenMat: [%d,%d %dx%d] %s", x, y, actW, actH, e); } Mat matOrg = new Mat(actH, actW, CvType.CV_8UC4); matOrg.put(0, 0, image); Mat matImage = new Mat(); Imgproc.cvtColor(matOrg, matImage, Imgproc.COLOR_RGBA2BGR, 3); return matImage; } private int byte2int(byte[] bytes, int start, int len) { int val = 0; int fact = 1; for (int i = start; i < start + len; i++) { int b = bytes[i] & 0xff; val += b * fact; fact *= 256; } return val; } private Dimension getDisplayDimension() { String dump = dumpsys("display"); String token = "mDefaultViewport= ... deviceWidth=1200, deviceHeight=1920}"; Dimension dim = null; Pattern displayDimension = Pattern.compile( "mDefaultViewport.*?=.*?deviceWidth=(\\d*).*?deviceHeight=(\\d*)"); Matcher match = displayDimension.matcher(dump); if (match.find()) { int w = Integer.parseInt(match.group(1)); int h = Integer.parseInt(match.group(2)); dim = new Dimension(w, h); } else { log(-1, "getDisplayDimension: dumpsys display: token not found: %s", token); } return dim; } public String exec(String command, String... args) { InputStream stdout = null; String out = ""; try { stdout = device.executeShell(command, args); out = inputStreamToString(stdout, "UTF-8"); } catch (IOException | JadbException e) { log(-1, "exec: %s: %s", command, e); } return out; } public String dumpsys(String component) { InputStream stdout = null; String out = ""; try { if (component == null || component.isEmpty()) { component = "power"; } if (component.toLowerCase().contains("all")) { stdout = device.executeShell("dumpsys"); } else { stdout = device.executeShell("dumpsys", component); } out = inputStreamToString(stdout, "UTF-8"); } catch (IOException | JadbException e) { log(-1, "dumpsys: %s: %s", component, e); } return out; } public String printDump(String component) { String dump = dumpsys(component); if (!dump.isEmpty()) { System.out.println("***** Android device dump: " + component); System.out.println(dump); } return dump; } public String printDump() { String dump = dumpsys("all"); if (!dump.isEmpty()) { File out = new File(RunTime.get().fSikulixStore, "android_dump_" + getDeviceSerial() + ".txt"); System.out.println("***** Android device dump all services"); System.out.println("written to file: " + out.getAbsolutePath()); FileManager.writeStringToFile(dump, out); } return dump; } private static final int BUFFER_SIZE = 4 * 1024; private static String inputStreamToString(InputStream inputStream, String charsetName) { StringBuilder builder = new StringBuilder(); InputStreamReader reader = null; try { reader = new InputStreamReader(inputStream, charsetName); char[] buffer = new char[BUFFER_SIZE]; int length; while ((length = reader.read(buffer)) != -1) { builder.append(buffer, 0, length); } return builder.toString(); } catch (Exception e) { return ""; } } public void wakeUp(int seconds) { int times = seconds * 4; try { if (null == isDisplayOn()) { log(-1, "wakeUp: not possible - see log"); return; } device.executeShell("input", "keyevent", "26"); while (0 < times--) { if (isDisplayOn()) { return; } else { RunTime.pause(0.25f); } } } catch (Exception e) { log(-1, "wakeUp: did not work: %s", e); } log(-1, "wakeUp: timeout: %d seconds", seconds); } public Boolean isDisplayOn() { // deviceidle | grep mScreenOn=true|false // v < 5: power | grep mScreenOn=true|false // v > 4: power | grep Display Power: state=ON|OFF String dump = dumpsys("power"); Pattern displayOn = Pattern.compile("mScreenOn=(..)"); String isOn = "tr"; if (deviceVersion > 4) { displayOn = Pattern.compile("Display Power: state=(..)"); isOn = "ON"; } Matcher match = displayOn.matcher(dump); if (match.find()) { if (match.group(1).contains(isOn)) { return true; } return false; } else { log(-1, "isDisplayOn: (Android version %d) dumpsys power: pattern not found: %s", deviceVersion, displayOn); } return null; } public void inputKeyEvent(int key) { try { device.executeShell("input", "keyevent", Integer.toString(key)); } catch (Exception e) { log(-1, "inputKeyEvent: %d did not work: %s", e.getMessage()); } } public void tap(int x, int y) { try { device.executeShell("input tap", Integer.toString(x), Integer.toString(y)); } catch (IOException | JadbException e) { log(-1, "tap: %s", e); } } public void swipe(int x1, int y1, int x2, int y2) { try { device.executeShell("input swipe", Integer.toString(x1), Integer.toString(y1), Integer.toString(x2), Integer.toString(y2)); } catch (IOException | JadbException e) { log(-1, "swipe: %s", e); } } private String textBuffer = ""; private boolean typing = false; public synchronized boolean typeStarts() { if (!typing) { textBuffer = ""; typing = true; return true; } return false; } public synchronized void typeEnds() { if (typing) { input(textBuffer); typing = false; } } public void typeChar(char character) { if (typing) { textBuffer += character; } } public static float inputDelay = 0.05f; public void input(String text) { try { device.executeShell("input text ", text); RunTime.pause(text.length() * inputDelay); } catch (Exception e) { log(-1, "input: %s", e); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/android/ADBRobot.java000077500000000000000000000104261315726130400242250ustar00rootroot00000000000000package org.sikuli.android; import org.sikuli.basics.Debug; import org.sikuli.script.*; import java.awt.*; import java.awt.image.BufferedImage; /** * Created by Törcsi on 2016. 06. 26. * Revised by RaiMan */ public class ADBRobot implements IRobot { private int mouse_X1 = -1; private int mouse_Y1 = -1; private int mouse_X2 = -1; private int mouse_Y2 = -1; private boolean mouseDown = false; private int autodelay = 0; private boolean waitForIdle = false; final static int MAX_DELAY = 60000; private ADBScreen screen; private ADBDevice device; public ADBRobot(ADBScreen screen, ADBDevice device) { this.screen = screen; this.device = device; } private void notSupported(String feature) { Debug.error("ADBRobot: %s: not supported yet", feature); } @Override public boolean isRemote() { return true; } @Override public IScreen getScreen() { return screen; } @Override public void cleanup() { notSupported("feature"); } // @Override public void keyDown(String keys) { notSupported("keyDown"); } @Override public void keyUp(String keys) { notSupported("keyUp"); } @Override public void keyDown(int code) { notSupported("keyDown"); } @Override public void keyUp(int code) { notSupported("keyUp"); } @Override public void keyUp() { notSupported("keyUp"); } @Override public void pressModifiers(int modifiers) { if (modifiers != 0) { notSupported("pressModifiers"); } } @Override public void releaseModifiers(int modifiers) { if (modifiers != 0) { notSupported("releaseModifiers"); } } @Override public void typeChar(char character, KeyMode mode) { if (device == null) { return; } device.typeChar(character); } @Override public void typeKey(int key) { notSupported("typeKey"); } @Override public void typeStarts() { if (device == null) { return; } while (!device.typeStarts()) { RunTime.pause(1); } } @Override public void typeEnds() { if (device == null) { return; } device.typeEnds(); } // @Override public void mouseMove(int x, int y) { if (!mouseDown) { mouse_X1 = x; mouse_Y1 = y; } else { mouse_X2 = x; mouse_Y2 = y; } } @Override public void mouseDown(int buttons) { clickStarts(); } @Override public int mouseUp(int buttons) { clickEnds(); return 0; } @Override public void mouseReset() { mouseDown = false; } @Override public void clickStarts() { mouseDown = true; mouse_X2 = mouse_X1; mouse_Y2 = mouse_Y1; } @Override public void clickEnds() { if (device == null) { return; } if (mouseDown) { mouseDown = false; if (mouse_X1 == mouse_X2 && mouse_Y1 == mouse_Y2) { device.tap(mouse_X1, mouse_Y1); } else { device.swipe(mouse_X1, mouse_Y1, mouse_X2, mouse_Y2); } } } // @Override public void smoothMove(Location dest) { mouseMove(dest.x, dest.y); } @Override public void smoothMove(Location src, Location dest, long ms) { notSupported("smoothMove"); } @Override public void mouseWheel(int wheelAmt) { notSupported("mouseWheel"); } // @Override public ScreenImage captureScreen(Rectangle screenRect) { if (device == null) { return null; } return device.captureScreen(screenRect); } @Override public Color getColorAt(int x, int y) { notSupported("getColorAt"); return null; } @Override public void waitForIdle() { try { new java.awt.Robot().waitForIdle(); } catch (AWTException e) { Debug.log(-1, "Error-could non instantiate robot: " + e); } } @Override public void delay(int ms) { if (ms < 0) { ms = 0; } if (ms > MAX_DELAY) { ms = MAX_DELAY; } try { Thread.sleep(ms); } catch (InterruptedException e) { Debug.log(-1, "Thread Interrupted: " + e); } } @Override public void setAutoDelay(int ms) { if (ms < 0) { ms = 0; } if (ms > MAX_DELAY) { ms = MAX_DELAY; } autodelay = ms; } } sikulix-1.1.1/API/src/main/java/org/sikuli/android/ADBScreen.java000077500000000000000000000126041315726130400243570ustar00rootroot00000000000000package org.sikuli.android; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; import org.sikuli.script.*; import org.sikuli.util.*; import java.awt.*; /** * Created by Törcsi on 2016. 06. 26. * Revised by RaiMan */ public class ADBScreen extends Region implements EventObserver, IScreen { static { RunTime.loadLibrary("VisionProxy"); } private static String me = "ADBScreen: "; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private static boolean isFake = false; protected IRobot robot = null; private static int logLvl = 3; private ScreenImage lastScreenImage = null; private Rectangle bounds; private boolean waitPrompt = false; protected OverlayCapturePrompt prompt; private String promptMsg = "Select a region on the screen"; private static int waitForScreenshot = 300; public boolean needsUnLock = false; public int waitAfterAction = 1; //---------------------------Inits private ADBDevice device = null; private static ADBScreen screen = null; public static ADBScreen start() { if (screen == null) { try { screen = new ADBScreen(); } catch (Exception e) { log(-1, "start: No devices attached"); screen = null; } } return screen; } public static void stop() { ADBDevice.reset(); screen = null; } public ADBScreen() { super(); setOtherScreen(this); device = ADBDevice.init(); if (device != null) { robot = device.getRobot(this); robot.setAutoDelay(10); bounds = device.getBounds(); w = bounds.width; h = bounds.height; } } public boolean isValid() { return null != device; } public ADBDevice getDevice() { return device; } public String toString() { if (null == device) { return "ADBScreen: No Android device attached"; } else { return String.format("ADBScreen: Android device: %s", getDeviceDescription()); } } public String getDeviceDescription() { return String.format("%s (%d x %d)", device.getDeviceSerial(), bounds.width, bounds.height); } public void wakeUp(int seconds) { if (null == device) { return; } if (null == device.isDisplayOn()) { log(-1, "wakeUp: not possible - see log"); return; } if (!device.isDisplayOn()) { device.wakeUp(seconds); if (needsUnLock) { aSwipeUp(); } } } public String exec(String command, String... args) { if (device == null) { return null; } return device.exec(command, args); } //-----------------------------Overrides @Override public IScreen getScreen() { return this; } @Override public void update(EventSubject s) { waitPrompt = false; } @Override public IRobot getRobot() { return robot; } @Override public Rectangle getBounds() { return bounds; } @Override public ScreenImage capture() { return capture(x, y, w, h); } @Override public ScreenImage capture(int x, int y, int w, int h) { ScreenImage simg = null; if (device != null) { log(logLvl, "ADBScreen.capture: (%d,%d) %dx%d", x, y, w, h); simg = device.captureScreen(new Rectangle(x, y, w, h)); } else { log(-1, "capture: no ADBRobot available"); } lastScreenImage = simg; return simg; } @Override public ScreenImage capture(Region reg) { return capture(reg.x, reg.y, reg.w, reg.h); } @Override public ScreenImage capture(Rectangle rect) { return capture(rect.x, rect.y, rect.width, rect.height); } public void showTarget(Location loc) { showTarget(loc, Settings.SlowMotionDelay); } protected void showTarget(Location loc, double secs) { if (Settings.isShowActions()) { ScreenHighlighter overlay = new ScreenHighlighter(this, null); overlay.showTarget(loc, (float) secs); } } @Override public int getID() { return 0; } public String getIDString() { return "Android"; } @Override public ScreenImage getLastScreenImageFromScreen() { return lastScreenImage; } private EventObserver captureObserver = null; @Override public ScreenImage userCapture(final String msg) { if (robot == null) { return null; } waitPrompt = true; Thread th = new Thread() { @Override public void run() { prompt = new OverlayCapturePrompt(ADBScreen.this); prompt.prompt(msg); } }; th.start(); boolean hasShot = false; ScreenImage simg = null; int count = 0; while (!hasShot) { this.wait(0.1f); if (count++ > waitForScreenshot) { break; } if (prompt == null) { continue; } if (prompt.isComplete()) { simg = prompt.getSelection(); if (simg != null) { lastScreenImage = simg; hasShot = true; } prompt.close(); } } prompt.close(); prompt = null; return simg; } @Override public int getIdFromPoint(int srcx, int srcy) { return 0; } public Region newRegion(Location loc, int width, int height) { return new Region(loc.x, loc.y, width, height, this); } public Region newRegion(int _x, int _y, int width, int height) { return new Region(_x, _y, width, height, this); } public Location newLocation(int _x, int _y) { return new Location(_x, _y).setOtherScreen(this); } } sikulix-1.1.1/API/src/main/java/org/sikuli/android/ADBTest.java000066400000000000000000000062441315726130400240570ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.android; import org.sikuli.basics.Debug; import org.sikuli.script.*; import org.sikuli.script.Image; /** * Created by RaiMan on 12.07.16. *

* Test for the basic ADB based features */ public class ADBTest { private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, "ADBDevice: " + message, args); } private static void logp(String message, Object... args) { System.out.println(String.format(message, args)); } private static RunTime rt = null; private static boolean runTests = true; public static void main(String[] args) throws FindFailed { ADBScreen aScr = startTest(); if (aScr.isValid()) { if (runTests) { basicTest(aScr); ADBScreen.stop(); System.exit(0); } } else { System.exit(1); } // ********* playground } private static ADBScreen startTest() { Debug.on(3); rt = RunTime.get(); ADBScreen adbs = new ADBScreen(); if (adbs.isValid()) { adbs.wakeUp(2); adbs.wait(1f); if (runTests) { adbs.aKey(ADBDevice.KEY_HOME); adbs.wait(1f); } } return adbs; } private static void basicTest(ADBScreen adbs) throws FindFailed { log(lvl, "**************** running basic test"); adbs.aSwipeLeft(); adbs.aSwipeRight(); adbs.wait(1f); ScreenImage sIMg = adbs.userCapture("Android"); sIMg.save(RunTime.get().fSikulixStore.getAbsolutePath(), "android"); adbs.aTap(new Image(sIMg)); } /** * used in SikuliIDE menu tool to run a test against an attached device * * @param aScr */ public static void ideTest(ADBScreen aScr) { String title = "Android Support - Testing device"; Sikulix.popup("Take care\n\nthat device is on and unlocked\n\nbefore clicking ok", title); aScr.wakeUp(2); aScr.aKey(ADBDevice.KEY_HOME); if (Sikulix.popAsk("Now the device should show the HOME screen.\n" + "\nclick YES to proceed watching the test on the device" + "\nclick NO to end the test now", title)) { aScr.aSwipeLeft(); aScr.aSwipeRight(); aScr.wait(1f); if (Sikulix.popAsk("You should have seen a swipe left and a swipe right.\n" + "\nclick YES to capture an icon from homescreen and then aTap it" + "\nclick NO to end the test now", title)) { ScreenImage sIMg = aScr.userCapture("AndroidTest"); sIMg.save(RunTime.get().fSikulixStore.getAbsolutePath(), "android"); try { aScr.aTap(new Image(sIMg)); Sikulix.popup("The image was found on the device's current screen" + "\nand should have been tapped.\n" + "\nIf you think it worked, you can now try\n" + "to capture needed images from the device.\n" + "\nYou have to come back here and click Default!", title); } catch (FindFailed findFailed) { Sikulix.popError("Sorry, the image you captured was\nnot found on the device's current screen", title); } } } } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/000077500000000000000000000000001315726130400216045ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/basics/Animator.java000066400000000000000000000004421315726130400242210ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; /** * INTERNAL USE * allows to implement timed animations (e.g. mouse move) */ public interface Animator { public float step(); public boolean running(); } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/AnimatorLinear.java000066400000000000000000000005121315726130400253520ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; public class AnimatorLinear extends AnimatorTimeBased { public AnimatorLinear(float beginVal, float endVal, long totalMS) { super(new AnimatorLinearInterpolation(beginVal, endVal, totalMS)); } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/AnimatorLinearInterpolation.java000066400000000000000000000010531315726130400301230ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; public class AnimatorLinearInterpolation extends AnimatorTimeValueFunction { float _stepUnit; public AnimatorLinearInterpolation(float beginVal, float endVal, long totalTime) { super(beginVal, endVal, totalTime); _stepUnit = (endVal - beginVal) / (float) totalTime; } @Override public float getValue(long t) { if (t > _totalTime) { return _endVal; } return _beginVal + _stepUnit * t; } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/AnimatorOutQuarticEase.java000066400000000000000000000011451315726130400270410ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; public class AnimatorOutQuarticEase extends AnimatorTimeValueFunction { public AnimatorOutQuarticEase(float beginVal, float endVal, long totalTime) { super(beginVal, endVal, totalTime); } @Override public float getValue(long t) { if (t > _totalTime) { return _endVal; } double t1 = (double) t / _totalTime; double t2 = t1 * t1; return (float) (_beginVal + (_endVal - _beginVal) * (-1 * t2 * t2 + 4 * t1 * t2 - 6 * t2 + 4 * t1)); } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/AnimatorPulse.java000066400000000000000000000016631315726130400252400ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import java.util.Date; public class AnimatorPulse implements Animator { protected float _v1, _v2; protected long _interval, _totalMS; protected boolean _running; protected long _begin_time = -1; public AnimatorPulse(float v1, float v2, long interval, long totalMS) { _v1 = v1; _v2 = v2; _interval = interval; _totalMS = totalMS; _running = true; } @Override public float step() { if (_begin_time == -1) { _begin_time = (new Date()).getTime(); return _v1; } long now = (new Date()).getTime(); long delta = now - _begin_time; if (delta >= _totalMS) { _running = false; } if ((delta / _interval) % 2 == 0) { return _v1; } else { return _v2; } } @Override public boolean running() { return _running; } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/AnimatorQuarticEase.java000066400000000000000000000010351315726130400263470ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; public class AnimatorQuarticEase extends AnimatorTimeValueFunction { public AnimatorQuarticEase(float beginVal, float endVal, long totalTime) { super(beginVal, endVal, totalTime); } @Override public float getValue(long t) { if (t > _totalTime) { return _endVal; } double t1 = (double) t / _totalTime; return (float) (_beginVal + (_endVal - _beginVal) * t1 * t1 * t1 * t1); } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/AnimatorStopExtention.java000066400000000000000000000011021315726130400267570ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; public class AnimatorStopExtention extends AnimatorTimeValueFunction { AnimatorTimeValueFunction _func; public AnimatorStopExtention(AnimatorTimeValueFunction func, long totalTime) { super(func._beginVal, func._endVal, totalTime); _func = func; _totalTime = totalTime; } @Override public float getValue(long t) { return _func.getValue(t); } @Override public boolean isEnd(long t) { return t >= _totalTime; } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/AnimatorTimeBased.java000066400000000000000000000014421315726130400260000ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; public class AnimatorTimeBased implements Animator { private long _begin_time; private boolean _running; private AnimatorTimeValueFunction _func; public AnimatorTimeBased(AnimatorTimeValueFunction func) { _begin_time = -1; _running = true; _func = func; } @Override public float step() { if (_begin_time == -1) { _begin_time = System.currentTimeMillis(); return _func.getValue(0); } long now = System.currentTimeMillis(); long delta = now - _begin_time; float ret = _func.getValue(delta); _running = !_func.isEnd(delta); return ret; } @Override public boolean running() { return _running; } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/AnimatorTimeValueFunction.java000066400000000000000000000010071315726130400275410ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; public abstract class AnimatorTimeValueFunction { protected float _beginVal, _endVal; protected long _totalTime; public AnimatorTimeValueFunction(float beginVal, float endVal, long totalTime) { _beginVal = beginVal; _endVal = endVal; _totalTime = totalTime; } public boolean isEnd(long t) { return t >= _totalTime; } abstract public float getValue(long t); } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/Debug.java000066400000000000000000000647361315726130400235150ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import java.io.FileNotFoundException; import java.io.PrintStream; import java.lang.reflect.Method; import java.text.DateFormat; import java.util.Arrays; import java.util.Date; import org.sikuli.util.JythonHelper; /** * Debug is a utility class that wraps println statements and allows more or less command line * output to be turned on.

For debug messages only ( Debug.log() ):
Use system * property: sikuli.Debug to set the debug level (default = 1)
On the command line, use * -Dsikuli.Debug=n to set it to level n
-Dsikuli.Debug will disable any debug messages
* (which is equivalent to using Settings.Debuglogs = false)

It prints if the level * number is less than or equal to the currently set DEBUG_LEVEL.

For messages * ActionLogs, InfoLogs see Settings

You might send all messages generated by this * class to a file:
-Dsikuli.Logfile=pathname (no path given: SikuliLog.txt in working * folder)
This can be restricted to Debug.user only (others go to System.out):
* -Dsikuli.LogfileUser=pathname (no path given: UserLog.txt in working folder)
* * You might redirect info, action, error and debug messages to your own logger object
* Start with setLogger() and then define with setLoggerXyz() the redirection targets * * This solution is NOT threadsafe !!! */ public class Debug { private static int DEBUG_LEVEL = 0; private static boolean loggerRedirectSupported = true; public static boolean shouldLogJython = false; private long _beginTime = 0; private String _message; private String _title = null; private static PrintStream printout = null; private static PrintStream printoutuser = null; private static final DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM); public static String logfile; private static Object privateLogger = null; private static boolean privateLoggerPrefixAll = true; private static Method privateLoggerUser = null; private static String privateLoggerUserName = ""; private static String privateLoggerUserPrefix = ""; private static Method privateLoggerInfo = null; private static String privateLoggerInfoName = ""; private static final String infoPrefix = "info"; private static String privateLoggerInfoPrefix = "[" + infoPrefix + "] "; private static Method privateLoggerAction = null; private static String privateLoggerActionName = ""; private static final String actionPrefix = "log"; private static String privateLoggerActionPrefix = "[" + actionPrefix + "] "; private static Method privateLoggerError = null; private static String privateLoggerErrorName = ""; private static final String errorPrefix = "error"; private static String privateLoggerErrorPrefix = "[" + errorPrefix + "] "; private static Method privateLoggerDebug = null; private static String privateLoggerDebugName = ""; private static final String debugPrefix = "debug"; private static String privateLoggerDebugPrefix = ""; private static boolean isJython; private static boolean isJRuby; private static Object scriptRunner = null; private static boolean searchHighlight = false; private static PrintStream redirectedOut = null, redirectedErr = null; static { String debug = System.getProperty("sikuli.Debug"); if (debug != null && "".equals(debug)) { DEBUG_LEVEL = 0; Settings.DebugLogs = false; } else { try { DEBUG_LEVEL = Integer.parseInt(debug); if (DEBUG_LEVEL > 0) { Settings.DebugLogs = true; } else { Settings.DebugLogs = false; } } catch (NumberFormatException numberFormatException) { } } setLogFile(null); setUserLogFile(null); } public static void init() { if (DEBUG_LEVEL > 0) { logx(DEBUG_LEVEL, "Debug.init: from sikuli.Debug: on: %d", DEBUG_LEVEL); } } public static void highlightOn() { searchHighlight = true; Settings.Highlight = true; } public static void highlightOff() { searchHighlight = false; Settings.Highlight = false; } public static boolean shouldHighlight() { return searchHighlight; } /** * A logger object that is intended, to get Sikuli's log messages per redirection * @param logger the logger object */ public static void setLogger(Object logger) { if (!doSetLogger(logger)) return; privateLoggerPrefixAll = true; logx(3, "Debug: setLogger %s", logger); } /** * same as setLogger(), but the Sikuli prefixes are omitted in all redirected messages * @param logger the logger object */ public static void setLoggerNoPrefix(Object logger) { if (!doSetLogger(logger)) return; privateLoggerPrefixAll = false; } private static boolean doSetLogger(Object logger) { String className = logger.getClass().getName(); isJython = className.contains("org.python"); isJRuby = className.contains("org.jruby"); if ( isJRuby ) { logx(3, "Debug: setLogger: given instance's class: %s", className); error("setLogger: not yet supported in JRuby script"); loggerRedirectSupported=false; return false; } privateLogger = logger; return true; } /** * sets the redirection for all message types user, info, action, error and debug * must be the name of an instance method of the previously defined logger and
* must accept exactly one string parameter, that contains the message text * @param mAll name of the method where the message should be sent * @return true if the method is available false otherwise */ public static boolean setLoggerAll(String mAll) { if (!loggerRedirectSupported) { logx(3, "Debug: setLoggerAll: logger redirect not supported"); return false; } if (privateLogger != null) { logx(3, "Debug.setLoggerAll: %s", mAll); boolean success = true; success &= setLoggerUser(mAll); success &= setLoggerInfo(mAll); success &= setLoggerAction(mAll); success &= setLoggerError(mAll); success &= setLoggerDebug(mAll); return success; } return false; } private static boolean doSetLoggerCallback(String mName, CallbackType type) { if (privateLogger == null) { error("Debug: setLogger: no logger specified yet"); return false; } if (!loggerRedirectSupported) { logx(3, "Debug: setLogger: %s (%s) logger redirect not supported", mName, type); } if (isJython) { Object[] args = new Object[]{privateLogger, mName, type.toString()}; if (!JythonHelper.get().checkCallback(args)) { logx(3, "Debug: setLogger: Jython: checkCallback returned: %s", args[0]); return false; } } try { if (type == CallbackType.INFO) { if ( !isJython && !isJRuby ) { privateLoggerInfo = privateLogger.getClass().getMethod(mName, new Class[]{String.class}); } privateLoggerInfoName = mName; return true; } else if (type == CallbackType.ACTION) { if ( !isJython && !isJRuby ) { privateLoggerAction = privateLogger.getClass().getMethod(mName, new Class[]{String.class}); } privateLoggerActionName = mName; return true; } else if (type == CallbackType.ERROR) { if ( !isJython && !isJRuby ) { privateLoggerError = privateLogger.getClass().getMethod(mName, new Class[]{String.class}); } privateLoggerErrorName = mName; return true; } else if (type == CallbackType.DEBUG) { if ( !isJython && !isJRuby ) { privateLoggerDebug = privateLogger.getClass().getMethod(mName, new Class[]{String.class}); } privateLoggerDebugName = mName; return true; } else if (type == CallbackType.USER) { if ( !isJython && !isJRuby ) { privateLoggerUser = privateLogger.getClass().getMethod(mName, new Class[]{String.class}); } privateLoggerUserName = mName; return true; } else { return false; } } catch (Exception e) { error("Debug: setLoggerInfo: redirecting to %s failed: \n%s", mName, e.getMessage()); } return false; } /** * specify the target method for redirection of Sikuli's user log messages [user]
* must be the name of an instance method of the previously defined logger and
* must accept exactly one string parameter, that contains the info message * @param mUser name of the method where the message should be sent *
reset to default logging by either null or empty string * @return true if the method is available false otherwise */ public static boolean setLoggerUser(String mUser) { if (mUser == null || mUser.isEmpty()) { privateLoggerUserName = ""; return true; } return doSetLoggerCallback(mUser, CallbackType.USER); } /** * specify the target method for redirection of Sikuli's info messages [info]
* must be the name of an instance method of the previously defined logger and
* must accept exactly one string parameter, that contains the info message * @param mInfo name of the method where the message should be sent *
reset to default logging by either null or empty string * @return true if the method is available false otherwise */ public static boolean setLoggerInfo(String mInfo) { if (mInfo == null || mInfo.isEmpty()) { privateLoggerInfoName = ""; return true; } return doSetLoggerCallback(mInfo, CallbackType.INFO); } /** * specify the target method for redirection of Sikuli's action messages [log]
* must be the name of an instance method of the previously defined logger and
* must accept exactly one string parameter, that contains the info message * @param mAction name of the method where the message should be sent *
reset to default logging by either null or empty string * @return true if the method is available false otherwise */ public static boolean setLoggerAction(String mAction) { if (mAction == null || mAction.isEmpty()) { privateLoggerActionName = ""; return true; } return doSetLoggerCallback(mAction, CallbackType.ACTION); } /** * specify the target method for redirection of Sikuli's error messages [error]
* must be the name of an instance method of the previously defined logger and
* must accept exactly one string parameter, that contains the info message * @param mError name of the method where the message should be sent *
reset to default logging by either null or empty string * @return true if the method is available false otherwise */ public static boolean setLoggerError(String mError) { if (mError == null || mError.isEmpty()) { privateLoggerErrorName = ""; return true; } return doSetLoggerCallback(mError, CallbackType.ERROR); } /** * specify the target method for redirection of Sikuli's debug messages [debug]
* must be the name of an instance method of the previously defined logger and
* must accept exactly one string parameter, that contains the info message * @param mDebug name of the method where the message should be sent *
reset to default logging by either null or empty string * @return true if the method is available false otherwise */ public static boolean setLoggerDebug(String mDebug) { if (mDebug == null || mDebug.isEmpty()) { privateLoggerDebugName = ""; return true; } return doSetLoggerCallback(mDebug, CallbackType.DEBUG); } public static void saveRedirected(PrintStream rdo, PrintStream rde) { redirectedOut = rdo; redirectedErr = rde; } public static void out(String msg) { if (redirectedOut != null && DEBUG_LEVEL > 2) { redirectedOut.println(msg); } } /** * specify, where the logs should be written:
* null - use from property sikuli.Logfile * empty - use SikuliLog.txt in working folder * not empty - use given filename * @param fileName null, empty or absolute filename * @return success */ public static boolean setLogFile(String fileName) { if (fileName == null) { fileName = System.getProperty("sikuli.Logfile"); } if (fileName != null) { if ("".equals(fileName)) { if (Settings.isMacApp) { fileName = "SikulixLog.txt"; } else { fileName = FileManager.slashify(System.getProperty("user.dir"), true) + "SikulixLog.txt"; } } try { logfile = fileName; if (printout != null) { printout.close(); } printout = new PrintStream(fileName); log(3, "Debug: setLogFile: " + fileName); return true; } catch (Exception ex) { System.out.printf("[Error] Logfile %s not accessible - check given path", fileName); System.out.println(); return false; } } return false; } /** * does Sikuli log go to a file? * @return true if yes, false otherwise */ public static boolean isLogToFile() { return (printout != null); } /** * specify, where the user logs (Debug.user) should be written:
* null - use from property sikuli.LogfileUser * empty - use UserLog.txt in working folder * not empty - use given filename * @param fileName null, empty or absolute filename * @return success */ public static boolean setUserLogFile(String fileName) { if (fileName == null) { fileName = System.getProperty("sikuli.LogfileUser"); } if (fileName != null) { if ("".equals(fileName)) { if (Settings.isMacApp) { fileName = "UserLog.txt"; } else { fileName = FileManager.slashify(System.getProperty("user.dir"), true) + "UserLog.txt"; } } try { if (printoutuser != null) { printoutuser.close(); } printoutuser = new PrintStream(fileName); log(3, "Debug: setLogFile: " + fileName); return true; } catch (FileNotFoundException ex) { System.out.printf("[Error] User logfile %s not accessible - check given path", fileName); System.out.println(); return false; } } return false; } /** * does user log go to a file? * @return true if yes, false otherwise */ public static boolean isUserLogToFile() { return (printoutuser != null); } /** * * @return current debug level */ public static int getDebugLevel() { return DEBUG_LEVEL; } /** * set debug level to default level * * @return default level */ public static int setDebugLevel() { setDebugLevel(0); return DEBUG_LEVEL; } /** * set debug level to given value * * @param level value */ public static void setDebugLevel(int level) { DEBUG_LEVEL = level; if (DEBUG_LEVEL > 0) { Settings.DebugLogs = true; } else { Settings.DebugLogs = false; } } public static void on(int level) { setDebugLevel(level); } public static void on(String level) { setDebugLevel(level); } public static boolean is(int level) { return DEBUG_LEVEL >= level; } public static int is() { return DEBUG_LEVEL; } public static void off() { setDebugLevel(0); } /** * set debug level to given number value as string (ignored if invalid) * * @param level valid number string */ public static void setDebugLevel(String level) { try { DEBUG_LEVEL = Integer.parseInt(level); if (DEBUG_LEVEL > 0) { Settings.DebugLogs = true; } else { Settings.DebugLogs = false; } } catch (NumberFormatException e) { } } private static boolean doRedirect(CallbackType type, String pre, String message, Object... args) { boolean success = false; String error = ""; if (privateLogger != null) { String prefix = "", pln = ""; Method plf = null; if (type == CallbackType.INFO && !privateLoggerInfoName.isEmpty()) { prefix = privateLoggerPrefixAll ? privateLoggerInfoPrefix : ""; plf = privateLoggerInfo; pln = privateLoggerInfoName; } else if (type == CallbackType.ACTION && !privateLoggerActionName.isEmpty()) { prefix = privateLoggerPrefixAll ? privateLoggerActionPrefix : ""; plf = privateLoggerAction; pln = privateLoggerActionName; } else if (type == CallbackType.ERROR && !privateLoggerErrorName.isEmpty()) { prefix = privateLoggerPrefixAll ? privateLoggerErrorPrefix : ""; plf = privateLoggerError; pln = privateLoggerErrorName; } else if (type == CallbackType.DEBUG && !privateLoggerDebugName.isEmpty()) { prefix = privateLoggerPrefixAll ? (privateLoggerDebugPrefix.isEmpty() ? pre : privateLoggerDebugPrefix) : ""; plf = privateLoggerDebug; pln = privateLoggerDebugName; } else if (type == CallbackType.USER && !privateLoggerUserName.isEmpty()) { prefix = privateLoggerPrefixAll ? (privateLoggerUserPrefix.isEmpty() ? pre : privateLoggerUserPrefix) : ""; plf = privateLoggerUser; pln = privateLoggerUserName; } if (!pln.isEmpty()) { String msg = null; if (args == null) { msg = prefix + message; } else { msg = String.format(prefix + message, args); } if (isJython) { success = JythonHelper.get().runLoggerCallback(new Object[]{privateLogger, pln, msg}); } else if (isJRuby) { success = false; } else { try { plf.invoke(privateLogger, new Object[]{msg}); return true; } catch (Exception e) { error = ": " + e.getMessage(); success = false; } } if (!success) { Debug.error("calling (%s) logger.%s failed - resetting to default%s", type, pln, error); if (type == CallbackType.INFO) { privateLoggerInfoName = ""; } else if (type == CallbackType.ACTION) { privateLoggerActionName = ""; } else if (type == CallbackType.ERROR) { privateLoggerErrorName = ""; } else if (type == CallbackType.DEBUG) { privateLoggerDebugName = ""; } else if (type == CallbackType.USER) { privateLoggerUserName = ""; } } } } return success; } /** * Sikuli messages from actions like click, ...
switch on/off: Settings.ActionLogs * * @param message String or format string (String.format) * @param args to use with format string */ public static void action(String message, Object... args) { if (Settings.ActionLogs) { if (doRedirect(CallbackType.ACTION, "", message, args)) { return; } if (is(3)) { logx(3, message, args); } else { log(-1, actionPrefix, message, args); } } } /** * use Debug.action() instead * @param message String or format string (String.format) * @param args to use with format string * @deprecated */ @Deprecated public static void history(String message, Object... args) { action(message, args); } /** * informative Sikuli messages
switch on/off: Settings.InfoLogs * * @param message String or format string (String.format) * @param args to use with format string */ public static void info(String message, Object... args) { if (Settings.InfoLogs) { if (doRedirect(CallbackType.INFO, "", message, args)) { return; } log(-1, infoPrefix, message, args); } if (is(3)) { logx(3, message, args); } } /** * Sikuli error messages
switch on/off: always on * * @param message String or format string (String.format) * @param args to use with format string */ public static void error(String message, Object... args) { if (doRedirect(CallbackType.ERROR, "", message, args)) { return; } log(-1, errorPrefix, message, args); } /** * Sikuli messages to use in tests
switch on/off: always on * * @param message String or format string (String.format) * @param args to use with format string */ public static void test(String message, Object... args) { if (message.contains("#returned#")) { message = message.replace("#returned#", "returned: " + ((Boolean) args[0] ? "true" : "false")); args = Arrays.copyOfRange(args, 1, args.length); } log(-1, "test", message, args); } /** * Sikuli debug messages with default level
switch on/off: Settings.DebugLogs (off) and/or * -Dsikuli.Debug * * @param message String or format string (String.format) * @param args to use with format string */ public static void log(String message, Object... args) { log(0, message, args); } public static boolean logJython() { return logJython(null); } public static boolean logJython(Boolean state) { if (null != state) { shouldLogJython = state; } return shouldLogJython; } public static void logj(String message, Object... args) { if (shouldLogJython) { log(0, "Jython: " + message, args); } } /** * messages given by the user
switch on/off: Settings.UserLogs
depending on * Settings.UserLogTime, the prefix contains a timestamp
the user prefix (default "user") * can be set: Settings,UserLogPrefix * * @param message String or format string (String.format) * @param args to use with format string */ public static void user(String message, Object... args) { if (Settings.UserLogs) { if (Settings.UserLogTime) { //TODO replace the hack -99 to filter user logs log(-99, String.format("%s (%s)", Settings.UserLogPrefix, df.format(new Date())), message, args); } else { log(-99, String.format("%s", Settings.UserLogPrefix), message, args); } } } /** * Sikuli debug messages with level
switch on/off: Settings.DebugLogs (off) and/or * -Dsikuli.Debug * * @param level value * @param message String or format string (String.format) * @param args to use with format string */ public static void log(int level, String message, Object... args) { if (Settings.DebugLogs) { log(level, debugPrefix, message, args); } } /** * INTERNAL USE: special debug messages * @param level value * @param message text or format string * @param args for use with format string * @return generated message */ public static String logx(int level, String message, Object... args) { String sout = ""; if (level == -1 || level == -100) { sout = log(level, errorPrefix, message, args); } else if (level == -2) { sout = log(level, actionPrefix, message, args); } else if (level == -3) { sout = log(level, "", message, args); } else { sout = log(level, debugPrefix, message, args); } return sout; } public static String logp(String msg, Object... args) { String out = String.format(msg, args); System.out.println(out); return out; } private static synchronized String log(int level, String prefix, String message, Object... args) { //TODO replace the hack -99 to filter user logs String sout = ""; String stime = ""; if (level <= DEBUG_LEVEL) { if (level == 3) { if (message.startsWith("TRACE: ")) { if (!Settings.TraceLogs) { return ""; } } } if (Settings.LogTime && level != -99) { stime = String.format(" (%s)", df.format(new Date())); } if (!prefix.isEmpty()) { prefix = "[" + prefix + stime + "] "; } sout = String.format(message, args); boolean isRedirected = false; if (level > -99) { isRedirected = doRedirect(CallbackType.DEBUG, prefix, sout, null); } else if (level == -99) { isRedirected = doRedirect(CallbackType.USER, prefix, sout, null); } if (!isRedirected) { if (level == -99 && printoutuser != null) { printoutuser.print(prefix + sout); printoutuser.println(); } else if (printout != null) { printout.print(prefix + sout); printout.println(); } else { System.out.print(prefix + sout); System.out.println(); } if (level == -1 || level == -100 || level > 2) { out(prefix + sout); } } } return prefix + sout; } /** * Sikuli profiling messages
switch on/off: Settings.ProfileLogs, default off * * @param message String or format string * @param args to use with format string */ public static void profile(String message, Object... args) { if (Settings.ProfileLogs) { log(-1, "profile", message, args); } } /** * profile convenience: entering a method * @param message String or format string * @param args to use with format string */ public static void enter(String message, Object... args) { profile("entering: " + message, args); } /** * profile convenience: exiting a method * @param message String or format string * @param args to use with format string */ public static void exit(String message, Object... args) { profile("exiting: " + message, args); } /** * start timer *
log output depends on Settings.ProfileLogs * @return timer */ public static Debug startTimer() { return startTimer(""); } /** * start timer with a message *
log output depends on Settings.ProfileLogs * @param message String or format string * @param args to use with format string * @return timer */ public static Debug startTimer(String message, Object... args) { Debug timer = new Debug(); timer.startTiming(message, args); return timer; } /** * stop timer and print timer message *
log output depends on Settings.ProfileLogs * * @return the time in msec */ public long end() { if (_title == null) { return endTiming(_message, false, new Object[0]); } else { return endTiming(_title, false, new Object[0]); } } /** * lap timer and print message with timer message *
log output depends on Settings.ProfileLogs * * @param message String or format string * @return the time in msec */ public long lap(String message) { if (_title == null) { return endTiming("(" + message + ") " + _message, true, new Object[0]); } else { return endTiming("(" + message + ") " + _title, true, new Object[0]); } } private void startTiming(String message, Object... args) { int pos; if ((pos = message.indexOf("\t")) < 0) { _title = null; _message = message; } else { _title = message.substring(0, pos); _message = message.replace("\t", " "); } if (!"".equals(_message)) { profile("TStart: " + _message, args); } _beginTime = (new Date()).getTime(); } private long endTiming(String message, boolean isLap, Object... args) { if (_beginTime == 0) { profile("TError: timer not started (%s)", message); return -1; } long t = (new Date()).getTime(); long dt = t - _beginTime; if (!isLap) { _beginTime = 0; } if (!"".equals(message)) { profile(String.format((isLap ? "TLap:" : "TEnd") + " (%.3f sec): ", (float) dt / 1000) + message, args); } return dt; } private static enum CallbackType { INFO, ACTION, ERROR, DEBUG, USER; } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/ExtensionManager.java000066400000000000000000000111341315726130400257160ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import org.sikuli.script.RunTime; import java.io.*; import java.net.URL; import java.util.ArrayList; import java.util.regex.Pattern; public class ExtensionManager { private static ExtensionManager _instance = null; private ArrayList extensions; private ExtensionManager() { extensions = new ArrayList(); Extension e; String p, n, v; File dir = new File(Settings.getUserExtPath()); for (File d : dir.listFiles()) { if (d.getAbsolutePath().endsWith(".jar")) { p = d.getAbsolutePath(); n = d.getName(); n = n.substring(0, n.length()-4); if (n.contains("-")) { v = n.substring(n.lastIndexOf("-")+1); n = n.substring(0, n.lastIndexOf("-")); } else { v = "0.0"; } e = new Extension(n, p, v); extensions.add(e); } } } public static ExtensionManager getInstance() { if (_instance == null) { _instance = new ExtensionManager(); } return _instance; } public boolean install(String name, String url, String version) { if (url.startsWith("---extensions---")) { url = RunTime.get().SikuliRepo + name + "-" + version + ".jar"; } String extPath = Settings.getUserExtPath(); String tmpdir = RunTime.get().fpBaseTempPath; try { File localFile = new File(FileManager.downloadURL(new URL(url), tmpdir)); String extName = localFile.getName(); File targetFile = new File(extPath, extName); if (targetFile.exists()) { targetFile.delete(); } if (!localFile.renameTo(targetFile)) { Debug.error("ExtensionManager: Failed to install " + localFile.getName() + " to " + targetFile.getAbsolutePath()); return false; } addExtension(name, localFile.getAbsolutePath(), version); } catch (IOException e) { Debug.error("ExtensionManager: Failed to download " + url); return false; } return true; } private void addExtension(String name, String path, String version) { Extension e = find(name, version); if (e == null) { extensions.add(new Extension(name, path, version)); } else { e.path = path; } } public boolean isInstalled(String name) { if (find(name) != null) { return true; } else { return false; } } public String getLoadPath(String name) { Extension e = find(name); if (e != null) { Debug.log(2, "ExtensionManager: found: "+ name + " ( " + e.version + " )"); return e.path; } else { if (!name.endsWith(".jar")) { Debug.error("ExtensionManager: not found: "+ name ); } return null; } } public boolean isOutOfDate(String name, String version) { Extension e = find(name); if (e == null) { return false; } else { String s1 = normalisedVersion(e.version); // installed version String s2 = normalisedVersion(version); // version number to check int cmp = s1.compareTo(s2); return cmp < 0; } } public String getVersion(String name) { Extension e = find(name); if (e != null) { return e.version; } else { return null; } } private Extension find(String name) { if (name.endsWith(".jar")) { name = name.substring(0, name.length()-4); } String v; if (name.contains("-")) { v = name.substring(name.lastIndexOf("-")+1); return find(name.substring(0, name.lastIndexOf("-")), v); } else { v = normalisedVersion("0.0"); } Extension ext = null; for (Extension e : extensions) { if (e.name.equals(name)) { if (v.compareTo(normalisedVersion(e.version)) <= 0) { ext = e; v = normalisedVersion(e.version); } } } return ext; } private Extension find(String name, String version) { String v = normalisedVersion(version); for (Extension e : extensions) { if (e.name.equals(name) && normalisedVersion(e.version).equals(v)) { return e; } } return null; } private static String normalisedVersion(String version) { return normalisedVersion(version, ".", 4); } private static String normalisedVersion(String version, String sep, int maxWidth) { String[] split = Pattern.compile(sep, Pattern.LITERAL).split(version); StringBuilder sb = new StringBuilder(); for (String s : split) { sb.append(String.format("%" + maxWidth + 's', s)); } return sb.toString(); } } class Extension implements Serializable { public String name, path, version; public Extension(String name_, String path_, String version_) { name = name_; path = path_; version = version_; } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/FileManager.java000066400000000000000000001326501315726130400246300ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import org.sikuli.script.RunTime; import java.awt.Desktop; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; import java.net.URLDecoder; import java.net.UnknownHostException; import java.nio.charset.Charset; import java.security.CodeSource; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Random; import java.util.Set; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import javax.imageio.ImageIO; import javax.swing.JFrame; import org.sikuli.script.Image; import org.sikuli.script.ImagePath; import org.sikuli.script.Sikulix; /** * INTERNAL USE: Support for accessing files and other ressources */ public class FileManager { private static String me = "FileManager"; private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + ": " + message, args); } static final int DOWNLOAD_BUFFER_SIZE = 153600; private static SplashFrame _progress = null; private static final String EXECUTABLE = "#executable"; public static int tryGetFileSize(URL aUrl) { HttpURLConnection conn = null; try { if (getProxy() != null) { conn = (HttpURLConnection) aUrl.openConnection(getProxy()); } else { conn = (HttpURLConnection) aUrl.openConnection(); } conn.setConnectTimeout(30000); conn.setReadTimeout(30000); conn.setRequestMethod("HEAD"); conn.getInputStream(); return conn.getContentLength(); } catch (Exception ex) { return 0; } finally { if (conn != null) { conn.disconnect(); } } } public static int isUrlUseabel(String sURL) { try { return isUrlUseabel(new URL(sURL)); } catch (Exception ex) { return -1; } } public static int isUrlUseabel(URL aURL) { HttpURLConnection conn = null; try { // HttpURLConnection.setFollowRedirects(false); if (getProxy() != null) { conn = (HttpURLConnection) aURL.openConnection(getProxy()); } else { conn = (HttpURLConnection) aURL.openConnection(); } // con.setInstanceFollowRedirects(false); conn.setRequestMethod("HEAD"); int retval = conn.getResponseCode(); // HttpURLConnection.HTTP_BAD_METHOD 405 // HttpURLConnection.HTTP_NOT_FOUND 404 if (retval == HttpURLConnection.HTTP_OK) { return 1; } else if (retval == HttpURLConnection.HTTP_NOT_FOUND) { return 0; } else if (retval == HttpURLConnection.HTTP_FORBIDDEN) { return 0; } else { return -1; } } catch (Exception ex) { return -1; } finally { if (conn != null) { conn.disconnect(); } } } public static Proxy getProxy() { Proxy proxy = Settings.proxy; if (!Settings.proxyChecked) { String phost = Settings.proxyName; String padr = Settings.proxyIP; String pport = Settings.proxyPort; InetAddress a = null; int p = -1; if (phost != null) { a = getProxyAddress(phost); } if (a == null && padr != null) { a = getProxyAddress(padr); } if (a != null && pport != null) { p = getProxyPort(pport); } if (a != null && p > 1024) { proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(a, p)); log(lvl, "Proxy defined: %s : %d", a.getHostAddress(), p); } Settings.proxyChecked = true; Settings.proxy = proxy; } return proxy; } public static boolean setProxy(String pName, String pPort) { InetAddress a = null; String host = null; String adr = null; int p = -1; if (pName != null) { a = getProxyAddress(pName); if (a == null) { a = getProxyAddress(pName); if (a != null) { adr = pName; } } else { host = pName; } } if (a != null && pPort != null) { p = getProxyPort(pPort); } if (a != null && p > 1024) { log(lvl, "Proxy stored: %s : %d", a.getHostAddress(), p); Settings.proxyChecked = true; Settings.proxyName = host; Settings.proxyIP = adr; Settings.proxyPort = pPort; Settings.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(a, p)); PreferencesUser prefs = PreferencesUser.getInstance(); prefs.put("ProxyName", (host == null ? "" : host)); prefs.put("ProxyIP", (adr == null ? "" : adr)); prefs.put("ProxyPort", ""+p); return true; } return false; } /** * download a file at the given url to a local folder * * @param url a valid url * @param localPath the folder where the file should go (will be created if necessary) * @return the absolute path to the downloaded file or null on any error */ public static String downloadURL(URL url, String localPath) { String[] path = url.getPath().split("/"); String filename = path[path.length - 1]; String targetPath = null; int srcLength = 1; int srcLengthKB = 0; int done; int totalBytesRead = 0; File fullpath = new File(localPath); if (fullpath.exists()) { if (fullpath.isFile()) { log(-1, "download: target path must be a folder:\n%s", localPath); fullpath = null; } } else { if (!fullpath.mkdirs()) { log(-1, "download: could not create target folder:\n%s", localPath); fullpath = null; } } if (fullpath != null) { srcLength = tryGetFileSize(url); srcLengthKB = (int) (srcLength / 1024); if (srcLength > 0) { log(lvl, "Downloading %s having %d KB", filename, srcLengthKB); } else { log(lvl, "Downloading %s with unknown size", filename); } fullpath = new File(localPath, filename); targetPath = fullpath.getAbsolutePath(); done = 0; if (_progress != null) { _progress.setProFile(filename); _progress.setProSize(srcLengthKB); _progress.setProDone(0); _progress.setVisible(true); } InputStream reader = null; FileOutputStream writer = null; try { writer = new FileOutputStream(fullpath); if (getProxy() != null) { reader = url.openConnection(getProxy()).getInputStream(); } else { reader = url.openConnection().getInputStream(); } byte[] buffer = new byte[DOWNLOAD_BUFFER_SIZE]; int bytesRead = 0; long begin_t = (new Date()).getTime(); long chunk = (new Date()).getTime(); while ((bytesRead = reader.read(buffer)) > 0) { writer.write(buffer, 0, bytesRead); totalBytesRead += bytesRead; if (srcLength > 0) { done = (int) ((totalBytesRead / (double) srcLength) * 100); } else { done = (int) (totalBytesRead / 1024); } if (((new Date()).getTime() - chunk) > 1000) { if (_progress != null) { _progress.setProDone(done); } chunk = (new Date()).getTime(); } } writer.close(); log(lvl, "downloaded %d KB to:\n%s", (int) (totalBytesRead / 1024), targetPath); log(lvl, "download time: %d", (int) (((new Date()).getTime() - begin_t) / 1000)); } catch (Exception ex) { log(-1, "problems while downloading\n%s", ex); targetPath = null; } finally { if (reader != null) { try { reader.close(); } catch (IOException ex) { } } if (writer != null) { try { writer.close(); } catch (IOException ex) { } } } if (_progress != null) { if (targetPath == null) { _progress.setProDone(-1); } else { if (srcLength <= 0) { _progress.setProSize((int) (totalBytesRead / 1024)); } _progress.setProDone(100); } _progress.closeAfter(3); _progress = null; } } if (targetPath == null) { fullpath.delete(); } return targetPath; } /** * download a file at the given url to a local folder * * @param url a string representing a valid url * @param localPath the folder where the file should go (will be created if necessary) * @return the absolute path to the downloaded file or null on any error */ public static String downloadURL(String url, String localPath) { URL urlSrc = null; try { urlSrc = new URL(url); } catch (MalformedURLException ex) { log(-1, "download: bad URL: " + url); return null; } return downloadURL(urlSrc, localPath); } public static String downloadURL(String url, String localPath, JFrame progress) { _progress = (SplashFrame) progress; return downloadURL(url, localPath); } public static String downloadURLtoString(String src) { URL url = null; try { url = new URL(src); } catch (MalformedURLException ex) { log(-1, "download to string: bad URL:\n%s", src); return null; } return downloadURLtoString(url); } public static String downloadURLtoString(URL uSrc) { String content = ""; InputStream reader = null; log(lvl, "download to string from:\n%s,", uSrc); try { if (getProxy() != null) { reader = uSrc.openConnection(getProxy()).getInputStream(); } else { reader = uSrc.openConnection().getInputStream(); } byte[] buffer = new byte[DOWNLOAD_BUFFER_SIZE]; int bytesRead = 0; while ((bytesRead = reader.read(buffer)) > 0) { content += (new String(Arrays.copyOfRange(buffer, 0, bytesRead), Charset.forName("utf-8"))); } } catch (Exception ex) { log(-1, "problems while downloading\n" + ex.getMessage()); } finally { if (reader != null) { try { reader.close(); } catch (IOException ex) { } } } return content; } /** * open the given url in the standard browser * * @param url string representing a valid url * @return false on error, true otherwise */ public static boolean openURL(String url) { try { URL u = new URL(url); Desktop.getDesktop().browse(u.toURI()); } catch (Exception ex) { log(-1, "show in browser: bad URL: " + url); return false; } return true; } public static File createTempDir(String path) { File fTempDir = new File(RunTime.get().fpBaseTempPath, path); log(lvl, "createTempDir:\n%s", fTempDir); if (!fTempDir.exists()) { fTempDir.mkdirs(); } else { FileManager.resetFolder(fTempDir); } if (!fTempDir.exists()) { log(-1, "createTempDir: not possible: %s", fTempDir); return null; } return fTempDir; } public static File createTempDir() { File fTempDir = createTempDir("tmp-" + getRandomInt() + ".sikuli"); if (null != fTempDir) { fTempDir.deleteOnExit(); } return fTempDir; } public static int getRandomInt() { int rand = 1 + new Random().nextInt(); return (rand < 0 ? rand * -1 : rand); } public static void deleteTempDir(String path) { if (!deleteFileOrFolder(path)) { log(-1, "deleteTempDir: not possible"); } } public static boolean deleteFileOrFolder(File fPath, FileFilter filter) { return doDeleteFileOrFolder(fPath, filter); } public static boolean deleteFileOrFolder(File fPath) { return doDeleteFileOrFolder(fPath, null); } public static boolean deleteFileOrFolder(String fpPath, FileFilter filter) { if (fpPath.startsWith("#")) { fpPath = fpPath.substring(1); } else { log(lvl, "deleteFileOrFolder: %s\n%s", (filter == null ? "" : "filtered: "), fpPath); } return doDeleteFileOrFolder(new File(fpPath), filter); } public static boolean deleteFileOrFolder(String fpPath) { if (fpPath.startsWith("#")) { fpPath = fpPath.substring(1); } else { log(lvl, "deleteFileOrFolder:\n%s", fpPath); } return doDeleteFileOrFolder(new File(fpPath), null); } public static void resetFolder(File fPath) { log(lvl, "resetFolder:\n%s", fPath); doDeleteFileOrFolder(fPath, null); fPath.mkdirs(); } private static boolean doDeleteFileOrFolder(File fPath, FileFilter filter) { if (fPath == null) { return false; } File aFile; String[] entries; boolean somethingLeft = false; if (fPath.exists() && fPath.isDirectory()) { entries = fPath.list(); for (int i = 0; i < entries.length; i++) { aFile = new File(fPath, entries[i]); if (filter != null && !filter.accept(aFile)) { somethingLeft = true; continue; } if (aFile.isDirectory()) { if (!doDeleteFileOrFolder(aFile, filter)) { return false; } } else { try { aFile.delete(); } catch (Exception ex) { log(-1, "deleteFile: not deleted:\n%s\n%s", aFile, ex); return false; } } } } // deletes intermediate empty directories and finally the top now empty dir if (!somethingLeft && fPath.exists()) { try { fPath.delete(); } catch (Exception ex) { log(-1, "deleteFolder: not deleted:\n" + fPath.getAbsolutePath() + "\n" + ex.getMessage()); return false; } } return true; } public static void traverseFolder(File fPath, FileFilter filter) { if (fPath == null) { return; } File aFile; String[] entries; if (fPath.isDirectory()) { entries = fPath.list(); for (int i = 0; i < entries.length; i++) { aFile = new File(fPath, entries[i]); if (filter != null) { filter.accept(aFile); } if (aFile.isDirectory()) { traverseFolder(aFile, filter); } } } } public static File createTempFile(String suffix) { return createTempFile(suffix, null); } public static File createTempFile(String suffix, String path) { String temp1 = "sikuli-"; String temp2 = "." + suffix; File fpath = new File(RunTime.get().fpBaseTempPath); if (path != null) { fpath = new File(path); } try { fpath.mkdirs(); File temp = File.createTempFile(temp1, temp2, fpath); temp.deleteOnExit(); String fpTemp = temp.getAbsolutePath(); if (!fpTemp.endsWith(".script")) { log(lvl, "tempfile create:\n%s", temp.getAbsolutePath()); } return temp; } catch (IOException ex) { log(-1, "createTempFile: IOException: %s\n%s", ex.getMessage(), fpath + File.separator + temp1 + "12....56" + temp2); return null; } } public static String saveTmpImage(BufferedImage img) { return saveTmpImage(img, null); } public static String saveTmpImage(BufferedImage img, String path) { File tempFile; try { tempFile = createTempFile("png", path); if (tempFile != null) { ImageIO.write(img, "png", tempFile); return tempFile.getAbsolutePath(); } } catch (IOException e) { e.printStackTrace(); } return null; } public static String saveTimedImage(BufferedImage img) { return saveTimedImage(img, ImagePath.getBundlePath(), null); } public static String saveTimedImage(BufferedImage img, String path) { return saveTimedImage(img, path, null); } public static String saveTimedImage(BufferedImage img, String path, String name) { RunTime.pause(0.01f); File fImage = new File(path, String.format("%s-%d.png", name, new Date().getTime())); try { ImageIO.write(img, "png", fImage); } catch (Exception ex) { return ""; } return fImage.getAbsolutePath(); } public static boolean unzip(String inpZip, String target) { return unzip(new File(inpZip), new File(target)); } public static boolean unzip(File fZip, File fTarget) { String fpZip = null; String fpTarget = null; log(lvl, "unzip: from: %s\nto: %s", fZip, fTarget); try { fpZip = fZip.getCanonicalPath(); if (!new File(fpZip).exists()) { throw new IOException(); } } catch (IOException ex) { log(-1, "unzip: source not found:\n%s\n%s", fpZip, ex); return false; } try { fpTarget = fTarget.getCanonicalPath(); deleteFileOrFolder(fpTarget); new File(fpTarget).mkdirs(); if (!new File(fpTarget).exists()) { throw new IOException(); } } catch (IOException ex) { log(-1, "unzip: target cannot be created:\n%s\n%s", fpTarget, ex); return false; } ZipInputStream inpZip = null; ZipEntry entry = null; try { final int BUF_SIZE = 2048; inpZip = new ZipInputStream(new BufferedInputStream(new FileInputStream(fZip))); while ((entry = inpZip.getNextEntry()) != null) { if (entry.getName().endsWith("/") || entry.getName().endsWith("\\")) { new File(fpTarget, entry.getName()).mkdir(); continue; } int count; byte data[] = new byte[BUF_SIZE]; File outFile = new File(fpTarget, entry.getName()); File outFileParent = outFile.getParentFile(); if (! outFileParent.exists()) { outFileParent.mkdirs(); } FileOutputStream fos = new FileOutputStream(outFile); BufferedOutputStream dest = new BufferedOutputStream(fos, BUF_SIZE); while ((count = inpZip.read(data, 0, BUF_SIZE)) != -1) { dest.write(data, 0, count); } dest.close(); } } catch (Exception ex) { log(-1, "unzip: not possible: source:\n%s\ntarget:\n%s\n(%s)%s", fpZip, fpTarget, entry.getName(), ex); return false; } finally { try { inpZip.close(); } catch (IOException ex) { log(-1, "unzip: closing source:\n%s\n%s", fpZip, ex); } } return true; } public static boolean xcopy(File fSrc, File fDest) { if (fSrc == null || fDest == null) { return false; } try { doXcopy(fSrc, fDest, null); } catch (Exception ex) { log(lvl, "xcopy from: %s\nto: %s\n%s", fSrc, fDest, ex); return false; } return true; } public static boolean xcopy(File fSrc, File fDest, FileFilter filter) { if (fSrc == null || fDest == null) { return false; } try { doXcopy(fSrc, fDest, filter); } catch (Exception ex) { log(lvl, "xcopy from: %s\nto: %s\n%s", fSrc, fDest, ex); return false; } return true; } public static void xcopy(String src, String dest) throws IOException { doXcopy(new File(src), new File(dest), null); } public static void xcopy(String src, String dest, FileFilter filter) throws IOException { doXcopy(new File(src), new File(dest), filter); } private static void doXcopy(File fSrc, File fDest, FileFilter filter) throws IOException { if (fSrc.getAbsolutePath().equals(fDest.getAbsolutePath())) { return; } if (fSrc.isDirectory()) { if (filter == null || filter.accept(fSrc)) { if (!fDest.exists()) { fDest.mkdirs(); } String[] children = fSrc.list(); for (String child : children) { if (child.equals(fDest.getName())) { continue; } doXcopy(new File(fSrc, child), new File(fDest, child), filter); } } } else { if (filter == null || filter.accept(fSrc)) { if (fDest.isDirectory()) { fDest = new File(fDest, fSrc.getName()); } InputStream in = new FileInputStream(fSrc); OutputStream out = new FileOutputStream(fDest); // Copy the bits from instream to outstream byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } in.close(); out.close(); } } } private static String makeFileListString; private static String makeFileListPrefix; public static String makeFileList(File path, String prefix) { makeFileListPrefix = prefix; return makeFileListDo(path, true); } private static String makeFileListDo(File path, boolean starting) { String x; if (starting) { makeFileListString = ""; } if (!path.exists()) { return makeFileListString; } if (path.isDirectory()) { String[] fcl = path.list(); for (String fc : fcl) { makeFileListDo(new File(path, fc), false); } } else { x = path.getAbsolutePath(); if (!makeFileListPrefix.isEmpty()) { x = x.replace(makeFileListPrefix, "").replace("\\", "/"); if (x.startsWith("/")) { x = x.substring(1); } } makeFileListString += x + "\n"; } return makeFileListString; } /** * Copy a file *src* to the path *dest* and check if the file name conflicts. If a file with the * same name exists in that path, rename *src* to an alternative name. * @param src source file * @param dest destination path * @return the destination file if ok, null otherwise * @throws java.io.IOException on failure */ public static File smartCopy(String src, String dest) throws IOException { File fSrc = new File(src); String newName = fSrc.getName(); File fDest = new File(dest, newName); if (fSrc.equals(fDest)) { return fDest; } while (fDest.exists()) { newName = getAltFilename(newName); fDest = new File(dest, newName); } xcopy(src, fDest.getAbsolutePath()); if (fDest.exists()) { return fDest; } return null; } public static String convertStreamToString(InputStream is) { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line; try { while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); } public static String getAltFilename(String filename) { int pDot = filename.lastIndexOf('.'); int pDash = filename.lastIndexOf('-'); int ver = 1; String postfix = filename.substring(pDot); String name; if (pDash >= 0) { name = filename.substring(0, pDash); ver = Integer.parseInt(filename.substring(pDash + 1, pDot)); ver++; } else { name = filename.substring(0, pDot); } return name + "-" + ver + postfix; } public static boolean exists(String path) { File f = new File(path); return f.exists(); } public static void mkdir(String path) { File f = new File(path); if (!f.exists()) { f.mkdirs(); } } public static String getName(String filename) { File f = new File(filename); return f.getName(); } public static String slashify(String path, Boolean isDirectory) { if (path != null) { if (path.contains("%")) { try { path = URLDecoder.decode(path, "UTF-8"); } catch (Exception ex) { log(lvl, "slashify: decoding problem with %s\nwarning: filename might not be useable.", path); } } if (File.separatorChar != '/') { path = path.replace(File.separatorChar, '/'); } if (isDirectory != null) { if (isDirectory) { if (!path.endsWith("/")) { path = path + "/"; } } else if (path.endsWith("/")) { path = path.substring(0, path.length() - 1); } } if (path.startsWith("./")) { path = path.substring(2); } return path; } else { return ""; } } public static String normalize(String filename) { return slashify(filename, false); } public static String normalizeAbsolute(String filename, boolean withTrailingSlash) { filename = slashify(filename, false); String jarSuffix = ""; int nJarSuffix; if (-1 < (nJarSuffix = filename.indexOf(".jar!/"))) { jarSuffix = filename.substring(nJarSuffix + 4); filename = filename.substring(0, nJarSuffix + 4); } File aFile = new File(filename); try { filename = aFile.getCanonicalPath(); aFile = new File(filename); } catch (Exception ex) { } String fpFile = aFile.getAbsolutePath(); if (!fpFile.startsWith("/")) { fpFile = "/" + fpFile; } return slashify(fpFile + jarSuffix, withTrailingSlash); } public static boolean isFilenameDotted(String name) { String nameParent = new File(name).getParent(); if (nameParent != null && nameParent.contains(".")) { return true; } return false; } /** * Returns the directory that contains the images used by the ScriptRunner. * * @param scriptFile The file containing the script. * @return The directory containing the images. */ public static File resolveImagePath(File scriptFile) { if (!scriptFile.isDirectory()) { return scriptFile.getParentFile(); } return scriptFile; } public static URL makeURL(String fName) { return makeURL(fName, "file"); } public static URL makeURL(String fName, String type) { try { if ("file".equals(type)) { fName = normalizeAbsolute(fName, false); if (!fName.startsWith("/")) { fName = "/" + fName; } } if ("jar".equals(type)) { if (!fName.contains("!/")) { fName += "!/"; } return new URL("jar:" + fName); } else if ("file".equals(type)) { File aFile = new File(fName); if (aFile.exists() && aFile.isDirectory()) { if (!fName.endsWith("/")) { fName += "/"; } } } return new URL(type, null, fName); } catch (MalformedURLException ex) { return null; } } public static URL makeURL(URL path, String fName) { try { if ("file".equals(path.getProtocol())) { return makeURL(new File(path.getFile(), fName).getAbsolutePath()); } else if ("jar".equals(path.getProtocol())) { String jp = path.getPath(); if (!jp.contains("!/")) { jp += "!/"; } String jpu = "jar:" + jp; if (jp.endsWith("!/")) { jpu += fName; } else { jpu += "/" + fName; } return new URL(jpu); } return new URL(path, slashify(fName, false)); } catch (MalformedURLException ex) { return null; } } public static URL getURLForContentFromURL(URL uRes, String fName) { URL aURL = null; if ("jar".equals(uRes.getProtocol())) { return makeURL(uRes, fName); } else if ("file".equals(uRes.getProtocol())) { aURL = makeURL(new File(slashify(uRes.getPath(), false), slashify(fName, false)).getPath(), uRes.getProtocol()); } else if (uRes.getProtocol().startsWith("http")) { String sRes = uRes.toString(); if (!sRes.endsWith("/")) { sRes += "/"; } try { aURL = new URL(sRes + fName); if (1 == isUrlUseabel(aURL)) { return aURL; } else { return null; } } catch (MalformedURLException ex) { return null; } } try { if (aURL != null) { aURL.getContent(); return aURL; } } catch (IOException ex) { return null; } return aURL; } public static boolean checkJarContent(String jarPath, String jarContent) { URL jpu = makeURL(jarPath, "jar"); if (jpu != null && jarContent != null) { jpu = makeURL(jpu, jarContent); } if (jpu != null) { try { jpu.getContent(); return true; } catch (IOException ex) { ex.getMessage(); } } return false; } public static int getPort(String p) { int port; int pDefault = 50000; if (p != null) { try { port = Integer.parseInt(p); } catch (NumberFormatException ex) { return -1; } } else { return pDefault; } if (port < 1024) { port += pDefault; } return port; } public static int getProxyPort(String p) { int port; int pDefault = 8080; if (p != null) { try { port = Integer.parseInt(p); } catch (NumberFormatException ex) { return -1; } } else { return pDefault; } return port; } public static String getAddress(String arg) { try { if (arg == null) { return InetAddress.getLocalHost().getHostAddress(); } return InetAddress.getByName(arg).getHostAddress(); } catch (UnknownHostException ex) { return null; } } public static InetAddress getProxyAddress(String arg) { try { return InetAddress.getByName(arg); } catch (UnknownHostException ex) { return null; } } public static String saveImage(BufferedImage img, String sImage, String bundlePath) { final int MAX_ALT_NUM = 3; String fullpath = bundlePath; File fBundle = new File(fullpath); if (!fBundle.exists()) { fBundle.mkdir(); } if (!sImage.endsWith(".png")) { sImage += ".png"; } File fImage = new File(fBundle, sImage); boolean shouldReload = false; int count = 0; String msg = fImage.getName() + " exists - using "; while (count < MAX_ALT_NUM) { if (fImage.exists()) { if (Settings.OverwriteImages) { shouldReload = true; break; } else { fImage = new File(fBundle, FileManager.getAltFilename(fImage.getName())); } } else { if (count > 0) { Debug.log(msg + fImage.getName() + " (Utils.saveImage)"); } break; } count++; } if (count >= MAX_ALT_NUM) { fImage = new File(fBundle, Settings.getTimestamp() + ".png"); Debug.log(msg + fImage.getName() + " (Utils.saveImage)"); } String fpImage = fImage.getAbsolutePath(); fpImage = fpImage.replaceAll("\\\\", "/"); try { ImageIO.write(img, "png", new File(fpImage)); } catch (IOException e) { Debug.error("Util.saveImage: Problem trying to save image file: %s\n%s", fpImage, e.getMessage()); return null; } if (shouldReload) { Image.reload(sImage); } return fpImage; } //TODO consolidate with FileManager and Settings public static void deleteNotUsedImages(String bundle, Set usedImages) { File scriptFolder = new File(bundle); if (!scriptFolder.isDirectory()) { return; } String path; for (File image : scriptFolder.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { if ((name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg"))) { if (!name.startsWith("_")) { return true; } } return false; } })) { if (!usedImages.contains(image.getName())) { Debug.log(3, "FileManager: delete not used: %s", image.getName()); image.delete(); } } } public static boolean isBundle(String dir) { return dir.endsWith(".sikuli"); } // public static IResourceLoader getNativeLoader(String name, String[] args) { // if (nativeLoader != null) { // return nativeLoader; // } // IResourceLoader nl = null; // ServiceLoader loader = ServiceLoader.load(IResourceLoader.class); // Iterator resourceLoaderIterator = loader.iterator(); // while (resourceLoaderIterator.hasNext()) { // IResourceLoader currentLoader = resourceLoaderIterator.next(); // if ((name != null && currentLoader.getName().toLowerCase().equals(name.toLowerCase()))) { // nl = currentLoader; // nl.init(args); // break; // } // } // if (nl == null) { // log0(-1, "Fatal error 121: Could not load any NativeLoader!"); // (121); // } else { // nativeLoader = nl; // } // return nativeLoader; // } // public static String getJarParentFolder() { CodeSource src = FileManager.class.getProtectionDomain().getCodeSource(); String jarParentPath = "--- not known ---"; String RunningFromJar = "Y"; if (src.getLocation() != null) { String jarPath = src.getLocation().getPath(); if (!jarPath.endsWith(".jar")) RunningFromJar = "N"; jarParentPath = FileManager.slashify((new File(jarPath)).getParent(), true); } else { log(-1, "Fatal Error 101: Not possible to access the jar files!"); Sikulix.terminate(101); } return RunningFromJar + jarParentPath; } public static String getJarPath(Class cname) { CodeSource src = cname.getProtectionDomain().getCodeSource(); if (src.getLocation() != null) { return new File(src.getLocation().getPath()).getAbsolutePath(); } return ""; } public static String getJarName(Class cname) { String jp = getJarPath(cname); if (jp.isEmpty()) { return ""; } return new File(jp).getName(); } public static boolean writeStringToFile(String text, String path) { return writeStringToFile(text, new File(path)); } public static boolean writeStringToFile(String text, File fPath) { PrintStream out = null; try { out = new PrintStream(new FileOutputStream(fPath)); out.print(text); } catch (Exception e) { log(-1,"writeStringToFile: did not work: " + fPath + "\n" + e.getMessage()); } if (out != null) { out.close(); return true; } return false; } public static String readFileToString(File fPath) { try { return doRreadFileToString(fPath); } catch (Exception ex) { return ""; } } private static String doRreadFileToString(File fPath) throws IOException { StringBuilder result = new StringBuilder(); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(fPath)); char[] buf = new char[1024]; int r = 0; while ((r = reader.read(buf)) != -1) { result.append(buf, 0, r); } } finally { if (reader != null) { reader.close(); } } return result.toString(); } public static boolean packJar(String folderName, String jarName, String prefix) { jarName = FileManager.slashify(jarName, false); if (!jarName.endsWith(".jar")) { jarName += ".jar"; } folderName = FileManager.slashify(folderName, true); if (!(new File(folderName)).isDirectory()) { log(-1, "packJar: not a directory or does not exist: " + folderName); return false; } try { File dir = new File((new File(jarName)).getAbsolutePath()).getParentFile(); if (dir != null) { if (!dir.exists()) { dir.mkdirs(); } } else { throw new Exception("workdir is null"); } log(lvl, "packJar: %s from %s in workDir %s", jarName, folderName, dir.getAbsolutePath()); if (!folderName.startsWith("http://") && !folderName.startsWith("https://")) { folderName = "file://" + (new File(folderName)).getAbsolutePath(); } URL src = new URL(folderName); JarOutputStream jout = new JarOutputStream(new FileOutputStream(jarName)); addToJar(jout, new File(src.getFile()), prefix); jout.close(); } catch (Exception ex) { log(-1, "packJar: " + ex.getMessage()); return false; } log(lvl, "packJar: completed"); return true; } public static boolean buildJar(String targetJar, String[] jars, String[] files, String[] prefixs, FileManager.JarFileFilter filter) { boolean logShort = false; if (targetJar.startsWith("#")) { logShort = true; targetJar = targetJar.substring(1); log(lvl, "buildJar: %s", new File(targetJar).getName()); } else { log(lvl, "buildJar:\n%s", targetJar); } try { JarOutputStream jout = new JarOutputStream(new FileOutputStream(targetJar)); ArrayList done = new ArrayList(); for (int i = 0; i < jars.length; i++) { if (jars[i] == null) { continue; } if (logShort) { log(lvl, "buildJar: adding: %s", new File(jars[i]).getName()); } else { log(lvl, "buildJar: adding:\n%s", jars[i]); } BufferedInputStream bin = new BufferedInputStream(new FileInputStream(jars[i])); ZipInputStream zin = new ZipInputStream(bin); for (ZipEntry zipentry = zin.getNextEntry(); zipentry != null; zipentry = zin.getNextEntry()) { if (filter == null || filter.accept(zipentry, jars[i])) { if (!done.contains(zipentry.getName())) { jout.putNextEntry(zipentry); if (!zipentry.isDirectory()) { bufferedWrite(zin, jout); } done.add(zipentry.getName()); log(lvl+1, "adding: %s", zipentry.getName()); } } } zin.close(); bin.close(); } if (files != null) { for (int i = 0; i < files.length; i++) { if (files[i] == null) { continue; } if (logShort) { log(lvl, "buildJar: adding %s at %s", new File(files[i]).getName(), prefixs[i]); } else { log(lvl, "buildJar: adding %s at %s", files[i], prefixs[i]); } addToJar(jout, new File(files[i]), prefixs[i]); } } jout.close(); } catch (Exception ex) { log(-1, "buildJar: %s", ex); return false; } log(lvl, "buildJar: completed"); return true; } /** * unpack a jar file to a folder * @param jarName absolute path to jar file * @param folderName absolute path to the target folder * @param del true if the folder should be deleted before unpack * @param strip true if the path should be stripped * @param filter to select specific content * @return true if success, false otherwise */ public static boolean unpackJar(String jarName, String folderName, boolean del, boolean strip, FileManager.JarFileFilter filter) { jarName = FileManager.slashify(jarName, false); if (!jarName.endsWith(".jar")) { jarName += ".jar"; } if (!new File(jarName).isAbsolute()) { log(-1, "unpackJar: jar path not absolute"); return false; } if (folderName == null) { folderName = jarName.substring(0, jarName.length() - 4); } else if (!new File(folderName).isAbsolute()) { log(-1, "unpackJar: folder path not absolute"); return false; } folderName = FileManager.slashify(folderName, true); ZipInputStream in; BufferedOutputStream out; try { if (del) { FileManager.deleteFileOrFolder(folderName); } in = new ZipInputStream(new BufferedInputStream(new FileInputStream(jarName))); log(lvl, "unpackJar: %s to %s", jarName, folderName); boolean isExecutable; int n; File f; for (ZipEntry z = in.getNextEntry(); z != null; z = in.getNextEntry()) { if (filter == null || filter.accept(z, null)) { if (z.isDirectory()) { (new File(folderName, z.getName())).mkdirs(); } else { n = z.getName().lastIndexOf(EXECUTABLE); if (n >= 0) { f = new File(folderName, z.getName().substring(0, n)); isExecutable = true; } else { f = new File(folderName, z.getName()); isExecutable = false; } if (strip) { f = new File(folderName, f.getName()); } else { f.getParentFile().mkdirs(); } out = new BufferedOutputStream(new FileOutputStream(f)); bufferedWrite(in, out); out.close(); if (isExecutable) { f.setExecutable(true, false); } } } } in.close(); } catch (Exception ex) { log(-1, "unpackJar: " + ex.getMessage()); return false; } log(lvl, "unpackJar: completed"); return true; } private static void addToJar(JarOutputStream jar, File dir, String prefix) throws IOException { File[] content; prefix = prefix == null ? "" : prefix; if (dir.isDirectory()) { content = dir.listFiles(); for (int i = 0, l = content.length; i < l; ++i) { if (content[i].isDirectory()) { jar.putNextEntry(new ZipEntry(prefix + (prefix.equals("") ? "" : "/") + content[i].getName() + "/")); addToJar(jar, content[i], prefix + (prefix.equals("") ? "" : "/") + content[i].getName()); } else { addToJarWriteFile(jar, content[i], prefix); } } } else { addToJarWriteFile(jar, dir, prefix); } } private static void addToJarWriteFile(JarOutputStream jar, File file, String prefix) throws IOException { if (file.getName().startsWith(".")) { return; } String suffix = ""; //TODO buildjar: suffix EXECUTABL // if (file.canExecute()) { // suffix = EXECUTABLE; // } jar.putNextEntry(new ZipEntry(prefix + (prefix.equals("") ? "" : "/") + file.getName() + suffix)); FileInputStream in = new FileInputStream(file); bufferedWrite(in, jar); in.close(); } public static File[] getScriptFile(File fScriptFolder) { if (fScriptFolder == null) { return null; } String scriptName; String scriptType = ""; String fpUnzippedSkl = null; File[] content = null; if (fScriptFolder.getName().endsWith(".skl") || fScriptFolder.getName().endsWith(".zip")) { fpUnzippedSkl = FileManager.unzipSKL(fScriptFolder.getAbsolutePath()); if (fpUnzippedSkl == null) { return null; } scriptType = "sikuli-zipped"; fScriptFolder = new File(fpUnzippedSkl); } int pos = fScriptFolder.getName().lastIndexOf("."); if (pos == -1) { scriptName = fScriptFolder.getName(); scriptType = "sikuli-plain"; } else { scriptName = fScriptFolder.getName().substring(0, pos); scriptType = fScriptFolder.getName().substring(pos + 1); } boolean success = true; if (!fScriptFolder.exists()) { if ("sikuli-plain".equals(scriptType)) { fScriptFolder = new File(fScriptFolder.getAbsolutePath() + ".sikuli"); if (!fScriptFolder.exists()) { success = false; } } else { success = false; } } if (!success) { log(-1, "Not a valid Sikuli script project:\n%s", fScriptFolder.getAbsolutePath()); return null; } if (scriptType.startsWith("sikuli")) { content = fScriptFolder.listFiles(new FileFilterScript(scriptName + ".")); if (content == null || content.length == 0) { log(-1, "Script project %s \n has no script file %s.xxx", fScriptFolder, scriptName); return null; } } else if ("jar".equals(scriptType)) { log(-1, "Sorry, script projects as jar-files are not yet supported;"); //TODO try to load and run as extension return null; // until ready } return content; } private static class FileFilterScript implements FilenameFilter { private String _check; public FileFilterScript(String check) { _check = check; } @Override public boolean accept(File dir, String fileName) { return fileName.startsWith(_check); } } public static String unzipSKL(String fpSkl) { File fSkl = new File(fpSkl); if (!fSkl.exists()) { log(-1, "unzipSKL: file not found: %s", fpSkl); } String name = fSkl.getName(); name = name.substring(0, name.lastIndexOf('.')); File fSikuliDir = FileManager.createTempDir(name + ".sikuli"); if (null != fSikuliDir) { fSikuliDir.deleteOnExit(); FileManager.unzip(fSkl, fSikuliDir); } if (null == fSikuliDir) { log(-1, "unzipSKL: not possible for:\n%s", fpSkl); return null; } return fSikuliDir.getAbsolutePath(); } public interface JarFileFilter { public boolean accept(ZipEntry entry, String jarname); } public interface FileFilter { public boolean accept(File entry); } public static String extractResourceAsLines(String src) { String res = null; ClassLoader cl = FileManager.class.getClassLoader(); InputStream isContent = cl.getResourceAsStream(src); if (isContent != null) { res = ""; String line; try { BufferedReader cnt = new BufferedReader(new InputStreamReader(isContent)); line = cnt.readLine(); while (line != null) { res += line + "\n"; line = cnt.readLine(); } cnt.close(); } catch (Exception ex) { log(-1, "extractResourceAsLines: %s\n%s", src, ex); } } return res; } public static boolean extractResource(String src, File tgt) { ClassLoader cl = FileManager.class.getClassLoader(); InputStream isContent = cl.getResourceAsStream(src); if (isContent != null) { try { log(lvl + 1, "extractResource: %s to %s", src, tgt); tgt.getParentFile().mkdirs(); OutputStream osTgt = new FileOutputStream(tgt); bufferedWrite(isContent, osTgt); osTgt.close(); } catch (Exception ex) { log(-1, "extractResource:\n%s", src, ex); return false; } } else { return false; } return true; } private static synchronized void bufferedWrite(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024 * 512]; int read; while (true) { read = in.read(buffer); if (read == -1) { break; } out.write(buffer, 0, read); } out.flush(); } /** * compares to path strings using java.io.File.equals() * @param path1 string * @param path2 string * @return true if same file or folder */ public static boolean pathEquals(String path1, String path2) { File f1 = new File(path1); File f2 = new File(path2); boolean isEqual = f1.equals(f2); return isEqual; } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/HotkeyEvent.java000066400000000000000000000005551315726130400247210ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; public class HotkeyEvent { public int keyCode; public int modifiers; public HotkeyEvent(int code_, int mod_){ init(code_, mod_); } void init(int code_, int mod_){ keyCode = code_; modifiers = mod_; } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/HotkeyListener.java000066400000000000000000000011711315726130400254200ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; public abstract class HotkeyListener { /** * Override this to implement your own hotkey handler. * * @param e HotkeyEvent */ abstract public void hotkeyPressed(HotkeyEvent e); /** * INTERNAL USE: system specific handler implementation * * @param e HotkeyEvent */ public void invokeHotkeyPressed(final HotkeyEvent e) { Thread hotkeyThread = new Thread() { @Override public void run() { hotkeyPressed(e); } }; hotkeyThread.start(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/HotkeyManager.java000066400000000000000000000217221315726130400252110ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import java.awt.event.KeyEvent; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; import org.sikuli.basics.Debug; import org.sikuli.basics.PreferencesUser; import org.sikuli.basics.Settings; import org.sikuli.script.Key; import org.sikuli.script.Key; /** * Singleton class to bind hotkeys to hotkey listeners */ public abstract class HotkeyManager { private static HotkeyManager _instance = null; private static Map hotkeys; private static Map hotkeysGlobal = new HashMap(); private static final String HotkeyTypeCapture = "Capture"; private static int HotkeyTypeCaptureKey; private static int HotkeyTypeCaptureMod; private static final String HotkeyTypeAbort = "Abort"; private static int HotkeyTypeAbortKey; private static int HotkeyTypeAbortMod; public static HotkeyManager getInstance() { if (_instance == null) { /* uncomment for debugging puposes if (Settings.isWindows()) { _instance = new WindowsHotkeyManager(); } else if (Settings.isMac()) { _instance = new MacHotkeyManager(); } else if (Settings.isLinux()) { _instance = new LinuxHotkeyManager(); } return _instance; */ String cls = getOSHotkeyManagerClass(); if (cls != null) { try { Class c = Class.forName(cls); Constructor constr = c.getConstructor(); _instance = (HotkeyManager) constr.newInstance(); } catch (Exception e) { Debug.error("HotkeyManager: Can't create " + cls + ": " + e.getMessage()); } } hotkeys = new HashMap(); } return _instance; } /** * remove all hotkeys */ public static void reset() { if (_instance == null || hotkeys.isEmpty()) { return; } Debug.log(3, "HotkeyManager: reset - removing all defined hotkeys."); boolean res; int[] hk = new int[hotkeys.size()]; int i = 0; for (Integer k : hotkeys.keySet()) { res = _instance._removeHotkey(k, hotkeys.get(k)); if (!res) { Debug.error("HotkeyManager: reset: failed to remove hotkey: %s %s", getKeyModifierText(hotkeys.get(k)), getKeyCodeText(k)); hk[i++] = -1; } else { hk[i++] = k; Debug.log(3, "removed (%d, %d)" , k, hotkeys.get(k)); } } for (int k : hk) { if (k == -1) { continue; } hotkeys.remove(k); } } private static String getOSHotkeyManagerClass() { String pkg = "org.sikuli.basics."; int theOS = Settings.getOS(); switch (theOS) { case Settings.ISMAC: return pkg + "MacHotkeyManager"; case Settings.ISWINDOWS: return pkg + "WindowsHotkeyManager"; case Settings.ISLINUX: return pkg + "LinuxHotkeyManager"; default: Debug.error("HotkeyManager: Hotkey registration is not supported on your OS."); } return null; } private static String getKeyCodeText(int key) { return KeyEvent.getKeyText(key).toUpperCase(); } private static String getKeyModifierText(int modifiers) { String txtMod = KeyEvent.getKeyModifiersText(modifiers).toUpperCase(); if (Settings.isMac()) { txtMod = txtMod.replace("META", "CMD"); txtMod = txtMod.replace("WINDOWS", "CMD"); } else { txtMod = txtMod.replace("META", "WIN"); txtMod = txtMod.replace("WINDOWS", "WIN"); } return txtMod; } /** * install a hotkey listener for a global hotkey (capture, abort, ...) * @param hotkeyType a type string * @param callback HotkeyListener * @return success */ public boolean addHotkey(String hotkeyType, HotkeyListener callback) { PreferencesUser pref = PreferencesUser.getInstance(); if (hotkeyType == HotkeyTypeCapture) { HotkeyTypeCaptureKey = pref.getCaptureHotkey(); HotkeyTypeCaptureMod = pref.getCaptureHotkeyModifiers(); return installHotkey(HotkeyTypeCaptureKey, HotkeyTypeCaptureMod, callback, hotkeyType); } else if (hotkeyType == HotkeyTypeAbort) { HotkeyTypeAbortKey = pref.getStopHotkey(); HotkeyTypeAbortMod = pref.getStopHotkeyModifiers(); return installHotkey(HotkeyTypeAbortKey, HotkeyTypeAbortMod, callback, hotkeyType); } else { Debug.error("HotkeyManager: addHotkey: HotkeyType %s not supported", hotkeyType); return false; } } public String getHotKeyText(String hotkeyType) { PreferencesUser pref = PreferencesUser.getInstance(); String key = ""; String mod = ""; if (hotkeyType == HotkeyTypeCapture) { key = getKeyCodeText(pref.getCaptureHotkey()); mod = getKeyModifierText(pref.getCaptureHotkeyModifiers()); } else if (hotkeyType == HotkeyTypeAbort) { key = getKeyCodeText(pref.getStopHotkey()); mod = getKeyModifierText(pref.getStopHotkeyModifiers()); } else { Debug.error("HotkeyManager: getHotKeyText: HotkeyType %s not supported", hotkeyType); } return mod + " " + key; } /** * install a hotkey listener. * * @param key key character (class Key) * @param modifiers modifiers flag * @param callback HotkeyListener * @return true if success. false otherwise. */ public boolean addHotkey(char key, int modifiers, HotkeyListener callback) { return addHotkey("" + key, modifiers, callback); } /** * install a hotkey listener. * * @param key key character (class Key) * @param modifiers modifiers flag * @param callback HotkeyListener * @return true if success. false otherwise. */ public boolean addHotkey(String key, int modifiers, HotkeyListener callback) { int[] keyCodes = Key.toJavaKeyCode(key.toLowerCase()); int keyCode = keyCodes[0]; return installHotkey(keyCode, modifiers, callback, ""); } private boolean installHotkey(int key, int mod, HotkeyListener callback, String hotkeyType) { boolean res; String txtMod = getKeyModifierText(mod); String txtCode = getKeyCodeText(key); Debug.info("HotkeyManager: add %s Hotkey: %s %s (%d, %d)" , hotkeyType, txtMod, txtCode, key, mod); boolean checkGlobal = true; for (Integer k : hotkeys.keySet()) { if (k == key && mod == hotkeys.get(key)) { res = _instance._removeHotkey(key, hotkeys.get(key)); if (!res) { Debug.error("HotkeyManager: addHotkey: failed to remove already defined hotkey"); return false; } else { checkGlobal = false; } } } if (checkGlobal) { for (Integer kg : hotkeysGlobal.keySet()) { if (kg == key && mod == hotkeysGlobal.get(key)) { Debug.error("HotkeyManager: addHotkey: ignored: trying to redefine a global hotkey"); return false; } } } res = _instance._addHotkey(key, mod, callback); if (res) { if (hotkeyType.isEmpty()) { hotkeys.put(key, mod); } else { hotkeysGlobal.put(key, mod); } } else { Debug.error("HotkeyManager: addHotkey: failed"); } return res; } /** * remove a hotkey by type (not supported yet) * @param hotkeyType capture, abort, ... * @return success */ public boolean removeHotkey(String hotkeyType) { if (hotkeyType == HotkeyTypeCapture) { return uninstallHotkey(HotkeyTypeCaptureKey, HotkeyTypeCaptureMod, hotkeyType); } else if (hotkeyType == HotkeyTypeAbort) { return uninstallHotkey(HotkeyTypeAbortKey, HotkeyTypeAbortMod, hotkeyType); } else { Debug.error("HotkeyManager: removeHotkey: using HotkeyType as %s not supported yet", hotkeyType); return false; } } /** * remove a hotkey and uninstall a hotkey listener. * * @param key key character (class Key) * @param modifiers modifiers flag * @return true if success. false otherwise. */ public boolean removeHotkey(char key, int modifiers) { return removeHotkey("" + key, modifiers); } /** * uninstall a hotkey listener. * * @param key key string (class Key) * @param modifiers modifiers flag * @return true if success. false otherwise. */ public boolean removeHotkey(String key, int modifiers) { int[] keyCodes = Key.toJavaKeyCode(key.toLowerCase()); int keyCode = keyCodes[0]; return uninstallHotkey(keyCode, modifiers, ""); } private boolean uninstallHotkey(int key, int mod, String hotkeyType) { boolean res; String txtMod = getKeyModifierText(mod); String txtCode = getKeyCodeText(key); Debug.info("HotkeyManager: remove %s Hotkey: %s %s (%d, %d)" , hotkeyType, txtMod, txtCode, key, mod); res = _instance._removeHotkey(key, mod); if (res) { hotkeys.remove(key); } else { Debug.error("HotkeyManager: removeHotkey: failed"); } return res; } abstract public boolean _addHotkey(int keyCode, int modifiers, HotkeyListener callback); abstract public boolean _removeHotkey(int keyCode, int modifiers); abstract public void cleanUp(); } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/LinuxHotkeyManager.java000066400000000000000000000050441315726130400262300ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import org.sikuli.script.RunTime; import java.util.*; import jxgrabkey.HotkeyConflictException; import jxgrabkey.JXGrabKey; import org.sikuli.basics.Debug; import org.sikuli.script.RunTime; public class LinuxHotkeyManager extends HotkeyManager { static{ RunTime.loadLibrary("JXGrabKey"); } class HotkeyData { int key, modifiers; HotkeyListener listener; public HotkeyData(int key_, int mod_, HotkeyListener l_){ key = key_; modifiers = mod_; listener = l_; } }; class MyHotkeyHandler implements jxgrabkey.HotkeyListener{ public void onHotkey(int id){ Debug.log(4, "Hotkey pressed"); HotkeyData data = _idCallbackMap.get(id); HotkeyEvent e = new HotkeyEvent(data.key, data.modifiers); data.listener.invokeHotkeyPressed(e); } }; private Map _idCallbackMap = new HashMap(); private int _gHotkeyId = 1; public boolean _addHotkey(int keyCode, int modifiers, HotkeyListener listener){ JXGrabKey grabKey = JXGrabKey.getInstance(); if(_gHotkeyId == 1){ grabKey.addHotkeyListener(new MyHotkeyHandler()); } _removeHotkey(keyCode, modifiers); int id = _gHotkeyId++; HotkeyData data = new HotkeyData(keyCode, modifiers, listener); _idCallbackMap.put(id, data); try{ //JXGrabKey.setDebugOutput(true); grabKey.registerAwtHotkey(id, modifiers, keyCode); }catch(HotkeyConflictException e){ Debug.error("Hot key conflicts"); return false; } return true; } public boolean _removeHotkey(int keyCode, int modifiers){ for( Map.Entry entry : _idCallbackMap.entrySet() ){ HotkeyData data = entry.getValue(); if(data.key == keyCode && data.modifiers == modifiers){ JXGrabKey grabKey = JXGrabKey.getInstance(); int id = entry.getKey(); grabKey.unregisterHotKey(id); _idCallbackMap.remove(id); return true; } } return false; } public void cleanUp(){ JXGrabKey grabKey = JXGrabKey.getInstance(); for( Map.Entry entry : _idCallbackMap.entrySet() ){ int id = entry.getKey(); grabKey.unregisterHotKey(id); } _gHotkeyId = 1; _idCallbackMap.clear(); grabKey.getInstance().cleanUp(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/MacHotkeyManager.java000066400000000000000000000154311315726130400256320ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import org.sikuli.script.RunTime; // http://lists.apple.com/archives/mac-games-dev/2001/Sep/msg00113.html // full key table: http://www.mactech.com/articles/mactech/Vol.04/04.12/Macinkeys/ // modifiers code: http://www.mactech.com/macintosh-c/chap02-1.html public class MacHotkeyManager extends HotkeyManager { static final int CARBON_MASK_CMD = 0x0100; static final int CARBON_MASK_SHIFT = 0x0200; static final int CARBON_MASK_OPT = 0x0800; static final int CARBON_MASK_CTRL = 0x1000; static { RunTime.loadLibrary("MacHotkeyManager"); } @Override public boolean _addHotkey(int keyCode, int modifiers, HotkeyListener listener) { int ckey = convertToCarbonKey(keyCode); int cmod = convertToCarbonModifiers(modifiers); boolean ret = installGlobalHotkey(keyCode, modifiers, ckey, cmod, listener); return ret; } @Override public boolean _removeHotkey(int keyCode, int modifiers) { int ckey = convertToCarbonKey(keyCode); int cmod = convertToCarbonModifiers(modifiers); return uninstallGlobalHotkey(ckey, cmod); } private native boolean installGlobalHotkey(int jKey, int jMod, int keyCode, int modifiers, HotkeyListener listener); private native boolean uninstallGlobalHotkey(int keyCode, int modifiers); @Override public native void cleanUp(); private int convertToCarbonModifiers(int mod) { int cmod = 0; if ((mod & InputEvent.SHIFT_MASK) != 0) { cmod |= CARBON_MASK_SHIFT; } if ((mod & InputEvent.META_MASK) != 0) { cmod |= CARBON_MASK_CMD; } if ((mod & InputEvent.ALT_MASK) != 0) { cmod |= CARBON_MASK_OPT; } if ((mod & InputEvent.CTRL_MASK) != 0) { cmod |= CARBON_MASK_CTRL; } return cmod; } private int convertToCarbonKey(int keycode) { switch (keycode) { case KeyEvent.VK_BACK_SPACE: return 0x33; case KeyEvent.VK_TAB: return 0x30; case KeyEvent.VK_CLEAR: return 0x47; case KeyEvent.VK_ENTER: return 0x24; case KeyEvent.VK_SHIFT: return 0xF0; case KeyEvent.VK_CONTROL: return 0xF1; case KeyEvent.VK_META: return 0xF2; case KeyEvent.VK_PAUSE: return 0x71; // = F15 case KeyEvent.VK_ESCAPE: return 0x35; case KeyEvent.VK_SPACE: return 0x31; case KeyEvent.VK_OPEN_BRACKET: return 0x21; case KeyEvent.VK_BACK_SLASH: return 0x2A; case KeyEvent.VK_CLOSE_BRACKET: return 0x1E; case KeyEvent.VK_SLASH: return 0x2C; case KeyEvent.VK_PERIOD: return 0x2F; case KeyEvent.VK_COMMA: return 0x2B; case KeyEvent.VK_SEMICOLON: return 0x29; case KeyEvent.VK_END: return 0x77; case KeyEvent.VK_HOME: return 0x73; case KeyEvent.VK_LEFT: return 0x7B; case KeyEvent.VK_UP: return 0x7E; case KeyEvent.VK_RIGHT: return 0x7C; case KeyEvent.VK_DOWN: return 0x7D; case KeyEvent.VK_PRINTSCREEN: return 0x69; // F13 case KeyEvent.VK_INSERT: return 0x72; // help case KeyEvent.VK_DELETE: return 0x75; case KeyEvent.VK_HELP: return 0x72; case KeyEvent.VK_0: return 0x1D; case KeyEvent.VK_1: return 0x12; case KeyEvent.VK_2: return 0x13; case KeyEvent.VK_3: return 0x14; case KeyEvent.VK_4: return 0x15; case KeyEvent.VK_5: return 0x17; case KeyEvent.VK_6: return 0x16; case KeyEvent.VK_7: return 0x1A; case KeyEvent.VK_8: return 0x1C; case KeyEvent.VK_9: return 0x19; case KeyEvent.VK_MINUS: return 0x1B; case KeyEvent.VK_EQUALS: return 0x18; case KeyEvent.VK_A: return 0x00; case KeyEvent.VK_B: return 0x0B; case KeyEvent.VK_C: return 0x08; case KeyEvent.VK_D: return 0x02; case KeyEvent.VK_E: return 0x0E; case KeyEvent.VK_F: return 0x03; case KeyEvent.VK_G: return 0x05; case KeyEvent.VK_H: return 0x04; case KeyEvent.VK_I: return 0x22; case KeyEvent.VK_J: return 0x26; case KeyEvent.VK_K: return 0x28; case KeyEvent.VK_L: return 0x25; case KeyEvent.VK_M: return 0x2E; case KeyEvent.VK_N: return 0x2D; case KeyEvent.VK_O: return 0x1F; case KeyEvent.VK_P: return 0x23; case KeyEvent.VK_Q: return 0x0C; case KeyEvent.VK_R: return 0x0F; case KeyEvent.VK_S: return 0x01; case KeyEvent.VK_T: return 0x11; case KeyEvent.VK_U: return 0x20; case KeyEvent.VK_V: return 0x09; case KeyEvent.VK_W: return 0x0D; case KeyEvent.VK_X: return 0x07; case KeyEvent.VK_Y: return 0x10; case KeyEvent.VK_Z: return 0x06; case KeyEvent.VK_NUMPAD0: return 0x52; case KeyEvent.VK_NUMPAD1: return 0x53; case KeyEvent.VK_NUMPAD2: return 0x54; case KeyEvent.VK_NUMPAD3: return 0x55; case KeyEvent.VK_NUMPAD4: return 0x56; case KeyEvent.VK_NUMPAD5: return 0x57; case KeyEvent.VK_NUMPAD6: return 0x58; case KeyEvent.VK_NUMPAD7: return 0x59; case KeyEvent.VK_NUMPAD8: return 0x5B; case KeyEvent.VK_NUMPAD9: return 0x5C; case KeyEvent.VK_MULTIPLY: return 0x43; case KeyEvent.VK_ADD: return 0x45; case KeyEvent.VK_SEPARATOR: return 0xFF; // not supported with Button or GetKeys case KeyEvent.VK_SUBTRACT: return 0x4E; case KeyEvent.VK_DECIMAL: return 0x41; case KeyEvent.VK_DIVIDE: return 0x4B; case KeyEvent.VK_F1: return 0x7A; case KeyEvent.VK_F2: return 0x7B; case KeyEvent.VK_F3: return 0x63; case KeyEvent.VK_F4: return 0x76; case KeyEvent.VK_F5: return 0x60; case KeyEvent.VK_F6: return 0x61; case KeyEvent.VK_F7: return 0x62; case KeyEvent.VK_F8: return 0x64; case KeyEvent.VK_F9: return 0x65; case KeyEvent.VK_F10: return 0x6D; case KeyEvent.VK_F11: return 0x67; case KeyEvent.VK_F12: return 0x6F; case KeyEvent.VK_F13: return 0x69; case KeyEvent.VK_F14: return 0x6B; case KeyEvent.VK_F15: return 0x71; case KeyEvent.VK_NUM_LOCK: return 0x47; default: return 0xFF; } } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/OS.java000066400000000000000000000004311315726130400227660ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; /** * @deprecated use Settings.isWindows(), ...isMac() or ...isLinux() instead */ @Deprecated public enum OS { MAC, WINDOWS, LINUX, NOT_SUPPORTED } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/PreferencesUser.java000066400000000000000000000320411315726130400255470ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import java.awt.Dimension; import java.awt.Event; import java.awt.Point; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.Arrays; import java.util.Date; import java.util.Locale; import java.util.prefs.PreferenceChangeListener; import java.util.prefs.Preferences; import org.sikuli.script.Sikulix; public class PreferencesUser { public final static int yes = 1; public final static int no = 0; public final static int AUTO_NAMING_TIMESTAMP = 0; public final static int AUTO_NAMING_OCR = 1; public final static int AUTO_NAMING_OFF = 2; public final static int HORIZONTAL = 0; public final static int VERTICAL = 1; public final static int UNKNOWN = -1; public final static int NEWBEE = 0; public final static int SCRIPTER = 1; public final static int SIKULI_USER = 2; public final static int THUMB_HEIGHT = 50; public final static String DEFAULT_CONSOLE_CSS = "body { font-family:serif; font-size: 12px; }" + ".normal{ color: black; }" + ".debug { color:#505000; }" + ".info { color: blue; }" + ".log { color: #09806A; }" + ".error { color: red; }"; static PreferencesUser _instance = null; Preferences pref = Preferences.userNodeForPackage(Sikulix.class); public static PreferencesUser getInstance() { if (_instance == null) { _instance = new PreferencesUser(); } return _instance; } private PreferencesUser() { Debug.log(2, "init user preferences"); } public boolean exportPrefs(String path) { try { FileOutputStream pout = new FileOutputStream(new File(path)); ; pref.exportSubtree(pout); pout.close(); } catch (Exception ex) { Debug.error("UserPrefs: export: did not work\n" + ex.getMessage()); return false; } return true; } public boolean importPrefs(String path) { try { Preferences.importPreferences(new FileInputStream(new File(path))); } catch (Exception ex) { Debug.error("UserPrefs: import: did not work\n" + ex.getMessage()); return false; } return true; } public void remove(String key) { pref.remove(key); } public void removeAll(String prefix) { try { for (String item : pref.keys()) { if (!item.startsWith(prefix)) { continue; } pref.remove(item); } } catch (Exception ex) { Debug.error("Prefs.removeAll: prefix (%s) did not work", prefix); } } public void addPreferenceChangeListener(PreferenceChangeListener pcl) { pref.addPreferenceChangeListener(pcl); } // ***** user type public void setUserType(int typ) { pref.putInt("USER_TYPE", typ); } public int getUserType() { return pref.getInt("USER_TYPE", UNKNOWN); } // ***** capture hot key public void setCaptureHotkey(int hkey) { pref.putInt("CAPTURE_HOTKEY", hkey); } public int getCaptureHotkey() { return pref.getInt("CAPTURE_HOTKEY", 50); // default: '2' } public void setCaptureHotkeyModifiers(int mod) { if (mod < 0) { } pref.putInt("CAPTURE_HOTKEY_MODIFIERS", mod); } public int getCaptureHotkeyModifiers() { return pref.getInt("CAPTURE_HOTKEY_MODIFIERS", defaultCaptureHotkeyModifiers()); } private int defaultCaptureHotkeyModifiers() { int mod = Event.SHIFT_MASK + Event.META_MASK; if (!Settings.isMac()) { mod = Event.SHIFT_MASK + Event.CTRL_MASK; } return mod; } public void setCaptureDelay(double v) { pref.putDouble("CAPTURE_DELAY", v); } public double getCaptureDelay() { return pref.getDouble("CAPTURE_DELAY", 1.0); } // ***** abort key public void setStopHotkey(int hkey) { pref.putInt("STOP_HOTKEY", hkey); } public int getStopHotkey() { return pref.getInt("STOP_HOTKEY", 67); // default: 'c' } public void setStopHotkeyModifiers(int mod) { pref.putInt("STOP_HOTKEY_MODIFIERS", mod); } public int getStopHotkeyModifiers() { return pref.getInt("GET_HOTKEY_MODIFIERS", defaultStopHotkeyModifiers()); } private int defaultStopHotkeyModifiers() { int mod = Event.SHIFT_MASK + Event.META_MASK; if (!Settings.isMac()) { mod = Event.SHIFT_MASK + Event.ALT_MASK; } return mod; } // ***** indentation support public void setExpandTab(boolean flag) { pref.putBoolean("EXPAND_TAB", flag); } public boolean getExpandTab() { return pref.getBoolean("EXPAND_TAB", true); } public void setTabWidth(int width) { pref.putInt("TAB_WIDTH", width); } public int getTabWidth() { return pref.getInt("TAB_WIDTH", 4); } public String getTabWhitespace() { if (getExpandTab()) { char[] blanks = new char[getTabWidth()]; Arrays.fill(blanks, ' '); return new String(blanks); } else { return "\t"; } } // ***** font settings public void setFontSize(int size) { pref.putInt("FONT_SIZE", size); } public int getFontSize() { return pref.getInt("FONT_SIZE", 18); } public void setFontName(String font) { pref.put("FONT_NAME", font); } public String getFontName() { return pref.get("FONT_NAME", "Monospaced"); } // ***** locale support public void setLocale(Locale l) { pref.put("LOCALE", l.toString()); } public Locale getLocale() { String locale = pref.get("LOCALE", Locale.getDefault().toString()); String[] code = locale.split("_"); if (code.length == 1) { return new Locale(code[0]); } else if (code.length == 2) { return new Locale(code[0], code[1]); } else { return new Locale(code[0], code[1], code[2]); } } // ***** image capture and naming public void setAutoNamingMethod(int m) { pref.putInt("AUTO_NAMING", m); } public int getAutoNamingMethod() { return pref.getInt("AUTO_NAMING", AUTO_NAMING_OCR); } public void setDefaultThumbHeight(int h) { pref.putInt("DEFAULT_THUMB_HEIGHT", h); } public void resetDefaultThumbHeight() { pref.putInt("DEFAULT_THUMB_HEIGHT", THUMB_HEIGHT); } public int getDefaultThumbHeight() { return pref.getInt("DEFAULT_THUMB_HEIGHT", THUMB_HEIGHT); } // ***** command bar public void setPrefMoreCommandBar(boolean flag) { pref.putInt("PREF_MORE_COMMAND_BAR", flag ? 1 : 0); } public boolean getPrefMoreCommandBar() { return pref.getInt("PREF_MORE_COMMAND_BAR", 1) != 0; } public void setAutoCaptureForCmdButtons(boolean flag) { pref.putInt("AUTO_CAPTURE_FOR_CMD_BUTTONS", flag ? 1 : 0); } public boolean getAutoCaptureForCmdButtons() { return pref.getInt("AUTO_CAPTURE_FOR_CMD_BUTTONS", 1) != 0; } // ***** save options public void setAtSaveMakeHTML(boolean flag) { pref.putBoolean("AT_SAVE_MAKE_HTML", flag); } public boolean getAtSaveMakeHTML() { return pref.getBoolean("AT_SAVE_MAKE_HTML", false); } public void setAtSaveCleanBundle(boolean flag) { pref.putBoolean("AT_SAVE_CLEAN_BUNDLE", flag); } public boolean getAtSaveCleanBundle() { return pref.getBoolean("AT_SAVE_CLEAN_BUNDLE", true); } // ***** script run options public void setPrefMoreRunSave(boolean flag) { pref.putBoolean("PREF_MORE_RUN_SAVE", flag); } public boolean getPrefMoreRunSave() { return pref.getBoolean("PREF_MORE_RUN_SAVE", false); } public void setPrefMoreHighlight(boolean flag) { pref.putBoolean("PREF_MORE_HIGHLIGHT", flag); } public boolean getPrefMoreHighlight() { return pref.getBoolean("PREF_MORE_HIGHLIGHT", false); } // ***** auto update support public void setCheckUpdate(boolean flag) { pref.putBoolean("CHECK_UPDATE", flag); } public boolean getCheckUpdate() { return pref.getBoolean("CHECK_UPDATE", true); } public void setWantBeta(boolean flag) { pref.putBoolean("WANT_BETA", flag); } public boolean getWantBeta() { return pref.getBoolean("WANT_BETA", false); } public void setLastSeenUpdate(String ver) { pref.put("LAST_SEEN_UPDATE", ver); } public String getLastSeenUpdate() { return pref.get("LAST_SEEN_UPDATE", "0.0"); } public void setCheckUpdateTime() { pref.putLong("LAST_CHECK_UPDATE", (new Date()).getTime()); } public long getCheckUpdateTime() { return pref.getLong("LAST_CHECK_UPDATE", (new Date()).getTime()); } // ***** IDE general support public void setIdeSize(Dimension size) { String str = (int) size.getWidth() + "x" + (int) size.getHeight(); pref.put("IDE_SIZE", str); } public Dimension getIdeSize() { String str = pref.get("IDE_SIZE", "1024x700"); String[] w_h = str.split("x"); return new Dimension(Integer.parseInt(w_h[0]), Integer.parseInt(w_h[1])); } public void setIdeLocation(Point p) { String str = p.x + "," + p.y; pref.put("IDE_LOCATION", str); } public Point getIdeLocation() { String str = pref.get("IDE_LOCATION", "0,0"); String[] x_y = str.split(","); return new Point(Integer.parseInt(x_y[0]), Integer.parseInt(x_y[1])); } // ***** IDE Editor options public void setPrefMoreImageThumbs(boolean flag) { pref.putBoolean("PREF_MORE_IMAGE_THUMBS", flag); } public boolean getPrefMoreImageThumbs() { return pref.getBoolean("PREF_MORE_IMAGE_THUMBS", true); } public void setPrefMorePlainText(boolean flag) { pref.putBoolean("PREF_MORE_PLAIN_TEXT", flag); } public boolean getPrefMorePlainText() { return pref.getBoolean("PREF_MORE_PLAIN_TEXT", false); } // currently: last open filenames public void setIdeSession(String session_str) { pref.put("IDE_SESSION", session_str); } public String getIdeSession() { return pref.get("IDE_SESSION", null); } // support for IDE image path public void setPrefMoreImages(boolean flag) { pref.putBoolean("PREF_MORE_IMAGES", flag); } public boolean getPrefMoreImages() { return pref.getBoolean("PREF_MORE_IMAGES", false); } public void setPrefMoreImagesPath(String path) { pref.put("PREF_MORE_IMAGES_PATH", path); } public String getPrefMoreImagesPath() { return pref.get("PREF_MORE_IMAGES_PATH", null); } // ***** message area settings public void setPrefMoreMessage(int typ) { pref.putInt("PREF_MORE_MESSAGE", typ); } public int getPrefMoreMessage() { return pref.getInt("PREF_MORE_MESSAGE", HORIZONTAL); } public void setPrefMoreLogActions(boolean flag) { pref.putBoolean("PREF_MORE_LOG_ACTIONS", flag); } public boolean getPrefMoreLogActions() { return pref.getBoolean("PREF_MORE_LOG_ACTIONS", true); } public void setPrefMoreLogInfo(boolean flag) { pref.putBoolean("PREF_MORE_LOG_INFO", flag); } public boolean getPrefMoreLogInfo() { return pref.getBoolean("PREF_MORE_LOG_INFO", true); } public void setPrefMoreLogDebug(boolean flag) { pref.putBoolean("PREF_MORE_LOG_INFO", flag); } public boolean getPrefMoreLogDebug() { return pref.getBoolean("PREF_MORE_LOG_DEBUG", true); } public void setConsoleCSS(String css) { pref.put("CONSOLE_CSS", css); } public String getConsoleCSS() { return pref.get("CONSOLE_CSS", DEFAULT_CONSOLE_CSS); } // ***** text search and OCR public void setPrefMoreTextSearch(boolean flag) { pref.putBoolean("PREF_MORE_TEXT_SEARCH", flag); } public boolean getPrefMoreTextSearch() { return pref.getBoolean("PREF_MORE_TEXT_SEARCH", false); } public void setPrefMoreTextOCR(boolean flag) { pref.putBoolean("PREF_MORE_TEXT_OCR", flag); } public boolean getPrefMoreTextOCR() { return pref.getBoolean("PREF_MORE_TEXT_OCR", false); } // ***** general setter getter public void put(String key, String val) { pref.put(key, val); } public String get(String key, String default_) { return pref.get(key, default_); } public void setDefaults(int typ) { // ***** capture hot key if (NEWBEE == typ) { setCaptureHotkey(50); setCaptureHotkeyModifiers(defaultCaptureHotkeyModifiers()); setCaptureDelay(1.0); } // ***** abort key setStopHotkey(67); setStopHotkeyModifiers(defaultStopHotkeyModifiers()); // ***** indentation support if (NEWBEE == typ) { setExpandTab(true); setTabWidth(4); } // ***** font settings if (NEWBEE == typ) { setFontSize(14); setFontName("Monospaced"); } // ***** locale support if (NEWBEE == typ) { setLocale(Locale.getDefault()); } // ***** image capture and naming if (NEWBEE == typ) { setAutoNamingMethod(AUTO_NAMING_TIMESTAMP); } if (getPrefMoreImageThumbs()) { setDefaultThumbHeight(THUMB_HEIGHT); } else { setDefaultThumbHeight(0); } // ***** command bar if (NEWBEE == typ) { setPrefMoreCommandBar(true); setAutoCaptureForCmdButtons(true); } else { setPrefMoreCommandBar(false); } // ***** save options if (NEWBEE == typ) { setAtSaveMakeHTML(true); } else { setAtSaveMakeHTML(false); } setAtSaveCleanBundle(true); // ***** script run options setPrefMoreRunSave(false); setPrefMoreHighlight(false); // ***** auto update support setCheckUpdate(false); setLastSeenUpdate("0.0.0"); setCheckUpdateTime(); // ***** IDE general support if (NEWBEE == typ) { setIdeSize(new Dimension(0, 0)); setIdeLocation(new Point(0, 0)); } setPrefMoreImages(false); setPrefMoreImagesPath(""); // ***** message area settings if (NEWBEE == typ) { setPrefMoreMessage(HORIZONTAL); } else { setPrefMoreMessage(VERTICAL); } setPrefMoreLogActions(true); setPrefMoreLogInfo(true); setPrefMoreLogDebug(true); setConsoleCSS(DEFAULT_CONSOLE_CSS); // ***** text search and OCR setPrefMoreTextSearch(false); setPrefMoreTextOCR(false); } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/Settings.java000066400000000000000000000437671315726130400242700ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import org.sikuli.script.RunTime; import java.io.File; import java.net.InetAddress; import java.net.Proxy; import java.util.Date; import org.sikuli.script.Image; /** * This is the container for all */ public class Settings { public static boolean experimental = false; private static String me = "Settings: "; private static int lvl = 3; public static boolean noPupUps = false; public static boolean FindProfiling = false; public static boolean InputFontMono = false; public static int InputFontSize = 14; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } public static boolean runningSetupInValidContext = false; public static String runningSetupInContext = null; public static String runningSetupWithJar = null; public static boolean isRunningIDE = false; public static int breakPoint = 0; public static boolean handlesMacBundles = true; public static boolean runningSetup = false; private static PreferencesUser prefs; /** * location of folder Tessdata */ public static String OcrDataPath = null; /** * standard place in the net to get information about extensions
* needs a file extensions.json with content
* {"extension-list":
*  {"extensions":
*   [
*    {
*     "name":"SikuliGuide",
*     "version":"0.3",
*     "description":"visual annotations",
*     "imgurl":"somewhere in the net",
*     "infourl":"http://doc.sikuli.org",
*     "jarurl":"---extensions---"
*    },
*   ]
*  }
* }
* imgurl: to get an icon from
* infourl: where to get more information
* jarurl: where to download the jar from (no url: this standard place)
*/ // // public static int SikuliVersionMajor; // public static int SikuliVersionMinor; // public static int SikuliVersionSub; // public static int SikuliVersionBetaN; // public static String SikuliProjectVersionUsed = ""; // public static String SikuliProjectVersion = ""; // public static String SikuliVersionBuild; // public static String SikuliVersionType; // public static String SikuliVersionTypeText; // public static String downloadBaseDirBase; // public static String downloadBaseDirWeb; // public static String downloadBaseDir; // // used for download of production versions // private static final String dlProdLink = "https://launchpad.net/raiman/sikulix2013+/"; // private static final String dlProdLink1 = ".0"; // private static final String dlProdLink2 = "/+download/"; // // used for download of development versions (nightly builds) // private static final String dlDevLink = "http://nightly.sikuli.de/"; // public static String SikuliRepo; // public static String SikuliLocalRepo = ""; // public static String[] ServerList = {"http://dl.dropboxusercontent.com/u/42895525/SikuliX"}; // private static String sversion; // private static String bversion; // public static String SikuliVersionDefault; // public static String SikuliVersionBeta; // public static String SikuliVersionDefaultIDE; // public static String SikuliVersionBetaIDE; // public static String SikuliVersionDefaultScript; // public static String SikuliVersionBetaScript; // public static String SikuliVersion; // public static String SikuliVersionIDE; // public static String SikuliVersionScript; // public static String SikuliJythonVersion; // public static String SikuliJythonVersion25 = "2.5.4-rc1"; // public static String SikuliJythonMaven; // public static String SikuliJythonMaven25; // public static String SikuliJython; // public static String SikuliJRubyVersion; // public static String SikuliJRuby; // public static String SikuliJRubyMaven; // public static String dlMavenRelease = "https://repo1.maven.org/maven2/"; // public static String dlMavenSnapshot = "https://oss.sonatype.org/content/groups/public/"; // // public static Map tessData = new HashMap(); // // //TODO needed ??? // public static final String libOpenCV = "libopencv_java248"; // // public static String osName; // public static String SikuliVersionLong; // public static String SikuliSystemVersion; // public static String SikuliJavaVersion; // public static final float FOREVER = Float.POSITIVE_INFINITY; public static final int JavaVersion = makeJavaVersion(); private static int makeJavaVersion() { try { return Integer.parseInt(java.lang.System.getProperty("java.version").substring(2, 3)); } catch (Exception ex) { //TODO Java9 return 9; } } public static final String JREVersion = java.lang.System.getProperty("java.runtime.version"); public static final String JavaArch = System.getProperty("os.arch"); /** * Resource types to be used with IResourceLoader implementations */ public static final String SIKULI_LIB = "*sikuli_lib"; public static String proxyName = ""; public static String proxyIP = ""; public static InetAddress proxyAddress = null; public static String proxyPort = ""; public static boolean proxyChecked = false; public static Proxy proxy = null; /** * INTERNAL USE: to trigger the initialization */ public static synchronized void init() { // TODO check existence of an extension repository //TODO Windows: //Mrz 23, 2015 12:25:43 PM java.util.prefs.WindowsPreferences //WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs //at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5. // prefs = PreferencesUser.getInstance(); // proxyName = prefs.get("ProxyName", null); // String proxyIP = prefs.get("ProxyIP", null); // InetAddress proxyAddress = null; // String proxyPort = prefs.get("ProxyPort", null); // // SikuliRepo = null; // Properties prop = new Properties(); // String svf = "sikulixversion.txt"; // try { // InputStream is; // is = Settings.class.getClassLoader().getResourceAsStream("Settings/" + svf); // prop.load(is); // is.close(); // String svt = prop.getProperty("sikulixdev"); // SikuliVersionMajor = Integer.decode(prop.getProperty("sikulixvmaj")); // SikuliVersionMinor = Integer.decode(prop.getProperty("sikulixvmin")); // SikuliVersionSub = Integer.decode(prop.getProperty("sikulixvsub")); // SikuliVersionBetaN = Integer.decode(prop.getProperty("sikulixbeta")); // String ssxbeta = ""; // if (SikuliVersionBetaN > 0) { // ssxbeta = String.format("-Beta%d", SikuliVersionBetaN); // } // SikuliVersionBuild = prop.getProperty("sikulixbuild"); // log(lvl + 1, "%s version from %s: %d.%d.%d%s build: %s", svf, // SikuliVersionMajor, SikuliVersionMinor, SikuliVersionSub, ssxbeta, // SikuliVersionBuild, svt); // sversion = String.format("%d.%d.%d", // SikuliVersionMajor, SikuliVersionMinor, SikuliVersionSub); // bversion = String.format("%d.%d.%d-Beta%d", // SikuliVersionMajor, SikuliVersionMinor, SikuliVersionSub, SikuliVersionBetaN); // SikuliVersionDefault = "SikuliX " + sversion; // SikuliVersionBeta = "Sikuli " + bversion; // SikuliVersionDefaultIDE = "SikulixIDE " + sversion; // SikuliVersionBetaIDE = "SikulixIDE " + bversion; // SikuliVersionDefaultScript = "SikulixScript " + sversion; // SikuliVersionBetaScript = "SikulixScript " + bversion; // // if ("release".equals(svt)) { // downloadBaseDirBase = dlProdLink; // downloadBaseDirWeb = downloadBaseDirBase + getVersionShortBasic() + dlProdLink1; // downloadBaseDir = downloadBaseDirWeb + dlProdLink2; // SikuliVersionType = ""; // SikuliVersionTypeText = ""; // } else { // downloadBaseDirBase = dlDevLink; // downloadBaseDirWeb = dlDevLink; // downloadBaseDir = dlDevLink; // SikuliVersionTypeText = "nightly"; // SikuliVersionBuild += SikuliVersionTypeText; // SikuliVersionType = svt; // } // if (SikuliVersionBetaN > 0) { // SikuliVersion = SikuliVersionBeta; // SikuliVersionIDE = SikuliVersionBetaIDE; // SikuliVersionScript = SikuliVersionBetaScript; // SikuliVersionLong = bversion + "(" + SikuliVersionBuild + ")"; // } else { // SikuliVersion = SikuliVersionDefault; // SikuliVersionIDE = SikuliVersionDefaultIDE; // SikuliVersionScript = SikuliVersionDefaultScript; // SikuliVersionLong = sversion + "(" + SikuliVersionBuild + ")"; // } // SikuliProjectVersionUsed = prop.getProperty("sikulixvused"); // SikuliProjectVersion = prop.getProperty("sikulixvproject"); // String osn = "UnKnown"; // String os = System.getProperty("os.name").toLowerCase(); // if (os.startsWith("mac")) { // osn = "Mac"; // } else if (os.startsWith("windows")) { // osn = "Windows"; // } else if (os.startsWith("linux")) { // osn = "Linux"; // } // // SikuliLocalRepo = FileManager.slashify(prop.getProperty("sikulixlocalrepo"), true); // SikuliJythonVersion = prop.getProperty("sikulixvjython"); // SikuliJythonMaven = "org/python/jython-standalone/" + // SikuliJythonVersion + "/jython-standalone-" + SikuliJythonVersion + ".jar"; // SikuliJythonMaven25 = "org/python/jython-standalone/" + // SikuliJythonVersion25 + "/jython-standalone-" + SikuliJythonVersion25 + ".jar"; // SikuliJython = SikuliLocalRepo + SikuliJythonMaven; // SikuliJRubyVersion = prop.getProperty("sikulixvjruby"); // SikuliJRubyMaven = "org/jruby/jruby-complete/" + // SikuliJRubyVersion + "/jruby-complete-" + SikuliJRubyVersion + ".jar"; // SikuliJRuby = SikuliLocalRepo + SikuliJRubyMaven; // // SikuliSystemVersion = osn + System.getProperty("os.version"); // SikuliJavaVersion = "Java" + JavaVersion + "(" + JavaArch + ")" + JREVersion; ////TODO this should be in RunSetup only ////TODO debug version: where to do in sikulixapi.jar ////TODO need a function: reveal all environment and system information //// log(lvl, "%s version: downloading from %s", svt, downloadBaseDir); // } catch (Exception e) { // Debug.error("Settings: load version file %s did not work", svf); // Sikulix.terminate(999); // } // tessData.put("eng", "http://tesseract-ocr.googlecode.com/files/tesseract-ocr-3.02.eng.tar.gz"); // getOS(); } @Deprecated public static String getInstallBase() { return RunTime.get().fSxBase.getAbsolutePath(); } public static boolean isValidImageFilename(String fname) { String validEndings = ".png.jpg.jpeg"; String defaultEnding = ".png"; int dot = fname.lastIndexOf("."); String ending = defaultEnding; if (dot > 0) { ending = fname.substring(dot); if (validEndings.contains(ending.toLowerCase())) { return true; } } return false; } public static String getValidImageFilename(String fname) { if (isValidImageFilename(fname)) { return fname; } return fname + ".png"; } public static final int ISWINDOWS = 0; public static final int ISMAC = 1; public static final int ISLINUX = 2; public static final int ISNOTSUPPORTED = 3; public static boolean isMacApp = false; public static boolean isWinApp = false; public static final String appPathMac = "/Applications/SikuliX-IDE.app/Contents"; public static boolean ThrowException = true; // throw FindFailed exception public static float AutoWaitTimeout = 3f; // in seconds public static float WaitScanRate = 3f; // frames per second public static float ObserveScanRate = 3f; // frames per second public static int ObserveMinChangedPixels = 50; // in pixels public static int RepeatWaitTime = 1; // wait 1 second for visual to vanish after action public static double MinSimilarity = 0.7; public static boolean CheckLastSeen = true; public static float CheckLastSeenSimilar = 0.95f; public static boolean UseImageFinder = false; private static int ImageCache = 64; /** * set the maximum to be used for the {@link Image} cache *
the start up value is 64 (meaning MB) *
using 0 switches off caching and clears the cache in that moment * * @param max cache size in MB */ public static void setImageCache(int max) { if (ImageCache > max) { Image.clearCache(max); } ImageCache = max; } public static int getImageCache() { return ImageCache; } public static double DelayValue = 0.3; public static double DelayBeforeMouseDown = DelayValue; @Deprecated // use DelayBeforeDrag instead public static double DelayAfterDrag = DelayValue; public static double DelayBeforeDrag = -DelayValue; public static double DelayBeforeDrop = DelayValue; /** * Specify a delay between the key presses in seconds as 0.nnn. This only * applies to the next type and is then reset to 0 again. A value > 1 is cut * to 1.0 (max delay of 1 second) */ public static double TypeDelay = 0.0; /** * Specify a delay between the mouse down and up in seconds as 0.nnn. This * only applies to the next click action and is then reset to 0 again. A value * > 1 is cut to 1.0 (max delay of 1 second) */ public static double ClickDelay = 0.0; public static boolean ClickFast = false; public static boolean RobotFake = true; public static String BundlePath = null; public static boolean OcrTextSearch = false; public static boolean OcrTextRead = false; public static String OcrLanguage = "eng"; /** * true = start slow motion mode, false: stop it (default: false) show a * visual for SlowMotionDelay seconds (default: 2) */ public static boolean TRUE = true; public static boolean FALSE = false; private static boolean ShowActions = false; public static boolean OverwriteImages = false; public static boolean isShowActions() { return ShowActions; } public static void setShowActions(boolean ShowActions) { if (ShowActions) { MoveMouseDelaySaved = MoveMouseDelay; } else { MoveMouseDelay = MoveMouseDelaySaved; } Settings.ShowActions = ShowActions; } public static float SlowMotionDelay = 2.0f; // in seconds public static float MoveMouseDelay = 0.5f; // in seconds private static float MoveMouseDelaySaved = MoveMouseDelay; /** * true = highlight every match (default: false) (show red rectangle around) * for DefaultHighlightTime seconds (default: 2) */ public static boolean Highlight = false; public static float DefaultHighlightTime = 2f; public static float WaitAfterHighlight = 0.3f; public static boolean ActionLogs = true; public static boolean InfoLogs = true; public static boolean DebugLogs = false; public static boolean ProfileLogs = false; public static boolean LogTime = false; public static boolean UserLogs = true; public static String UserLogPrefix = "user"; public static boolean UserLogTime = true; public static boolean TraceLogs = false; /** * default pixels to add around with nearby() and grow() */ public static final int DefaultPadding = 50; public static boolean isJava7() { return JavaVersion > 6; } public static boolean isJava6() { return JavaVersion < 7; } // public static void showJavaInfo() { // Debug.log(1, "Running on Java " + JavaVersion + " (" + JREVersion + ")"); // } public static String getFilePathSeperator() { return File.separator; } public static String getPathSeparator() { if (isWindows()) { return ";"; } return ":"; } public static String getSikuliDataPath() { String home, sikuliPath; if (isWindows()) { home = System.getenv("APPDATA"); sikuliPath = "Sikulix"; } else if (isMac()) { home = System.getProperty("user.home") + "/Library/Application Support"; sikuliPath = "Sikulix"; } else { home = System.getProperty("user.home"); sikuliPath = ".Sikulix"; } File fHome = new File(home, sikuliPath); return fHome.getAbsolutePath(); } /** * @return absolute path to the user's extension path */ public static String getUserExtPath() { String ret = getSikuliDataPath() + File.separator + "Extensions"; File f = new File(ret); if (!f.exists()) { f.mkdirs(); } return ret; } public static int getOS() { int osRet; String os = System.getProperty("os.name").toLowerCase(); if (os.startsWith("mac")) { osRet = ISMAC; } else if (os.startsWith("windows")) { osRet = ISWINDOWS; } else { osRet = ISLINUX; } return osRet; } public static boolean isWindows() { return getOS() == ISWINDOWS; } public static boolean isLinux() { return getOS() == ISLINUX; } public static boolean isMac() { return getOS() == ISMAC; } public static boolean isMac10() { if (isMac() && Settings.getOSVersion().startsWith("10.1")) { return true; } return false; } public static String getShortOS() { if (isWindows()) { return "win"; } if (isMac()) { return "mac"; } return "lux"; } public static String getOSVersion() { return System.getProperty("os.version"); } public static String getTimestamp() { return (new Date()).getTime() + ""; } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/SikulixForJython.java000066400000000000000000000004761315726130400257510ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; /** * to activate Jython support use org.sikuli.script.SikulixForJython instead */ @Deprecated public class SikulixForJython { static { org.sikuli.script.SikulixForJython.get(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/SplashFrame.java000066400000000000000000000102461315726130400246570ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.sikuli.basics; import java.awt.Color; import java.awt.Container; import java.util.Date; import javax.swing.BoxLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JSeparator; import org.sikuli.script.RunTime; /** * * @author rhocke */ public class SplashFrame extends JFrame { private static JFrame splash = null; private static long start = 0; public static void displaySplash(String[] args) { if (args == null) { if (splash != null) { splash.dispose(); } if (start > 0) { Debug.log(3, "Sikuli-Script startup: " + ((new Date()).getTime() - start)); start = 0; } return; } if (args.length > 0 && (args[0].contains("-testSetup") || args[0].startsWith("-i"))) { start = (new Date()).getTime(); String[] splashArgs = new String[]{ "splash", "#", "#" + RunTime.get().SikuliVersionScript, "", "#", "#... starting - please wait ..."}; for (String e : args) { splashArgs[3] += e + " "; } splashArgs[3] = splashArgs[3].trim(); splash = new SplashFrame(splashArgs); } } public static void displaySplashFirstTime(String[] args) { if (args == null) { if (splash != null) { splash.dispose(); } if (start > 0) { Debug.log(3, "Sikuli-IDE environment setup: " + ((new Date()).getTime() - start)); start = 0; } return; } start = (new Date()).getTime(); String[] splashArgs = new String[]{ "splash", "#", "#" + RunTime.get().SikuliVersionIDE, "", "#", "#... setting up environement - please wait ..."}; splash = new SplashFrame(splashArgs); } private JLabel lbl, txt; private Container pane; private int proSize; private int fw, fh; public SplashFrame(String type) { init(new String[]{type}); } public SplashFrame(String[] args) { init(args); } private void init(String[] args) { setResizable(false); setUndecorated(true); pane = getContentPane(); if ("download".equals(args[0])) { pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); pane.add(new JLabel(" ")); lbl = new JLabel(""); lbl.setAlignmentX(CENTER_ALIGNMENT); pane.add(lbl); pane.add(new JLabel(" ")); txt = new JLabel("... waiting"); txt.setAlignmentX(CENTER_ALIGNMENT); pane.add(txt); fw = 350; fh = 80; } if ("splash".equals(args[0])) { pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); pane.setBackground(Color.yellow); int n = args.length; String e; int l = 0; int nlbl = 0; for (int i = 1; i < n; i++) { e = args[i]; if (e.length() > l) { l = e.length(); } if (e.length() > 1 && e.startsWith("#")) { nlbl++; } } JLabel[] lbls = new JLabel[nlbl]; nlbl = 0; for (int i = 1; i < n; i++) { e = args[i]; if (e.startsWith("#")) { if (e.length() > 1) { lbls[nlbl] = new JLabel(e.substring(1)); lbls[nlbl].setAlignmentX(CENTER_ALIGNMENT); pane.add(lbls[nlbl]); nlbl++; } pane.add(new JSeparator()); } else { pane.add(new JLabel(e)); } } fw = 10 + 10*l; fh = 10 + n*15 + nlbl*15; } pack(); setSize(fw, fh); setLocationRelativeTo(null); setVisible(true); } public void setProFile(String proFile) { lbl.setText("Downloading: " + proFile); } public void setProSize(int proSize) { this.proSize = proSize; } public void setProDone(int done) { if (done < 0) { txt.setText(" ..... failed !!!"); } else if (proSize > 0) { txt.setText(done + " % out of " + proSize + " KB"); } else { txt.setText(done + " KB out of ??? KB"); } repaint(); } public void closeAfter(int secs) { try { Thread.sleep(secs*1000); } catch (InterruptedException ex) { } setVisible(false); } } sikulix-1.1.1/API/src/main/java/org/sikuli/basics/VDictProxy.java000066400000000000000000000073431315726130400245310ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.basics; import org.sikuli.script.RunTime; import java.io.File; import java.io.FileNotFoundException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; public class VDictProxy { private long _instance; private Map _i2obj = new HashMap(); static { RunTime.loadLibrary("VDictProxy"); } public VDictProxy(){ _instance = getInstance(); } private native long getInstance(); private String getAbsolutePath(String filename) throws FileNotFoundException{ if(new File(filename).exists()) return filename; filename = Settings.BundlePath + File.separator + filename; if(new File(filename).exists()) return filename; throw new FileNotFoundException("No such file: " + filename); } // insert an (key,value) entry using an image key public void insert(String imagekey_filename, T value) throws FileNotFoundException { imagekey_filename = getAbsolutePath(imagekey_filename); int hash = value.hashCode(); while(true){ if( hash != -1 && !_i2obj.containsKey(hash) ){ _i2obj.put(hash, value); break; } else{ hash += (int)(Math.random()*100); } } _insert(_instance, imagekey_filename, hash); } public native void _insert(long instance, String imagekey_filename, int value); // lookup the entry using an image key (exact match) public T lookup(String imagekey_filename) throws FileNotFoundException{ imagekey_filename = getAbsolutePath(imagekey_filename); int hash = _lookup(_instance, imagekey_filename); if(hash==-1) return null; return _i2obj.get(hash); } private native int _lookup(long instance, String imagekey_filename); // lookup the first entry with a similar image key public T lookup_similar(String imagekey_filename, double similarity_threshold) throws FileNotFoundException{ imagekey_filename = getAbsolutePath(imagekey_filename); int hash = _lookup_similar(_instance, imagekey_filename, similarity_threshold); if(hash==-1) return null; return _i2obj.get(hash); } private native int _lookup_similar(long instance, String imagekey_filename, double similarity_threshold); // lookup at most n entries with keys similar to the given image (n = 0 : all) public List lookup_similar_n(String imagekey_filename, double similarity_threshold, int n) throws FileNotFoundException{ imagekey_filename = getAbsolutePath(imagekey_filename); int h[] = _lookup_similar_n(_instance, imagekey_filename, similarity_threshold, n); List ret = new Vector(h.length); for(int i=0;i _idCallbackMap = new HashMap(); private int _gHotkeyId = 1; class HotkeyData { int key, modifiers; HotkeyListener listener; public HotkeyData(int key_, int mod_, HotkeyListener l_) { key = key_; modifiers = mod_; listener = l_; } } class JIntellitypeHandler implements com.melloware.jintellitype.HotkeyListener { @Override public void onHotKey(int id) { Debug.log(4, "Hotkey pressed"); HotkeyData data = _idCallbackMap.get(id); HotkeyEvent e = new HotkeyEvent(data.key, data.modifiers); data.listener.invokeHotkeyPressed(e); } } @Override public boolean _addHotkey(int keyCode, int modifiers, HotkeyListener listener) { JIntellitype itype = null; try { itype = JIntellitype.getInstance(); } catch (Exception ex) { Debug.error("WindowsHotkeyManager: JIntellitype problem: %s", ex.getMessage()); } if (itype == null) { System.exit(1); } if (_gHotkeyId == 1) { itype.addHotKeyListener(new JIntellitypeHandler()); } _removeHotkey(keyCode, modifiers); int id = _gHotkeyId++; HotkeyData data = new HotkeyData(keyCode, modifiers, listener); _idCallbackMap.put(id, data); itype.registerHotKey(id, translateMod(modifiers), keyCode); return true; } @Override public boolean _removeHotkey(int keyCode, int modifiers) { for (Map.Entry entry : _idCallbackMap.entrySet()) { HotkeyData data = entry.getValue(); if (data.key == keyCode && data.modifiers == modifiers) { JIntellitype itype = JIntellitype.getInstance(); int id = entry.getKey(); itype.unregisterHotKey(id); _idCallbackMap.remove(id); return true; } } return false; } @Override public void cleanUp() { JIntellitype itype = JIntellitype.getInstance(); for (Map.Entry entry : _idCallbackMap.entrySet()) { int id = entry.getKey(); itype.unregisterHotKey(id); } _gHotkeyId = 1; _idCallbackMap.clear(); itype.cleanUp(); } private int translateMod(int mod) { int newMod = 0; if (1 == (mod & 1)) { newMod += 4; } if (2 == (mod & 2)) { newMod += 2; } if (4 == (mod & 4)) { newMod += 8; } if (8 == (mod & 8)) { newMod += 1; } return newMod; } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/000077500000000000000000000000001315726130400214355ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/guide/AnimationFactory.java000066400000000000000000000224231315726130400255520ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.Timer; public class AnimationFactory { static NewAnimator createResizeAnimation(Visual sklComponent, Dimension currentSize, Dimension targetSize) { return new ResizeAnimator(sklComponent, currentSize, targetSize); } static NewAnimator createCenteredResizeToAnimation(Visual sklComponent, Dimension targetSize) { return new CenteredResizeToAnimator(sklComponent, targetSize); } static NewAnimator createCenteredMoveAnimation(Visual sklComponent, Point source, Point destination) { NewMoveAnimator anim = new NewMoveAnimator(sklComponent, source, destination); anim.centered = true; return anim; } static NewAnimator createMoveAnimation(Visual sklComponent, Point source, Point destination) { return new NewMoveAnimator(sklComponent, source, destination); } static public NewAnimator createCircleAnimation( Visual sklComponent, Point origin, float radius) { return new CircleAnimator(sklComponent, origin, radius); } static public NewAnimator createOpacityAnimation( Visual sklComponent, float sourceOpacity, float targetOpacity) { return new OpacityAnimator(sklComponent, sourceOpacity, targetOpacity); } } interface AnimationListener { void animationCompleted(); } class LinearStepper { float beginVal; float endVal; int step; int steps; public LinearStepper(float beginVal, float endVal, int steps) { this.step = 0; this.steps = steps; this.beginVal = beginVal; this.endVal = endVal; } public float next() { float ret = beginVal + step * (endVal - beginVal) / steps; step += 1; return ret; } public boolean hasNext() { return step <= steps; } } class NewAnimator implements ActionListener { Timer timer; boolean looping = false; boolean animationRunning = false; Visual sklComponent; NewAnimator(Visual sklComponent) { this.sklComponent = sklComponent; } protected void init() { } public void start() { init(); timer = new Timer(25, this); timer.start(); animationRunning = true; } protected boolean isRunning() { return animationRunning; } public void setLooping(boolean looping) { this.looping = looping; } AnimationListener listener; public void setListener(AnimationListener listener) { this.listener = listener; } protected void animate() { } @Override public void actionPerformed(ActionEvent e) { if (isRunning()) { Rectangle r = sklComponent.getBounds(); //setActualLocation((int) x, (int) y); animate(); r.add(sklComponent.getBounds()); if (sklComponent.getTopLevelAncestor() != null) { sklComponent.getTopLevelAncestor().repaint(r.x, r.y, r.width, r.height); } } else { timer.stop(); if (looping) { start(); } else { animationRunning = false; if (listener != null) { listener.animationCompleted(); } } } } } class NewMoveAnimator extends NewAnimator { LinearStepper xStepper; LinearStepper yStepper; Point source; Point destination; NewMoveAnimator(Visual sklComponent, Point source, Point destination) { super(sklComponent); this.source = source; this.destination = destination; } boolean centered = false; @Override protected void init() { xStepper = new LinearStepper(source.x, destination.x, 10); yStepper = new LinearStepper(source.y, destination.y, 10); } @Override protected boolean isRunning() { return xStepper.hasNext(); } @Override protected void animate() { float x = xStepper.next(); float y = yStepper.next(); if (centered) { x -= sklComponent.getActualWidth() / 2; y -= sklComponent.getActualHeight() / 2; } sklComponent.setActualLocation((int) x, (int) y); } } class CircleAnimator extends NewAnimator { LinearStepper radiusStepper; Point origin; float radius; CircleAnimator(Visual sklComponent, Point origin, float radius) { super(sklComponent); this.radius = radius; this.origin = origin; setLooping(true); } @Override protected void init() { radiusStepper = new LinearStepper(0, (float) (2 * Math.PI), 20); } @Override protected boolean isRunning() { return radiusStepper.hasNext(); } @Override protected void animate() { float theta = radiusStepper.next(); int x = (int) (origin.x + (int) radius * Math.sin(theta)); int y = (int) (origin.y + (int) radius * Math.cos(theta)); sklComponent.setActualLocation((int) x, (int) y); } } class ResizeAnimator extends NewAnimator { LinearStepper widthStepper; LinearStepper heightStepper; Dimension currentSize; Dimension targetSize; ResizeAnimator(Visual sklComponent, Dimension currentSize, Dimension targetSize) { super(sklComponent); this.currentSize = currentSize; this.targetSize = targetSize; } @Override protected void init() { widthStepper = new LinearStepper(currentSize.width, targetSize.width, 10); heightStepper = new LinearStepper(currentSize.height, targetSize.height, 10); } @Override protected boolean isRunning() { return widthStepper.hasNext(); } @Override protected void animate() { float width = widthStepper.next(); float height = heightStepper.next(); sklComponent.setActualSize(new Dimension((int) width, (int) height)); } } class CenteredResizeToAnimator extends NewAnimator { LinearStepper widthStepper; LinearStepper heightStepper; Dimension currentSize; Dimension targetSize; Point centerLocation; CenteredResizeToAnimator(Visual sklComponent, Dimension targetSize) { super(sklComponent); this.targetSize = targetSize; } @Override protected void init() { centerLocation = sklComponent.getCenter(); widthStepper = new LinearStepper(sklComponent.getActualWidth(), targetSize.width, 10); heightStepper = new LinearStepper(sklComponent.getActualHeight(), targetSize.height, 10); } @Override protected boolean isRunning() { return widthStepper.hasNext(); } @Override protected void animate() { float width = widthStepper.next(); float height = heightStepper.next(); Point newLocation = new Point(centerLocation); newLocation.x -= width / 2; newLocation.y -= height / 2; sklComponent.setActualSize(new Dimension((int) width, (int) height)); sklComponent.setActualLocation(newLocation); } } class OpacityAnimator extends NewAnimator { LinearStepper stepper; float sourceOpacity; float targetOpacity; OpacityAnimator(Visual sklComponent, float sourceOpacity, float targetOpacity) { super(sklComponent); this.sourceOpacity = sourceOpacity; this.targetOpacity = targetOpacity; } @Override protected void init() { stepper = new LinearStepper(sourceOpacity, targetOpacity, 10); } @Override protected boolean isRunning() { return stepper.hasNext(); } @Override public void animate() { float f = stepper.next(); sklComponent.setOpacity(f); } } class PopupAnimator implements ActionListener { LinearStepper shadowSizeStepper; LinearStepper offsetStepper; LinearStepper scaleStepper; LinearStepper widthStepper; LinearStepper heightStepper; Timer timer; Point centerLocation; PopupAnimator() { // shadowSizeStepper = new LinearStepper(5,13,10); // offsetStepper = new LinearStepper(0,10,5); // //scaleStepper = new LinearStepper(1f,1.2f,); // widthStepper = new LinearStepper(getActualWidth(),1.0f*getActualWidth()*1.1f,10); // heightStepper = new LinearStepper(getActualHeight(),1.0f*getActualHeight()*1.1f,10); // // centerLocation = new Point(getActualLocation()); // centerLocation.x = centerLocation.x + getActualWidth()/2; // centerLocation.y = centerLocation.y + getActualHeight()/2; } public void start() { // Timer timer = new Timer(25, this); // timer.start(); // animationRunning = true; } @Override public void actionPerformed(ActionEvent e) { // if (shadowSizeStepper.hasNext()){ // // float shadowSize = shadowSizeStepper.next(); // float offset = offsetStepper.next(); // float width = widthStepper.next(); // float height = heightStepper.next(); // // Rectangle r = getBounds(); // // setActualLocation((int)(centerLocation.x - width/2), (int)(centerLocation.y - height/2)); // setActualSize((int)width, (int)height); // // setShadow((int)shadowSize,(int) 2); // //Point p = getActualLocation(); // //p.x -= 1; // //p.y -= 1; // //setActualLocation(p); // r.add(getBounds()); // // getParent().getParent().repaint();//r.x,r.y,r.width,r.height); // }else{ // ((Timer)e.getSource()).stop(); // animationRunning = false; // animationCompleted(); // } } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/Animator.java000066400000000000000000000061751315726130400240630ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import java.awt.Container; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.Timer; public abstract class Animator implements ActionListener { Timer timer; Visual comp; int duration = 1000; int cycle = 50; boolean played = false; public Animator(Visual comp){ this.comp = comp; timer = new Timer(cycle,this); } public void start(){ played = true; timer.start(); } public void stop(){ timer.stop(); } public boolean isRunning(){ return timer.isRunning(); } public boolean isPlayed(){ return played; } } // class CircleAnimatoOld extends Animator{ int repeatCount; int count; LinearInterpolation funcr; Point origin; int radius; public CircleAnimatoOld(Visual comp, int radius){ super(comp); repeatCount = duration / cycle; count = 0; funcr = new LinearInterpolation(0,(float) (2*Math.PI),repeatCount); origin = comp.getLocation(); this.radius = radius; } @Override public void actionPerformed(ActionEvent e) { float r = funcr.getValue(count); int x = (int) (origin.x + (int) radius * Math.sin(r)); int y= (int) (origin.y + (int) radius * Math.cos(r)); Point p = new Point(x,y); Rectangle r1 = comp.getBounds(); comp.setLocation(p); // r1 stores the union of the bounds before/after the animated step Rectangle r2 = comp.getBounds(); r1.add(r2); comp.getParent().getParent().repaint(r1.x,r1.y,r1.width,r1.height); //repaint(r1); // comp.getParent().invalidate(); // comp.repaint(); if (count == repeatCount) count = 0; else count++; } } // class MoveAnimator extends Animator{ int repeatCount; int count; QuarticEase tfuncx; QuarticEase tfuncy; Point src,dest; public MoveAnimator(Visual comp, Point src, Point dest){ super(comp); repeatCount = duration / cycle; count = 0; tfuncx = new QuarticEase(src.x,dest.x,repeatCount); tfuncy = new QuarticEase(src.y,dest.y,repeatCount); this.dest = dest; this.src = src; } @Override public void actionPerformed(ActionEvent e){ if ( count <= repeatCount){ int x = (int) tfuncx.getValue(count); int y = (int) tfuncy.getValue(count); count++; Rectangle r1 = comp.getBounds(); comp.setActualLocation(x,y); Rectangle r2 = comp.getBounds(); r1.add(r2); Container c = comp.getParent(); if (c != null){ c.getParent().getParent().repaint(r1.x,r1.y,r1.width,r1.height); } }else{ timer.stop(); } } @Override public void start(){ comp.setActualLocation(src.x,src.y); super.start(); } @Override public void stop(){ //comp.setLocation(dest.x,dest.y); super.stop(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/ClickableWindow.java000066400000000000000000000115271315726130400253470ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import java.awt.Color; import java.awt.Cursor; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import javax.swing.JPanel; import org.sikuli.guide.Visual.Layout; import org.sikuli.basics.Debug; import org.sikuli.util.OverlayTransparentWindow; import org.sikuli.script.Region; import org.sikuli.basics.Settings; import org.sikuli.natives.SysUtil; // TODO: Automatically move mouse cursor to the click target. The current implementation // is problematic for non-rectangular clickable widgets, for instance, a round buttone. // Since the highlighted region is always rectangular and is larger than the area that // is actually cliable, users may click on the edge of the region and dismiss the window // errorneously. This needs to be fixed. public class ClickableWindow extends OverlayTransparentWindow implements MouseListener, Transition, GlobalMouseMotionListener { Guide guide; JPanel jp = null; ArrayList clickables = new ArrayList(); private SxClickable lastClicked; private Rectangle maxR; Point clickLocation; GlobalMouseMotionTracker mouseTracker; TransitionListener token; Cursor handCursor = new Cursor(Cursor.HAND_CURSOR); Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR); Cursor hourglassCursor = new Cursor(Cursor.WAIT_CURSOR); Cursor currentCursor = null; public ClickableWindow(Guide guide) { super(new Color(0.1f, 0f, 0f, 0.005f), null); this.guide = guide; jp = getJPanel(); mouseTracker = GlobalMouseMotionTracker.getInstance(); mouseTracker.addListener(this); setVisible(false); setFocusableWindowState(false); addMouseListener(this); addWindowListener(new WindowAdapter() { public void windowClosed(WindowEvent e) { // stop the global mouse tracker's timer thread mouseTracker.stop(); } }); } @Override public void toFront() { if (Settings.isMac()) { // this call is necessary to allow clicks to go through the window (ignoreMouse == true) if (Settings.JavaVersion < 7) { SysUtil.getOSUtil().bringWindowToFront(this, true); } else { } } super.toFront(); } public void addClickable(SxClickable c) { clickables.add(c); SxClickable c1 = new SxClickable(null); c1.setLocationRelativeToComponent(c, Layout.OVER); jp.add(c1); } public void addClickableRegion(Region region, String name) { SxClickable c = new SxClickable(region); c.setName(name); addClickable(c); } @Override public void mouseClicked(MouseEvent e) { Debug.log("[ClickableWindow] clicked on " + e.getX() + "," + e.getY()); Point p = e.getPoint(); lastClicked = null; for (SxClickable c : clickables) { if (c.getActualBounds().contains(p)) { lastClicked = c; p.x -= c.getX(); p.y -= c.getY(); c.globalMouseClicked(p); } } if (lastClicked != null) { if (token != null) { setVisible(false); token.transitionOccurred(this); } } } // @Override public void mouseEntered(MouseEvent arg0) { } @Override public void mouseExited(MouseEvent arg0) { } @Override public void mousePressed(MouseEvent arg0) { } @Override public void mouseReleased(MouseEvent arg0) { } // @Override public String waitForTransition(TransitionListener token) { this.token = token; maxR = clickables.get(0).getBounds(); if (clickables.size() > 1) { for (SxClickable c : clickables.subList(1, clickables.size())) { maxR = maxR.union(c.getActualBounds()); } } setBounds(maxR); setVisible(true); mouseTracker.start(); return "Next"; } @Override public void globalMouseMoved(int x, int y) { Point p = new Point(x, y); SxClickable cc = null; for (SxClickable c : clickables) { if (c.getBounds().contains(p)) { c.setMouseOver(true); cc = c; } else { c.setMouseOver(false); } } //TODO keep moving to (0,0) to nullify the dragged move bug if (cc != null) { setLocation(0, 0); setSize(cc.getActualLocation().x+cc.getActualWidth(), cc.getActualLocation().y+cc.getActualHeight()); } else { setBounds(maxR); } } @Override public void globalMouseIdled(int x, int y) { } public SxClickable getLastClicked() { return lastClicked; } public ArrayList getClickables() { return clickables; } public void clear() { clickables.clear(); getContentPane().removeAll(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/ComponentMover.java000066400000000000000000000173701315726130400252630ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JComponent; import javax.swing.SwingUtilities; public class ComponentMover extends MouseAdapter{ // private boolean moveWindow; private Class destinationClass; private Component destinationComponent; private Component destination; private Component source; private boolean changeCursor = true; private Point pressed; private Point location; private Cursor originalCursor; private boolean autoscrolls; private Insets dragInsets = new Insets(0, 0, 0, 0); private Dimension snapSize = new Dimension(1, 1); /** * Constructor for moving individual components. The components must be * regisetered using the registerComponent() method. */ public ComponentMover() { } /** * Constructor to specify a Class of Component that will be moved when * drag events are generated on a registered child component. The events * will be passed to the first ancestor of this specified class. * * @param destinationClass the Class of the ancestor component * @param components the Components to be registered for forwarding * drag events to the ancestor Component. */ public ComponentMover(Class destinationClass, Component... components) { this.destinationClass = destinationClass; registerComponent( components ); } /** * Constructor to specify a parent component that will be moved when drag * events are generated on a registered child component. * * @param destinationComponent the component drage events should be forwareded to * @param components the Components to be registered for forwarding drag * events to the parent component to be moved */ public ComponentMover(Component destinationComponent, Component... components) { this.destinationComponent = destinationComponent; registerComponent( components ); } /** * Get the change cursor property * * @return the change cursor property */ public boolean isChangeCursor() { return changeCursor; } /** * Set the change cursor property * * @param changeCursor when true the cursor will be changed to the * Cursor.MOVE_CURSOR while the mouse is pressed */ public void setChangeCursor(boolean changeCursor) { this.changeCursor = changeCursor; } /** * Get the drag insets * * @return the drag insets */ public Insets getDragInsets() { return dragInsets; } /** * Set the drag insets. The insets specify an area where mouseDragged * events should be ignored and therefore the component will not be moved. * This will prevent these events from being confused with a * MouseMotionListener that supports component resizing. * * @param dragInsets */ public void setDragInsets(Insets dragInsets) { this.dragInsets = dragInsets; } /** * Remove listeners from the specified component * * @param components components the listeners are removed from */ public void deregisterComponent(Component... components) { for (Component component : components) component.removeMouseListener( this ); } /** * Add the required listeners to the specified component * * @param components components the listeners are added to */ public void registerComponent(Component... components) { for (Component component : components) component.addMouseListener( this ); } /** * Get the snap size * * @return the snap size */ public Dimension getSnapSize() { return snapSize; } /** * Set the snap size. Forces the component to be snapped to * the closest grid position. Snapping will occur when the mouse is * dragged half way. */ public void setSnapSize(Dimension snapSize) { this.snapSize = snapSize; } /** * Setup the variables used to control the moving of the component: * * source - the source component of the mouse event * destination - the component that will ultimately be moved * pressed - the Point where the mouse was pressed in the destination * component coordinates. */ @Override public void mousePressed(MouseEvent e) { source = e.getComponent(); int width = source.getSize().width - dragInsets.left - dragInsets.right; int height = source.getSize().height - dragInsets.top - dragInsets.bottom; Rectangle r = new Rectangle(dragInsets.left, dragInsets.top, width, height); if (r.contains(e.getPoint())) setupForDragging(e); } private void setupForDragging(MouseEvent e) { source.addMouseMotionListener( this ); // Determine the component that will ultimately be moved if (destinationComponent != null) { destination = destinationComponent; } else if (destinationClass == null) { destination = source; } else // forward events to destination component { destination = SwingUtilities.getAncestorOfClass(destinationClass, source); } pressed = e.getLocationOnScreen(); if (destination instanceof Visual){ location = ((Visual) destination).getActualLocation(); }else{ location = destination.getLocation(); } if (changeCursor) { originalCursor = source.getCursor(); source.setCursor( Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR) ); } // Making sure autoscrolls is false will allow for smoother dragging of // individual components if (destination instanceof JComponent) { JComponent jc = (JComponent)destination; autoscrolls = jc.getAutoscrolls(); jc.setAutoscrolls( false ); } } /** * Move the component to its new location. The dragged Point must be in * the destination coordinates. */ @Override public void mouseDragged(MouseEvent e) { Point dragged = e.getLocationOnScreen(); int dragX = getDragDistance(dragged.x, pressed.x, snapSize.width); int dragY = getDragDistance(dragged.y, pressed.y, snapSize.height); if (destination instanceof Visual) ((Visual) destination).setActualLocation(location.x + dragX, location.y + dragY); else destination.setLocation(location.x + dragX, location.y + dragY); } /* * Determine how far the mouse has moved from where dragging started * (Assume drag direction is down and right for positive drag distance) */ private int getDragDistance(int larger, int smaller, int snapSize) { int halfway = snapSize / 2; int drag = larger - smaller; drag += (drag < 0) ? -halfway : halfway; drag = (drag / snapSize) * snapSize; return drag; } /** * Restore the original state of the Component */ @Override public void mouseReleased(MouseEvent e) { source.removeMouseMotionListener( this ); if (changeCursor) source.setCursor( originalCursor ); if (destination instanceof JComponent) { ((JComponent)destination).setAutoscrolls( autoscrolls ); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/GlobalMouseMotionListener.java000066400000000000000000000004121315726130400274020ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; public interface GlobalMouseMotionListener { public void globalMouseMoved(int x, int y); public void globalMouseIdled(int x, int y); } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/GlobalMouseMotionTracker.java000066400000000000000000000042161315726130400272160ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.Timer; import org.sikuli.script.Env; import org.sikuli.script.Location; public class GlobalMouseMotionTracker implements ActionListener { final static int IDLE_COUNT_THRESHOLD = 200; // this keeps track of how many times the cursor stays stationary int idle_count; Location lastLocation = null; static GlobalMouseMotionTracker _instance = null; static public GlobalMouseMotionTracker getInstance(){ if (_instance == null){ _instance = new GlobalMouseMotionTracker(); } return _instance; } ArrayList listeners = new ArrayList(); public void addListener(GlobalMouseMotionListener listener){ listeners.add(listener); } Timer timer; private GlobalMouseMotionTracker(){ timer = new Timer(10, this); } public void start(){ timer.start(); //Debug.info("[GlobalMouseMotionTracker] started"); } public void stop(){ timer.stop(); //Debug.info("[GlobalMouseMotionTracker] stopped"); } @Override public void actionPerformed(ActionEvent arg) { Location newLocation = Env.getMouseLocation(); //Debug.info("Mouse loction: " + newLocation); if (lastLocation != null){ if (lastLocation.x != newLocation.x || lastLocation.y != newLocation.y){ for (GlobalMouseMotionListener listener : listeners){ listener.globalMouseMoved(newLocation.x,newLocation.y); } idle_count = 0; }else{ idle_count++; } //Debug.info("idle: " + idle_count); if (idle_count > IDLE_COUNT_THRESHOLD){ for (GlobalMouseMotionListener listener : listeners){ listener.globalMouseIdled(newLocation.x,newLocation.y); } idle_count = 0; } } lastLocation = newLocation; } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/Guide.java000066400000000000000000000517151315726130400233460ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import java.awt.AWTException; import java.awt.Color; import java.awt.Component; import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.util.ArrayList; import javax.swing.JComponent; import javax.swing.JPanel; import org.sikuli.guide.Transition.TransitionListener; import org.sikuli.basics.Debug; import org.sikuli.util.EventObserver; import org.sikuli.util.EventSubject; import org.sikuli.script.Location; import org.sikuli.script.Pattern; import org.sikuli.script.Region; import org.sikuli.script.Screen; import org.sikuli.util.OverlayTransparentWindow; import org.sikuli.basics.Settings; import org.sikuli.natives.SysUtil; public class Guide extends OverlayTransparentWindow implements EventObserver { static float DEFAULT_TIMEOUT = 10.0f; static public final int FIRST = 0; static public final int MIDDLE = 1; static public final int LAST = 2; static public final int SIMPLE = 4; final float DIMMING_OPACITY = 0.5f; Robot robot; Region _region; JPanel content = null; Transition transition; ArrayList transitions = new ArrayList(); ArrayList trackers = new ArrayList(); Transition triggeredTransition; ClickableWindow clickableWindow = null; SxBeam beam = null; /** * create a new guide overlay on whole primary screen */ public Guide() { super(new Color(0.1f, 0f, 0f, 0.1f), null); super.addObserver(this); init(new Screen()); } /** * create a new guide overlay on given region */ public Guide(Region region) { super(new Color(0.1f, 0f, 0f, 0.1f), null); super.addObserver(this); init(region); } private void init(Region region) { try { robot = new Robot(); } catch (AWTException e1) { e1.printStackTrace(); } content = getJPanel(); _region = region; Rectangle rect = _region.getRect(); content.setPreferredSize(rect.getSize()); add(content); setBounds(rect); getRootPane().putClientProperty( "Window.shadow", Boolean.FALSE ); ((JPanel)getContentPane()).setDoubleBuffered(true); setVisible(false); setFocusableWindowState(false); } public void focusBelow() { if (Settings.isMac()) { // TODO: replace this hack with a more robust method // Mac's hack to bring focus to the window directly underneath // this hack works on the assumption that the caller has // the input focus but no interaction area at the current // mouse cursor position // This hack does not work well with applications that // can receive mouse clicks without having the input focus // (e.g., finder, system preferences) // robot.mousePress(InputEvent.BUTTON1_MASK); // robot.mouseRelease(InputEvent.BUTTON1_MASK); // Another temporary hack to switch to the previous window on Mac robot.keyPress(KeyEvent.VK_META); robot.keyPress(KeyEvent.VK_TAB); robot.keyRelease(KeyEvent.VK_META); robot.keyRelease(KeyEvent.VK_TAB); // wait a little bit for the switch to complete robot.delay(1000); } } @Override public void toFront() { if ( Settings.isMac() || Settings.isWindows() ) { // this call is necessary to allow clicks to go through the window (ignoreMouse == true) if (Settings.JavaVersion < 7) { SysUtil.getOSUtil().bringWindowToFront(this, true); } else { } } super.toFront(); } @Override public void update(EventSubject es) { //TODO transparent paint } public String showNow(float secs) { transitions.add(new TimeoutTransition((int) secs * 1000)); return showNow(); } public String showNow() { String cmd = "Next"; if (content.getComponentCount() == 0 && transitions.isEmpty() ) { //&& search == null) { return cmd; } // startAnimation(); startTracking(); setVisible(true); toFront(); // /* if (search != null) { * search.setVisible(true); * search.requestFocus(); * synchronized (this) { * try { * wait(); * } catch (InterruptedException e) { * e.printStackTrace(); * } * } * search.dispose(); * search.setVisible(false); * String key = search.getSelectedKey(); * search = null; * reset(); * focusBelow(); * return key; * }*/ // if (transitions.isEmpty()) { // if no transition is added, use the default timeout transition transitions.add(new TimeoutTransition((int) DEFAULT_TIMEOUT * 1000)); } final Object token = new Object(); synchronized (token) { for (Transition transition : transitions) { transition.waitForTransition(new TransitionListener() { @Override public void transitionOccurred(Object source) { triggeredTransition = (Transition) source; synchronized (token) { token.notify(); } } }); } try { token.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (triggeredTransition instanceof ClickableWindow) { ClickableWindow cw = (ClickableWindow) triggeredTransition; //TODO click through if not button cmd = cw.getLastClicked().getName(); } else if (triggeredTransition instanceof TimeoutTransition) { cmd = "timeout"; } reset(); return cmd; } public void addToFront(JComponent comp) { addComponent(comp); } public void addComponent(JComponent comp) { content.add(comp, 0); } public void addToFront(Visual comp) { addComponent(comp, 0); } public void addComponent(Visual comp, int index) { if (comp instanceof SxClickable) { // add to the guide window content.add(comp, 0); if (clickableWindow == null) { clickableWindow = new ClickableWindow(this); addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { //Debug.info("[Guide] window closed"); GlobalMouseMotionTracker.getInstance().stop(); } }); } clickableWindow.addClickable((SxClickable) comp); addTransition(clickableWindow); return; } content.add(comp, index); if (comp instanceof SxSpotlight) { setDarken(true); } } public void removeComponent(Component comp) { content.remove(comp); } private void reset() { clear(); transitions.clear(); // now we dipose window so the .py script can terminate if (clickableWindow != null) { clickableWindow.dispose(); clickableWindow = null; } dispose(); } public void clear() { if (clickableWindow != null) { clickableWindow.clear(); } stopAnimation(); stopTracking(); content.removeAll(); transition = null; beam = null; setDarken(false); setVisible(false); GlobalMouseMotionTracker.getInstance().stop(); } public void setDefaultTimeout(float timeout_in_seconds) { DEFAULT_TIMEOUT = timeout_in_seconds; } public Region getRegion() { return _region; } Point convertToRegionLocation(Point point_in_global_coordinate) { Point ret = new Point(point_in_global_coordinate); ret.translate(-_region.x, -_region.y); return ret; } // // SearchDialog search = null; /* public void addSearchDialog() { * search = new SearchDialog(this, "Enter the search string:"); * //search = new GUISearchDialog(this); * search.setLocationRelativeTo(null); * search.setAlwaysOnTop(true); * } * * public void setSearchDialog(SearchDialog search) { * this.search = search; * } * * public void addSearchEntry(String key, Region region) { * if (search == null) { * addSearchDialog(); * } * search.addEntry(key, region); * }*/ // boolean hasSpotlight() { for (Component comp : content.getComponents()) { if (comp instanceof SxSpotlight) { return true; } } return false; } public void updateSpotlights(ArrayList regions) { removeSpotlights(); if (regions.isEmpty()) { setBackground(null); content.setBackground(null); } else { // if there are spotlights added, darken the background setBackground(new Color(0f, 0f, 0f, DIMMING_OPACITY)); content.setBackground(new Color(0f, 0f, 0f, DIMMING_OPACITY)); for (Region r : regions) { SxSpotlight spotlight = new SxSpotlight(r); spotlight.setShape(SxSpotlight.CIRCLE); //addSpotlight(r,SxSpotlight.CIRCLE); } } repaint(); } public void removeSpotlights() { for (Component co : content.getComponents()) { if (co instanceof SxSpotlight) { content.remove(co); } } } public void setDarken(boolean darken) { //TODO check against transparency if (darken) { //setBackground(new Color(0f,0f,0f,DIMMING_OPACITY)); content.setBackground(new Color(0f, 0f, 0f, DIMMING_OPACITY)); } else { setBackground(null); content.setBackground(null); } } public Visual addBeam(Region r) { beam = new SxBeam(this, r); SxAnchor anchor = new SxAnchor(r); addTransition(beam); return anchor; } // /* public void addMagnifier(Region region) { * Magnifier mag = new Magnifier(this, region); * content.add(mag); * }*/ // // public void setDialog(String message) { TransitionDialog dialog = new TransitionDialog(); dialog.setText(message); transition = dialog; } public void setDialog(TransitionDialog dialog_) { //dialog = dialog_; transition = dialog_; } // public void stopAnimation() { for (Component co : content.getComponents()) { /* if (co instanceof Magnifier) { * ((Magnifier) co).start(); * }*/ if (co instanceof Visual) { ((Visual) co).stopAnimation(); } } } public void startAnimation() { for (Component co : content.getComponents()) { /* if (co instanceof Magnifier) { * ((Magnifier) co).start(); * }*/ if (co instanceof Visual) { ((Visual) co).startAnimation(); } } } public void addTransition(Transition t) { if (! transitions.contains(t)) { transitions.add(t); } } public Transition getTransition() { return transition; } public void startTracking() { for (Component co : content.getComponents()) { if (co instanceof SxAnchor) { ((SxAnchor) co).startTracking(); } } } public void stopTracking() { for (Component co : content.getComponents()) { if (co instanceof SxAnchor) { ((SxAnchor) co).stopTracking(); } } } // public void addTracker(Pattern pattern, SxAnchor anchor) { Tracker tracker = null; // // find a tracker already assigned to this pattern // for (Tracker t : trackers){ // if (t.isAlreadyTracking(pattern,r)){ // tracker = t; // break; // } // } // if (tracker == null){ tracker = new Tracker(this, pattern, null); trackers.add(tracker); // } BufferedImage img; try { img = pattern.getBImage(); anchor.setActualSize(img.getWidth(), img.getHeight()); tracker.setAnchor(anchor); } catch (Exception e) { e.printStackTrace(); } } public void addTracker(Pattern pattern, Region r, Visual c) { Tracker tracker = null; // find a tracker already assigned to this pattern for (Tracker t : trackers) { if (t.isAlreadyTracking(pattern, r)) { tracker = t; break; } } if (tracker == null) { tracker = new Tracker(this, pattern, r); trackers.add(tracker); } tracker.setAnchor(c); } public void addTracker(Pattern pattern, Region r, ArrayList components) { Tracker tracker = new Tracker(this, pattern, r); for (Visual c : components) { tracker.setAnchor(c); } trackers.add(tracker); } abstract class TrackerAdapter { abstract void patternAnchored(); } // // /* public void playStepOnWebpage(Step step, Region leftmarker, Region rightmarker) { * * //Point screenshotOrigin = new Point(614,166); * * int originalWidth = step.getScreenImage().getWidth(); * int originalHeight = step.getScreenImage().getHeight(); * * int displayWidth = rightmarker.x + rightmarker.w - leftmarker.x; * float scale = 1.0f * displayWidth / originalWidth; * Debug.info("scale:" + scale); * int displayHeight = (int) (originalHeight * scale); * * int originX = leftmarker.x; * int originY = leftmarker.y - displayHeight; * Point screenshotOrigin = new Point(originX, originY); * * //scale = 1.0f; * //Point screenshotOrigin = new Point(715,192); * //Point screenshotOrigin = new Point(953,257);// * * Debug.info("Step size:" + step.getView().getSize()); * //Dimension displaySize = new Dimension(480,363); * //float scale = 480f/640f; * * Screen s = new Screen(); * for (Part part : step.getParts()) { * * Point targetOrigin = part.getTargetOrigin(); * * Point screenOrigin = new Point(); * screenOrigin.x = screenshotOrigin.x + (int) (targetOrigin.x * scale); * screenOrigin.y = screenshotOrigin.y + (int) (targetOrigin.y * scale); * * part.setAnchorScreenLocation(screenOrigin); * } * * playStep(step, scale); * } * * public void playStep(Step step) { * playStep(step, 1.0f); * } * * public void play(ArrayList steps) { * for (SklStepModel step : steps) { * String ret = playStep(step); * if (ret == null) { * continue; * } * * if (ret.equals("Exit")) { * return; * } * } * * }*/ // public void playStepList(SklStepListModel stepListModel){ // for (Enumeration e = stepListModel.elements() ; e.hasMoreElements() ;) { // SklStepModel eachStepModel = (SklStepModel) e.nextElement(); // String ret = playStep(eachStepModel); // if (ret == null) // continue; // // if (ret.equals("Exit")){ // return; // } // } // // } /* public String playStep(SklStepModel step_) { * * SklStepModel step = null; * try { * step = (SklStepModel) step_.clone(); * } catch (CloneNotSupportedException e1) { * e1.printStackTrace(); * } * * for (SklModel model : step.getModels()) { * model.setOpacity(0f); * SklView view = SklViewFactory.createView(model); * addToFront(view); * } * * * SxButton btn = new SxButton("Exit"); * btn.setActualLocation(10, 50); * addToFront(btn); * * SxButton skip = new SxButton("Skip"); * skip.setActualLocation(90, 50); * addToFront(skip); * * // do these to allow static elements to be drawn * setVisible(true); * toFront(); * * step.startTracking(this); * step.startAnimation(); * * String ret = showNow(); * Debug.info("[Guide.playStep] ret = " + ret); * * return ret; * } * * public void playStep(Step step, final float scale) { * * * SxImage screenshot = new SxImage(step.getScreenImage()); * screenshot.setLocationRelativeToRegion(new Screen(), Layout.INSIDE); * // screenshot.setOpacity(0); * // screenshot.addAnimation(AnimationFactory.createOpacityAnimation(screenshot,0.1f,0.9f)); * // // TODO replace these with a Pause animation * // screenshot.addAnimation(AnimationFactory.createOpacityAnimation(screenshot,0.9f,0.9f)); * // screenshot.addAnimation(AnimationFactory.createOpacityAnimation(screenshot,0.9f,0.9f)); * // screenshot.addAnimation(AnimationFactory.createOpacityAnimation(screenshot,1,0)); * // addToFront(screenshot); * * for (final Part part : step.getParts()) { * * Pattern pattern = part.getTargetPattern(); * * BufferedImage patternImage = null; * try { * patternImage = pattern.getBImage(); * } catch (Exception e) { * } * * final SxAnchor anchor = new SxAnchor(pattern); * anchor.setEditable(true); * anchor.setOpacity(1f); * anchor.setAnimateAnchoring(true); * * Point anchorLocation = new Point(part.getTargetOrigin()); * Point screenshotLocation = screenshot.getActualLocation(); * anchorLocation.translate(screenshotLocation.x, screenshotLocation.y); * anchor.setActualLocation(anchorLocation); * * SxClickable clickable = new SxClickable(null); * clickable.setLocationRelativeToComponent(anchor, Layout.OVER); * addToFront(clickable); * * // add an image to visualize the target pattern * final SxImage sklImage = new SxImage(patternImage); * sklImage.setLocationRelativeToComponent(anchor, Layout.OVER); * * anchor.addListener(new AnchorListener() { * @Override * public void anchored() { * sklImage.removeFromLeader(); * sklImage.setVisible(false); * repaint(); * * * for (Visual comp : anchor.getFollowers()) { * if (comp instanceof SxText || comp instanceof SxFlag) { * comp.popin(); * } * } * * anchor.popin(); * } * * @Override * public void found(SxAnchor source) { * } * }); * * addToFront(sklImage); * addToFront(anchor); * * Point o = part.getTargetOrigin(); * Point p = anchor.getActualLocation(); * * for (Visual compo : part.getAnnotationComponents()) { * * Visual comp = (Visual) compo.clone(); * * Point loc = comp.getActualLocation(); * loc.x = (int) ((int) (loc.x - o.x) * scale) + p.x; * loc.y = (int) ((int) (loc.y - o.y) * scale) + p.y; * * comp.setActualLocation(loc); * comp.setLocationRelativeToComponent(anchor); * comp.setActualSize((int) (comp.getActualWidth() * scale), (int) (comp.getActualHeight() * scale)); * * addToFront(comp); * * comp.popout(); * * } * * anchor.popout(); * * } * * SxButton btn = new SxButton("Exit"); * btn.setActualLocation(50, 50); * addToFront(btn); * * showNow(); * * } * * public void playSteps(ArrayList steps) throws FindFailed { * * Screen s = new Screen(); * * for (Step step : steps) { * SxButton btn = new SxButton("Next"); * btn.setLocation(s.getTopRight().left(200).below(50)); * * * addToFront(btn); * * step.setTransition(getTransition()); * * playStep(step, 1.0f); * } * * }*/// /** * create a rectangle in this guide plane and add to front * @return the rectangle */ public Visual rectangle() { Visual gc = new SxRectangle(); gc.setGuide(this); addToFront(gc); return gc; } public Visual circle() { Visual gc = new SxCircle(); gc.setGuide(this); addToFront(gc); return gc; } public Visual text(String text) { Visual gc = new SxText(text); gc.setGuide(this); addToFront(gc); return gc; } public Visual flag(String text) { Visual gc = new SxFlag(text); gc.setGuide(this); addToFront(gc); return gc; } public Visual callout(String text) { Visual gc = new SxCallout(text); gc.setGuide(this); addToFront(gc); return gc; } public Visual image(Object img) { Visual gc = null; if (img instanceof String) { gc = new SxImage((String) img); } else if (img instanceof BufferedImage) { gc = new SxImage((BufferedImage) img); } if (gc != null) { gc.setGuide(this); addToFront(gc); } else { Debug.log(2, "Guide.image: invalid argument"); } return gc; } public Visual bracket() { Visual gc = new SxBracket(); gc.setGuide(this); addToFront(gc); return gc; } public Visual arrow(Object from, Object to) { Visual gc = null; if (from instanceof Region) { gc = new SxArrow(((Region) from).getCenter().getPoint(), ((Region) to).getCenter().getPoint()); } else if (from instanceof Point || from instanceof Location) { gc = new SxArrow((Point) from, (Point) to); } else if (from instanceof Visual) { gc = new SxArrow((Visual) from, (Visual) to); } if (gc != null) { gc.setGuide(this); addToFront(gc); } else { Debug.log(2, "Guide.arrow: invalid arguments"); } return gc; } public Visual button(String name) { Visual gc = new SxButton(name); gc.setGuide(this); addToFront(gc); return gc; } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/HTMLTextPane.java000066400000000000000000000030771315726130400245240ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.Dimension; import javax.swing.JLabel; import javax.swing.JTextPane; class HTMLTextPane extends JTextPane { int maximum_width; String text; public Dimension preferredDimension; Visual comp = null; String htmltxt; public HTMLTextPane(Visual comp) { this.comp = comp; maximum_width = comp.maxWidth - 10; init(); } public HTMLTextPane() { maximum_width = 400; init(); } private void init() { setContentType("text/html"); } @Override public void setText(String text) { this.text = text; if (comp != null) { maximum_width = comp.maxWidth - 2 * comp.PADDING_X; htmltxt = "

" + text + "
"; } else { htmltxt = ""+text+""; } super.setText(htmltxt); JTextPane tp = new JTextPane(); tp.setText(htmltxt); if (getPreferredSize().getWidth() > maximum_width) { // hack to limit the width of the text to width if (comp != null) { htmltxt = "
" + text + "
"; } else { htmltxt = "
"+text+"
"; } super.setText(htmltxt); } setSize(getPreferredSize()); } @Override public String getText() { return this.text; } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/IAnimator.java000066400000000000000000000076541315726130400241770ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import java.util.Date; public interface IAnimator { public float step(); public boolean running(); } abstract class TimeValueFunction { protected float _beginVal, _endVal; protected long _totalTime; public TimeValueFunction(float beginVal, float endVal, long totalTime) { _beginVal = beginVal; _endVal = endVal; _totalTime = totalTime; } public boolean isEnd(long t) { return t >= _totalTime; } abstract public float getValue(long t); } class LinearInterpolation extends TimeValueFunction { float _stepUnit; public LinearInterpolation(float beginVal, float endVal, long totalTime) { super(beginVal, endVal, totalTime); _stepUnit = (endVal - beginVal) / (float) totalTime; } @Override public float getValue(long t) { if (t > _totalTime) { return _endVal; } return _beginVal + _stepUnit * t; } } class QuarticEase extends TimeValueFunction { public QuarticEase(float beginVal, float endVal, long totalTime) { super(beginVal, endVal, totalTime); } @Override public float getValue(long t) { if (t > _totalTime) { return _endVal; } double t1 = (double) t / _totalTime; return (float) (_beginVal + (_endVal - _beginVal) * t1 * t1 * t1 * t1); } } class OutQuarticEase extends TimeValueFunction { public OutQuarticEase(float beginVal, float endVal, long totalTime) { super(beginVal, endVal, totalTime); } @Override public float getValue(long t) { if (t > _totalTime) { return _endVal; } double t1 = (double) t / _totalTime; double t2 = t1 * t1; return (float) (_beginVal + (_endVal - _beginVal) * (-1 * t2 * t2 + 4 * t1 * t2 - 6 * t2 + 4 * t1)); } } class StopExtention extends TimeValueFunction { TimeValueFunction _func; public StopExtention(TimeValueFunction func, long totalTime) { super(func._beginVal, func._endVal, totalTime); _func = func; _totalTime = totalTime; } @Override public float getValue(long t) { return _func.getValue(t); } @Override public boolean isEnd(long t) { return t >= _totalTime; } } class TimeBasedAnimator implements IAnimator { protected long _begin_time; protected float _beginVal, _endVal, _stepUnit; protected long _totalMS; protected boolean _running; protected TimeValueFunction _func; public TimeBasedAnimator(TimeValueFunction func) { _begin_time = -1; _running = true; _func = func; } @Override public float step() { if (_begin_time == -1) { _begin_time = (new Date()).getTime(); return _func.getValue(0); } long now = (new Date()).getTime(); long delta = now - _begin_time; float ret = _func.getValue(delta); _running = !_func.isEnd(delta); return ret; } @Override public boolean running() { return _running; } public void stop() { _running = false; } } class PulseAnimator implements IAnimator { protected float _v1, _v2; protected long _interval, _totalMS; protected boolean _running; protected long _begin_time = -1; public PulseAnimator(float v1, float v2, long interval, long totalMS) { _v1 = v1; _v2 = v2; _interval = interval; _totalMS = totalMS; _running = true; } @Override public float step() { if (_begin_time == -1) { _begin_time = (new Date()).getTime(); return _v1; } long now = (new Date()).getTime(); long delta = now - _begin_time; if (delta >= _totalMS) { _running = false; } if ((delta / _interval) % 2 == 0) { return _v1; } else { return _v2; } } @Override public boolean running() { return _running; } } class LinearAnimator extends TimeBasedAnimator { public LinearAnimator(float beginVal, float endVal, long totalMS) { super(new LinearInterpolation(beginVal, endVal, totalMS)); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/Run.java000066400000000000000000000021161315726130400230440ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package org.sikuli.guide; import java.awt.Color; import org.sikuli.script.*; /** * * @author rhocke */ public class Run { Guide guide = null; static Screen scr; static Visual sgc; public static void main(String[] args) { Run sgr = new Run(); sgr.scr = new Screen(); ImagePath.add("org.sikuli.script.RunTime/ImagesAPI.sikuli"); sgr.setUp(); sgr.testButton(); sgr.tearDown(); } private void setUp() { guide = new Guide(); } private void tearDown() { System.out.println(guide.showNow(5f)); guide = null; } public void testButton() { System.out.println("button"); Visual g = guide.button("Continue"); g.setLocationRelativeToRegion(scr.getCenter().grow(500), Visual.Layout.BOTTOM); // g.setFontSize(12); // g.setColor(Color.white); // g.setTextColor(Color.black); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/ShadowRenderer.java000066400000000000000000000053021315726130400252140ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; public class ShadowRenderer { Visual source; public ShadowRenderer(Visual source, int shadowSize){ this.source = source; sourceActualSize = source.getActualSize(); this.shadowSize = shadowSize; } float shadowOpacity = 0.8f; int shadowSize = 10; Color shadowColor = Color.black; BufferedImage createShadowMask(BufferedImage image){ BufferedImage mask = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = mask.createGraphics(); g2d.drawImage(image, 0, 0, null); // Ar = As*Ad - Cr = Cs*Ad -> extract 'Ad' g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, shadowOpacity)); g2d.setColor(shadowColor); g2d.fillRect(0, 0, image.getWidth(), image.getHeight()); g2d.dispose(); return mask; } ConvolveOp getBlurOp(int size) { float[] data = new float[size * size]; float value = 1 / (float) (size * size); for (int i = 0; i < data.length; i++) { data[i] = value; } return new ConvolveOp(new Kernel(size, size, data)); } BufferedImage shadowImage = null; Dimension sourceActualSize = null; public BufferedImage createShadowImage(){ BufferedImage image = new BufferedImage(source.getActualWidth() + shadowSize * 2, source.getActualHeight() + shadowSize * 2, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); g2.translate(shadowSize,shadowSize); source.paintPlain(g2); shadowImage = new BufferedImage(image.getWidth(),image.getHeight(),BufferedImage.TYPE_INT_ARGB); getBlurOp(shadowSize).filter(createShadowMask(image), shadowImage); g2.dispose(); //Debug.info("[Shadow] shadowImage: " + shadowImage); return shadowImage; } public void paintComponent(Graphics g){ Graphics2D g2d = (Graphics2D)g; // create shadow image if the size of the source component has changed since last rendering attempt if (shadowImage == null || source.getActualHeight() != sourceActualSize.height || source.getActualWidth() != sourceActualSize.width){ createShadowImage(); sourceActualSize = source.getActualSize(); } //Debug.info("[Shadow] painting shadow" + shadowImage); g2d.drawImage(shadowImage, 0, 0, null, null); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxAnchor.java000066400000000000000000000073741315726130400240400ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.util.ArrayList; import org.sikuli.script.Pattern; import org.sikuli.script.Region; public class SxAnchor extends Visual { Region region; ArrayList listeners = new ArrayList(); private boolean animateAnchoring = false; Pattern pattern = null; Tracker tracker = null; public SxAnchor() { super(); setForeground(Color.black); } public SxAnchor(Pattern pattern) { super(); this.pattern = pattern; setTracker(pattern); } public SxAnchor(Region region) { super(); if (region != null) { this.region = region; setActualBounds(region.getRect()); } setForeground(Color.black); } public Pattern getPattern() { return pattern; } public void setAnimateAnchoring(boolean animateAnchoring) { this.animateAnchoring = animateAnchoring; } public boolean isAnimateAnchoring() { return animateAnchoring; } public interface AnchorListener { public void anchored(); public void found(SxAnchor source); // public class AnchorAdapter implements AnchorListener { // public void anchored(){}; // public void found(){}; // } } public void addListener(AnchorListener listener) { listeners.add(listener); } public void found(Rectangle bounds) { for (AnchorListener listener : listeners) { listener.found(this); } if (isAnimateAnchoring()) { Point center = new Point(); center.x = (int) (bounds.x + bounds.width / 2); center.y = (int) (bounds.y + bounds.height / 2); moveTo(center, new AnimationListener() { public void animationCompleted() { anchored(); } }); } else { setActualLocation(bounds.x, bounds.y); anchored(); } } public void anchored() { for (AnchorListener listener : listeners) { listener.anchored(); } // this implements the default behavior for fadein entrance when // the anchor pattern is found and anchored for the first time addFadeinAnimation(); startAnimation(); } public void setTracker(Pattern pattern) { setOpacity(0f); tracker = new Tracker(pattern); BufferedImage img; try { img = pattern.getBImage(); setActualSize(img.getWidth(), img.getHeight()); tracker.setAnchor(this); } catch (Exception e) { e.printStackTrace(); } } public void startTracking() { if (tracker != null) { //Debug.info("[SxAnchor] start tracking"); tracker.start(); } } public void stopTracking() { if (tracker != null) { tracker.stopTracking(); } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; // /* if (editable) { * if (true) { * Rectangle r = getActualBounds(); * g2d.setColor(getForeground()); * g2d.drawRect(0, 0, r.width - 1, r.height - 1); * g2d.setColor(Color.white); * g2d.drawRect(1, 1, r.width - 3, r.height - 3); * g2d.setColor(getForeground()); * g2d.drawRect(2, 2, r.width - 5, r.height - 5); * g2d.setColor(Color.white); * g2d.drawRect(3, 3, r.width - 7, r.height - 7); * } else { * Rectangle r = getActualBounds(); * g2d.setColor(Color.red); * g2d.setStroke(new BasicStroke(3f)); * g2d.drawRect(1, 1, r.width - 3, r.height - 3); * } * }*/ // } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxArea.java000066400000000000000000000101141315726130400234600ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.util.ArrayList; import org.sikuli.basics.Debug; import org.sikuli.script.Region; import org.sikuli.script.Screen; import com.sun.crypto.provider.RSACipher; public class SxArea extends Visual implements ComponentListener{ ArrayList regions = new ArrayList(); ArrayList landmarks = new ArrayList(); public SxArea(){ super(); // default to transparent so it can be faded in when it becomes visible later setOpacity(0); } public static final int BOUNDING = 0; public static final int INTERSECTION = 1; int relationship = BOUNDING; public void setRelationship(int relationship){ this.relationship = relationship; } int mode = 0; public static int VERTICAL = 1; public static int HORIZONTAL = 2; public void setMode(int mode){ this.mode = mode; } // update the bounds to the union of all the rectangles void updateBounds(){ Rectangle rect = null; Screen s = new Screen(); for (Visual comp : landmarks){ if (rect == null){ rect = new Rectangle(comp.getBounds()); continue; }else { if (relationship == BOUNDING){ rect.add(comp.getBounds()); }else if (relationship == INTERSECTION){ rect = rect.intersection(comp.getBounds()); } } } if (rect.width<0 || rect.height<=0){ setVisible(false); }else{ setVisible(true); // for (Visual sklComp : getFollowers()) // hack to get the locations of the followers to update if (mode == 0){ setActualLocation(rect.x,rect.y); setActualSize(rect.getSize()); } else if (mode == VERTICAL){ setActualLocation(rect.x,0); setActualSize(rect.width, s.h); } else if (mode == HORIZONTAL){ setActualLocation(0, rect.y); setActualSize(s.w, rect.height); } } updateVisibility(); } public void addLandmark(Visual comp){ landmarks.add(comp); updateBounds(); comp.addComponentListener(this); } public void addRegion(Region region){ if (regions.isEmpty()){ setActualBounds(region.getRect()); }else{ Rectangle bounds = getBounds(); bounds.add(region.getRect()); setActualBounds(bounds); } regions.add(region); } public void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; if (false){ Rectangle r = getActualBounds(); g2d.setColor(Color.black); g2d.drawRect(0,0,r.width-1,r.height-1); //g2d.setColor(Color.white); g2d.setColor(Color.green); g2d.setStroke(new BasicStroke(3f)); g2d.drawRect(1,1,r.width-3,r.height-3); } } private void updateVisibility(){ boolean allHidden = true; for (Visual landmark : landmarks){ allHidden = allHidden && !landmark.isVisible(); } if (allHidden){ //Debug.info("SxArea is hidden"); } setVisible(!allHidden); // if area is visible, do fadein if (isVisible()){ addFadeinAnimation(); startAnimation(); } } @Override public void componentHidden(ComponentEvent e) { updateVisibility(); } @Override public void componentMoved(ComponentEvent e) { Rectangle r = getBounds(); updateBounds(); r.add(getBounds()); if (getTopLevelAncestor() != null) getTopLevelAncestor().repaint(r.x,r.y,r.width,r.height); } @Override public void componentResized(ComponentEvent e) { } @Override public void componentShown(ComponentEvent e) { setVisible(true); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxArrow.java000066400000000000000000000121661315726130400237130ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.lang.reflect.Array; import org.sikuli.basics.Debug; public class SxArrow extends Visual implements ComponentListener { public static final int STRAIGHT = 0; public static final int ELBOW_X = 1; public static final int ELBOW_Y = 2; int style; private Point source = null; private Point destination = null; private Visual from = null; private Visual to = null; boolean hasComponents = false; public SxArrow(Point from, Point to) { super(); this.source = from; this.destination = to; init(); } public SxArrow(Visual from, Visual to) { super(); hasComponents = true; this.from = from; this.to = to; from.addComponentListener(this); to.addComponentListener(this); init(); } private void init() { colorFront = Color.RED; style = STRAIGHT; updateComponent(); } @Override public void updateComponent() { setForeground(colorFront); Rectangle dirtyBounds = getBounds(); if (from != null && to != null) { source = from.getCenter(); destination = to.getCenter(); } Rectangle r = new Rectangle(getSource()); r.add(getDestination()); r.grow(10, 10); setActualBounds(r); dirtyBounds.add(getBounds()); if (getTopLevelAncestor() != null) { getTopLevelAncestor().repaint(dirtyBounds.x, dirtyBounds.y, dirtyBounds.width, dirtyBounds.height); } if (hasComponents) { updateVisibility(); } } public void setStyle(int style) { this.style = style; } private void drawPolylineArrow(Graphics g, int[] xPoints, int[] yPoints, int headLength, int headwidth) { double theta1; Object tempX1 = ((Array.get(xPoints, ((xPoints.length) - 2)))); Object tempX2 = ((Array.get(xPoints, ((xPoints.length) - 1)))); Integer fooX1 = (Integer) tempX1; int x1 = fooX1.intValue(); Integer fooX2 = (Integer) tempX2; int x2 = fooX2.intValue(); Object tempY1 = ((Array.get(yPoints, ((yPoints.length) - 2)))); Object tempY2 = ((Array.get(yPoints, ((yPoints.length) - 1)))); Integer fooY1 = (Integer) tempY1; int y1 = fooY1.intValue(); Integer fooY2 = (Integer) tempY2; int y2 = fooY2.intValue(); int deltaX = (x2 - x1); int deltaY = (y2 - y1); double theta = Math.atan((double) (deltaY) / (double) (deltaX)); if (deltaX < 0.0) { theta1 = theta + Math.PI; //If theta is negative make it positive } else { theta1 = theta; //else leave it alone } int lengthdeltaX = -(int) (Math.cos(theta1) * headLength); int lengthdeltaY = -(int) (Math.sin(theta1) * headLength); int widthdeltaX = (int) (Math.sin(theta1) * headwidth); int widthdeltaY = (int) (Math.cos(theta1) * headwidth); g.drawPolyline(xPoints, yPoints, xPoints.length); g.drawLine(x2, y2, x2 + lengthdeltaX + widthdeltaX, y2 + lengthdeltaY - widthdeltaY); g.drawLine(x2, y2, x2 + lengthdeltaX - widthdeltaX, y2 + lengthdeltaY + widthdeltaY); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Rectangle r = getActualBounds(); Stroke pen = new BasicStroke(3.0F); g2d.setStroke(pen); g2d.translate(-r.x, -r.y); if (style == STRAIGHT) { drawPolylineArrow(g, new int[]{getSource().x, getDestination().x}, new int[]{getSource().y, getDestination().y}, 6, 6); } else if (style == ELBOW_X) { Point m = new Point(getDestination().x, getSource().y); g2d.drawLine(getSource().x, getSource().y, m.x, m.y); drawPolylineArrow(g, new int[]{m.x, getDestination().x}, new int[]{m.y, getDestination().y}, 6, 6); } else if (style == ELBOW_Y) { Point m = new Point(getSource().x, getDestination().y); g2d.drawLine(getSource().x, getSource().y, m.x, m.y); drawPolylineArrow(g, new int[]{m.x, getDestination().x}, new int[]{m.y, getDestination().y}, 6, 6); } } public void setDestination(Point destination) { this.destination = destination; updateComponent(); } public Point getDestination() { return destination; } public void setSource(Point source) { this.source = source; updateComponent(); } public Point getSource() { return source; } void updateVisibility() { setVisible(from.isVisible() && to.isVisible()); } @Override public void componentHidden(ComponentEvent arg0) { updateVisibility(); } @Override public void componentMoved(ComponentEvent arg0) { updateComponent(); } @Override public void componentResized(ComponentEvent arg0) { } @Override public void componentShown(ComponentEvent arg0) { updateComponent(); updateVisibility(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxBeam.java000066400000000000000000000170351315726130400234650ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import org.sikuli.script.Region; import org.sikuli.util.OverlayTransparentWindow; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import org.sikuli.util.EventObserver; import org.sikuli.util.EventSubject; import org.sikuli.basics.Settings; import org.sikuli.natives.SysUtil; public class SxBeam extends OverlayTransparentWindow implements Transition, GlobalMouseMotionListener, EventObserver { Guide guide; public SxBeam(Guide guide, Region target) { super(new Color(1f, 0f, 0f, 0.7f), null); super.addObserver(this); this.guide = guide; this.target = target; /* setBackground(null); // when opaque is set to false, the content seems to get cleared properly // this is tested on both Windows and Mac SysUtil.getOSUtil().setWindowOpaque(this, false); setOpacity(0.7f); */ } public Point current = null; public Point to = null; Region target; @Override public void update(EventSubject es) { Graphics2D g = ((OverlayTransparentWindow) es).getJPanelGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); drawRayPolygon(g, current, target.getRect()); } /* public void paint(Graphics g){ Graphics2D g2d = (Graphics2D)g; super.paint(g); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); drawRayPolygon(g, current, target.getRect()); } */ public void drawRayPolygon(Graphics g, Point p, Rectangle rect) { if (p == null || rect == null) { return; } Graphics2D g2d = (Graphics2D) g; Rectangle r = rect; Ellipse2D.Double ellipse = new Ellipse2D.Double(r.x, r.y, r.width - 1, r.height - 1); g2d.setColor(Color.red); g2d.fill(ellipse); //g2d.drawRect(rect.x,rect.y,rect.width,rect.height); // compute tangent points g2d.translate(rect.x + rect.width / 2, rect.y + rect.height / 2); float a0 = r.width / 2; float b0 = r.height / 2; float a = a0 * a0; float b = b0 * b0; float m = p.x - rect.x - rect.width / 2; float n = p.y - rect.y - rect.height / 2; float t1 = (1f + (a * n * n) / (b * m * m)); float t2 = -2f * a * n / (m * m); float t3 = (b * a) / (m * m) - b; float s = (float) Math.sqrt(t2 * t2 - 4 * t1 * t3); float y1 = (-t2 + s) / (2 * t1); float y2 = (-t2 - s) / (2 * t1); float x1 = a / m - y1 * (a * n) / (b * m); float x2 = a / m - y2 * (a * n) / (b * m); // g2d.drawLine((int)m,(int)n,(int)x1,(int)y1); // g2d.drawLine((int)m,(int)n,(int)x2,(int)y2); // // // g2d.setColor(Color.black); // g2d.drawLine(0,0,(int)a0,(int)b0); // // // GeneralPath flagShape = new GeneralPath(); flagShape.moveTo(m, n); flagShape.lineTo(x1, y1); flagShape.lineTo(x2, y2); flagShape.closePath(); g2d.fill(flagShape); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 1.0f)); //Rectangle smaller = new Rectangle(rect); ellipse = new Ellipse2D.Double(-r.width / 2 + 3, -r.height / 2 + 3, r.width - 6, r.height - 6); g2d.fill(ellipse); } public void drawRayPolygon1(Graphics g, Point p, Rectangle rect) { if (p == null || rect == null) { return; } Graphics2D g2d = (Graphics2D) g; Rectangle r = rect; // corners of the target rectangle int cxs[] = {r.x, r.x, r.x + r.width, r.x + r.width}; int cys[] = {r.y, r.y + r.height, r.y + r.height, r.height}; ArrayList corners = new ArrayList(); corners.add(new Point(r.x, r.y)); corners.add(new Point(r.x + r.width, r.y + r.height)); corners.add(new Point(r.x + r.width, r.y)); corners.add(new Point(r.x, r.y + r.height)); Collections.sort(corners, new Comparator() { @Override public int compare(Object arg0, Object arg1) { return (int) (current.distance((Point) arg0) - current.distance((Point) arg1)); } }); int[] xs; int[] ys; int d = 5; if (p.x > rect.getMinX() - 5 && p.x < rect.getMaxX() + 5 || p.y > rect.getMinY() - 5 && p.y < rect.getMaxY() + 5) { xs = new int[3]; ys = new int[3]; xs[0] = (int) p.x; xs[1] = (int) corners.get(0).x; xs[2] = (int) corners.get(1).x; ys[0] = (int) p.y; ys[1] = (int) corners.get(0).y; ys[2] = (int) corners.get(1).y; } else { xs = new int[4]; ys = new int[4]; xs[0] = (int) p.x; xs[1] = (int) corners.get(2).x; xs[2] = (int) corners.get(0).x; xs[3] = (int) corners.get(1).x; ys[0] = (int) p.y; ys[1] = (int) corners.get(2).y; ys[2] = (int) corners.get(0).y; ys[3] = (int) corners.get(1).y; } Polygon shape = new Polygon(xs, ys, xs.length); Stroke pen = new BasicStroke(3.0F); g2d.setStroke(pen); g2d.setColor(Color.black); //g2d.drawPolygon(pointing_triangle); //g2d.drawRect(x,y,w,h); g2d.setColor(Color.red); g2d.fillPolygon(shape); g2d.drawRect(rect.x, rect.y, rect.width, rect.height); } @Override public void toFront() { if ( Settings.isMac() || Settings.isWindows() ) { // this call is necessary to allow clicks to go through the window (ignoreMouse == true) if (Settings.JavaVersion < 7) { SysUtil.getOSUtil().bringWindowToFront(this, true); } else { } } super.toFront(); } GlobalMouseMotionTracker mouseTracker; TransitionListener listener; @Override public String waitForTransition(TransitionListener listener) { this.listener = listener; mouseTracker = GlobalMouseMotionTracker.getInstance(); mouseTracker.addListener(this); mouseTracker.start(); setBounds(guide.getRegion().getRect()); setVisible(true); toFront(); // repaint(); // Cursor handCursor = new Cursor(Cursor.HAND_CURSOR); // Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR); // Cursor currentCursor = null; // // boolean running = true; // while (running){ // // Rectangle target_rect = target.getRect(); // Location m = Region.atMouse(); // // current = new Point(m.x,m.y); // // Cursor cursor = null; // if (target_rect.contains(current)){ // running = false; // cursor = handCursor; // // setVisible(false); // dispose(); //// synchronized(guide){ //// guide.notify(); //// dispose(); //// return "Next"; //// } // token.transitionOccurred(this); // return "Next"; // // }else{ // cursor = defaultCursor; // repaint(); // } // // // if (cursor != currentCursor){ // setCursor(cursor); // currentCursor = cursor; // } // // } return "Next"; } @Override public void globalMouseIdled(int x, int y) { } @Override public void globalMouseMoved(int x, int y) { current = new Point(x, y); repaint(); if (target.getRect().contains(current)) { setVisible(false); dispose(); listener.transitionOccurred(this); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxBracket.java000066400000000000000000000071551315726130400241760ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import javax.swing.JLabel; import org.sikuli.script.Region; public class SxBracket extends Visual{ public final static int DIRECTION_EAST = 1; public final static int DIRECTION_WEST = 2; public final static int DIRECTION_SOUTH = 3; public final static int DIRECTION_NORTH = 4; int direction; public int PADDING_X = 2; public int PADDING_Y = 2; public int SHADOW_SIZE = 2; int thickness = 10; int margin = 5; JLabel label; int length; boolean entrance = false; public SxBracket(){ super(); init(); } private void init(){ this.length = 30; colorFront = Color.RED; stroke = 3; } @Override public void updateComponent() { setLocationRelativeToRegion(getTarget(), layout); } @Override public Visual setLocationRelativeToRegion(Region region, Layout side) { if (side == Layout.TOP){ setActualSize(region.w,thickness); setDirection(DIRECTION_SOUTH); } else if (side == Layout.BOTTOM){ setActualSize(region.w,thickness); setDirection(DIRECTION_NORTH); } else if (side == Layout.LEFT){ setActualSize(thickness,region.h); setDirection(DIRECTION_EAST); } else if (side == Layout.RIGHT){ setActualSize(thickness,region.h); setDirection(DIRECTION_WEST); } if (side == Layout.LEFT || side == Layout.RIGHT){ length = region.h; }else{ length = region.w; } return super.setLocationRelativeToRegion(region,side); } @Override public void startAnimation(){ // TODO: move this somewhere else // this should only be called first time animation // is started // Why called here? Because ... // we want the location to be decided if (direction == DIRECTION_EAST){ setEntranceAnimation(createSlidingAnimator(-20,0)); } else if (direction == DIRECTION_WEST){ setEntranceAnimation(createSlidingAnimator(20,0)); } else if (direction == DIRECTION_SOUTH){ setEntranceAnimation(createSlidingAnimator(0,-20)); } else if (direction == DIRECTION_NORTH){ setEntranceAnimation(createSlidingAnimator(0,20)); } super.startAnimation(); } public void setDirection(int direction){ this.direction = direction; } @Override public void paintComponent(Graphics g){ Graphics2D g2d = (Graphics2D) g; Stroke pen = new BasicStroke(stroke); g2d.setStroke(pen); g2d.setColor(colorFront); GeneralPath polyline = new GeneralPath(); polyline.moveTo(0,0); polyline.lineTo(5,5); polyline.lineTo(5,length/2-6); polyline.lineTo(8,length/2); polyline.lineTo(5,length/2+6); polyline.lineTo(5,length-5); polyline.lineTo(0,length); AffineTransform rat = new AffineTransform(); if (direction == DIRECTION_EAST){ rat.translate(thickness,length); rat.rotate(Math.PI); } else if (direction == DIRECTION_SOUTH){ rat.translate(0,thickness); rat.rotate(-Math.PI/2); } else if (direction == DIRECTION_NORTH){ rat.translate(length,0); rat.rotate(Math.PI/2); } g2d.transform(rat); g2d.draw(polyline); super.paintComponent(g); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxButton.java000066400000000000000000000040531315726130400240700ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.RoundRectangle2D; import javax.swing.JLabel; import org.sikuli.script.Region; public class SxButton extends SxClickable { Font f = new Font("sansserif", Font.BOLD, 18); JLabel label = null; public SxButton(String name) { super(new Region(0, 0, 0, 0)); init(name); } private void init(String name) { PADDING_X = PADDING_Y = 10; fontSize = 18; setName(name); setColors(null, null, null, null, Color.WHITE); mouseOverColor = new Color(0.3f, 0.3f, 0.3f); layout = Layout.BOTTOM; } @Override public void setName(String name) { if (label == null) { super.setName(name); this.label = new JLabel(name); add(label); } label.setFont(new Font("sansserif", Font.BOLD, fontSize)); label.setForeground(colorText); Dimension s = label.getPreferredSize(); label.setLocation((int) (PADDING_X/2), (int) (PADDING_Y/2)); label.setSize(s); s.height += PADDING_Y; s.width += PADDING_X; setActualSize(s); } @Override public void updateComponent() { setName(name); setLocationRelative(layout); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; Color cb = null; if (isMouseOver()) { cb = mouseOverColor; } else { cb = colorFront; } g2d.setColor(cb); RoundRectangle2D roundedRectangle = new RoundRectangle2D.Float(0, 0, getActualWidth() - 1, getActualHeight() - 1, PADDING_X, PADDING_Y); g2d.fill(roundedRectangle); g2d.setColor(cb); g2d.draw(roundedRectangle); roundedRectangle = new RoundRectangle2D.Float(1, 1, getActualWidth() - 3, getActualHeight() - 3, PADDING_X, PADDING_Y); g2d.setColor(colorFrame); g2d.draw(roundedRectangle); label.paintComponents(g); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxCallout.java000066400000000000000000000102251315726130400242160ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.RoundRectangle2D; import org.sikuli.script.Region; public class SxCallout extends Visual { static final int TRIANGLE_SIZE = 20; static int defMaxWidth = 400; static int defFontSize = 14; static String defFont = "Verdana"; HTMLTextPane textArea; RoundedBox rbox; Triangle triangle; int dx = 0; int dy = 0; public SxCallout(String text) { super(); init(text); } void init(String text) { layout = Layout.RIGHT; maxWidth = defMaxWidth; fontName = defFont; fontSize = defFontSize; this.text = text; textArea = new HTMLTextPane(this); rbox = new RoundedBox(textArea.getBounds()); add(textArea); add(rbox); triangle = new Triangle(); add(triangle); targetRegion = null; setColors(null, Color.YELLOW, Color.YELLOW, null, null); makeComponent(); } @Override public void updateComponent() { textArea.setText(text); textArea.setLocation(PADDING_X, PADDING_Y); Rectangle rect = textArea.getBounds(); rect.grow(PADDING_X, PADDING_Y); rbox.setBounds(rect); makeComponent(); triangle.setForeground(colorBack); rbox.setForeground(colorBack); if (targetRegion != null) { super.setLocationRelativeToRegion(targetRegion, layout); } } @Override public Visual setLocationRelativeToRegion(Region region, Layout side) { if (side != layout) { layout = side; updateComponent(); } targetRegion = region; return super.setLocationRelativeToRegion(targetRegion, side); } private void makeComponent() { if (layout == Layout.TOP) { triangle.rotate(0); dx = 0; dy = 0; triangle.setLocationRelativeToComponent(rbox, Layout.BOTTOM); } else if (layout == Layout.BOTTOM) { dx = 0; dy = TRIANGLE_SIZE; triangle.rotate(Math.PI); triangle.setLocationRelativeToComponent(rbox, Layout.TOP); } else if (layout == Layout.LEFT) { dx = 0; dy = 0; triangle.rotate(-Math.PI / 2); triangle.setLocationRelativeToComponent(rbox, Layout.RIGHT); } else if (layout == Layout.RIGHT) { dx = TRIANGLE_SIZE; dy = 0; triangle.rotate(Math.PI / 2); triangle.setLocationRelativeToComponent(rbox, Layout.LEFT); } Rectangle bounds = rbox.getBounds(); bounds.add(triangle.getBounds()); setActualBounds(bounds); } @Override public void paintComponent(Graphics g) { g.translate(dx, dy); super.paintComponent(g); } class Triangle extends Visual { GeneralPath gp; public Triangle() { super(); init(); } private void init() { gp = new GeneralPath(); gp.moveTo(TRIANGLE_SIZE * 0.45, 0); gp.lineTo(TRIANGLE_SIZE * 0.5, TRIANGLE_SIZE); gp.lineTo(TRIANGLE_SIZE * 0.85, 0); gp.closePath(); setActualSize(new Dimension(TRIANGLE_SIZE, TRIANGLE_SIZE)); } public void rotate(double radius) { init(); AffineTransform rat = new AffineTransform(); rat.rotate(radius, TRIANGLE_SIZE / 2, TRIANGLE_SIZE / 2); gp.transform(rat); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.fill(gp); } } class RoundedBox extends Visual { public RoundedBox(Rectangle rect) { super(); setActualBounds(rect); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); RoundRectangle2D roundedRectangle = new RoundRectangle2D.Float(0, 0, getWidth(), getHeight(), 15, 15); g2d.fill(roundedRectangle); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxCircle.java000066400000000000000000000031771315726130400240240ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import org.sikuli.basics.Debug; import org.sikuli.script.Region; public class SxCircle extends Visual { public SxCircle(Region region) { super(); init(region); } public SxCircle(Visual comp) { super(); init(comp.getRegion()); } public SxCircle() { super(); init(null); } private void init(Region region) { if (region != null) { targetRegion = region; } else { Debug.log(2, "SikuliGuideRectangle: targetRegion is given as null"); targetRegion = Region.create(0, 0, 2*stroke, 2*stroke); } setColor(Color.RED); } @Override public void updateComponent() { setActualBounds(getTarget().getRect()); setForeground(colorFront); super.setLocationRelative(Layout.OVER); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Stroke pen = new BasicStroke((float) stroke); g2d.setStroke(pen); Rectangle r = new Rectangle(getActualBounds()); r.grow(-(stroke-1), -(stroke-1)); g2d.translate(stroke-1, stroke-1); Ellipse2D.Double ellipse = new Ellipse2D.Double(0, 0, r.width - 1, r.height - 1); g2d.draw(ellipse); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxClickable.java000066400000000000000000000043061315726130400244670ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import org.sikuli.script.Region; public class SxClickable extends Visual { Color normalColor = new Color(1.0f, 1.0f, 0, 0.1f); Color mouseOverColor = new Color(1.0f, 0, 0, 0.1f); String name; Region region; boolean borderVisible = true; boolean mouseOverVisible = false; boolean mouseOver; public SxClickable(Region region) { this.region = region; if (region != null) { this.setActualBounds(region.getRect()); this.setActualLocation(region.x, region.y); } } Point clickPoint = null; public SxClickable() { } @Override public void setName(String name) { this.name = name; } @Override public String getName() { return name; } public void setBorderVisible(boolean borderVisible) { this.borderVisible = borderVisible; } public void setMouseOverVisible(boolean visible) { mouseOverVisible = visible; } public void setMouseOver(boolean mouseOver) { if (this.mouseOver != mouseOver) { if (this.mouseOver) { globalMouseExited(); } else { globalMouseEntered(); } Rectangle r = getBounds(); this.getTopLevelAncestor().repaint(r.x, r.y, r.width, r.height); } this.mouseOver = mouseOver; } public boolean isMouseOver() { return mouseOver; } public void globalMouseMoved(Point p) { } public void globalMouseEntered() { mouseOver = true; } public void globalMouseExited() { mouseOver = false; } public void globalMouseClicked(Point p) { } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(1, 1, 1, 0.05f)); g2d.fillRect(0, 0, getActualWidth() - 1, getActualHeight() - 1); if (mouseOverVisible) { if (mouseOver) { g2d.setColor(mouseOverColor); } else { g2d.setColor(normalColor); } g2d.fillRect(0, 0, getActualWidth() - 1, getActualHeight() - 1); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxFlag.java000066400000000000000000000127571315726130400235000ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import javax.swing.JLabel; import org.sikuli.script.Region; public class SxFlag extends Visual{ // which direction this element is pointing public final static int DIRECTION_EAST = 1; public final static int DIRECTION_WEST = 2; public final static int DIRECTION_SOUTH = 3; public final static int DIRECTION_NORTH = 4; JLabel label; Rectangle textBox; Rectangle triangle; FontMetrics fm; String defFont = "sansserif"; Font font; int defFontSize = 16; int direction; Dimension canonicalSize; GeneralPath flagShape; public SxFlag(String text){ super(); init(text); } private void init(String text){ this.text = text; setForeground(colorText); setBackground(colorFront); textBox = new Rectangle(); triangle = new Rectangle(); font = new Font(defFont, Font.BOLD, defFontSize); } @Override public void updateComponent() { fm = getFontMetrics(font); textBox.setSize(fm.stringWidth(text),fm.getHeight()); textBox.grow(PADDING_X, PADDING_Y); setLocationRelativeToRegion(getTarget(), layout); } @Override public Visual setText(String text){ this.text = text; updateComponent(); return this; } @Override public Visual setFont(String fontName, int fsize) { font = new Font(fontName, Font.BOLD, fsize>0 ? fsize : fontSize); updateComponent(); return this; } @Override public Visual setLocationRelativeToRegion(Region region, Layout side) { if (side == Layout.TOP){ setDirection(DIRECTION_SOUTH); } else if (side == Layout.BOTTOM){ setDirection(DIRECTION_NORTH); } else if (side == Layout.LEFT){ setDirection(DIRECTION_EAST); } else if (side == Layout.RIGHT){ setDirection(DIRECTION_WEST); } return super.setLocationRelativeToRegion(region,side); } public void setDirection(int direction){ this.direction = direction; if (direction == DIRECTION_EAST || direction == DIRECTION_WEST){ triangle.setSize(10,textBox.height); canonicalSize = new Dimension(textBox.width + triangle.width, textBox.height); }else{ triangle.setSize(20, 10); setActualSize(textBox.width, textBox.height + triangle.height); canonicalSize = new Dimension(textBox.width, textBox.height + triangle.height); } setActualSize(canonicalSize); if (direction == DIRECTION_EAST){ textBox.setLocation(0, 0); } else if (direction == DIRECTION_WEST){ textBox.setLocation(triangle.width, 0); } else if (direction == DIRECTION_SOUTH){ textBox.setLocation(0, 0); } else if (direction == DIRECTION_NORTH){ textBox.setLocation(0, triangle.height); } flagShape = new GeneralPath(); if (direction == DIRECTION_WEST || direction == DIRECTION_EAST) { flagShape.moveTo(0,0); flagShape.lineTo(textBox.width,0); flagShape.lineTo(textBox.width+triangle.width, textBox.height/2); flagShape.lineTo(textBox.width, textBox.height); flagShape.lineTo(0,textBox.height); flagShape.closePath(); }else{ flagShape.moveTo(0,0); flagShape.lineTo(textBox.width,0); flagShape.lineTo(textBox.width, textBox.height); flagShape.lineTo(textBox.width/2+8, textBox.height); flagShape.lineTo(textBox.width/2,textBox.height+triangle.height); flagShape.lineTo(textBox.width/2-8, textBox.height); flagShape.lineTo(0,textBox.height); flagShape.closePath(); } if (direction == DIRECTION_WEST){ AffineTransform rat = new AffineTransform(); rat.setToTranslation(textBox.width + triangle.width, textBox.height); rat.rotate(Math.PI); flagShape.transform(rat); }else if (direction == DIRECTION_NORTH){ AffineTransform rat = new AffineTransform(); rat.setToTranslation(textBox.width, textBox.height+triangle.height); rat.rotate(Math.PI); flagShape.transform(rat); } } @Override public void paintComponent(Graphics g){ Dimension d = new Dimension(textBox.width + triangle.width, textBox.height); Dimension originalSize = canonicalSize; Dimension actualSize = getActualSize(); float scalex = 1f * actualSize.width / originalSize.width; float scaley = 1f * actualSize.height / originalSize.height; ((Graphics2D) g).scale(scalex, scaley); super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setFont(font); g2d.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); g2d.setColor(colorFront); g2d.fill(flagShape); // draw outline Stroke pen = new BasicStroke(1.0F); g2d.setStroke(pen); g2d.setColor(colorFrame); g2d.draw(flagShape); g2d.setColor(colorText); g2d.drawString(text, textBox.x + PADDING_X, textBox.y + textBox.height - fm.getDescent() - PADDING_Y); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxHotspot.java000066400000000000000000000046621315726130400242630ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import javax.swing.JLabel; import org.sikuli.basics.Debug; import org.sikuli.script.Region; public class SxHotspot extends SxClickable { Font f = new Font("sansserif", Font.BOLD, 18); JLabel label; Guide guide; SxSpotlight spotlight; Visual text; JLabel symbol; SxCircle circle; public SxHotspot(Region region, Visual text, Guide g) { super(region); guide = g; spotlight = new SxSpotlight(region); spotlight.setShape(SxSpotlight.CIRCLE); Rectangle bounds = spotlight.getBounds(); bounds.grow(10, 10); spotlight.setBounds(bounds); this.text = text; text.setLocationRelativeToComponent(this, Layout.RIGHT); //this.text.setLocationRelativeToRegion(new Region(bounds), Visual.RIGHT); // draw a question mark centered on the region Font f = new Font("sansserif", Font.BOLD, 18); symbol = new JLabel("?"); symbol.setFont(f); Dimension size = symbol.getPreferredSize(); symbol.setSize(size); symbol.setForeground(Color.white); symbol.setLocation(region.x + region.w / 2 - size.width / 2, region.y + region.h / 2 - size.height / 2); // draw a circle around the question mark Rectangle cc = new Rectangle(symbol.getBounds()); cc.grow(7, 0); circle = new SxCircle(new Region(cc)); circle.setForeground(Color.white); circle.setShadow(5, 2); g.content.add(symbol); g.addToFront(circle); g.addToFront(spotlight); g.addToFront(text); text.setVisible(false); spotlight.setVisible(false); } @Override public void globalMouseEntered() { Debug.info("Entered"); circle.setVisible(false); symbol.setVisible(false); spotlight.setVisible(true); text.setVisible(true); guide.repaint(); } @Override public void globalMouseExited() { Debug.info("Exited"); circle.setVisible(true); symbol.setVisible(true); spotlight.setVisible(false); text.setVisible(false); guide.repaint(); } @Override public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (mouseOver) { g2d.setColor(new Color(0, 0, 0, 0)); } else { g2d.setColor(normalColor); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxImage.java000066400000000000000000000033351315726130400236410ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import org.sikuli.script.Region; import org.sikuli.script.Image; public class SxImage extends Visual { private BufferedImage image = null; float scale; int w, h; public SxImage(String filename) { super(); init(Image.create(filename).get()); } public SxImage(BufferedImage image) { super(); init(image); } private void init(BufferedImage image) { this.image = image; setScale(1.0f); } @Override public void updateComponent() { setActualBounds(getTarget().getRect()); } @Override public Visual setScale(float scale) { this.scale = scale; if (scale == 0) { int x = (int) (getTarget().getCenter().x - image.getWidth()/2); int y = (int) (getTarget().getCenter().y - image.getHeight()/2); setTarget(Region.create(x, y, image.getWidth(), image.getHeight())); } else { w = (int) (scale * image.getWidth()); h = (int) (scale * image.getHeight()); } return this; } @Override public void paintComponent(Graphics g) { if (image == null) { return; } Graphics2D g2d = (Graphics2D) g; int aw = w > getActualWidth() ? getActualWidth() : w; int ah = h> getActualHeight() ? getActualHeight() : h; int ay = (int) ((getActualHeight() - ah)/2); g2d.drawImage(image, 0, ay, aw, ah, null); g2d.drawRect(0, 0, getActualWidth() - 1, getActualHeight() - 1); } public void setImage(BufferedImage image) { this.image = image; } public BufferedImage getImage() { return image; } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxMagnet.java000066400000000000000000000154151315726130400240340ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import java.awt.Dimension; import java.awt.Point; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import org.sikuli.guide.SxAnchor.AnchorListener; import org.sikuli.guide.Visual.Layout; import org.sikuli.basics.Debug; import org.sikuli.script.Env; import org.sikuli.script.Location; import org.sikuli.script.Pattern; import org.sikuli.script.Region; public class SxMagnet implements Transition, GlobalMouseMotionListener { Guide guide; GlobalMouseMotionTracker mouseTracker; private SxClickable lastClickedClickable; public SxMagnet(Guide guide) { this.guide = guide; mouseTracker = GlobalMouseMotionTracker.getInstance(); mouseTracker.addListener(this); // TOOD: fix this hack // use this trick to engage clickablewindow guide.addComponent(new SxClickable(), 0); } ArrayList targets = new ArrayList(); ArrayList links = new ArrayList(); // void flyTarget(SxAnchor a){ // // Location mouseLocation = Env.getMouseLocation(); // // try { // Pattern pattern = a.getPattern(); // SxImage img = new SxImage(pattern.getBImage()); // img.setActualLocation(a.getActualLocation()); // // Dimension currentSize = a.getActualSize(); // Dimension targetSize = new Dimension(currentSize); // targetSize.width *= 1.5; // targetSize.height *= 1.5; // // img.addResizeAnimation(currentSize, targetSize); // // // Point currentLocation = new Point(a.getActualLocation()); // currentLocation.x += img.getActualWidth(); // currentLocation.y += img.getActualHeight(); // // int dx = mouseLocation.x - currentLocation.x; // int dy = mouseLocation.y - currentLocation.y; // // int radius = 50; // double distance = mouseLocation.distance(currentLocation); // double m = radius / distance; // // Point targetLocation = new Point(); // targetLocation.x = (int) (mouseLocation.x - dx*m) - img.getActualWidth()/2; // targetLocation.y = (int) (mouseLocation.y - dy*m) - img.getActualHeight()/2; // // // Rectangle desiredSpot = new Rectangle(targetLocation, targetSize); // desiredSpot = getFreeSpot(desiredSpot); // targetLocation = desiredSpot.getLocation(); // // // img.addMoveAnimation(currentLocation, targetLocation); // guide.addToFront(img); // img.startAnimation(); // // // Region r = new Region(mouseLocation.x-radius,mouseLocation.y-radius,radius*2,radius*2); // SxCircle c = new SxCircle(r); // guide.addComponent(c,1); // // guide.repaint(); // // // } catch (IOException e) { // e.printStackTrace(); // } class Link { SxImage image; SxAnchor anchor; } void attractTarget(SxAnchor a, Point targetLocation) { try { Pattern pattern = a.getPattern(); SxImage img = new SxImage(pattern.getBImage()); SxClickable clickable = new SxClickable(); clickable.setLocationRelativeToComponent(img, Layout.OVER); guide.addToFront(clickable); clickable.clickPoint = a.getCenter(); Link link = new Link(); link.image = img; link.anchor = a; links.add(link); img.setShadowDefault(); img.setActualLocation(a.getActualLocation()); Dimension currentSize = a.getActualSize(); Dimension targetSize = new Dimension(currentSize); targetSize.width *= 1.5; targetSize.height *= 1.5; img.addResizeAnimation(currentSize, targetSize); Point currentLocation = new Point(a.getActualLocation()); targetLocation.x -= targetSize.width / 2; targetLocation.y -= targetSize.height / 2; img.addMoveAnimation(currentLocation, targetLocation); guide.addToFront(img); img.startAnimation(); guide.repaint(); } catch (Exception e) { e.printStackTrace(); } } SxCircle selection; public void allTargetAnchored() { double theta = 0; double dtheta = 2.0f * Math.PI / (double) targets.size(); Location mouseLocation = Env.getMouseLocation(); int x = mouseLocation.x; int y = mouseLocation.y; int radius = 50; Region r = new Region(mouseLocation.x - radius, mouseLocation.y - radius, radius * 2, radius * 2); SxCircle c = new SxCircle(r); guide.addToFront(c); selection = new SxCircle(); guide.addToFront(selection); // sort targets along x-axis Collections.sort(targets, new Comparator() { @Override public int compare(SxAnchor a, SxAnchor b) { return b.getX() - a.getX(); } }); for (SxAnchor target : targets) { int px = (int) (x + radius * Math.cos(theta)); int py = (int) (y + radius * Math.sin(theta)); theta += dtheta; attractTarget(target, new Point(px, py)); } } int anchoredCount = 0; public void addTarget(final Pattern pattern) { final SxAnchor a = new SxAnchor(pattern); guide.addToFront(a); targets.add(a); SxFlag f = new SxFlag("Flag"); f.setLocationRelativeToComponent(a, Layout.LEFT); guide.addToFront(f); a.addListener(new AnchorListener() { @Override public void anchored() { Debug.info("[Magnet] pattern anchored"); anchoredCount += 1; if (anchoredCount == targets.size()) { allTargetAnchored(); } } @Override public void found(SxAnchor source) { // TODO Auto-generated method stub } }); } // ArrayList occupiedSpots = new ArrayList(); // Rectangle getFreeSpot(Rectangle desired){ // // Rectangle freeSpot = new Rectangle(desired); // // for (Rectangle occupiedSpot : occupiedSpots){ // // if (freeSpot.intersects(occupiedSpot)){ // freeSpot.x = occupiedSpot.x + occupiedSpot.width + 10; // } // // } // // occupiedSpots.add(freeSpot); // // return freeSpot; // } TransitionListener token; @Override public String waitForTransition(TransitionListener token) { this.token = token; mouseTracker.start(); return "Next"; } @Override public void globalMouseMoved(int x, int y) { // Debug.log("[SxMagnet] moved to " + x + "," + y); Point p = new Point(x, y); for (Link link : links) { if (link.image.getActualBounds().contains(p)) { //Debug.info("[SxMagnet] mouseover on a target"); if (selection != null) { selection.setMargin(5, 5, 5, 5); selection.setLocationRelativeToComponent(link.anchor, Layout.OVER); guide.repaint(); } } } } @Override public void globalMouseIdled(int x, int y) { } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxRectangle.java000066400000000000000000000024261315726130400245230ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Stroke; import org.sikuli.basics.Debug; import org.sikuli.script.Region; public class SxRectangle extends Visual { public SxRectangle(Region region) { super(); init(region); } public SxRectangle(Visual comp) { super(); init(comp.getRegion()); } public SxRectangle() { super(); init(null); } private void init(Region region) { if (region != null) { targetRegion = region; } else { Debug.log(2, "SikuliGuideRectangle: targetRegion is given as null"); targetRegion = Region.create(0, 0, 2*stroke, 2*stroke); } setColor(Color.RED); } @Override public void updateComponent() { setActualBounds(targetRegion.getRect()); setForeground(colorFront); super.setLocationRelative(Layout.OVER); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; Stroke pen = new BasicStroke(stroke); g2d.setStroke(pen); g2d.drawRect(0, 0, getActualWidth() - 1, getActualHeight() - 1); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxSpotlight.java000066400000000000000000000056121315726130400245740ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Ellipse2D; import org.sikuli.script.Region; public class SxSpotlight extends Visual{ public final static int RECTANGLE = 0; public final static int CIRCLE = 1; boolean border = true; Color border_color = Color.black; int shape = RECTANGLE; Region region; public SxSpotlight(){ setShape(RECTANGLE); } public SxSpotlight(Region region){ super(); this.region = region; if (region != null) setActualBounds(region.getRect()); setShape(RECTANGLE); } public void setShape(int shape_constant){ this.shape = shape_constant; } @Override public void paint(Graphics g){ //paintComponent(g);////super.paint(g); super.paintPlain(g); } @Override public void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; if (opacity == 0) return; Rectangle r = getBounds(); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 1.0f)); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if (shape == RECTANGLE){ g2d.fillRect(0,0,r.width-1,r.height-1); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f*(1 - opacity))); g2d.fillRect(0,0,r.width-1,r.height-1); }else if (shape == CIRCLE){ Ellipse2D.Double ellipse = new Ellipse2D.Double(0,0,r.width,r.height); g2d.fill(ellipse); // adding visual ringing effect g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity)); int ds[] = {0,2,4,6}; float bs[] = {0.25f,0.15f,0.1f}; for (int i = 0; i < 3; ++i){ int d = ds[i]; float b = bs[i]; g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity)); ellipse = new Ellipse2D.Double(d,d,r.width-2*d,r.height-2*d); g2d.setColor(new Color(0f,0f,0f,b)); g2d.fill(ellipse); d = ds[i+1]; g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 1.0f)); ellipse = new Ellipse2D.Double(d,d,r.width-2*d,r.height-2*d); g2d.setColor(Color.black); g2d.fill(ellipse); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f*(1 - opacity))); g2d.fill(ellipse);//0,0,r.width-1,r.height-1); } } // g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/SxText.java000066400000000000000000000034221315726130400235400ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.JLabel; public class SxText extends Visual { JLabel label; boolean initDone = false; public SxText(String text) { super(); init(text); } private void init(String text) { this.text = text; label = new JLabel(); add(label); fontSize = 12; label.setFont(new Font("SansSerif", Font.PLAIN, 36)); updateComponent(); initDone = true; } @Override public void updateComponent() { String htmltxt = "
" + text + "
"; label.setText(htmltxt); Dimension size = label.getPreferredSize(); if (size.width > maxWidth) { // hack to limit the width of the text to width htmltxt = "
" + text + "
"; label.setText(htmltxt); size = label.getPreferredSize(); } label.setSize(size); setActualSize(size); } @Override public void paintComponent(Graphics g) { Dimension originalSize = label.getPreferredSize(); Dimension actualSize = getActualSize(); float scalex = 1f * actualSize.width / originalSize.width; float scaley = 1f * actualSize.height / originalSize.height; ((Graphics2D) g).scale(scalex, scaley); super.paintComponent(g); } // /* TextPropertyEditor ed = null; * * public void setEditable(boolean editable) { * if (editable) { * } else { * } * }*/ // } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/TimeoutTransition.java000066400000000000000000000012721315726130400260030ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.Timer; public class TimeoutTransition implements Transition, ActionListener { Timer timer; TransitionListener listener; public TimeoutTransition(int timeout){ timer = new Timer(timeout,this); } public String waitForTransition(final TransitionListener listener){ this.listener = listener; timer.start(); return "Next"; } @Override public void actionPerformed(ActionEvent arg0) { listener.transitionOccurred(this); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/Tracker.java000066400000000000000000000170051315726130400236760ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.BufferedImage; import static java.lang.Thread.sleep; import java.util.ArrayList; import org.sikuli.basics.Debug; import org.sikuli.script.Match; import org.sikuli.script.Pattern; import org.sikuli.script.Region; import org.sikuli.script.Screen; interface TrackerListener { void patternAnchored(); } public class Tracker extends Thread { Guide guide; Pattern pattern; Region match; Screen screen; String image_filename; Pattern centerPattern; boolean initialFound = false; ArrayList components = new ArrayList(); ArrayList offsets = new ArrayList(); SxAnchor anchor; TrackerListener listener; boolean running; public Tracker(Pattern pattern){ //this.guide = guide; //this.match = match; screen = new Screen(); BufferedImage image; BufferedImage center; this.pattern = pattern; try { image = pattern.getBImage(); int w = image.getWidth(); int h = image.getHeight(); center = image.getSubimage(w/4,h/4,w/2,h/2); centerPattern = new Pattern(center); } catch (Exception e) { e.printStackTrace(); } //TODO Pattern with BufferedImage centerPattern = new Pattern(pattern); } public Tracker(Guide guide, Pattern pattern, Region match){ this.guide = guide; //this.match = match; screen = new Screen(); BufferedImage image; BufferedImage center; this.pattern = pattern; try { image = pattern.getBImage(); int w = image.getWidth(); int h = image.getHeight(); center = image.getSubimage(w/4,h/4,w/2,h/2); centerPattern = new Pattern(center); } catch (Exception e) { e.printStackTrace(); } } public void setAnchor(Visual component) { Point loc = component.getLocation(); //Point offset = new Point(loc.x - match.x, loc.y - match.y); Point offset = new Point(0,0);//loc.x - match.x, loc.y - match.y); offsets.add(offset); components.add(component); anchor = (SxAnchor) component; } @Override public void run(){ running = true; initialFound = true; match = null; // Looking for the target for the first time Debug.log("[Tracker] Looking for the target for the first time"); while (running && (match == null)){ match = screen.exists(pattern,0.5); } // this means the tracker has been stopped before the pattern is found if (match == null) { return; } Debug.log("[Tracker] Pattern is found for the first time"); // // if (true){ // Rectangle bounds = match.getRect(); // anchor.found(bounds); // }else{ // // animate the initial movement to the anchor position // // // uncomment this for popup demo // anchor.moveTo(new Point(match.x, match.y), new AnimationListener(){ // public void animationCompleted(){ // anchor.anchored(); // if (listener != null){ // listener.patternAnchored(); // } // } // }); // } // // Rectangle bounds = match.getRect(); anchor.found(bounds); while (running){ if (match != null && isPatternStillThereInTheSameLocation()){ //Debug.log("[Tracker] Pattern is seen in the same location."); continue; } // try for at least 1.0 sec. to have a better chance of finding the // new position of the pattern. // the first attempt often fails because the target is only a few // pixels away when the screen capture is made and it is still // due to occlusion by foreground annotations // however, it would mean it takes at least 1.0 sec to realize // the pattern has disappeared and the referencing annotations should // be hidden Match newMatch = screen.exists(pattern,1.0); if (newMatch == null){ Debug.log("[Tracker] Pattern is not found on the screen"); //anchor.setOpacity(0.0f); //not_found_counter += 1; //if (not_found_counter > 2){ anchor.addFadeoutAnimation(); anchor.startAnimation(); // not_found_counter = 0; //} }else { Debug.log("[Tracker] Pattern is found in a new location: " + newMatch); // make it visible anchor.addFadeinAnimation(); anchor.startAnimation(); // anchor.setVisible(true); // // if the match is in a different location // if (match.x != newMatch.x || match.y != newMatch.y){ // for (int i=0; i < components.size(); ++i){ // comp = components.get(i); //Point offset = offsets.get(0); // int dest_x = newMatch.x + offset.x; // int dest_y = newMatch.y + offset.y; int dest_x = newMatch.x + newMatch.w/2; int dest_y = newMatch.y + newMatch.h/2; // comp.setEmphasisAnimation(comp.createMoveAnimator(dest_x, dest_y)); //comp.startAnimation(); Debug.log("[Tracker] Pattern is moving to: (" + dest_x + "," + dest_y + ")"); anchor.moveTo(new Point(dest_x, dest_y)); } match = newMatch; } // if (!initialFound){ // Debug.log("[Tracker] Pattern has disappeared after initial find"); // // // // for (Visual comp : components){ // comp.setVisible(false); // } // guide.repaint(); // } } public void stopTracking(){ running = false; } public boolean isAlreadyTracking(Pattern pattern, Region match) { try { boolean sameMatch = this.match == match; boolean sameBufferedImage = this.pattern.getBImage() == pattern.getBImage(); boolean sameFilename = (this.pattern.getFilename() != null && (this.pattern.getFilename().compareTo(pattern.getFilename()) == 0)); return sameMatch || sameBufferedImage || sameFilename; } catch (Exception e) { return false; } } boolean isAnimationStillRunning(){ for (Visual comp : components){ if (comp instanceof SxAnchor){ if (comp.animationRunning) return true;; } } return false; } boolean isPatternStillThereInTheSameLocation(){ try { sleep(1000); } catch (InterruptedException e) { } Region center = new Region(match); // /* center.x += center.w/4-2; * center.y += center.h/4-2; * center.w = center.w/2+4; * center.h = center.h/2+4; */ // Match m = center.exists(centerPattern,0); if (m == null) Debug.log("[Tracker] Pattern is not seen in the same location."); return m != null; // Debug.log("[Tracker] Pattern is still in the same location" + m); } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/Transition.java000066400000000000000000000004631315726130400244350ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; public interface Transition { public interface TransitionListener { void transitionOccurred(Object source); } String waitForTransition(TransitionListener token); } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/TransitionDialog.java000066400000000000000000000135031315726130400255540ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.guide; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.Timer; import org.sikuli.util.OverlayTransparentWindow; public class TransitionDialog extends OverlayTransparentWindow implements Transition{ // the location user moved to by dragging static Point userPreferredLocation = null; Box buttons; JLabel titleBar; Button defaultButton; TextPane textPane; String command = null; boolean isLocationSet = false; protected void dismiss(){ setVisible(false); dispose(); synchronized(this){ this.notify(); } } public void setTitle(String title){ titleBar.setText(title); titleBar.setVisible(true); } public String getActionCommand(){ return command; } public TransitionDialog(){ init(""); } public TransitionDialog(String text){ init(text); } public void setLocationToUserPreferredLocation(){ if (userPreferredLocation != null) setLocation(userPreferredLocation); else setLocationRelativeTo(null); } public void setTimeout(int timeout){ Timer timer = new Timer(timeout, new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { command = null; dismiss(); } }); timer.setRepeats(false); timer.start(); } void init(String text){ setBackground(Color.yellow); setForeground(Color.black); JPanel content = new JPanel(); content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS)); add(content); textPane = new TextPane(); textPane.setText(text); textPane.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5)); Color darkyellow = new Color(238,185,57); titleBar = new JLabel(); titleBar.setFont(new Font("sansserif", Font.BOLD, 14)); titleBar.setBackground(darkyellow); titleBar.setOpaque(true); titleBar.setBorder(BorderFactory.createEmptyBorder(5, 5, 3, 5)); titleBar.setSize(titleBar.getPreferredSize()); titleBar.setVisible(false); buttons = new Box(BoxLayout.X_AXIS); defaultButton = new Button("Close"); buttons.add(defaultButton); buttons.setBorder(BorderFactory.createEmptyBorder(15,5,5,5)); content.add(titleBar); content.add(textPane); content.add(buttons); // this allows the title bar to take the whole width of the dialog box titleBar.setMaximumSize(new Dimension(Integer.MAX_VALUE,Integer.MAX_VALUE)); buttons.setMaximumSize(new Dimension(Integer.MAX_VALUE,Integer.MAX_VALUE)); textPane.setMaximumSize(new Dimension(Integer.MAX_VALUE,Integer.MAX_VALUE)); // these allow all the parts to left aligned titleBar.setAlignmentX(Component.LEFT_ALIGNMENT); textPane.setAlignmentX(Component.LEFT_ALIGNMENT); buttons.setAlignmentX(Component.LEFT_ALIGNMENT); // these are meant to prevent the message box from stealing // focus when it's clicked, but they don't seem to work // setFocusableWindowState(false); // setFocusable(false); // this allows the window to be dragged to another location on the screen ComponentMover cm = new ComponentMover(); cm.registerComponent(this); pack(); } public String waitForTransition(TransitionListener token){ // must be set visible setVisible(true); // before being brought to the front toFront(); setAlwaysOnTop(true); Point startLocation = getLocation(); //Debug.info("init location:" + getLocation()); // pack needs to be called after the component is set visible pack(); // these do not seem necessary any more // force the dialog to paint right away before animation starts //repaint(); synchronized(this){ try { this.wait(); } catch (InterruptedException e) { } } Point endLocation = getLocation(); //Debug.info("end location:" + getLocation()); // if the location has changed if (endLocation.x != startLocation.x || endLocation.y != startLocation.y){ //Debug.info("user has moved the position of this dialog box"); // it must be the result of user's moving the dialog box // to a presumably preferred location userPreferredLocation = endLocation; } setVisible(false); return command; } public void addButton(String name){ if (defaultButton != null){ //Debug.info("removing" + defaultButton); buttons.remove(defaultButton); buttons.removeAll(); defaultButton = null; } buttons.add(new Button(name)); } public void setText(String text) { textPane.setText(text); pack(); } class Button extends JButton implements ActionListener{ public Button(String text){ super(text); Font f = new Font("sansserif", Font.BOLD, 12); setFont(f); setFocusable(false); setActionCommand(text); addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { command = e.getActionCommand(); dismiss(); } } class TextPane extends HTMLTextPane{ public TextPane(){ super(); setFont(new Font("sansserif", Font.PLAIN, 14)); setBorder(BorderFactory.createEmptyBorder(5, 3, 5, 3)); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/guide/Visual.java000066400000000000000000001011161315726130400235430ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /** * */ package org.sikuli.guide; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; import javax.swing.JComponent; import org.sikuli.basics.Debug; import org.sikuli.script.Pattern; import org.sikuli.script.Region; public class Visual extends JComponent implements Cloneable { public enum Layout { TOP, BOTTOM, LEFT, RIGHT, FOLLOWERS, INSIDE, OVER, ORIGIN, CENTER, AROUND }; public boolean hasChanged = false; public int PADDING_X = 4; public int PADDING_Y = 4; // Region targetRegion = null; Visual targetComponent = null; Pattern targetPattern = null; Guide currentGuide = null; public void setGuide(Guide g) { currentGuide = g; } public Region getTarget() { if (targetRegion != null) { return targetRegion; } return getRegion(); } public Visual setScale(float scale) { return this; } public Layout layout = Layout.OVER; public Layout currentLayout = layout; public Visual setLayout(Layout lo) { currentLayout = layout; layout = lo; setLocationRelative(lo); updateComponent(); return this; } public static Color defColor = Color.RED; Color color; public static Color defColorFront = Color.MAGENTA; Color colorFront; public static Color defColorBack = Color.WHITE; Color colorBack; public static Color defColorFrame = Color.BLACK; Color colorFrame; public static Color defColorText = Color.BLACK; Color colorText; /** * set a Java Color for the component
* not all components have all color options * @param all sets the color for all options (which have to be null in this case) * @param front sets the foreground color * @param back sets the background color * @param frame sets the color of the frame * @param text sets the color of the text * @return the component itself for dot-chaining */ public Visual setColors(Color all, Color front, Color back, Color frame, Color text) { if (all != null) { color = all; } if (front != null) { colorFront = front; setForeground(colorFront); } if (back != null) { colorBack = back; setBackground(colorBack); } if (frame != null) { colorFrame = frame; } if (text != null) { colorText = text; } updateComponent(); return this; } /** * mainly for Jython layer: colors given as (r, g, b) integer array * * @param front sets the foreground color * @param back sets the background color * @param frame sets the color of the frame * @param text sets the color of the text * @return the component itself for dot-chaining */ public Visual setColors(int[] front, int[] back, int[] frame, int[] text) { Color cf = null; Color cb = null; Color cr = null; Color ct = null; if (front != null) { cf = new Color(front[0], front[1], front[2]); } if (back != null) { cb = new Color(back[0], back[1], back[2]); } if (frame != null) { cr = new Color(frame[0], frame[1], frame[2]); } if (text != null) { ct = new Color(text[0], text[1], text[2]); } setColors(null, cf, cb, cr, ct); updateComponent(); return this; } /** * set the front and back color * @param color * @return the component itself */ public Visual setColor(Color color) { setColors(null, color, color, null, null); return this; } /** * set the front and back color as (r, g, b) integer array * @return the component itself */ public Visual setColor(int r, int g, int b) { setColor(new Color(r, g, b)); return this; } /** * set the text color * @param color * @return the component itself */ public Visual setTextColor(Color color) { setColors(null, null, null, null, color); return this; } /** * set text color as (r, g, b) integer array * @return the component itself */ public Visual setTextColor(int r, int g, int b) { setTextColor(new Color(r, g, b)); return this; } public static String getColorHex(Color col) { String rgb = Integer.toHexString(col.getRGB()); return rgb.substring(2, rgb.length()).toUpperCase(); } public static int defStroke = 3; int stroke; public Visual setStroke(int stk) { stroke = stk; return this; } public static String defFont = ""; String fontName = ""; public static int defFontSize = 0; int fontSize = 0; public Visual setFont(String font, int fontSize) { if (font != null && !this.fontName.isEmpty()) { this.fontName = font; hasChanged = true; } if (fontSize > 0 && this.fontSize > 0) { this.fontSize = fontSize; hasChanged = true; } if (hasChanged) { hasChanged = false; updateComponent(); } return this; } public Visual setFontSize(int i) { setFont(null, i); return this; } String getStyleString() { String s = "font-size:" + fontSize + "px;color:#" + getColorHex(colorText) + ";background-color:#" + getColorHex(colorBack) + ";padding:3px"; if (!fontName.isEmpty()) { s = "font:" + fontName + ";" + s; } return s; } static int defMaxWidth = 300; int maxWidth; public Visual setMaxWidth(int w) { maxWidth = w; return this; } String text = ""; public String getText() { return text; } public Visual setText(String text) { if (!this.text.isEmpty()) { this.text = text; updateComponent(); } return this; } //
public Visual() { super(); init(); } private void init() { cm = new ComponentMover(); setMovable(false); setActualLocation(0, 0); setActualSize(new Dimension(0, 0)); color = defColor; colorFront = defColorFront; colorBack = defColorBack; colorFrame = defColorFrame; colorText = defColorText; stroke = defStroke; fontName = defFont; fontSize = defFontSize; maxWidth = defMaxWidth; } // private boolean autoLayoutEnabled = false; private boolean autoResizeEnabled = false; private boolean autoMoveEnabled = false; private boolean autoVisibilityEnabled = false; public void setAutoLayoutEnabled(boolean autoLayoutEnabled) { this.autoLayoutEnabled = autoLayoutEnabled; } public boolean isAutoLayoutEnabled() { return autoLayoutEnabled; } public void setAutoResizeEnabled(boolean autoResizeEnabled) { this.autoResizeEnabled = autoResizeEnabled; } public boolean isAutoResizeEnabled() { return autoResizeEnabled; } public void setAutoMoveEnabled(boolean autoMoveEnabled) { this.autoMoveEnabled = autoMoveEnabled; } public boolean isAutoMoveEnabled() { return autoMoveEnabled; } public void setAutoVisibilityEnabled(boolean autoVisibilityEnabled) { this.autoVisibilityEnabled = autoVisibilityEnabled; } public boolean isAutoVisibilityEnabled() { return autoVisibilityEnabled; } // // private Rectangle actualBounds = new Rectangle(); public Rectangle getActualBounds() { return actualBounds; } public Region getRegion() { return Region.create(getBounds()); } public void setActualBounds(Rectangle actualBounds) { this.actualBounds = (Rectangle) actualBounds.clone(); Rectangle paintBounds = (Rectangle) actualBounds.clone(); if (hasShadow()) { paintBounds.x -= (shadowSize - shadowOffset); paintBounds.y -= (shadowSize - shadowOffset); paintBounds.width += (2 * shadowSize); paintBounds.height += (2 * shadowSize); } super.setBounds(paintBounds); updateAllFollowers(); } public Point getCenter() { Point loc = new Point(getActualLocation()); Dimension size = getActualSize(); loc.x += size.width / 2; loc.y += size.height / 2; return loc; } public Dimension getActualSize() { return new Dimension(getActualWidth(), getActualHeight()); } public void setActualSize(int width, int height) { if (height == 0) { setActualSize(new Dimension(width, getActualHeight())); } else if (width == 0) { setActualSize(new Dimension(getActualWidth(), height)); } else { setActualSize(new Dimension(width, height)); } } public void setActualSize(Dimension actualSize) { actualBounds.setSize(actualSize); Dimension paintSize = (Dimension) actualSize.clone(); if (hasShadow()) { paintSize.width += (2 * shadowSize); paintSize.height += (2 * shadowSize); } super.setSize(paintSize); updateAllFollowers(); } public int getActualWidth() { return getActualBounds().width; } public int getActualHeight() { return getActualBounds().height; } public Point getActualLocation() { return actualBounds.getLocation(); } public void offsetLocation(int x, int y) { setActualLocation(getActualLocation().x + x, getActualLocation().y + y); } public void setActualLocation(Point location) { setActualLocation(location.x, location.y); } public void setActualLocation(int x, int y) { int paintX = x; int paintY = y; actualBounds.setLocation(x, y); if (hasShadow()) { paintX -= (shadowSize - shadowOffset); paintY -= (shadowSize - shadowOffset); } super.setLocation(paintX, paintY); updateAllFollowers(); } class Margin { int top; int left; int bottom; int right; } Margin margin = null; public void setMargin(int top, int left, int bottom, int right) { margin = new Margin(); margin.top = top; margin.left = left; margin.bottom = bottom; margin.right = right; } int offsetx = 0; int offsety = 0; public void setOffset(int offsetx, int offsety) { this.offsetx = offsetx; this.offsety = offsety; offsetLocation(offsetx, offsety); } // TODO: fix this float zoomLevel = 1.0f; public void setZoomLevel(float zoomLevel) { if (true) { return; } this.zoomLevel = zoomLevel; for (Visual sklComp : getFollowers()) { if (sklComp.autolayout != null) { sklComp.setZoomLevel(zoomLevel); } } Debug.info("[setZoomLevel] Component:" + this); // Debug.info("Actual bounds:" + actualBounds); Rectangle bounds = new Rectangle(getActualBounds()); bounds.x *= zoomLevel; bounds.y *= zoomLevel; bounds.width *= zoomLevel; bounds.height *= zoomLevel; //super.setBounds(bounds); super.setBounds(bounds); for (Visual sklComp : getFollowers()) { if (sklComp.autolayout != null) { Debug.info("Updaing by offset:" + sklComp.autolayout); Debug.info("Updaing child:" + sklComp); if (sklComp.autolayout instanceof AutoLayoutByMovement) { ((AutoLayoutByMovement) sklComp.autolayout).x = bounds.x; ((AutoLayoutByMovement) sklComp.autolayout).y = bounds.y; } else if (sklComp.autolayout instanceof AutoLayoutByOffset) { // ((AutoLayoutByOffset) sklComp.autolayout).offsetx *= zoomLevel; // ((AutoLayoutByOffset) sklComp.autolayout).offsety *= zoomLevel; // sklComp.zoomLevel = zoomLevel; sklComp.autolayout.update(); } else { sklComp.autolayout.update(); } } } } // this allows the component to be dragged to another location on the screen ComponentMover cm; public void setMovable(boolean movable) { if (movable) { cm.registerComponent(this); } else { cm.deregisterComponent(this); } } float opacity = 1.0f; // // public void setOpacity(float opacity) { if (opacity > 0) { setVisible(true); } else { setVisible(false); } this.opacity = opacity; for (Visual sklComp : getFollowers()) { sklComp.setOpacity(opacity); } // if (shadowRenderer != null){ // shadowRenderer.createShadowImage(); // } Rectangle r = getBounds(); if (getTopLevelAncestor() != null) //getTopLevelAncestor().repaint(r.x,r.y,r.width,r.height); // for some reason the whole thing needs to be repainted otherwise the // shadow of other compoments looks weird { getTopLevelAncestor().repaint(); } // public void changeOpacityTo(float targetOpacity){ // OpacityAnimator anim = new OpacityAnimator(this, opacity,targetOpacity); // anim.start(); // } } public void updateComponent() { } public void paintPlain(Graphics g) { super.paint(g); } @Override public void paint(Graphics g) { BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); if (shadowRenderer != null) { shadowRenderer.paintComponent(g2); g2.translate((shadowSize - shadowOffset), (shadowSize - shadowOffset)); } super.paint(g2); Graphics2D g2d = (Graphics2D) g; ((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity)); g2d.drawImage(image, 0, 0, null, null); } // // ShadowRenderer shadowRenderer; private int defShadowSize = 10; int shadowSize = 0; private int defShadowOffset = 2; int shadowOffset = 0; public void setShadowDefault() { setShadow(defShadowSize, defShadowOffset); } public void setShadow(int shadowSize, int shadowOffset) { this.shadowSize = shadowSize; this.shadowOffset = shadowOffset; shadowRenderer = new ShadowRenderer(this, shadowSize); super.setSize(getActualWidth() + 2 * shadowSize, getActualHeight() + 2 * shadowSize); Point p = getActualLocation(); p.x = p.x - shadowSize + shadowOffset; p.y = p.y - shadowSize + shadowOffset; super.setLocation(p.x, p.y); } boolean hasShadow() { return shadowRenderer != null; } // // boolean animationRunning = false; class AnimationSequence { Queue queue = new LinkedBlockingQueue(); private void startNextAnimation() { if (queue.peek() != null) { NewAnimator anim = queue.remove(); anim.start(); anim.setListener(new AnimationListener() { @Override public void animationCompleted() { startNextAnimation(); } }); } } public void add(NewAnimator animator) { queue.add(animator); } public void start() { startNextAnimation(); } } AnimationSequence animationSequence = new AnimationSequence(); AnimationFactory getAnimationFactory() { return new AnimationFactory(); } public void addAnimation(NewAnimator anim) { animationSequence.add(anim); } public void addMoveAnimation(Point source, Point destination) { animationSequence.add(AnimationFactory.createMoveAnimation(this, source, destination)); } public void addResizeAnimation(Dimension currentSize, Dimension targetSize) { animationSequence.add(AnimationFactory.createResizeAnimation(this, currentSize, targetSize)); } public void addCircleAnimation(Point origin, float radius) { animationSequence.add(AnimationFactory.createCircleAnimation(this, origin, radius)); } public void addFadeinAnimation() { if (opacity < 1f) { animationSequence.add(AnimationFactory.createOpacityAnimation(this, opacity, 1f)); } } public void addFadeoutAnimation() { if (opacity > 0f) { animationSequence.add(AnimationFactory.createOpacityAnimation(this, opacity, 0f)); } } public void addSlideAnimation(Point destination, Layout side) { Point p0 = new Point(destination); Point p1 = new Point(destination); if (side == Layout.RIGHT) { p0.x += 20; } else if (side == Layout.BOTTOM) { p0.y += 20; } else if (side == Layout.TOP) { p0.y -= 20; } else if (side == Layout.LEFT) { p0.x -= 20; } setActualLocation(p0); addMoveAnimation(p0, p1); } public void startAnimation() { animationSequence.start(); } public void stopAnimation() { if (emphasis_anim != null) { emphasis_anim.stop(); } if (entrance_anim != null) { entrance_anim.stop(); } } public Animator createSlidingAnimator(int offset_x, int offset_y) { Point dest = getActualLocation(); Point src = new Point(dest.x + offset_x, dest.y + offset_y); return new MoveAnimator(this, src, dest); } public Animator createMoveAnimator(int dest_x, int dest_y) { Point src = getActualLocation(); Point dest = new Point(dest_x, dest_y); return new MoveAnimator(this, src, dest); } public void resizeTo(Dimension targetSize) { //ResizeAnimator anim = new ResizeAnimator(this, getActualSize(),targetSize); //anim.start(); } public void moveTo(Point targetLocation) { NewAnimator anim = AnimationFactory.createCenteredMoveAnimation(this, getActualLocation(), targetLocation); anim.start(); } public void moveTo(Point targetLocation, AnimationListener listener) { NewAnimator anim = AnimationFactory.createCenteredMoveAnimation(this, getActualLocation(), targetLocation); anim.setListener(listener); anim.start(); } public void popin() { Dimension targetSize = new Dimension(getActualSize()); targetSize.width /= 1.2; targetSize.height /= 1.2; NewAnimator anim = AnimationFactory.createCenteredResizeToAnimation(this, targetSize); anim.start(); } public void popout() { setShadowDefault(); Dimension targetSize = new Dimension(getActualSize()); targetSize.width *= 1.2; targetSize.height *= 1.2; NewAnimator anim = AnimationFactory.createCenteredResizeToAnimation(this, targetSize); anim.start(); } Animator entrance_anim; Animator emphasis_anim; public void setEntranceAnimation(Animator anim) { if (entrance_anim != null) { entrance_anim.stop(); } else { entrance_anim = anim; } } public void setEmphasisAnimation(Animator anim) { if (emphasis_anim != null) { emphasis_anim.stop(); } if (entrance_anim != null) { entrance_anim.stop(); } emphasis_anim = anim; } AnimationListener animationListener; public void addAnimationListener(AnimationListener listener) { animationListener = listener; } public void animationCompleted() { if (animationListener != null) { animationListener.animationCompleted(); } } // // public Visual left() { left(0); return this; } public Visual left(int x) { setLayout(Layout.LEFT); if (x != 0) { setOffset(-x, 0); } return this; } public Visual right() { return right(0); } public Visual right(int x) { setLayout(Layout.RIGHT); if (x != 0) { setOffset(x, 0); } return this; } public Visual above() { return above(0); } public Visual above(int y) { setLayout(Layout.TOP); if (y != 0) { setOffset(0, -y); } return this; } public Visual below() { return below(0); } public Visual below(int y) { setLayout(Layout.BOTTOM); if (y != 0) { setOffset(0, y); } return this; } // // AutoLayout autolayout = null; class AutoLayout implements ComponentListener { private Visual targetComponent; AutoLayout(Visual targetComponent) { this.setTargetComponent(targetComponent); //targetComponent.addComponentListener(this); } public void setTargetComponent(Visual targetComponent) { this.targetComponent = targetComponent; } public Visual getTargetComponent() { return targetComponent; } void update() { //Debug.info("Update caused by leader:" + this); // TODO calculate necesary region to udpate // if (getParent()!=null){ // // if (getParent().getParent()!=null){ // getParent().getParent().repaint(); // }else{ // getParent().repaint(); // } // } } void stop() { // targetComponent.removeComponentListener(this); } @Override public void componentHidden(ComponentEvent e) { // if (isAutoVisibilityEnabled()){ // setVisible(false); // update(); // } } @Override public void componentMoved(ComponentEvent e) { // if (isAutoMoveEnabled()) // update(); } @Override public void componentResized(ComponentEvent e) { // if (isAutoResizeEnabled()) // update(); } @Override public void componentShown(ComponentEvent e) { // if (isAutoVisibilityEnabled()){ // setVisible(true); // update(); // } } } class AutoLayoutBySide extends AutoLayout { Layout side; AutoLayoutBySide(Visual targetComponent, Layout side) { super(targetComponent); this.side = side; } @Override void update() { if (side == Layout.FOLLOWERS) { // set to the total bounds of the other followers // first set its bounds to be equal to the targets, so that // its current bounds won't have effect on the calculation // of the total bounds setBounds(getTargetComponent().getBounds()); // then this call will gives us the total bounds of the // rest of the followers Rectangle totalBounds = getTargetComponent().getFollowerBounds(); totalBounds.grow(5, 5); setBounds(totalBounds); } else { setLocationRelativeToRegion(getTargetComponent().getRegion(), side); } super.update(); } } class AutoLayoutByMovement extends AutoLayout { // previous known location of the target this component follows int x; int y; Point targetLocation; AutoLayoutByMovement(Visual targetComponent) { super(targetComponent); targetLocation = new Point(targetComponent.getActualLocation()); this.x = targetComponent.getX(); this.y = targetComponent.getY(); } @Override public void update() { //Debug.info("auto moved by leader"); Point newTargetLocation = getTargetComponent().getActualLocation(); int dx = newTargetLocation.x - targetLocation.x; int dy = newTargetLocation.y - targetLocation.y; targetLocation = newTargetLocation; Point actualLocation = getActualLocation(); actualLocation.x += dx; actualLocation.y += dy; setActualLocation(actualLocation.x, actualLocation.y); } } class AutoLayoutByOffset extends AutoLayout { int offsetx; int offsety; AutoLayoutByOffset(Visual targetComponent, int offsetx, int offsety) { super(targetComponent); this.offsetx = offsetx; this.offsety = offsety; } @Override void update() { setOffset(offsetx, offsety); Region region = new Region(leader.getBounds()); setLocationRelativeToRegion(region, Layout.ORIGIN); super.update(); } } class AutoLayoutByRatio extends AutoLayout { float x, y; AutoLayoutByRatio(Visual targetComponent, float x, float y) { super(targetComponent); this.x = x; this.y = y; } @Override void update() { Region region = new Region(getTargetComponent().getBounds()); setHorizontalAlignmentWithRegion(region, x); setVerticalAlignmentWithRegion(region, y); super.update(); } } // // public Visual setTarget(RCPS target) { if (target instanceof Region) { targetRegion = (Region) target; } else if (target instanceof Visual) { targetComponent = (Visual) target; } else if (target instanceof Pattern) { targetPattern = (Pattern) target; } else if (target instanceof String) { targetPattern = new Pattern((String) target); } if (targetPattern != null) { targetComponent = new SxAnchor(targetPattern); currentGuide.addToFront(targetComponent); setLayout(layout); } updateComponent(); return this; } public Visual setLocationRelative(Layout side) { if (targetRegion != null) { setLocationRelativeToRegion(targetRegion, side); } else if (targetComponent != null) { setLocationRelativeToComponent(targetComponent, side); } return this; } public void setLocationRelativeToComponent(Visual comp, Layout side) { if (autolayout != null) { autolayout.stop(); } comp.addFollower(this); autolayout = new AutoLayoutBySide(comp, side); autolayout.update(); } public void setLocationRelativeToComponent(Visual comp, int offsetx, int offsety) { if (autolayout != null) { autolayout.stop(); } comp.addFollower(this); autolayout = new AutoLayoutByOffset(comp, offsetx, offsety); autolayout.update(); } public void setLocationRelativeToComponent(Visual comp, float relativeX, float relativeY) { if (autolayout != null) { autolayout.stop(); } autolayout = new AutoLayoutByRatio(comp, relativeX, relativeY); autolayout.update(); } public void setLocationRelativeToComponent(Visual leader) { if (autolayout != null) { autolayout.stop(); } leader.addFollower(this); autolayout = new AutoLayoutByMovement(leader); autolayout.update(); } public void setLocationRelativeToPoint(Point point, Layout side) { Rectangle bounds = getActualBounds(); // TODO implement other positioning parameters if (side == Layout.CENTER) { setActualLocation(point.x - bounds.width / 2, point.y - bounds.height / 2); } } public Visual setLocationRelativeToRegion(Region region, Layout side) { if (margin != null) { Region rectWithSpacing = new Region(region); rectWithSpacing.x -= margin.left; rectWithSpacing.y -= margin.top; rectWithSpacing.w += (margin.left + margin.right); rectWithSpacing.h += (margin.top + margin.bottom); region = rectWithSpacing; } region.x += offsetx; region.y += offsety; targetRegion = region; layout = side; int height = getActualHeight(); int width = getActualWidth(); if (side == Layout.TOP) { setActualLocation(region.x + region.w / 2 - width / 2, region.y - height); } else if (side == Layout.BOTTOM) { setActualLocation(region.x + region.w / 2 - width / 2, region.y + region.h); } else if (side == Layout.LEFT) { setActualLocation(region.x - width, region.y + region.h / 2 - height / 2); } else if (side == Layout.RIGHT) { setActualLocation(region.x + region.w, region.y + region.h / 2 - height / 2); } else if (side == Layout.INSIDE) { setActualLocation(region.x + region.w / 2 - width / 2, region.y + region.h / 2 - height / 2); } else if (side == Layout.OVER) { setActualBounds(region.getRect()); } else if (side == Layout.ORIGIN) { setActualLocation(region.x, region.y); } return this; } public void setHorizontalAlignmentWithRegion(Region region, float f) { int x0 = region.x; int x1 = region.x + region.w - getActualWidth(); int x = (int) (x0 + (x1 - x0) * f); setActualLocation(x, getActualLocation().y); } public void setVerticalAlignmentWithRegion(Region region, float f) { int y0 = region.y; int y1 = region.y + region.h - getActualHeight(); int y = (int) (y0 + (y1 - y0) * f); setActualLocation(getActualLocation().x, y); } // // private ArrayList followers = new ArrayList(); Visual leader; public void removeFromLeader() { if (leader != null) { leader.removeFollower(this); } leader = null; } public void addFollower(Visual sklComp) { // force the follower to have the same visibility sklComp.setVisible(isVisible()); sklComp.setOpacity(opacity); if (followers.indexOf(sklComp) < 0) { // if this component is not already a follower // add it to the list of follower followers.add(sklComp); // remove its previous leader sklComp.removeFromLeader(); // set its new leader to self sklComp.leader = this; } } private void updateAllFollowers() { for (Visual sklComp : getFollowers()) { if (sklComp.autolayout != null) { sklComp.autolayout.update(); } } } @Override public void setVisible(boolean visible) { for (Visual follower : getFollowers()) { follower.setVisible(visible); } super.setVisible(visible); } // // @Override // public void setLocation(Point location){ // setLocation(location.x, location.y); // } // @Override // public void setLocation(int x, int y){ // //// if (shadowRenderer != null){ //// x -= 8; //// y -= 8; //// } // // getActualBounds().x = (int) (x/zoomLevel); // getActualBounds().y = (int) (y/zoomLevel); // // super.setLocation(x,y); // updateAllFollowers(); // } // // @Override // public void setBounds(int x, int y, int w, int h){ // // Rectangle bounds = new Rectangle(x,y,w,h); // // actualBounds = new Rectangle(bounds); // actualBounds.x /= zoomLevel; // actualBounds.y /= zoomLevel; // actualBounds.width /= zoomLevel; // actualBounds.height /= zoomLevel; // // for (Visual sklComp : getFollowers()){ // if (sklComp.autolayout != null){ // sklComp.autolayout.update(); // } // } // super.setBounds(x,y,w,h); // } // @Override // public void setBounds(Rectangle bounds){ // // setActualBounds(new Rectangle(bounds)); // getActualBounds().x /= zoomLevel; // getActualBounds().y /= zoomLevel; // getActualBounds().width /= zoomLevel; // getActualBounds().height /= zoomLevel; // // super.setBounds(bounds); // updateAllFollowers(); // } // @Override // public void setSize(int width, int height){ // getActualBounds().width = (int) (width/zoomLevel); // getActualBounds().height = (int) (height/zoomLevel); // // if (hasShadow()){ // width += 20; // height += 20; // } // // super.setSize(width, height); //// updateAllFollowers(); // } // // @Override // public void setSize(Dimension size){ //// getActualBounds().width = (int) (size.width/zoomLevel); //// getActualBounds().height = (int) (size.height/zoomLevel); // //// if (hasShadow()){ //// size.width += 20; //// size.height += 20; //// } // // super.setSize(size); //// updateAllFollowers(); // } // public ArrayList getFollowers() { return followers; } public Visual getLeader() { return leader; } public void removeFollower(Visual comp) { followers.remove(comp); } public Rectangle getFollowerBounds() { // find the total bounds of all the components Rectangle bounds = new Rectangle(getBounds()); for (Visual sklComp : getFollowers()) { bounds.add(sklComp.getBounds()); } return bounds; } // public void removeFrom(Container container) { for (Visual follower : getFollowers()) { follower.removeFrom(container); } container.remove(this); } @Override public String toString() { return "" + getClass() + " " + "[actualBounds=" + getActualBounds() + "]"; } @Override public Object clone() { Visual clone; try { clone = (Visual) super.clone(); // do not clone references to other components clone.followers = new ArrayList(); clone.removeFromLeader(); clone.actualBounds = new Rectangle(actualBounds); clone.autolayout = null; //clone.connectors = new ArrayList(); return clone; } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/natives/000077500000000000000000000000001315726130400220115ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/natives/CommandExecutorException.java000066400000000000000000000027471315726130400276420ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.natives; /** * Wrapper for all Exception which occurs during native command execution. * * @author tschneck * Date: 9/15/15 */ public class CommandExecutorException extends RuntimeException { private final CommandExecutorResult commandExecutorResult; public CommandExecutorException(String message) { super(message); this.commandExecutorResult = null; } public CommandExecutorException(String message, CommandExecutorResult commandExecutorResult) { super(message); this.commandExecutorResult = commandExecutorResult; } public CommandExecutorResult getCommandExecutorResult() { return commandExecutorResult; } @Override public String getMessage() { StringBuilder sb = new StringBuilder("[error] "); if (super.getMessage() != null) { sb.append(super.getMessage()); } if (commandExecutorResult != null) { String stout = commandExecutorResult.getStandardOutput(); if (stout != null && !stout.isEmpty()) { sb.append("\n[stout] ").append(stout); } String errorOutput = commandExecutorResult.getErrorOutput(); if (errorOutput != null && !errorOutput.isEmpty()) { sb.append("\n[errout] ").append(errorOutput); } } return sb.toString(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/natives/CommandExecutorHelper.java000066400000000000000000000027711315726130400271200ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.natives; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteException; import org.apache.commons.exec.PumpStreamHandler; import java.io.ByteArrayOutputStream; public class CommandExecutorHelper { public static CommandExecutorResult execute(String commandString, int expectedExitValue) throws Exception { ByteArrayOutputStream error = new ByteArrayOutputStream(); ByteArrayOutputStream stout = new ByteArrayOutputStream(); CommandLine cmd = CommandLine.parse(commandString); try { DefaultExecutor executor = new DefaultExecutor(); executor.setExitValue(expectedExitValue); executor.setStreamHandler(new PumpStreamHandler(stout, error)); //if exit value != expectedExitValue => Exception int exitValue = executor.execute(cmd); return new CommandExecutorResult(exitValue, stout.toString(), error.toString()); } catch (Exception e) { int exitValue = -1; if (e instanceof ExecuteException) { exitValue = ((ExecuteException) e).getExitValue(); } throw new CommandExecutorException( "error in command " + cmd.toString(), new CommandExecutorResult(exitValue, stout.toString(), error.toString())); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/natives/CommandExecutorResult.java000066400000000000000000000012711315726130400271510ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.natives; public class CommandExecutorResult { private int exitValue; private String errorOutput; private String standardOutput; public CommandExecutorResult(int exitValue, String standardOutput, String errorOutput) { this.exitValue = exitValue; this.errorOutput = errorOutput; this.standardOutput = standardOutput; } public int getExitValue() { return exitValue; } public String getErrorOutput() { return errorOutput; } public String getStandardOutput() { return standardOutput; } } sikulix-1.1.1/API/src/main/java/org/sikuli/natives/LinuxUtil.java000066400000000000000000000255651315726130400246260ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.natives; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteException; import org.apache.commons.exec.PumpStreamHandler; import org.sikuli.basics.Debug; import org.sikuli.script.App; import java.awt.*; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Arrays; import java.util.List; import java.util.Map; public class LinuxUtil implements OSUtil { private static boolean wmctrlAvail = true; private static boolean xdoToolAvail = true; @Override public void checkFeatureAvailability() { List commands = Arrays.asList( CommandLine.parse("wmctrl -m"), CommandLine.parse("xdotool version") ); for (CommandLine cmd : commands) { String executable = cmd.toStrings()[0]; try { DefaultExecutor executor = new DefaultExecutor(); // other return values throw exception executor.setExitValue(0); //suppress system output executor.setStreamHandler(new PumpStreamHandler(null)); executor.execute(cmd); } catch (ExecuteException e) { // it ran, but exited with non-zero status -- accept Debug.info("App: command %s ran, but failed: `%s'. Hoping for the best", executable, e.toString()); } catch (Exception e) { if (executable.equals("wmctrl")) { wmctrlAvail = false; } if (executable.equals("xdotool")) { xdoToolAvail = false; } Debug.error("App: command %s is not executable, the App features will not work", executable); } } } private boolean isAvailable(boolean module, String cmd, String feature) { if (module) { return true; } Debug.error("%s: feature %s: not available or not working", cmd, feature); return false; } @Override public App.AppEntry getApp(int appPID, String appName) { return new App.AppEntry(appName, "" + appPID, "", "", ""); } @Override public int isRunning(App.AppEntry app) { return -1; } @Override public int open(String appName) { try { String cmd[] = {"sh", "-c", "(" + appName + ") &\necho -n $!"}; Process p = Runtime.getRuntime().exec(cmd); InputStream in = p.getInputStream(); byte pidBytes[] = new byte[64]; int len = in.read(pidBytes); String pidStr = new String(pidBytes, 0, len); int pid = Integer.parseInt(pidStr); p.waitFor(); return pid; //return p.exitValue(); } catch (Exception e) { System.out.println("[error] openApp:\n" + e.getMessage()); return 0; } } @Override public int open(App.AppEntry app) { return open(app.execName); } @Override public int switchto(String appName, int winNum) { int windowPID = findWindowPID(appName, winNum); if (windowPID > 1) { return switchto(windowPID, winNum); } System.err.println("[error] switchApp: could not identify process with search name '" + appName + "'"); return -1; } @Override public int switchto(String appName) { return switchto(appName, 0); } @Override public int switchto(App.AppEntry app, int num) { if (app.pid > 0) { return switchto(app.pid, num); } return switchto(app.execName, num); } @Override public int close(String appName) { try { //on the success exit value = 0 -> so no exception will be thrown CommandExecutorResult result1 = CommandExecutorHelper.execute("pidof " + appName, 0); String pid = result1.getStandardOutput(); if (pid == null || pid.isEmpty()) { throw new CommandExecutorException("No app could be found with Name '" + appName + "'"); } //use kill incase that killall could maybe not work in all environments return CommandExecutorHelper.execute("kill " + pid, 0).getExitValue(); } catch (Exception e) { //try to search for the appName Integer windowPID = findWindowPID(appName, 1); if (windowPID > 1) { try { return CommandExecutorHelper.execute("kill " + windowPID.toString(), 0).getExitValue(); } catch (Exception e1) { e.addSuppressed(e1); } } System.out.println("[error] closeApp:\n" + e.getMessage()); return -1; } } @Override public int close(App.AppEntry app) { if (app.pid > 0) { return close(app.pid); } return close(app.execName); } @Override public Map getApps(String name) { return null; } @Override public Rectangle getFocusedWindow() { if (!isAvailable(xdoToolAvail, "getFocusedWindow", "xdoTool")) { return null; } String cmd[] = {"xdotool", "getactivewindow"}; try { Process p = Runtime.getRuntime().exec(cmd); InputStream in = p.getInputStream(); BufferedReader bufin = new BufferedReader(new InputStreamReader(in)); String str = bufin.readLine(); long id = Integer.parseInt(str); String hexid = String.format("0x%08x", id); return findRegion(hexid, 0, SearchType.WINDOW_ID); } catch (IOException e) { System.out.println("[error] getFocusedWindow:\n" + e.getMessage()); return null; } } @Override public Rectangle getWindow(String appName) { return getWindow(appName, 0); } private Rectangle findRegion(String appName, int winNum, SearchType type) { String[] winLine = findWindow(appName, winNum, type); if (winLine != null && winLine.length >= 7) { int x = new Integer(winLine[3]); int y = Integer.parseInt(winLine[4]); int w = Integer.parseInt(winLine[5]); int h = Integer.parseInt(winLine[6]); return new Rectangle(x, y, w, h); } return null; } private String[] findWindow(String appName, int winNum, SearchType type) { String[] found = {}; int numFound = 0; try { CommandExecutorResult result = CommandExecutorHelper.execute("wmctrl -lpGx", 0); int slash = appName.lastIndexOf("/"); if (slash >= 0) { // remove path: /usr/bin/.... appName = appName.substring(slash + 1); } if (type == SearchType.APP_NAME) { appName = appName.toLowerCase(); } String[] lines = result.getStandardOutput().split("\\n"); for (String str : lines) { //Debug.log("read: " + str); String winLine[] = str.split("\\s+"); boolean ok = false; if (type == SearchType.WINDOW_ID) { if (appName.equals(winLine[0])) { ok = true; } } else if (type == SearchType.PID) { if (appName.equals(winLine[2])) { ok = true; } } else if (type == SearchType.APP_NAME) { String winLineName = winLine[9].toLowerCase(); if (appName.equals(winLineName)) { ok = true; } if (!ok && winLine[7].toLowerCase().contains(appName)) { ok = true; } } if (ok) { if (numFound >= winNum) { //Debug.log("Found window" + winLine); found = winLine; break; } numFound++; } } } catch (Exception e) { System.out.println("[error] findWindow:\n" + e.getMessage()); return null; } return found; } /** * Returns a PID of the givenAppname and the winNumber * * @param appName * @param winNum * @return the PID or -1 on errors */ protected int findWindowPID(String appName, int winNum) { String[] window = findWindow(appName, winNum, SearchType.APP_NAME); if (window != null && window.length > 1) { return Integer.parseInt(window[2]); } return -1; } @Override public Rectangle getWindow(String appName, int winNum) { return findRegion(appName, winNum, SearchType.APP_NAME); } @Override public Rectangle getWindow(int pid) { return getWindow(pid, 0); } @Override public Rectangle getWindow(int pid, int winNum) { return findRegion("" + pid, winNum, SearchType.PID); } @Override public int close(int pid) { if (!isAvailable(wmctrlAvail, "closeApp", "wmctrl")) { return -1; } String winLine[] = findWindow("" + pid, 0, SearchType.PID); if (winLine == null) { return -1; } String cmd[] = {"wmctrl", "-ic", winLine[0]}; try { Process p = Runtime.getRuntime().exec(cmd); p.waitFor(); return p.exitValue(); } catch (Exception e) { System.out.println("[error] closeApp:\n" + e.getMessage()); return -1; } } @Override public int switchto(int pid, int num) { if (!isAvailable(wmctrlAvail, "switchApp", "wmctrl")) { return -1; } String winLine[] = findWindow("" + pid, num, SearchType.PID); if (winLine == null || winLine.length < 1) { System.err.println("[error] switchApp: window of PID '" + pid + "' couldn't be found!"); return -1; } try { // execute wmctrl with hex, e.g. 'wmctrl -ia 0x00000' CommandExecutorHelper.execute("wmctrl -ia " + winLine[0], 0); //on the success exit value = 0 -> so no exception will be thrown return pid; } catch (Exception e) { e.printStackTrace(); System.err.println("[error] switchApp:\n" + e.getMessage()); return -1; } } @Override public void bringWindowToFront(Window win, boolean ignoreMouse) { } private enum SearchType { APP_NAME, WINDOW_ID, PID } } sikulix-1.1.1/API/src/main/java/org/sikuli/natives/MacUtil.java000066400000000000000000000213141315726130400242130ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.natives; import org.sikuli.script.App; import org.sikuli.script.RunTime; import org.sikuli.script.Runner; import javax.swing.*; import java.awt.*; import java.util.Map; public class MacUtil implements OSUtil { private static boolean _askedToEnableAX = false; private String usedFeature; private static RunTime runTime = null; @Override public void checkFeatureAvailability() { runTime = RunTime.get(); RunTime.loadLibrary("MacUtil"); } /* tell application "System Events" set found to "NotFound" try set found to first item of (processes whose name is "#APP#") set found to first item of (processes whose unix id is equal to #PID#) end try found end tell if not found is equal to "NotFound" then set windowName to "" try set windowName to name of first window of application "#APP#" end try set found to {name of found, «class idux» of found, windowName} end if found */ static String cmd = "tell application \"System Events\"\n" + "set found to \"NotFound\"\n" + "try\n" + "#LINE#\n" + "end try\n" + "end tell\n" + "if not found is equal to \"NotFound\" then\n" + "set windowName to \"\"\n" + "try\n" + "set windowName to name of first window of application (name of found)\n" + "end try\n" + "set found to {name of found, «class idux» of found, windowName}\n" + "end if\n" + "found\n"; static String cmdLineApp = "set found to first item of (processes whose name is \"#APP#\")"; static String cmdLinePID = "set found to first item of (processes whose unix id is equal to #PID#)"; @Override public App.AppEntry getApp(int appPID, String appName) { App.AppEntry app = null; String name = ""; String theCmd = ""; int pid = -1; Object filter; if (appPID < 0) { filter = appName; } else { filter = appPID; } if (filter instanceof String) { name = (String) filter; theCmd = cmd.replace("#LINE#", cmdLineApp); theCmd = theCmd.replaceAll("#APP#", name); } else if (filter instanceof Integer) { pid = (Integer) filter; theCmd = cmd.replace("#LINE#", cmdLinePID); theCmd = theCmd.replaceAll("#PID#", "" + pid); } else { return app; } int retVal = Runner.runas(theCmd, true); String result = RunTime.get().getLastCommandResult(); String title = "???"; String sPid = "-1"; String sName = "NotKnown"; if (retVal > -1) { if (!result.contains("NotFound")) { String[] parts = result.split(","); if (parts.length > 1) { sName = parts[0]; sPid = parts[1]; } if (parts.length > 2) { title = parts[2]; } if (parts.length > 3) { for (int i = 3; i < parts.length; i++) { title += "," + parts[i]; } } app = new App.AppEntry(sName.trim(), sPid.trim(), title.trim(), "", ""); } } return app; } @Override public int isRunning(App.AppEntry app) { if (app.pid > 0) { return 1; } if (app.name.isEmpty()) { return -1; } if (getWindow(app.name, 0) != null) { return 1; } App.AppEntry ae = getApp(app.pid, app.name); if (ae != null && ae.pid > 0) { return 1; } return 0; } @Override public int open(String appName) { if (_openApp(appName)) { return 0; } return -1; } @Override public int open(App.AppEntry app) { String appName = app.execName.startsWith(app.name) ? app.name : app.execName; int retval = 0; if (runTime.osVersion.startsWith("10.10.")) { if (Runner.runas(String.format("tell app \"%s\" to activate", appName), true) != 0) { retval = -1; } } else { retval = open(appName); } if (retval == 0) { retval = getPID(appName); } return retval; } @Override public int switchto(String appName) { return open(appName); } @Override public int switchto(int pid, int num) { return -1; } // ignore winNum on Mac @Override public int switchto(String appName, int winNum) { return open(appName); } @Override public int switchto(App.AppEntry app, int num) { String appName = app.execName.startsWith(app.name) ? app.name : app.execName; int retval = 0; if (runTime.osVersion.startsWith("10.10.")) { if (Runner.runas(String.format("tell app \"%s\" to activate", appName), true) < 0) { retval = -1; } } else { retval = open(appName); } if (retval == 0) { retval = getPID(appName); } return retval; } @Override public int close(String appName) { try { String cmd[] = {"sh", "-c", "ps aux | grep \"" + appName + "\" | awk '{print $2}' | xargs kill"}; Process p = Runtime.getRuntime().exec(cmd); p.waitFor(); return p.exitValue(); } catch (Exception e) { return -1; } } @Override public int close(int pid) { try { String cmd[] = {"sh", "-c", "kill " + pid}; Process p = Runtime.getRuntime().exec(cmd); p.waitFor(); return p.exitValue(); } catch (Exception e) { return -1; } } @Override public int close(App.AppEntry app) { if (app.pid > -1) { return close(app.pid); } String appName = app.execName.startsWith(app.name) ? app.name : app.execName; return close(appName); } private void checkAxEnabled(String name) { if (!System.getProperty("os.name").toLowerCase().startsWith("mac")) { return; } if (Integer.parseInt(System.getProperty("os.version").replace(".", "")) > 108 && !isAxEnabled()) { if (name == null) { JOptionPane.showMessageDialog(null, "This app uses Sikuli feature " + usedFeature + ", which needs\n" + "access to the Mac's assistive device support.\n" + "You have to explicitly allow this in the System Preferences.\n" + "(System Preferences -> Security & Privacy -> Privacy)\n" + "Currently we cannot do this for you.\n\n" + "Be prepared to get some crash after clicking ok.\n" + "Please check the System Preferences and come back.", "SikuliX on Mac Mavericks Special", JOptionPane.PLAIN_MESSAGE); System.out.println("[error] MacUtil: on Mavericks: no access to assistive device support"); } usedFeature = name; return; } if (!isAxEnabled()) { if (_askedToEnableAX) { return; } int ret = JOptionPane.showConfirmDialog(null, "You need to enable Accessibility API to use the function \"" + name + "\".\n" + "Should I open te System Preferences for you?", "Accessibility API not enabled", JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE); if (ret == JOptionPane.YES_OPTION) { openAxSetting(); JOptionPane.showMessageDialog(null, "Check \"Enable access for assistant devices\"" + "in the System Preferences\n and then close this dialog.", "Enable Accessibility API", JOptionPane.INFORMATION_MESSAGE); } _askedToEnableAX = true; } } //Mac Mavericks: delete app entry from list - in terminal on one line //sudo sqlite3 /Library/Application\ Support/com.apple.TCC/Tcc.db //'delete from access where client like "%part of app name%"' @Override public Rectangle getWindow(String appName, int winNum) { checkAxEnabled("getWindow"); int pid = getPID(appName); return getWindow(pid, winNum); } @Override public Rectangle getWindow(String appName) { return getWindow(appName, 0); } @Override public Rectangle getWindow(int pid) { return getWindow(pid, 0); } @Override public Rectangle getWindow(int pid, int winNum) { Rectangle rect = getRegion(pid, winNum); checkAxEnabled(null); return rect; } @Override public Rectangle getFocusedWindow() { checkAxEnabled("getFocusedWindow"); Rectangle rect = getFocusedRegion(); checkAxEnabled(null); return rect; } @Override public native void bringWindowToFront(Window win, boolean ignoreMouse); public static native boolean _openApp(String appName); public static native int getPID(String appName); public static native Rectangle getRegion(int pid, int winNum); public static native Rectangle getFocusedRegion(); public static native boolean isAxEnabled(); public static native void openAxSetting(); @Override public Map getApps(String name) { return null; } } sikulix-1.1.1/API/src/main/java/org/sikuli/natives/OSUtil.java000066400000000000000000000027231315726130400240370ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.natives; import org.sikuli.script.App; import java.awt.*; import java.util.Map; public interface OSUtil { // Windows: returns PID, 0 if fails // Others: return 0 if succeeds, -1 if fails /** * check if needed command libraries or packages are installed and working
* if not ok, respective features will do nothing but issue error messages */ public void checkFeatureAvailability(); public App.AppEntry getApp(int pid, String name); public Map getApps(String name); public int isRunning(App.AppEntry app); public int open(String appName); public int open(App.AppEntry app); // Windows: returns PID, 0 if fails // Others: return 0 if succeeds, -1 if fails public int switchto(String appName); public int switchto(String appName, int winNum); //internal use public int switchto(int pid, int num); public int switchto(App.AppEntry app, int num); // returns 0 if succeeds, -1 if fails public int close(String appName); //internal use public int close(int pid); public int close(App.AppEntry app); public Rectangle getWindow(String appName); public Rectangle getWindow(String appName, int winNum); Rectangle getWindow(int pid); Rectangle getWindow(int pid, int winNum); public Rectangle getFocusedWindow(); public void bringWindowToFront(Window win, boolean ignoreMouse); } sikulix-1.1.1/API/src/main/java/org/sikuli/natives/SysUtil.java000066400000000000000000000020651315726130400242730ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.sikuli.natives; import java.lang.reflect.Constructor; /** * * @author rhocke */ public class SysUtil { static OSUtil osUtil = null; static String getOSUtilClass() { String pkg = "org.sikuli.natives."; String os = System.getProperty("os.name").toLowerCase(); if (os.startsWith("mac")) { return pkg + "MacUtil"; } else if (os.startsWith("windows")) { return pkg + "WinUtil"; } else { return pkg + "LinuxUtil"; } } public static OSUtil getOSUtil() { if (osUtil == null) { try { Class c = Class.forName(SysUtil.getOSUtilClass()); Constructor constr = c.getConstructor(); osUtil = (OSUtil) constr.newInstance(); } catch (Exception e) { System.out.println("[error] fatal: getOSUtil\n" + e.getMessage()); System.exit(1); } } return osUtil; } } sikulix-1.1.1/API/src/main/java/org/sikuli/natives/WinUtil.java000066400000000000000000000212241315726130400242500ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.natives; import org.sikuli.basics.Debug; import org.sikuli.script.App; import org.sikuli.script.Key; import org.sikuli.script.RunTime; import org.sikuli.script.Screen; import java.awt.*; import java.io.File; import java.util.HashMap; import java.util.Map; public class WinUtil implements OSUtil { @Override public void checkFeatureAvailability() { RunTime.loadLibrary("WinUtil"); } @Override public App.AppEntry getApp(int appPID, String appName) { if (appPID == 0) { return null; } App.AppEntry app = null; Object filter; if (appPID < 0) { filter = appName; } else { filter = appPID; } String name = ""; String execName = ""; String options = ""; Integer pid = -1; String[] parts; if (filter instanceof String) { name = (String) filter; if (name.startsWith("!")) { name = name.substring(1); execName = name; } else { if (name.startsWith("\"")) { parts = name.substring(1).split("\""); if (parts.length > 1) { options = name.substring(parts[0].length() + 3); name = "\"" + parts[0] + "\""; } } else { parts = name.split(" "); if (parts.length > 1) { options = name.substring(parts[0].length() + 1); name = parts[0]; } } if (name.startsWith("\"")) { execName = new File(name.substring(1, name.length()-1)).getName().toUpperCase(); } else { execName = new File(name).getName().toUpperCase(); } } } else if (filter instanceof Integer) { pid = (Integer) filter; } else { return app; } Debug.log(3, "WinUtil.getApp: %s", filter); String cmd; if (pid < 0) { cmd = cmd = "!tasklist /V /FO CSV /NH /FI \"SESSIONNAME eq Console\""; } else { cmd = cmd = "!tasklist /V /FO CSV /NH /FI \"PID eq " + pid.toString() + "\""; } String result = RunTime.get().runcmd(cmd); String[] lines = result.split("\r\n"); if ("0".equals(lines[0].trim())) { for (int nl = 1; nl < lines.length; nl++) { parts = lines[nl].split("\""); if (parts.length < 2) { continue; } String theWindow = parts[parts.length - 1]; String theName = parts[1]; String thePID = parts[3]; //Debug.log(3, "WinUtil.getApp: %s:%s(%s)", thePID, theName, theWindow); if (!name.isEmpty()) { if ((theName.toUpperCase().contains(execName) && !theWindow.contains("N/A")) || theWindow.contains(name)) { return new App.AppEntry(theName, thePID, theWindow, "", ""); } } else { try { if (Integer.parseInt(thePID) == pid) { return new App.AppEntry(theName, thePID, theWindow, "", ""); } } catch (Exception ex) { } } } } else { Debug.logp(result); } if (!options.isEmpty()) { return new App.AppEntry(name, "", "", "", options); } if (app == null) { cmd = String.format("!tasklist /V /FO CSV /NH /FI \"IMAGENAME eq %s\"", execName); result = RunTime.get().runcmd(cmd); lines = result.split("\r\n"); if ("0".equals(lines[0].trim())) { for (int nl = 1; nl < lines.length; nl++) { parts = lines[nl].split("\""); if (parts.length < 2) { continue; } String theWindow = parts[parts.length - 1]; String theName = parts[1]; String thePID = parts[3]; if (theWindow.contains("N/A")) continue; app = new App.AppEntry(theName, thePID, theWindow, "", ""); break; } } } return app; } @Override public Map getApps(String name) { Map apps = new HashMap(); String cmd; if (name == null || name.isEmpty()) { cmd = cmd = "!tasklist /V /FO CSV /NH /FI \"SESSIONNAME eq Console\""; } else { cmd = String.format("!tasklist /V /FO CSV /NH /FI \"IMAGENAME eq %s\"", name); } String result = RunTime.get().runcmd(cmd); String[] lines = result.split("\r\n"); if ("0".equals(lines[0].trim())) { for (int nl = 1; nl < lines.length; nl++) { String[] parts = lines[nl].split("\""); if (parts.length < 3) { continue; } String theWindow = parts[parts.length - 1]; String thePID = parts[3]; String theName = parts[1]; Integer pid = -1; try { pid = Integer.parseInt(thePID); } catch (Exception ex) { } if (pid != -1) { if (theWindow.contains("N/A")) { pid = -pid; } apps.put(pid, new String[] {theName, theWindow}); } } } else { Debug.logp(result); } return apps; } @Override public int isRunning(App.AppEntry app) { if (app.pid > 0) { return 1; } if (app.name.isEmpty()) { return -1; } if (getWindow(app.name, 0) != null) { return 1; } App.AppEntry ae = getApp(app.pid, app.name); if (ae != null && ae.pid > 0) { return 1; } return 0; } @Override public int open(String appName) { int pid = openApp(appName); return pid < 1 ? -1 : pid; } @Override public int open(App.AppEntry app) { if (app.pid > -1) { return switchApp(app.pid, 0); } String cmd = app.execName; if (!app.options.isEmpty()) { cmd += " " + app.options; } int pid = openApp(cmd); return pid < 1 ? -1 : pid; } @Override public int switchto(String appName) { return switchApp(appName, 0); } @Override public int switchto(String appName, int winNum) { return switchApp(appName, winNum); } @Override public int switchto(int pid, int num) { return switchApp(pid, num); } @Override public int switchto(App.AppEntry app, int num) { if (app.pid > -1) { String wname = app.window; if (wname.startsWith("!")) { wname = wname.substring(1); } return switchto(wname, 0); } if (app.window.startsWith("!")) { String token = app.window.substring(1); if(!token.isEmpty()) { return switchto(token, 0); } else { App.AppEntry newApp = getApp(app.pid, app.name); if (newApp == null) { return switchto(app.execName, 0); } else { return switchto(newApp.window, 0); } } } return switchto(app.execName, num); } @Override public int close(String appName) { return closeApp(appName); } @Override public int close(int pid) { return closeApp(pid); } @Override public int close(App.AppEntry app) { if (app.pid > -1) { return closeApp(app.pid); } if (app.window.startsWith("!")) { String token = app.window.substring(1); if(!token.isEmpty()) { switchto(app.window.substring(1), 0); RunTime.pause(1); new Screen().type(Key.F4, Key.ALT); return 0; } else { app = getApp(app.pid, app.name); } } if (app != null) { if (app.pid > -1) { return closeApp(app.pid); } else { return closeApp(app.execName.replaceAll("\"", "")); } } else { return -1; } } public native int switchApp(String appName, int num); public native int switchApp(int pid, int num); public native int openApp(String appName); public native int closeApp(String appName); public native int closeApp(int pid); @Override public Rectangle getWindow(String appName) { return getWindow(appName, 0); } @Override public Rectangle getWindow(int pid) { return getWindow(pid, 0); } @Override public Rectangle getWindow(String appName, int winNum) { long hwnd = getHwnd(appName, winNum); return _getWindow(hwnd, winNum); } @Override public Rectangle getWindow(int pid, int winNum) { long hwnd = getHwnd(pid, winNum); return _getWindow(hwnd, winNum); } @Override public Rectangle getFocusedWindow() { Rectangle rect = getFocusedRegion(); return rect; } @Override public native void bringWindowToFront(Window win, boolean ignoreMouse); private static native long getHwnd(String appName, int winNum); private static native long getHwnd(int pid, int winNum); private static native Rectangle getRegion(long hwnd, int winNum); private static native Rectangle getFocusedRegion(); private Rectangle _getWindow(long hwnd, int winNum) { Rectangle rect = getRegion(hwnd, winNum); return rect; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/000077500000000000000000000000001315726130400216445ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/script/App.java000066400000000000000000000636461315726130400232460ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Debug; import org.sikuli.natives.OSUtil; import org.sikuli.natives.SysUtil; import java.awt.*; import java.awt.datatransfer.*; import java.io.*; import java.net.URI; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.Date; import java.util.HashMap; import java.util.Map; //import org.apache.http.HttpEntity; //import org.apache.http.HttpResponse; //import org.apache.http.StatusLine; //import org.apache.http.client.ClientProtocolException; //import org.apache.http.client.HttpResponseException; //import org.apache.http.client.ResponseHandler; //import org.apache.http.client.methods.CloseableHttpResponse; //import org.apache.http.client.methods.HttpGet; //import org.apache.http.impl.client.CloseableHttpClient; //import org.apache.http.impl.client.HttpClients; /** * App implements features to manage (open, switch to, close) applications. on the system we are running on and to * access their assets like windows *
* TAKE CARE: function behavior differs depending on the running system (cosult the docs for more info) */ public class App { static RunTime runTime = RunTime.get(); private static final OSUtil _osUtil = SysUtil.getOSUtil(); private String appNameGiven; private String appOptions; private String appName; private String appWindow; private int appPID; private boolean isImmediate = false; private boolean notFound = false; private static final Map appsWindows; private static final Map appsMac; private static final Region aRegion = new Region(); static { //TODO Sikuli hangs if App is used before Screen new Screen(); _osUtil.checkFeatureAvailability(); appsWindows = new HashMap(); appsWindows.put(Type.EDITOR, "Notepad"); appsWindows.put(Type.BROWSER, "Google Chrome"); appsWindows.put(Type.VIEWER, ""); appsMac = new HashMap(); appsMac.put(Type.EDITOR, "TextEdit"); appsMac.put(Type.BROWSER, "Safari"); appsMac.put(Type.VIEWER, "Preview"); } // // // private static CloseableHttpClient httpclient = null; // // /** // * create a HTTP Client (for use of wwGet, ... multiple times as session) // * @return true on success, false otherwise // */ // public static boolean wwwStart() { // if (httpclient != null) { // return true; // } // httpclient = HttpClients.createDefault(); // if (httpclient != null) { // return true; // } // return false; // } // // /** // * stop a started HTTP Client // */ // public static void wwwStop() { // if (httpclient != null) { // try { // httpclient.close(); // } catch (IOException ex) { // } // httpclient = null; // } // } // // /** // * issue a http(s) request // * @param url a valid url as used in a browser // * @return textual content of the response or empty (UTF-8) // * @throws IOException // */ // public static String wwwGet(String url) throws IOException { // HttpGet httpget = new HttpGet(url); // CloseableHttpResponse response = null; // ResponseHandler rh = new ResponseHandler() { // @Override // public String handleResponse(final HttpResponse response) throws IOException { // StatusLine statusLine = response.getStatusLine(); // HttpEntity entity = response.getEntity(); // if (statusLine.getStatusCode() >= 300) { // throw new HttpResponseException( // statusLine.getStatusCode(), // statusLine.getReasonPhrase()); // } // if (entity == null) { // throw new ClientProtocolException("Response has no content"); // } // InputStream is = entity.getContent(); // ByteArrayOutputStream result = new ByteArrayOutputStream(); // byte[] buffer = new byte[1024]; // int length; // while ((length = is.read(buffer)) != -1) { // result.write(buffer, 0, length); // } // return result.toString("UTF-8"); // } // }; // boolean oneTime = false; // if (httpclient == null) { // wwwStart(); // oneTime = true; // } // Object content = httpclient.execute(httpget, rh); // if (oneTime) { // wwwStop(); // } // return (String) content; // } // // /** // * same as wwwGet(), but the content is also saved to a file // * @param url a valid url as used in a browser // * @param pOut absolute path to output file (overwritten) (if null: bundlePath/wwwSave.txt is taken) // * @return textual content of the response or empty (UTF-8) // * @throws IOException // */ // public static String wwwSave(String url, String pOut) throws IOException { // String content = wwwGet(url); // File out = null; // if (pOut == null) { // out = new File(ImagePath.getBundleFolder(), "wwwSave.txt"); // } else { // out = new File(pOut); // } // FileManager.writeStringToFile(content, out); // return content; // } // // // public static enum Type { EDITOR, BROWSER, VIEWER } public static Region start(Type appType) { App app = null; Region win; try { if (Type.EDITOR.equals(appType)) { if (runTime.runningMac) { app = new App(appsMac.get(appType)); if (app.window() != null) { app.focus(); aRegion.wait(0.5); win = app.window(); aRegion.click(win); aRegion.write("#M.a#B."); return win; } else { app.open(); win = app.waitForWindow(); app.focus(); aRegion.wait(0.5); aRegion.click(win); return win; } } if (runTime.runningWindows) { app = new App(appsWindows.get(appType)); if (app.window() != null) { app.focus(); aRegion.wait(0.5); win = app.window(); aRegion.click(win); aRegion.write("#C.a#B."); return win; } else { app.open(); win = app.waitForWindow(); app.focus(); aRegion.wait(0.5); aRegion.click(win); return win; } } } else if (Type.BROWSER.equals(appType)) { if (runTime.runningWindows) { app = new App(appsWindows.get(appType)); if (app.window() != null) { app.focus(); aRegion.wait(0.5); win = app.window(); aRegion.click(win); // aRegion.write("#C.a#B."); return win; } else { app.open(); win = app.waitForWindow(); app.focus(); aRegion.wait(0.5); aRegion.click(win); return win; } } return null; } else if (Type.VIEWER.equals(appType)) { return null; } } catch (Exception ex) { } return null; } public Region waitForWindow() { return waitForWindow(5); } public Region waitForWindow(int seconds) { Region win = null; while ((win = window()) == null && seconds > 0) { aRegion.wait(0.5); seconds -= 0.5; } return win; } public static boolean openLink(String url) { if (!Desktop.isDesktopSupported()) { return false; } try { Desktop.getDesktop().browse(new URI(url)); } catch (Exception ex) { return false; } return true; } private static Region asRegion(Rectangle r) { if (r != null) { return Region.create(r); } else { return null; } } public static void pause(int time) { try { Thread.sleep(time * 1000); } catch (InterruptedException ex) { } } public static void pause(float time) { try { Thread.sleep((int) (time * 1000)); } catch (InterruptedException ex) { } } // // public static class AppEntry { public String name; public String execName; public String options; public String window; public int pid; public AppEntry(String theName, String thePID, String theWindow, String theExec, String theOptions) { name = theName; window = theWindow; options = theOptions; pid = -1; execName = theExec; try { pid = Integer.parseInt(thePID); } catch (Exception ex) { } } } public AppEntry makeAppEntry() { String name = appName; String window = appWindow; if (name.isEmpty() && appOptions.isEmpty()) { name = appNameGiven; } if (isImmediate && !window.startsWith("!")) { window = "!" + window; } if (notFound) { name = "!" + name; } String pid = getPID().toString(); AppEntry appEntry = new AppEntry(name, pid, window, appNameGiven, appOptions); return appEntry; } // // /** * creates an instance for an app with this name (nothing done yet) * * @param name name */ public App(String name) { appNameGiven = name; appName = name; appPID = -1; appWindow = ""; appOptions = ""; String execName = ""; if (appNameGiven.startsWith("+")) { isImmediate = true; appNameGiven = appNameGiven.substring(1); Debug.log(3, "App.immediate: %s", appNameGiven); appName = appNameGiven; String[] parts; if (appName.startsWith("\"")) { parts = appName.substring(1).split("\""); if (parts.length > 1) { appOptions = appName.substring(parts[0].length() + 3); appName = "\"" + parts[0] + "\""; } } else { parts = appName.split(" "); if (parts.length > 1) { appOptions = appName.substring(parts[0].length() + 1); appName = parts[0]; } } if (appName.startsWith("\"")) { execName = appName.substring(1, appName.length() - 1); } else { execName = appName; } appName = new File(execName).getName(); File checkName = new File(execName); if (checkName.isAbsolute()) { if (!checkName.exists()) { appName = ""; appOptions = ""; appWindow = "!"; notFound = true; } } } else { init(appNameGiven); } Debug.log(3, "App.create: %s", toStringShort()); } private void init(String name) { AppEntry app = null; if (!(isImmediate && notFound)) { app = _osUtil.getApp(-1, name); } if (app != null) { appName = app.name; if (app.options.isEmpty()) { appPID = app.pid; if (!app.window.contains("N/A")) { appWindow = app.window; if (notFound) { notFound = false; } } } else { appOptions = app.options; appNameGiven = appName; } } } public App(int pid) { appNameGiven = "FromPID"; appName = ""; appPID = pid; appWindow = ""; init(pid); } private void init(int pid) { AppEntry app = _osUtil.getApp(pid, appName); if (app != null) { appName = app.name; appPID = app.pid; if (!app.window.contains("N/A")) { appWindow = app.window; } } else { appPID = -1; } } private void init() { if (appPID > -1) { init(appPID); } else { String name = appName; if (name.isEmpty() && appOptions.isEmpty()) { name = appNameGiven; } init(name); } } // // public static void getApps(String name) { Map theApps = _osUtil.getApps(name); int count = 0; String[] item; for (Integer pid : theApps.keySet()) { item = theApps.get(pid); if (pid < 0) { pid = -pid; Debug.logp("%d:%s (N/A)", pid, item[0]); } else { Debug.logp("%d:%s (%s)", pid, item[0], item[1]); count++; } } Debug.logp("App.getApps: %d apps (%d having window)", theApps.size(), count); } public static void getApps() { getApps(null); } public App setUsing(String options) { if (options != null) { appOptions = options; } else { appOptions = ""; } return this; } public Integer getPID() { return appPID; } public String getName() { return appName; } public String getWindow() { return appWindow; } public boolean isValid() { return !notFound; } public boolean isRunning() { return isRunning(1); } public boolean isRunning(int maxTime) { if (!isValid()) { return false; } long wait = -1; for (int n = 0; n < maxTime; n++) { wait = new Date().getTime(); int retVal = _osUtil.isRunning(makeAppEntry()); if (retVal > 0) { init(); break; } if (n == 0) { continue; } wait = 1000 - new Date().getTime() + wait; if (wait > 0) { RunTime.pause(wait / 1000f); } } return appPID > -1; } public boolean hasWindow() { if (!isValid()) { return false; } init(appName); return !getWindow().isEmpty(); } @Override public String toString() { if (!appWindow.startsWith("!")) { init(); } return String.format("[%d:%s (%s)] %s", appPID, appName, appWindow, appNameGiven); } public String toStringShort() { return String.format("[%d:%s]", appPID, appName); } // // /** * creates an instance for an app with this name and tries to open it * * @param appName name * @return the App instance or null on failure */ public static App open(String appName) { return new App("+" + appName).open(); } /** * tries to open the app defined by this App instance
* do not wait for the app to get running * * @return this or null on failure */ public App open() { return openAndWait(0); } /** * tries to open the app defined by this App instance
* and waits until app is running * * @param waitTime max waittime until running * @return this or null on failure */ public App open(int waitTime) { return openAndWait(waitTime); } private App openAndWait(int waitTime) { if (isImmediate) { appPID = _osUtil.open(appNameGiven); } else { AppEntry appEntry = makeAppEntry(); init(_osUtil.open(appEntry)); } if (appPID < 0) { Debug.error("App.open failed: " + appNameGiven + " not found"); notFound = true; } else { Debug.action("App.open " + this.toStringShort()); } if (isImmediate && notFound) { return null; } if (waitTime > 0) { if (!isRunning(waitTime)) { return null; } } return this; } //
// /** * tries to identify a running app with the given name and then tries to close it * * @param appName name * @return 0 for success -1 otherwise */ public static int close(String appName) { return new App("+" + appName).close(); } /** * tries to close the app defined by this App instance * * @return this or null on failure */ public int close() { if (!isValid()) { return 0; } if (appPID > -1) { init(appPID); } else if (isImmediate) { init(); } int ret = _osUtil.close(makeAppEntry()); if (ret > -1) { Debug.action("App.close: %s", this.toStringShort()); appPID = -1; appWindow = ""; } else { Debug.error("App.close %s did not work", this); } return ret; } // // /** * tries to identify a running app with name and if not running tries to open it and tries to make it the foreground * application bringing its topmost window to front * * @param appName name * @return the App instance or null on failure */ public static App focus(String appName) { return focus(appName, 0); } /** * tries to identify a running app with name and if not running tries to open it and tries to make it the foreground * application bringing its window with the given number to front * * @param appName name * @param num window * @return the App instance or null on failure */ public static App focus(String appName, int num) { return (new App("+" + appName)).focus(num); } /** * tries to make it the foreground application bringing its topmost window to front * * @return the App instance or null on failure */ public App focus() { if (appPID > -1) { init(appPID); } return focus(0); } /** * tries to make it the foreground application bringing its window with the given number to front * * @param num window * @return the App instance or null on failure */ public App focus(int num) { if (!isValid()) { if (!appWindow.startsWith("!")) { return this; } } if (isImmediate) { appPID = _osUtil.switchto(appNameGiven, num); } else { init(_osUtil.switchto(makeAppEntry(), num)); } if (appPID < 0) { Debug.error("App.focus failed: " + (num > 0 ? " #" + num : "") + " " + this.toString()); return null; } else { Debug.action("App.focus: " + (num > 0 ? " #" + num : "") + " " + this.toStringShort()); if (appPID < 1) { init(); } } return this; } // // /** * evaluates the region currently occupied by the topmost window of this App instance. The region might not be fully * visible, not visible at all or invalid with respect to the current monitor configuration (outside any screen) * * @return the region */ public Region window() { if (appPID != 0) { return asRegion(_osUtil.getWindow(appPID)); } return asRegion(_osUtil.getWindow(appNameGiven)); } /** * evaluates the region currently occupied by the window with the given number of this App instance. The region might * not be fully visible, not visible at all or invalid with respect to the current monitor configuration (outside any * screen) * * @param winNum window * @return the region */ public Region window(int winNum) { if (appPID != 0) { return asRegion(_osUtil.getWindow(appPID, winNum)); } return asRegion(_osUtil.getWindow(appNameGiven, winNum)); } /** * evaluates the region currently occupied by the systemwide frontmost window (usually the one that has focus for * mouse and keyboard actions) * * @return the region */ public static Region focusedWindow() { return asRegion(_osUtil.getFocusedWindow()); } // // public static int lastRunReturnCode = -1; public static String lastRunStdout = ""; public static String lastRunStderr = ""; public static String lastRunResult = ""; /** * the given text is parsed into a String[] suitable for issuing a Runtime.getRuntime().exec(args). quoting is * preserved/obeyed. the first item must be an executable valid for the running system.
* After completion, the following information is available:
* App.lastRunResult: a string containing the complete result according to the docs of the run() command
* App.lastRunStdout: a string containing only the output lines that went to stdout
* App.lastRunStderr: a string containing only the output lines that went to stderr
* App.lastRunReturnCode: the value, that is returnd as returncode * * @param cmd the command to run starting with an executable item * @return the final returncode of the command execution */ public static int run(String cmd) { lastRunResult = runTime.runcmd(cmd); String NL = runTime.runningWindows ? "\r\n" : "\n"; String[] res = lastRunResult.split(NL); try { lastRunReturnCode = Integer.parseInt(res[0].trim()); } catch (Exception ex) { } lastRunStdout = ""; lastRunStderr = ""; boolean isError = false; for (int n = 1; n < res.length; n++) { if (isError) { lastRunStderr += res[n] + NL; continue; } if (RunTime.runCmdError.equals(res[n])) { isError = true; continue; } lastRunStdout += res[n] + NL; } return lastRunReturnCode; } //
// /** * evaluates the current textual content of the system clipboard * * @return the textual content or empty string if not possible */ public static String getClipboard() { Transferable content = null; try { content = Clipboard.getSystemClipboard().getContents(null); } catch (Exception ex) { Debug.error("Env.getClipboard: clipboard not available:\n%s", ex.getMessage()); } if (content != null) { try { if (content.isDataFlavorSupported(DataFlavor.stringFlavor)) { return (String) content.getTransferData(DataFlavor.stringFlavor); } } catch (UnsupportedFlavorException ex) { Debug.error("Env.getClipboard: UnsupportedFlavorException: " + content); } catch (IOException ex) { Debug.error("Env.getClipboard: IOException:\n%s", ex.getMessage()); } } return ""; } /** * sets the current textual content of the system clipboard to the given text * * @param text text */ public static void setClipboard(String text) { Clipboard.putText(Clipboard.PLAIN, Clipboard.UTF8, Clipboard.CHAR_BUFFER, text); } private static class Clipboard { public static final TextType HTML = new TextType("text/html"); public static final TextType PLAIN = new TextType("text/plain"); public static final Charset UTF8 = new Charset("UTF-8"); public static final Charset UTF16 = new Charset("UTF-16"); public static final Charset UNICODE = new Charset("unicode"); public static final Charset US_ASCII = new Charset("US-ASCII"); public static final TransferType READER = new TransferType(Reader.class); public static final TransferType INPUT_STREAM = new TransferType(InputStream.class); public static final TransferType CHAR_BUFFER = new TransferType(CharBuffer.class); public static final TransferType BYTE_BUFFER = new TransferType(ByteBuffer.class); private Clipboard() { } /** * Dumps a given text (either String or StringBuffer) into the Clipboard, with a default MIME type */ public static void putText(CharSequence data) { StringSelection copy = new StringSelection(data.toString()); getSystemClipboard().setContents(copy, copy); } /** * Dumps a given text (either String or StringBuffer) into the Clipboard with a specified MIME type */ public static void putText(TextType type, Charset charset, TransferType transferType, CharSequence data) { String mimeType = type + "; charset=" + charset + "; class=" + transferType; TextTransferable transferable = new TextTransferable(mimeType, data.toString()); getSystemClipboard().setContents(transferable, transferable); } public static java.awt.datatransfer.Clipboard getSystemClipboard() { return Toolkit.getDefaultToolkit().getSystemClipboard(); } private static class TextTransferable implements Transferable, ClipboardOwner { private String data; private DataFlavor flavor; public TextTransferable(String mimeType, String data) { flavor = new DataFlavor(mimeType, "Text"); this.data = data; } @Override public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[]{flavor, DataFlavor.stringFlavor}; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) { boolean b = this.flavor.getPrimaryType().equals(flavor.getPrimaryType()); return b || flavor.equals(DataFlavor.stringFlavor); } @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (flavor.isRepresentationClassInputStream()) { return new StringReader(data); } else if (flavor.isRepresentationClassReader()) { return new StringReader(data); } else if (flavor.isRepresentationClassCharBuffer()) { return CharBuffer.wrap(data); } else if (flavor.isRepresentationClassByteBuffer()) { return ByteBuffer.wrap(data.getBytes()); } else if (flavor.equals(DataFlavor.stringFlavor)) { return data; } throw new UnsupportedFlavorException(flavor); } @Override public void lostOwnership(java.awt.datatransfer.Clipboard clipboard, Transferable contents) { } } /** * Enumeration for the text type property in MIME types */ public static class TextType { private String type; private TextType(String type) { this.type = type; } @Override public String toString() { return type; } } /** * Enumeration for the charset property in MIME types (UTF-8, UTF-16, etc.) */ public static class Charset { private String name; private Charset(String name) { this.name = name; } @Override public String toString() { return name; } } /** * Enumeration for the transferScriptt type property in MIME types (InputStream, CharBuffer, etc.) */ public static class TransferType { private Class dataClass; private TransferType(Class streamClass) { this.dataClass = streamClass; } public Class getDataClass() { return dataClass; } @Override public String toString() { return dataClass.getName(); } } } // } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Button.java000077500000000000000000000010171315726130400237640ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.event.InputEvent; /** * Defines the constants for use with the mouse actions * for the button to use and the wheel direction */ public class Button { public static int LEFT = InputEvent.BUTTON1_MASK; public static int MIDDLE = InputEvent.BUTTON2_MASK; public static int RIGHT = InputEvent.BUTTON3_MASK; public static int WHEEL_UP = -1; public static int WHEEL_DOWN = 1; } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Commands.java000066400000000000000000000452201315726130400242530ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.Point; import java.awt.Rectangle; import java.lang.reflect.Method; import org.sikuli.basics.Debug; /** * EXPERIMENTAL --- INTERNAL USE ONLY
* is not official API --- will not be in version 2 */ public class Commands { private static int lvl = 2; private static void log(int level, String message, Object... args) { Debug.logx(level, "Commands: " + message, args); } private static void logCmd(String cmd, Object... args) { String msg = cmd + ": "; if (args.length == 0) { log(lvl, msg + "no-args"); } else { for (int i = 0; i < args.length; i++) { msg += "%s "; } log(lvl, msg, args); } } int i = 0; private static Region scr = new Screen(); private static Region scrSaved = null; private static RunTime runTime = RunTime.get(); /** * @return true if we are on Java 8+ */ public static boolean isNashorn() { return runTime.isJava8(); } /** * INTERNAL USE: call interface for JavaScript to be used with predefined functions * * @param function the function's name * @param args the parameters * @return the object returned by the function or null */ public static Object call(String function, Object... args) { Method m = null; Object retVal = null; int count = 0; for (Object aObj : args) { if (aObj == null || aObj.getClass().getName().endsWith("Undefined")) { break; } if (aObj instanceof String && ((String) aObj).contains("undefined")) { break; } count++; } Object[] newArgs = new Object[count]; for (int n = 0; n < count; n++) { newArgs[n] = args[n]; } try { m = Commands.class.getMethod(function, Object[].class); retVal = m.invoke(null, (Object) newArgs); } catch (Exception ex) { m = null; } return retVal; } public static Object run(Object... args) { String script = args[0].toString(); String scriptArgs[] = new String[args.length - 1]; if (scriptArgs.length > 0) { for (int i = 1; i < args.length; i++) { scriptArgs[i - 1] = args[i].toString(); } } return Runner.run(script, scriptArgs); } public static Object circle(Object args) { return 0; } // private static boolean isNumber(Object aObj) { if (aObj instanceof Integer || aObj instanceof Long || aObj instanceof Float || aObj instanceof Double) { return true; } return false; } private static int getInteger(Object aObj, int deflt) { Integer val = deflt; if (aObj instanceof Integer || aObj instanceof Long) { val = (Integer) aObj; } if (aObj instanceof Float) { val = Math.round((Float) aObj); } if (aObj instanceof Double) { val = (int) Math.round((Double) aObj); } return val; } private static int getInteger(Object aObj) { return getInteger(aObj, 0); } private static double getNumber(Object aObj, Double deflt) { Double val = deflt; if (aObj instanceof Integer) { val = 0.0 + (Integer) aObj; } else if (aObj instanceof Long) { val = 0.0 + (Long) aObj; } else if (aObj instanceof Float) { val = 0.0 + (Float) aObj; } else if (aObj instanceof Double) { val = (Double) aObj; } return val; } private static double getNumber(Object aObj) { return getNumber(aObj, 0.0); } // // /** * all following undotted function calls will use the given screen or region * until this is changed by a later use()
* -- no args: use Screen(0) (this is the default after start)
* -- a number: use Screen(number), Screen(0) if not valid
* -- a region: use the given region
* * @param args * @return the used region */ public static Region use(Object... args) { logCmd("use", args); scrSaved = null; return usex(args); } /** * same as use(), but only affects the next processed undotted function * after that the use() active before is restored * * @param args see use() * @return the used region */ public static Region use1(Object... args) { logCmd("use1", args); scrSaved = scr; return usex(args); } /** * INTERNAL USE: restore a saved use() after a use1() */ public static void restoreUsed() { if (scrSaved != null) { scr = scrSaved; scrSaved = null; log(lvl, "restored: %s", scr); } } private static Region usex(Object... args) { int len = args.length; int nScreen = -1; if (len == 0 || len > 1) { scr = new Screen(); return scr; } nScreen = getInteger(args[0], -1); if (nScreen > -1) { scr = new Screen(nScreen); } else { Object oReg = args[0]; if (oReg instanceof Region) { scr = (Region) oReg; } } return scr; } //
// /** * wait for the given visual to appear within the given wait time
* args [String|Pattern|Double, [Double, [Float]]] (max 3 args)
* arg1: String/Pattern to search or double time to wait (rest ignored)
* arg2: time to wait in seconds
* arg3: minimum similarity to use for search (overwrites Pattern setting)
* * @param args * @return the match or throws FindFailed * @throws FindFailed */ public static Match wait(Object... args) throws FindFailed { logCmd("wait", args); Object[] realArgs = waitArgs(args); return waitx((String) realArgs[0], (Pattern) realArgs[1], (Double) realArgs[2], (Float) realArgs[3]); } private static Match waitx(String image, Pattern pimage, double timeout, float score) throws FindFailed { Object aPattern = null; if (image != null) { if (score > 0) { aPattern = new Pattern(image).similar(score); } else { aPattern = image; } } else if (pimage != null) { aPattern = pimage; } if (aPattern != null) { if (timeout > -1.0) { return scr.wait(aPattern, timeout); } return scr.wait(aPattern); } return null; } private static Object[] waitArgs(Object... args) { int len = args.length; String image = ""; float score = 0.0f; double timeout = -1.0f; boolean argsOK = true; Object[] realArgs = new Object[]{null, null, (Double) (-1.0), (Float) 0f}; if (len == 0 || len > 3) { argsOK = false; } else { Object aObj = args[0]; if (aObj == null) { return realArgs; } if (isJSON(aObj)) { aObj = fromJSON(aObj); } if (aObj instanceof String) { realArgs[0] = aObj; } else if (aObj instanceof Pattern) { realArgs[1] = aObj; if (len > 1 && isNumber(args[1])) { realArgs[2] = (Double) getNumber(args[1]); } } else if (isNumber(aObj)) { scr.wait(getNumber(aObj)); return null; } else { argsOK = false; } } if (argsOK && len > 1 && realArgs[1] == null) { if (len > 2 && isNumber(args[2])) { score = (float) getNumber(args[2]) / 100.0f; if (score < 0.7) { score = 0.7f; } else if (score > 0.99) { score = 0.99f; } } if (score > 0.0f) { realArgs[3] = (Float) score; } if (len > 1 && isNumber(args[1])) { realArgs[2] = (Double) getNumber(args[1]); } } if (!argsOK) { throw new UnsupportedOperationException( "Commands.wait: parameters: String/Pattern:image, float:timeout, int:score"); } return realArgs; } /** * wait for the given visual to vanish within the given wait time * * @param args see wait() * @return true if not there from beginning or vanished within wait time, false otherwise */ public static boolean waitVanish(Object... args) { logCmd("waitVanish", args); Object aPattern; Object[] realArgs = waitArgs(args); String image = (String) realArgs[0]; Pattern pimage = (Pattern) realArgs[1]; double timeout = (Double) realArgs[2]; float score = (Float) realArgs[3]; if (image != null) { if (score > 0) { aPattern = new Pattern(image).similar(score); } else { aPattern = image; } } else { aPattern = pimage; } if (timeout > -1.0) { return scr.waitVanish(aPattern, timeout); } return scr.waitVanish(aPattern); } /** * wait for the given visual to appear within the given wait time * * @param args see wait() * @return the match or null if not found within wait time (no FindFailed exception) */ public static Match exists(Object... args) { logCmd("exists", args); Match match = null; Object[] realArgs = waitArgs(args); if ((Double) realArgs[2] < 0.0) { realArgs[2] = 0.0; } try { match = waitx((String) realArgs[0], (Pattern) realArgs[1], (Double) realArgs[2], (Float) realArgs[3]); } catch (Exception ex) { return null; } return match; } //
// /** * move the mouse to the given location with a given offset
* 3 parameter configurations:
* --1: wait for a visual and move mouse to it (args see wait())
* --2: move to the given region/location/match with a given offset
* --3: move to the given offset relative to the last match of the region in use * * @param args * @return the evaluated location to where the mouse should have moved */ public static Location hover(Object... args) { logCmd("hover", args); return hoverx(args); } private static Location hoverx(Object... args) { int len = args.length; Match aMatch; if (len == 0 || args[0] == null) { Mouse.move(scr.checkMatch()); return Mouse.at(); } if (len < 4) { Object aObj = args[0]; Location loc = null; if (isJSON(aObj)) { aObj = fromJSON(aObj); } if (aObj instanceof String || aObj instanceof Pattern) { try { aMatch = wait(args); Mouse.move(aMatch.getTarget()); } catch (Exception ex) { Mouse.move(scr.checkMatch()); } return Mouse.at(); } else if (aObj instanceof Region) { loc = ((Region) aObj).getTarget(); } else if (aObj instanceof Location) { loc = (Location) aObj; } if (len > 1) { if (isNumber(aObj) && isNumber(args[1])) { Mouse.move(scr.checkMatch().offset(getInteger(aObj), getInteger(args[1]))); return Mouse.at(); } else if (len == 3 && loc != null && isNumber(args[1]) && isNumber(args[2])) { Mouse.move(loc.offset(getInteger(args[1], 0), getInteger(args[2], 0))); return Mouse.at(); } } if (loc != null) { Mouse.move(loc); return Mouse.at(); } } Mouse.move(scr.checkMatch()); return Mouse.at(); } /** * move the mouse with hover() and click using the left button * * @param args see hover() * @return the location, where the click was done */ public static Location click(Object... args) { logCmd("click", args); Location loc = hoverx(args); Mouse.click(null, Button.LEFT, 0, false, null); return Mouse.at(); } /** * move the mouse with hover() and double click using the left button * * @param args see hover() * @return the location, where the double click was done */ public static Location doubleClick(Object... args) { logCmd("doubleClick", args); Location loc = hoverx(args); Mouse.click(null, Button.LEFT, 0, true, null); return Mouse.at(); } /** * move the mouse with hover() and do a right click * * @param args see hover() * @return the location, where the right click was done */ public static Location rightClick(Object... args) { logCmd("rightClick", args); Location loc = hoverx(args); Mouse.click(null, Button.RIGHT, 0, false, null); return Mouse.at(); } //
// /** * just doing a currentRegion.paste(text) (see paste()) * * @param args only one parameter being a String * @return true if paste() returned 1, false otherwise */ public static boolean paste(Object... args) { logCmd("paste", args); Object[] realArgs = typeArgs(args); return 0 < scr.paste((String) realArgs[0]); } /** * just doing a currentRegion.write(text) (see write()) * * @param args only one parameter being a String * @return true if write() returned 1, false otherwise */ public static boolean write(Object... args) { logCmd("write", args); Object[] realArgs = typeArgs(args); return 0 < scr.write((String) realArgs[0]); } private static Object[] typeArgs(Object... args) { Object[] realArgs = new Object[]{null}; if (!(args[0] instanceof String)) { throw new UnsupportedOperationException("Commands.type/paste/write: parameters: String:text"); } realArgs[0] = args[0]; return realArgs; } // // /** * check wether the given object is in JSON format as ["ID", ...] * * @param aObj * @return true if object is in JSON format, false otherwise */ public static boolean isJSON(Object aObj) { if (aObj instanceof String) { return ((String) aObj).startsWith("[\""); } return false; } /** * experimental: create the real object from the given JSON
* take care: content length not checked if valid (risk for index out of bounds)
* planned to be used with a socket/RPC based interface to any framework (e.g. C#) * Region ["R", x, y, w, h]
* Location ["L", x, y]
* Match ["M", x, y, w, h, score, offx, offy]
* Screen ["S", x, y, w, h, id]
* Pattern ["P", "imagename", score, offx, offy]
* These real objects have a toJSON(), that returns these JSONs
* * @param aObj * @return the real object or the given object if it is not one of these JSONs */ public static Object fromJSON(Object aObj) { if (!isJSON(aObj)) { return aObj; } Object newObj = null; String[] json = ((String) aObj).split(","); String last = json[json.length - 1]; if (!last.endsWith("]")) { return aObj; } else { json[json.length - 1] = last.substring(0, last.length() - 1); } String oType = json[0].substring(2, 3); if (!"SRML".contains(oType)) { return aObj; } if ("S".equals(oType)) { aObj = new Screen(intFromJSON(json, 5)); ((Screen) aObj).setRect(rectFromJSON(json)); } else if ("R".equals(oType)) { newObj = new Region(rectFromJSON(json)); } else if ("M".equals(oType)) { double score = dblFromJSON(json, 5) / 100; newObj = new Match(new Region(rectFromJSON(json)), score); ((Match) newObj).setTarget(intFromJSON(json, 6), intFromJSON(json, 7)); } else if ("L".equals(oType)) { newObj = new Location(locFromJSON(json)); } else if ("P".equals(oType)) { newObj = new Pattern(json[1]); ((Pattern) newObj).similar(fltFromJSON(json, 2)); ((Pattern) newObj).targetOffset(intFromJSON(json, 3), intFromJSON(json, 4)); } return newObj; } private static Rectangle rectFromJSON(String[] json) { int[] vals = new int[4]; for (int n = 1; n < 5; n++) { try { vals[n - 1] = Integer.parseInt(json[n].trim()); } catch (Exception ex) { vals[n - 1] = 0; } } return new Rectangle(vals[0], vals[1], vals[2], vals[3]); } private static Point locFromJSON(String[] json) { int[] vals = new int[2]; for (int n = 1; n < 3; n++) { try { vals[n - 1] = Integer.parseInt(json[n].trim()); } catch (Exception ex) { vals[n - 1] = 0; } } return new Point(vals[0], vals[1]); } private static int intFromJSON(String[] json, int pos) { try { return Integer.parseInt(json[pos].trim()); } catch (Exception ex) { return 0; } } private static float fltFromJSON(String[] json, int pos) { try { return Float.parseFloat(json[pos].trim()); } catch (Exception ex) { return 0; } } private static double dblFromJSON(String[] json, int pos) { try { return Double.parseDouble(json[pos].trim()); } catch (Exception ex) { return 0; } } //
} sikulix-1.1.1/API/src/main/java/org/sikuli/script/Constants.java000077500000000000000000000005141315726130400244660ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; /** * reasonable symbolic constants to be used with Sikuli features */ public class Constants { /** * use it for a very long time value */ public static double FOREVER = Double.POSITIVE_INFINITY; } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Device.java000066400000000000000000000176131315726130400237160ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.MouseInfo; import java.awt.Point; import java.awt.PointerInfo; import java.util.Date; import org.sikuli.basics.Debug; /** * EXPERIMENTAL --- INTERNAL USE ONLY
* is not official API --- will not be in version 2 */ public class Device { static RunTime runTime = RunTime.get(); private static String me = "Device: "; private static final int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private Object device = null; private String devName = "Device"; protected boolean inUse = false; protected boolean keep = false; protected Object owner = null; private boolean blocked = false; private boolean suspended = false; protected Location lastPos = null; protected boolean isMouse = false; protected int MouseMovedIgnore = 0; protected int MouseMovedShow = 1; protected int MouseMovedPause = 2; protected int MouseMovedAction = 3; protected int mouseMovedResponse = MouseMovedIgnore; protected boolean MouseMovedHighlight = true; protected ObserverCallBack mouseMovedCallback = null; protected ObserverCallBack callback = null; private boolean shouldRunCallback = false; static boolean shouldTerminate = false; public static void setShouldTerminate() { shouldTerminate = true; log(lvl, "setShouldTerminate: request issued"); } public boolean isShouldRunCallback() { return shouldRunCallback; } public void setShouldRunCallback(boolean shouldRunCallback) { this.shouldRunCallback = shouldRunCallback; } protected Device(Mouse m) { device = m; devName = "Mouse"; } protected Device(Screen s) { device = s; devName = "Screen"; } public boolean isInUse() { return inUse; } public boolean isSuspended() { return suspended; } public boolean isBlocked() { return blocked; } public boolean isNotLocal(Object owner) { if (owner instanceof Region) { if (((Region) owner).isOtherScreen()) { return true; } } else if (owner instanceof Location) { if (((Location) owner).isOtherScreen()) { return true; } } return false; } /** * to block the device globally
* only the contained device methods without owner will be granted * * @return success */ public boolean block() { return block(null); } /** * to block the device globally for the given owner
* only the contained mouse methods having the same owner will be granted * * @param owner Object * @return success */ public boolean block(Object owner) { if (use(owner)) { blocked = true; return true; } else { return false; } } /** * free the mouse globally after a block() * * @return success (false means: not blocked currently) */ public boolean unblock() { return unblock(null); } /** * free the mouse globally for this owner after a block(owner) * * @param ownerGiven Object * @return success (false means: not blocked currently for this owner) */ public boolean unblock(Object ownerGiven) { if (ownerGiven == null) { ownerGiven = device; } else if (isNotLocal(ownerGiven)) { return false; } if (blocked && owner == ownerGiven) { blocked = false; let(ownerGiven); return true; } return false; } protected boolean use() { return use(null); } protected synchronized boolean use(Object owner) { if (owner == null) { owner = this; } else if (isNotLocal(owner)) { return false; } if ((blocked || inUse) && this.owner == owner) { return true; } while (inUse) { try { wait(); } catch (InterruptedException e) { } } if (!inUse) { inUse = true; try { checkLastPos(); } catch (Exception ex) {} checkShouldRunCallback(); if (shouldTerminate) { shouldTerminate = false; throw new AssertionError("aborted by unknown source"); } keep = false; this.owner = owner; log(lvl + 1, "%s: use start: %s", devName, owner); return true; } log(-1, "synch problem - use start: %s", owner); return false; } protected synchronized boolean keep(Object ownerGiven) { if (ownerGiven == null) { ownerGiven = this; } else if (isNotLocal(ownerGiven)) { return false; } if (inUse && owner == ownerGiven) { keep = true; log(lvl + 1, "%s: use keep: %s", devName, ownerGiven); return true; } return false; } protected boolean let() { return let(null); } protected synchronized boolean let(Object owner) { if (owner == null) { owner = this; } else if (isNotLocal(owner)) { return false; } if (inUse && this.owner == owner) { if (keep) { keep = false; return true; } if (isMouse) { lastPos = getLocation(); } inUse = false; this.owner = null; notify(); log(lvl + 1, "%s: use stop: %s", devName, owner); return true; } return false; } protected Location getLocation() { PointerInfo mp = MouseInfo.getPointerInfo(); if (mp != null) { return new Location(MouseInfo.getPointerInfo().getLocation()); } else { Debug.error("Mouse: not possible to get mouse position (PointerInfo == null)"); return null; } } private void checkLastPos() throws UnsupportedOperationException { if (lastPos == null) { return; } Location pos = getLocation(); if (pos != null && (lastPos.x != pos.x || lastPos.y != pos.y)) { log(lvl, "%s: moved externally: now (%d,%d) was (%d,%d) (mouseMovedResponse %d)", devName, pos.x, pos.y, lastPos.x, lastPos.y, mouseMovedResponse); if (mouseMovedResponse > 0) { if (MouseMovedHighlight) { showMousePos(pos.getPoint()); } } if (mouseMovedResponse == MouseMovedPause) { while (pos.x > 0 && pos.y > 0) { delay(500); pos = getLocation(); if (MouseMovedHighlight) { showMousePos(pos.getPoint()); } } if (pos.x < 1) { return; } throw new UnsupportedOperationException("Terminating in MouseMovedResponse = Pause"); } if (mouseMovedResponse == MouseMovedAction) { //TODO implement 3 if (mouseMovedCallback != null) { mouseMovedCallback.happened(new ObserveEvent("MouseMoved", ObserveEvent.Type.GENERIC, lastPos, new Location(pos), null, (new Date()).getTime())); if (shouldTerminate) { shouldTerminate = false; throw new AssertionError("aborted by Sikulix.MouseMovedCallBack"); } } } } } private void checkShouldRunCallback() { if (shouldRunCallback && callback != null) { callback.happened(new ObserveEvent("DeviceGeneric", ObserveEvent.Type.GENERIC, null, null, null, (new Date()).getTime())); if (shouldTerminate) { shouldTerminate = false; throw new AssertionError("aborted by Sikulix.GenericDeviceCallBack"); } } } /** * what to do if mouse is moved outside Sikuli's mouse protection
* in case of event the user provided callBack.happened is called * * @param givenCallBack */ public void setCallback(Object givenCallBack) { if (givenCallBack != null) { callback = new ObserverCallBack(givenCallBack, ObserveEvent.Type.GENERIC); } } private static void showMousePos(Point pos) { Location lPos = new Location(pos); Region inner = lPos.grow(20).highlight(); delay(500); lPos.grow(40).highlight(1); delay(500); inner.highlight(); } protected static void delay(int time) { if (time == 0) { return; } if (time < 10) { time = time * 1000; } try { Thread.sleep(time); } catch (InterruptedException e) { } } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Do.java000066400000000000000000000251041315726130400230530ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Debug; import javax.swing.*; import java.awt.GraphicsEnvironment; import java.awt.Point; import java.lang.reflect.Method; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * This class implements handy features from version 2 */ public class Do { private static Log log = new Log(); // private enum PopType { POPUP, POPASK, POPERROR, POPINPUT } public static String input(Object... args) { if (SX.isHeadless()) { log.error("running headless: input"); } else { return (String) doPop(PopType.POPINPUT, args); } return null; } public static Boolean popup(Object... args) { if (SX.isHeadless()) { log.error("running headless: popup"); } else { return (Boolean) doPop(PopType.POPUP, args); } return false; } public static Boolean popAsk(Object... args) { if (SX.isHeadless()) { log.error("running headless: popAsk"); } else { return (Boolean) doPop(PopType.POPASK, args); } return false; } public static Boolean popError(Object... args) { if (SX.isHeadless()) { log.error("running headless: popError"); } else { return (Boolean) doPop(PopType.POPERROR, args); } return false; } private static Object doPop(PopType popType, Object... args) { class RunInput implements Runnable { PopType popType = PopType.POPUP; JFrame frame = null; String title = ""; String message = ""; String preset = ""; Boolean hidden = false; Integer timeout = 0; boolean running = true; Map parameters = new HashMap<>(); Object returnValue; public RunInput(PopType popType, Object... args) { this.popType = popType; parameters = getPopParameters(args); title = (String) parameters.get("title"); message = (String) parameters.get("message"); preset = (String) parameters.get("preset"); hidden = (Boolean) parameters.get("hidden"); timeout = (Integer) parameters.get("timeout"); frame = getFrame(parameters.get("location")); } @Override public void run() { returnValue = null; if (PopType.POPUP.equals(popType)) { JOptionPane.showMessageDialog(frame, message, title, JOptionPane.PLAIN_MESSAGE); returnValue = new Boolean(true); } else if (PopType.POPASK.equals(popType)) { int ret = JOptionPane.showConfirmDialog(frame, message, title, JOptionPane.YES_NO_OPTION); returnValue = new Boolean(true); if (ret == JOptionPane.CLOSED_OPTION || ret == JOptionPane.NO_OPTION) { returnValue = new Boolean(false); } } else if (PopType.POPERROR.equals(popType)) { JOptionPane.showMessageDialog(frame, message, title, JOptionPane.ERROR_MESSAGE); returnValue = new Boolean(true); } else if (PopType.POPINPUT.equals(popType)) { if (!hidden) { if ("".equals(title)) { title = "Sikuli input request"; } returnValue = JOptionPane.showInputDialog(frame, message, title, JOptionPane.PLAIN_MESSAGE, null, null, preset); } else { JTextArea messageText = new JTextArea(message); messageText.setColumns(20); messageText.setLineWrap(true); messageText.setWrapStyleWord(true); messageText.setEditable(false); messageText.setBackground(new JLabel().getBackground()); final JPasswordField passwordField = new JPasswordField(preset); toGetFocus = passwordField; JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.add(passwordField); panel.add(Box.createVerticalStrut(10)); panel.add(messageText); int retval = JOptionPane.showConfirmDialog(frame, panel, title, JOptionPane.OK_CANCEL_OPTION); returnValue = ""; if (0 == retval) { char[] pwchar = passwordField.getPassword(); for (int i = 0; i < pwchar.length; i++) { returnValue = (String) returnValue + pwchar[i]; pwchar[i] = 0; } } } } stop(); } JComponent toGetFocus = null; public void setFocus() { if (SX.isNotNull(toGetFocus)) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { toGetFocus.requestFocusInWindow(); } }); } } public boolean isRunning() { return running; } public void stop() { frame.dispose(); running = false; } public int getTimeout() { if (Integer.MAX_VALUE == timeout) { return timeout; } return timeout * 1000; } public Object getReturnValue() { return returnValue; } } RunInput popRun = new RunInput(popType, args); new Thread(popRun).start(); SX.pause(0.3); popRun.setFocus(); long end = new Date().getTime() + popRun.getTimeout(); while (popRun.isRunning()) { SX.pause(0.3); if (end < new Date().getTime()) { popRun.stop(); } } return popRun.getReturnValue(); } private static Map getPopParameters(Object... args) { String parameterNames = "message,title,preset,hidden,timeout,location"; String parameterClass = "s,s,s,b,i,e"; Object[] parameterDefault = new Object[]{"not set", "SikuliX", "", false, Integer.MAX_VALUE, Do.on()}; return Parameters.get(parameterNames, parameterClass, parameterDefault, args); } private static JFrame getFrame(Object point) { int x; int y; if (point instanceof Point) { x = ((Point) point).x; y = ((Point) point).y; } else { if (SX.isVersion1()) { x = ((Region) point).getCenter().x; y = ((Region) point).getCenter().y; } else { x = ((Element) point).getCenter().x; y = ((Element) point).getCenter().y; } } JFrame anchor = new JFrame(); anchor.setAlwaysOnTop(true); anchor.setUndecorated(true); anchor.setSize(1, 1); anchor.setLocation(x, y); anchor.setVisible(true); return anchor; } private static Region on() { return Screen.getPrimaryScreen(); } static class SX { public static boolean isNotNull(Object obj) { return null != obj; } public static void pause(double time) { try { Thread.sleep((int) (time * 1000)); } catch (InterruptedException ex) { } } public static boolean isHeadless() { return GraphicsEnvironment.isHeadless(); } public static boolean isVersion1() { return true; }; } static class Log { public static void error(String msg, Object... args) { Debug.error("Do: " + msg, args); } } static class Element extends Region {} static private class Parameters { private Map parameterTypes = new HashMap<>(); private String[] parameterNames = null; private Object[] parameterDefaults = new Object[0]; public Parameters(String theNames, String theClasses, Object[] theDefaults) { String[] names = theNames.split(","); String[] classes = theClasses.split(","); if (names.length == classes.length) { for (int n = 0; n < names.length; n++) { String clazz = classes[n]; if (clazz.length() == 1) { clazz = clazz.toLowerCase(); if ("s".equals(clazz)) { clazz = "String"; } else if ("i".equals(clazz)) { clazz = "Integer"; } else if ("d".equals(clazz)) { clazz = "Double"; } else if ("b".equals(clazz)) { clazz = "Boolean"; } else if ("e".equals(clazz)) { if (SX.isVersion1()) { clazz = "Region"; } clazz = "Element"; } } if ("String".equals(clazz) || "Integer".equals(clazz) || "Double".equals(clazz) || "Boolean".equals(clazz) || "Element".equals(clazz)) { parameterTypes.put(names[n], clazz); } } parameterNames = names; parameterDefaults = theDefaults; } else { log.error("Parameters: different length: names: %s classes: %s", theNames, theClasses); } } public static Map get(Object... args) { String theNames = (String) args[0]; String theClasses = (String) args[1]; Object[] theDefaults = (Object[]) args[2]; Object[] theArgs = (Object[]) args[3]; Parameters theParameters = new Parameters(theNames, theClasses, theDefaults); return theParameters.getParameters(theArgs); } private Object getParameter(Object possibleValue, String parameterName) { String clazz = parameterTypes.get(parameterName); Object value = null; if ("String".equals(clazz)) { if (possibleValue instanceof String) { value = possibleValue; } } else if ("Integer".equals(clazz)) { if (possibleValue instanceof Integer) { value = possibleValue; } } else if ("Double".equals(clazz)) { if (possibleValue instanceof Double) { value = possibleValue; } } else if ("Boolean".equals(clazz)) { if (possibleValue instanceof Boolean) { value = possibleValue; } } else if ("Element".equals(clazz)) { if (SX.isVersion1()) { if (possibleValue instanceof Region) { value = possibleValue; } } else if (possibleValue instanceof Element) { value = possibleValue; } } return value; } public Map getParameters(Object[] args) { Map params = new HashMap<>(); if (SX.isNotNull(parameterNames)) { int n = 0; int argsn = 0; for (String parameterName : parameterNames) { params.put(parameterName, parameterDefaults[n]); if (args.length > 0 && argsn < args.length) { Object arg = getParameter(args[argsn], parameterName); if (SX.isNotNull(arg)) { params.put(parameterName, arg); argsn++; } } n++; } } return params; } } // } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Env.java000066400000000000000000000120001315726130400232300ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.HotkeyManager; import org.sikuli.basics.HotkeyListener; import org.sikuli.basics.OS; import org.sikuli.natives.OSUtil; import org.sikuli.basics.Settings; import org.sikuli.natives.SysUtil; /** * features moved to other classes, details below with the methods * @deprecated */ @Deprecated public class Env { /** * @deprecated use Settings.getVersion() instead */ @Deprecated public static String SikuliVersion = ""; /** * * @return where we store Sikuli specific data * @deprecated use Settings. ... instead */ @Deprecated public static String getSikuliDataPath() { return Settings.getSikuliDataPath(); } /** * @return version * @deprecated use Settings.SikuliVersion */ @Deprecated public static String getSikuliVersion() { return RunTime.get().SikuliVersion; } protected static void setSikuliVersion(String version) { SikuliVersion = version; } /** * @return current Location * @deprecated use {@link Mouse#at()} instead */ @Deprecated public static Location getMouseLocation() { return Mouse.at(); } @Deprecated public static OSUtil getOSUtil() { return SysUtil.getOSUtil(); } /** * @return version (java: os.version) * @deprecated use Settings. ... instead */ @Deprecated public static String getOSVersion() { return Settings.getOSVersion(); } /** * use Settings.isWindows .isMac .isLinux instead * @return the OS.XXX * @deprecated use the Settings features */ @Deprecated public static OS getOS() { if (Settings.isWindows()) { return OS.WINDOWS; } else if (Settings.isMac()) { return OS.MAC; } else if (Settings.isLinux()) { return OS.LINUX; } else { return OS.NOT_SUPPORTED; } } /** * @return true/false * @deprecated use Settings. ... instead */ @Deprecated public static boolean isWindows() { return Settings.isWindows(); } /** * @return true/false * @deprecated use Settings. ... instead */ @Deprecated public static boolean isLinux() { return Settings.isLinux(); } /** * @return true/false * @deprecated use Settings. ... instead */ @Deprecated public static boolean isMac() { return Settings.isMac(); } /** * @return path seperator : or ; * @deprecated use Settings.getPathSeparator() ... instead */ @Deprecated public static String getSeparator() { return Settings.getPathSeparator(); } /** * * @return content * @deprecated use App. ... instead */ @Deprecated public static String getClipboard() { return App.getClipboard(); } /** * set content * * @param text text * @deprecated use App. ... instead */ @Deprecated public static void setClipboard(String text) { App.setClipboard(text); } /** * get the lock state of the given key * @param key respective key specifier according class Key * @return true/false * @deprecated use Key. ... instead */ @Deprecated public static boolean isLockOn(char key) { return Key.isLockOn(key); } /** * * @return System dependent key * @deprecated use Key. ... instead */ @Deprecated public static int getHotkeyModifier() { return Key.getHotkeyModifier(); } /** * * @param key respective key specifier according class Key * @param modifiers respective key specifier according class KeyModifiers * @param listener a HotKeyListener instance * @return true if ok, false otherwise * @deprecated use Key. ... instead */ @Deprecated public static boolean addHotkey(String key, int modifiers, HotkeyListener listener) { return Key.addHotkey(key, modifiers, listener); } /** * * @param key respective key specifier according class Key * @param modifiers respective key specifier according class KeyModifiers * @param listener a HotKeyListener instance * @return true if ok, false otherwise * @deprecated use Key. ... instead */ @Deprecated public static boolean addHotkey(char key, int modifiers, HotkeyListener listener) { return Key.addHotkey(key, modifiers, listener); } /** * * @param key respective key specifier according class Key * @param modifiers respective key specifier according class KeyModifiers * @return true if ok, false otherwise * @deprecated use Key. ... instead */ @Deprecated public static boolean removeHotkey(String key, int modifiers) { return Key.removeHotkey(key, modifiers); } /** * * @param key respective key specifier according class Key * @param modifiers respective key specifier according class KeyModifiers * @return true if ok, false otherwise * @deprecated use Key. ... instead */ @Deprecated public static boolean removeHotkey(char key, int modifiers) { return Key.removeHotkey(key, modifiers); } //TODO where to use??? public static void cleanUp() { HotkeyManager.getInstance().cleanUp(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/FindFailed.java000077500000000000000000000072241315726130400245040ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Debug; /** * implements the SikuliX FindFailed exception class * and defines constants and settings for the feature FindFailedResponse */ public class FindFailed extends SikuliException { /** * default FindFailedResponse is ABORT */ public static FindFailedResponse defaultFindFailedResponse = FindFailedResponse.ABORT; /** * FindFailedResponse PROMPT: should display a prompt dialog with the failing image * having the options retry, skip and abort */ public static final FindFailedResponse PROMPT = FindFailedResponse.PROMPT; /** * FindFailedResponse RETRY: should retry the find op on FindFailed */ public static final FindFailedResponse RETRY = FindFailedResponse.RETRY; /** * FindFailedResponse SKIP: should silently continue on FindFailed */ public static final FindFailedResponse SKIP = FindFailedResponse.SKIP; /** * FindFailedResponse ABORT: should abort the SikuliX application */ public static final FindFailedResponse ABORT = FindFailedResponse.ABORT; /** * FindFailedResponse HANDLE: should call a given handler on FindFailed */ public static final FindFailedResponse HANDLE = FindFailedResponse.HANDLE; private static Object ffHandler = null; private static Object imHandler = null; private static Object defaultHandler = null; /** * the exception * @param message to be shown */ public FindFailed(String message) { super(message); _name = "FindFailed"; } public static String createdefault(Region reg, Image img) { String msg = ""; if (img.isText()) { msg = String.format("%s as text", img.getName()); } else if (img.getSize().width < 0 && img.getSize().height < 0) { msg = String.format("%s not loaded", img.getName()); } else { msg = String.format("%s in %s", img, reg); } return msg; } public static FindFailedResponse getResponse() { return defaultFindFailedResponse; } public static FindFailedResponse setResponse(FindFailedResponse response) { defaultFindFailedResponse = response; return defaultFindFailedResponse; } public static FindFailedResponse setHandler(Object observer) { if (observer != null && (observer.getClass().getName().contains("org.python") || observer.getClass().getName().contains("org.jruby"))) { observer = new ObserverCallBack(observer, ObserveEvent.Type.FINDFAILED); } else { ((ObserverCallBack) observer).setType(ObserveEvent.Type.FINDFAILED); } ffHandler = observer; Debug.log(3, "Setting Default FindFailedHandler"); return defaultFindFailedResponse; } protected void setFindFailedHandler(Object handler) { ffHandler = setHandler(handler, ObserveEvent.Type.FINDFAILED); } public void setImageMissingHandler(Object handler) { imHandler = setHandler(handler, ObserveEvent.Type.MISSING); } private Object setHandler(Object handler, ObserveEvent.Type type) { defaultFindFailedResponse = HANDLE; if (handler != null && (handler.getClass().getName().contains("org.python") || handler.getClass().getName().contains("org.jruby"))) { handler = new ObserverCallBack(handler, type); } else { ((ObserverCallBack) handler).setType(type); } return handler; } public static Object getFindFailedHandler() { return ffHandler; } public static Object getImageMissingHandler() { return imHandler; } public static FindFailedResponse reset() { defaultFindFailedResponse = ABORT; ffHandler = null; imHandler = null; return defaultFindFailedResponse; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/FindFailedDialog.java000077500000000000000000000101541315726130400256200ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; /** * INTERNAL USE */ class FindFailedDialog extends JDialog implements ActionListener { JButton retryButton; JButton skipButton; JButton abortButton; FindFailedResponse _response; boolean isCapture = false; public FindFailedDialog(org.sikuli.script.Image target) { init(target, false); } public FindFailedDialog(org.sikuli.script.Image target, boolean isCapture) { init(target, isCapture); } private void init(org.sikuli.script.Image target, boolean isCapture) { this.isCapture = isCapture; setModal(true); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); Component targetComp = createTargetComponent(target); panel.add(targetComp, BorderLayout.NORTH); JPanel buttons = new JPanel(); String textRetry = "Retry"; if (isCapture) { textRetry = "Capture"; } String textSkip = "Capture/Skip"; if (isCapture) { textSkip = "Skip"; } retryButton = new JButton(textRetry); retryButton.addActionListener(this); skipButton = new JButton(textSkip); skipButton.addActionListener(this); abortButton = new JButton("Abort"); abortButton.addActionListener(this); buttons.add(retryButton); buttons.add(skipButton); buttons.add(abortButton); panel.add(buttons, BorderLayout.SOUTH); add(panel); setDefaultCloseOperation(DISPOSE_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { _response = FindFailedResponse.ABORT; } }); } @Override public void actionPerformed(ActionEvent e) { if (retryButton == e.getSource()) { _response = FindFailedResponse.RETRY; } else if (abortButton == e.getSource()) { _response = FindFailedResponse.ABORT; } else if (skipButton == e.getSource()) { _response = FindFailedResponse.SKIP; } dispose(); } public FindFailedResponse getResponse() { return _response; } Component createTargetComponent(org.sikuli.script.Image img) { JLabel cause = null; JPanel dialog = new JPanel(); dialog.setLayout(new BorderLayout()); if (img.isValid()) { if (!img.isText()) { Image bimage = img.get(false); if (bimage != null) { String rescale = ""; JLabel iconLabel = new JLabel(); int w = bimage.getWidth(this); int h = bimage.getHeight(this); if (w > 500) { w = 500; h = -h; rescale = " (rescaled 500x...)"; } if (h > 300) { h = 300; w = -w; rescale = " (rescaled ...x300)"; } if (h < 0 && w < 0) { w = 500; h = 300; rescale = " (rescaled 500x300)"; } bimage = bimage.getScaledInstance(w, h, Image.SCALE_DEFAULT); iconLabel.setIcon(new ImageIcon(bimage)); cause = new JLabel("Cannot find " + img.getName() + rescale); dialog.add(iconLabel, BorderLayout.PAGE_END); } } else { cause = new JLabel("Sikuli cannot find text:" + img.getName()); } } if (isCapture) { cause = new JLabel("Request to capture: " + img.getName()); } dialog.add(cause, BorderLayout.PAGE_START); return dialog; } @Override public void setVisible(boolean flag) { if (flag) { //TODO Can not be called in the constructor (as JFRrame?) // Doing so somehow made it impossible to keep // the dialog always on top. pack(); setAlwaysOnTop(true); setResizable(false); setLocationRelativeTo(this); requestFocus(); } super.setVisible(flag); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/FindFailedResponse.java000077500000000000000000000003521315726130400262160ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; /** * INTERNAL USE */ public enum FindFailedResponse{ ABORT, PROMPT, SKIP, RETRY, HANDLE }; sikulix-1.1.1/API/src/main/java/org/sikuli/script/Finder.java000077500000000000000000000275441315726130400237350ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Settings; import org.sikuli.basics.Debug; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Iterator; import org.sikuli.natives.FindInput; import org.sikuli.natives.FindResult; import org.sikuli.natives.FindResults; import org.sikuli.natives.TARGET_TYPE; import org.sikuli.natives.Vision; /** * implements the process to find one image in another image
* this is the historical implementation * based on the C++ JNI access to the native OpenCV libraries
* It is being replaced by ImageFinder, that implements the Finder features * completely in Java using the OpenCV newly provided JAVA interface
* At time of realisation the Finder API will be redirected to ImageFinder */ public class Finder implements Iterator { static RunTime runTime = RunTime.get(); private Region _region = null; private Pattern _pattern = null; private Image _image = null; private FindInput _findInput = new FindInput(); private FindResults _results = null; private int _cur_result_i; private boolean repeating = false; private boolean valid = true; private boolean screenFinder = true; static { RunTime.loadLibrary("VisionProxy"); } private static String me = "Finder: "; private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } // /** * Just to force library initialization */ public Finder() {} /** * Finder constructor (finding within an image). *
internally used with a screen snapshot * * @param imageFilename a string (name, path, url) * @throws java.io.IOException if imagefile not found */ public Finder(String imageFilename) throws IOException { this(imageFilename, null); } /** * Finder constructor (finding within an image within the given region). *
internally used with a screen snapshot * * @param imageFilename a string (name, path, url) * @param region search Region within image - topleft = (0,0) * @throws java.io.IOException if imagefile not found */ public Finder(String imageFilename, Region region) throws IOException { Image img = Image.create(imageFilename); if (img.isValid()) { _findInput.setSource(Image.convertBufferedImageToMat(img.get())); _region = region; screenFinder = false; } else { log(-1, "imagefile not found:\n%s"); valid = false; } } /** * Constructor for special use from a BufferedImage * * @param bimg BufferedImage */ public Finder(BufferedImage bimg) { _findInput.setSource(Image.convertBufferedImageToMat(bimg)); } /** * Finder constructor for special use from a ScreenImage * * @param simg ScreenImage */ public Finder(ScreenImage simg) { initScreenFinder(simg, null); } /** * Finder constructor for special use from a ScreenImage * * @param simg ScreenImage * @param region the cropping region */ public Finder(ScreenImage simg, Region region) { initScreenFinder(simg, region); } /** * Finder constructor for special use from an Image * * @param img Image */ public Finder(Image img) { log(lvl, "Image: %s", img); _findInput.setSource(Image.convertBufferedImageToMat(img.get())); } public void resetImage(Image img) { _findInput.setSource(Image.convertBufferedImageToMat(img.get())); } private void initScreenFinder(ScreenImage simg, Region region) { setScreenImage(simg); _region = region; } /** * to explicitly free the Finder's resources */ public void destroy() { _findInput.delete(); _findInput = null; _results.delete(); _results = null; _pattern = null; } /** * not used */ @Override public void remove(){} @Override protected void finalize() throws Throwable { super.finalize(); destroy(); } /** * internal use: exchange the source image in existing Finder * * @param simg ScreenImage */ protected void setScreenImage(ScreenImage simg) { _findInput.setSource(Image.convertBufferedImageToMat(simg.getImage())); } public boolean isValid() { return valid; } //
// /** * internal use: to be able to reuse the same Finder */ protected void setRepeating() { repeating = true; } /** * internal use: repeat with same Finder */ protected void findRepeat() { _results = Vision.find(_findInput); _cur_result_i = 0; } /** * internal use: repeat with same Finder */ protected void findAllRepeat() { Debug timing = Debug.startTimer("Finder.findAll"); _results = Vision.find(_findInput); _cur_result_i = 0; timing.end(); } // // /** * do a find op with the given image or the given text in the Finder's image * (hasNext() and next() will reveal possible match results) * @param imageOrText image file name or text * @return null. if find setup not possible */ public String find(String imageOrText) { if (!valid) { log(-1, "not valid"); return null; } Image img = Image.create(imageOrText); if (img.isText()) { return findText(imageOrText); } if (img.isValid()) { return find(img); } return null; } /** * do a find op with the given pattern in the Finder's image * (hasNext() and next() will reveal possible match results) * @param aPtn Pattern * @return null. if find setup not possible */ public String find(Pattern aPtn) { if (!valid) { log(-1, "not valid"); return null; } if (aPtn.isValid()) { _pattern = aPtn; _findInput.setTarget(aPtn.getImage().getMatNative()); _findInput.setSimilarity(aPtn.getSimilar()); _results = Vision.find(_findInput); _cur_result_i = 0; return aPtn.getFilename(); } else { return null; } } /** * do a find op with the given pattern in the Finder's image * (hasNext() and next() will reveal possible match results) * @param img Image * @return null. if find setup not possible */ public String find(Image img) { if (!valid) { log(-1, "not valid"); return null; } if (img.isValid()) { _findInput.setTarget(img.getMatNative()); _findInput.setSimilarity(Settings.MinSimilarity); _results = Vision.find(_findInput); _cur_result_i = 0; return img.getFilename(); } else if (img.isUseable()) { return find(new Pattern(img)); } else { return null; } } /** * do a text find with the given text in the Finder's image * (hasNext() and next() will reveal possible match results) * @param text text * @return null. if find setup not possible */ public String findText(String text) { if (!valid) { log(-1, "not valid"); return null; } _findInput.setTarget(TARGET_TYPE.TEXT, text); _results = Vision.find(_findInput); _cur_result_i = 0; return text; } // // /** * do a findAll op with the given image or the given text in the Finder's image * (hasNext() and next() will reveal possible match results) * @param imageOrText iamge file name or text * @return null. if find setup not possible */ public String findAll(String imageOrText) { if (!valid) { log(-1, "not valid"); return null; } Image img = Image.create(imageOrText); _image = img; if (img.isText()) { return findAllText(imageOrText); } if (img.isValid()) { return findAll(img); } return null; } /** * do a find op with the given pattern in the Finder's image * (hasNext() and next() will reveal possible match results) * @param aPtn Pattern * @return null. if find setup not possible */ public String findAll(Pattern aPtn) { if (!valid) { log(-1, "not valid"); return null; } if (aPtn.isValid()) { _image = aPtn.getImage(); _pattern = aPtn; _findInput.setTarget(aPtn.getImage().getMatNative()); _findInput.setSimilarity(aPtn.getSimilar()); _findInput.setFindAll(true); Debug timing = Debug.startTimer("Finder.findAll"); _results = Vision.find(_findInput); _cur_result_i = 0; timing.end(); return aPtn.getFilename(); } else { return null; } } /** * do a findAll op with the given image in the Finder's image * (hasNext() and next() will reveal possible match results) * @param img Image * @return null. if find setup not possible */ public String findAll(Image img) { if (!valid) { log(-1, "not valid"); return null; } if (img.isValid()) { _findInput.setTarget(img.getMatNative()); _findInput.setSimilarity(Settings.MinSimilarity); _findInput.setFindAll(true); Debug timing = Debug.startTimer("Finder.findAll"); _results = Vision.find(_findInput); _cur_result_i = 0; timing.end(); return img.getFilename(); } else { return null; } } /** * do a findAll op with the given text in the Finder's image * (hasNext() and next() will reveal possible match results) * @param text text * @return null. if find setup not possible */ public String findAllText(String text) { if (!valid) { log(-1, "not valid"); return null; } _findInput.setTarget(TARGET_TYPE.TEXT, text); _findInput.setFindAll(true); Debug timing = Debug.startTimer("Finder.findAllText"); _results = Vision.find(_findInput); _cur_result_i = 0; timing.end(); return text; } // private String setTargetSmartly(FindInput fin, String target) { if (isImageFile(target)) { //assume it's a file first String filename = Image.create(target).getFilename(); if (filename != null) { fin.setTarget(TARGET_TYPE.IMAGE, filename); return filename; } else { if (!repeating) { Debug.error(target + " looks like a file, but not on disk. Assume it's text."); } } } if (!Settings.OcrTextSearch) { Debug.error("Region.find(text): text search is currently switched off"); return target + "???"; } else { fin.setTarget(TARGET_TYPE.TEXT, target); if (TextRecognizer.getInstance() == null) { Debug.error("Region.find(text): text search is now switched off"); return target + "???"; } return target; } } private static boolean isImageFile(String fname) { int dot = fname.lastIndexOf('.'); if (dot < 0) { return false; } String suffix = fname.substring(dot + 1).toLowerCase(); if (suffix.equals("png") || suffix.equals("jpg")) { return true; } return false; } /** * * @return true if Finder has a next match, false otherwise */ @Override public boolean hasNext() { if (_results != null && _results.size() > _cur_result_i) { return true; } return false; } /** * * @return the next match or null */ @Override public Match next() { Match match = null; if (hasNext()) { FindResult fr = _results.get(_cur_result_i++); IScreen parentScreen = null; if (screenFinder && _region != null) { parentScreen = _region.getScreen(); } match = new Match(fr, parentScreen); match.setOnScreen(screenFinder); fr.delete(); if (_region != null) { match = _region.toGlobalCoord(match); } if (_pattern != null) { Location offset = _pattern.getTargetOffset(); match.setTargetOffset(offset); } match.setImage(_image); } return match; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/IRobot.java000077500000000000000000000023671315726130400237200ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.Color; import java.awt.Rectangle; /** * INTERNAL USE
* function template for (alternative) Robot implementations */ public interface IRobot { enum KeyMode { PRESS_ONLY, RELEASE_ONLY, PRESS_RELEASE }; void keyDown(String keys); void keyUp(String keys); void keyDown(int code); void keyUp(int code); void keyUp(); void pressModifiers(int modifiers); void releaseModifiers(int modifiers); void typeChar(char character, KeyMode mode); void typeKey(int key); void typeStarts(); void typeEnds(); void mouseMove(int x, int y); void mouseDown(int buttons); int mouseUp(int buttons); void mouseReset(); void clickStarts(); void clickEnds(); void smoothMove(Location dest); void smoothMove(Location src, Location dest, long ms); void mouseWheel(int wheelAmt); ScreenImage captureScreen(Rectangle screenRect); void waitForIdle(); void delay(int ms); void setAutoDelay(int ms); Color getColorAt(int x, int y); void cleanup(); boolean isRemote(); /** * Return the underlying device object (if any). */ IScreen getScreen(); } sikulix-1.1.1/API/src/main/java/org/sikuli/script/IScreen.java000077500000000000000000000020321315726130400240370ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.Rectangle; import java.io.IOException; /** * INTERNAL USE * function template for (alternative) Screen implementations */ public interface IScreen { public IRobot getRobot(); public Rectangle getBounds(); public ScreenImage capture(); public ScreenImage capture(int x, int y, int w, int h); public ScreenImage capture(Rectangle rect); public ScreenImage capture(Region reg); public boolean isOtherScreen(); public Rectangle getRect(); public void showTarget(Location location); public int getID(); public String getIDString(); public ScreenImage getLastScreenImageFromScreen(); public String getLastScreenImageFile(String path, String name) throws IOException; public int getX(); public int getW(); public int getY(); public int getH(); public ScreenImage userCapture(String string); public int getIdFromPoint(int srcx, int srcy); public String toStringShort(); } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Image.java000066400000000000000000001166541315726130400235460ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.File; import java.net.URL; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.imgproc.Imgproc; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.basics.Settings; import org.sikuli.natives.Vision; /** * This class hides the complexity behind image names given as string. *
Image does not have public nor protected constructors: use create() *
It's companion is {@link ImagePath} that maintains a list of places, where image files are * loaded from.
* Another companion {@link ImageGroup} will allow to look at images in a folder as a * group.
* An Image object:
* - has a name, either given or taken from the basename
* - keeps it's in memory buffered image in a configurable cache avoiding reload * from source
* - remembers, where it was found when searched the last time
* - can be sourced from the filesystem, from jars, from the web and from other * in memory images
* - will have features for basic image manipulation and presentation
* - contains the stuff to communicate with the underlying OpenCV based search * engine
* * This class maintains
* - a list of all images ever loaded in this session with their source * reference and a ref to the image object
* - a list of all images currently having their content in memory (buffered * image) (managed as a configurable cache)
* The caching can be configured using {@link Settings#setImageCache(int)} */ public class Image { static RunTime runTime = RunTime.get(); private static String me = "Image: "; private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private static List images = Collections.synchronizedList(new ArrayList()); private static Map imageFiles = Collections.synchronizedMap(new HashMap()); private static Map imageNames = Collections.synchronizedMap(new HashMap()); private static final int KB = 1024; private static final int MB = KB * KB; private final static String isBImg = "__BufferedImage__"; private static long currentMemory = 0; private static synchronized long currentMemoryChange(long size, long max) { long maxMemory = max; if (max < 0) { maxMemory = Settings.getImageCache() * MB; currentMemory += size; } if (currentMemory > maxMemory) { Image first; while (images.size() > 0 && currentMemory > maxMemory) { first = images.remove(0); first.bimg = null; currentMemory -= first.bsize; } if (maxMemory == 0) { currentMemory = 0; } else { currentMemory = Math.max(0, currentMemory); } } if (size < 0) { currentMemory = Math.max(0, currentMemory); } return currentMemory; } private static long currentMemoryUp(long size) { return currentMemoryChange(size, -1); } private static long currentMemoryDown(long size) { currentMemory -= size; currentMemory = Math.max(0, currentMemory); return currentMemoryChange(-size, -1); } private static long currentMemoryDownUp(int sizeOld, int sizeNew) { currentMemoryDown(sizeOld); return currentMemoryUp(sizeNew); } private static boolean isCaching() { return Settings.getImageCache() > 0; } public static void clearCache(int maxSize) { currentMemoryChange(0, maxSize); } public static void reload(String fpImage) { // URL uImage = FileManager.makeURL(fpImage); URL uImage = imageNames.get(fpImage); if (imageFiles.containsKey(uImage)) { Image image = imageFiles.get(uImage); int sizeOld = image.bsize; if (null != image.loadAgain()) { currentMemoryDownUp(sizeOld, image.bsize); image.setLastSeen(null, 0); } } } private static boolean ideShouldReload = false; protected boolean wasRecaptured = false; public static void setIDEshouldReload(Image img) { ideShouldReload = true; img.wasRecaptured = true; img.lastSeen = null; } public static boolean getIDEshouldReload() { boolean state = ideShouldReload; ideShouldReload = false; return state; } public boolean isRecaptured() { boolean state = wasRecaptured; wasRecaptured = false; return state; } // private String imageName = null; private String imageNameGiven = null; private boolean bHasIOException = false; public boolean hasIOException() { return bHasIOException; } public void setHasIOException(boolean state) { bHasIOException = state; } public String getImageName() { return imageName; } public Image setImageName(String imageName) { this.imageName = imageName; return this; } // // private URL fileURL = null; private String imageAsFile = null; public URL getFileURL() { return fileURL; } public Image setFileURL(URL fileURL) { this.fileURL = fileURL; return this; } // // private BufferedImage bimg = null; protected Image setBimg(BufferedImage bimg) { this.bimg = bimg; if (bimg != null) { bwidth = bimg.getWidth(); bheight = bimg.getHeight(); bsize = bimg.getData().getDataBuffer().getSize(); } else { bsize = 0; bwidth = -1; bheight = -1; } return this; } private int bsize = 0; private int bwidth = -1; private int bheight = -1; // private ImageGroup group = null; // private boolean imageIsText = false; /** * * @return true if the given image name did not give a valid image so it might * be text to search */ public boolean isText() { return imageIsText; } private Image setIsText(boolean val) { imageIsText = val; return this; } public String getText() { return imageNameGiven; } // // private boolean imageIsAbsolute = false; /** * * @return true if image was given with absolute filepath */ public boolean isAbsolute() { return imageIsAbsolute; } public Image setIsAbsolute(boolean val) { imageIsAbsolute = val; return this; } // // private boolean imageIsBundled = false; private Image setIsBundled(boolean imageIsBundled) { this.imageIsBundled = imageIsBundled; return this; } /** * INTERNAL USE: image is contained in a bundle (.sikuli) * @return true/false */ public boolean isBundled() { return imageIsBundled; } // // private boolean imageIsPattern = false; /** * true if this image contains pattern aspects
* only useable with the new ImageFinder * @return true if yes, false otherwise */ public boolean isPattern() { return imageIsPattern; } public Image setIsPattern(boolean imageIsPattern) { this.imageIsPattern = imageIsPattern; return this; } //
// private int waitAfter; /** * Get the value of waitAfter * * @return the value of waitAfter */ public int getWaitAfter() { return waitAfter; } /** * Set the value of waitAfter * * @param waitAfter new value of waitAfter * @return the image */ public Image setWaitAfter(int waitAfter) { this.waitAfter = waitAfter; return this; } // // private Location offset = new Location(0, 0); /** * Get the value of offset * * @return the value of offset */ public Location getOffset() { return offset; } /** * Set the value of offset * * @param offset new value of offset * @return the image */ public Image setOffset(Location offset) { this.offset = offset; return this; } // // private float similarity = (float) Settings.MinSimilarity; /** * Get the value of similarity * * @return the value of similarity */ public float getSimilarity() { return similarity; } /** * Set the value of similarity * * @param similarity new value of similarity * @return the image */ public Image setSimilarity(float similarity) { this.similarity = similarity; return this; } // // private Rectangle lastSeen = null; private double lastScore = 0.0; /** * if the image was already found before * * @return the rectangle where it was found */ public Rectangle getLastSeen() { return lastSeen; } /** * if the image was already found before * * @return the similarity score */ public double getLastSeenScore() { return lastScore; } /** * Internal Use: set the last seen info after a find * * @param lastSeen Match * @param sim SimilarityScore * @return the image */ protected Image setLastSeen(Rectangle lastSeen, double sim) { this.lastSeen = lastSeen; this.lastScore = sim; if (group != null) { group.addImageFacts(this, lastSeen, sim); } return this; } // private boolean beSilent = false; /** * to support a raster over the image */ private int rows = 0; private int cols = 0; private int rowH = 0; private int colW = 0; private int rowHd = 0; private int colWd = 0; @Override public String toString() { return String.format( (imageName != null ? imageName : "__UNKNOWN__") + ": (%dx%d)", bwidth, bheight) + (lastSeen == null ? "" : String.format(" seen at (%d, %d) with %.2f", lastSeen.x, lastSeen.y, lastScore)); } private Image() { } private Image(String fname, URL fURL) { init(fname, fURL, true); } private Image(String fname, URL fURL, boolean silent) { init(fname, fURL, silent); } private void init(String fileName, URL fURL, boolean silent) { imageName = fileName; if (imageName.isEmpty() || fURL == null) { return; } fileURL = fURL; if (ImagePath.isImageBundled(fURL)) { imageIsBundled = true; imageName = new File(imageName).getName(); } beSilent = silent; load(); } private BufferedImage load() { BufferedImage bImage = null; if (fileURL != null) { bimg = null; try { bImage = ImageIO.read(fileURL); } catch (Exception e) { if (!beSilent) { log(-1, "could not be loaded: %s", fileURL); } bHasIOException = true; fileURL = null; return null; } if (imageName != null) { imageFiles.put(fileURL, this); imageNames.put(imageName, fileURL); bwidth = bImage.getWidth(); bheight = bImage.getHeight(); bsize = bImage.getData().getDataBuffer().getSize(); log(lvl, "loaded: %s (%s)", imageName, fileURL); if (isCaching()) { int maxMemory = Settings.getImageCache() * MB; currentMemoryUp(bsize); bimg = bImage; images.add(this); log(lvl, "cached: %s (%d KB) (# %d KB %d -- %d %% of %d MB)", imageName, getKB(), images.size(), (int) (currentMemory / KB), (int) (100 * currentMemory / maxMemory), (int) (maxMemory / MB)); } } else { log(-1, "invalid! not loaded! %s", fileURL); } } return bImage; } private BufferedImage loadAgain() { BufferedImage bImage = null; if (fileURL != null) { bimg = null; try { bImage = ImageIO.read(fileURL); } catch (Exception e) { if (!beSilent) { log(-1, "could not be loaded again: %s", fileURL); } bHasIOException = true; imageFiles.remove(fileURL); return null; } imageFiles.put(fileURL, this); imageNames.put(imageName, fileURL); bwidth = bImage.getWidth(); bheight = bImage.getHeight(); bsize = bImage.getData().getDataBuffer().getSize(); log(lvl, "loaded again: %s (%s)", imageName, fileURL); } return bImage; } private Image copy() { Image imgTarget = new Image(); imgTarget.setImageName(imageName); imgTarget.setFileURL(fileURL); imgTarget.setBimg(bimg); imgTarget.setGroup(group); imgTarget.setIsAbsolute(imageIsAbsolute); imgTarget.setIsText(imageIsText); imgTarget.setIsBundled(imageIsBundled); imgTarget.setLastSeen(getLastSeen(), getLastSeenScore()); imgTarget.setHasIOException(hasIOException()); if (isPattern()) { imgTarget.setSimilarity(similarity); imgTarget.setOffset(offset); imgTarget.setWaitAfter(waitAfter); imgTarget.setIsPattern(true); } return imgTarget; } /** * create a new Image as copy of the given Image * @param imgSrc given Image * @return new Image */ public static Image create(Image imgSrc) { return imgSrc.copy(); } /** * create a new image from a filename
* file ending .png is added if missing (currently valid: png, jpg, jpeg)
* relative filename: [...path.../]name[.png] is searched on current image path
* absolute filename is taken as is * if image exists, it is loaded to cache
* already loaded image with same name (given path) is reused (taken from cache)
* * if image not found, it might be a text to be searched (imageIsText = true) * * @param fName image filename * @return an Image object (might not be valid - check with isValid()) */ public static Image create(String fName) { Image img = get(fName, false); return createImageValidate(img, true); } /** * create a new Image with Pattern aspects from an existing Pattern * @param p a Pattern * @return the new Image */ public static Image create(Pattern p) { Image img = p.getImage().copy(); img.setIsPattern(true); img.setSimilarity(p.getSimilar()); img.setOffset(p.getTargetOffset()); img.setWaitAfter(p.getTimeAfter()); return img; } /** * create a new image from the given url
* file ending .png is added if missing
* filename: ...url-path.../name[.png] is loaded from the url and and cached *
* already loaded image with same url is reused (reference) and taken from * cache * * @param url image file URL * @return the image */ public static Image create(URL url) { Image img = get(url); if (img == null) { img = new Image(url); } return createImageValidate(img, true); } protected static Image getImageFromTarget(PSI target) { if (target instanceof Pattern) { return ((Pattern) target).getImage(); } else if (target instanceof String) { Image img = get((String) target, true); img = createImageValidate(img, true); return img; } else if (target instanceof Image) { return (Image) target; } else { runTime.abortScripting("aborting script at:", String.format("find, wait, exists: invalid parameter: %s", target)); } return null; } /** * FOR INTERNAL USE: from IDE - suppresses load error message * * @param fName image filename * @return this */ public static Image createThumbNail(String fName) { Image img = get(fName, true); return createImageValidate(img, false); } private static Image createImageValidate(Image img, boolean verbose) { if (img == null) { log(-1, "Image not valid, creating empty Image"); return new Image("", null); } if (!img.isValid()) { if (Settings.OcrTextSearch) { img.setIsText(true); if (Settings.isValidImageFilename(img.getName())) { img.setIsText(false); } } else { if (verbose) { log(-1, "Image not valid, but TextSearch is switched off!"); } } } return img; } /** * stores the image as PNG file in the standard temp folder * with a created filename (sikuli-image-#unique-random#.png) * if not yet stored before * * @return absolute path to stored file */ public String asFile() { if (imageAsFile == null) { if (bimg != null) { imageAsFile = FileManager.saveTmpImage(bimg); } } return imageAsFile; } /** * FOR INTERNAL USE: see get(String, boolean) * * @param fName image filename * @return this */ protected static Image get(String fName) { return get(fName, false); } /** * FOR INTERNAL USE: tries to get the image from the cache, if not cached yet: * create and load a new image * * @param fName image filename * @param silent true: suppress some error messages * @return this */ private static Image get(String fName, boolean silent) { if (fName == null || fName.isEmpty()) { return null; } Image img = null; if (fName.startsWith("\t") && fName.endsWith("\t")) { fName = fName.substring(1, fName.length() - 1); img = new Image(); img.setIsText(true); } else { fName = FileManager.slashify(fName, false); URL fURL = null; String fileName = Settings.getValidImageFilename(fName); if (fileName.isEmpty()) { log(-1, "not a valid image type: " + fName); fileName = fName; } File imgFile = new File(fileName); if (imgFile.isAbsolute()) { if (imgFile.exists()) { fURL = FileManager.makeURL(fileName); } } else { fURL = imageNames.get(fileName); if (fURL == null) { fURL = ImagePath.find(fileName); } } if (fURL != null) { img = imageFiles.get(fURL); if (img != null && null == imageNames.get(img.imageName)) { imageNames.put(img.imageName, fURL); } } if (img == null) { img = new Image(fileName, fURL, silent); img.setIsAbsolute(imgFile.isAbsolute()); } else { if (img.bimg != null) { log(3, "reused: %s (%s)", img.imageName, img.fileURL); } else { if (Settings.getImageCache() > 0) { img.load(); } } } } img.imageNameGiven = fName; return img; } protected static void set(Image img) { URL fURL = null; File imgFile = new File(img.getName()); if (imgFile.isAbsolute()) { if (imgFile.exists()) { fURL = FileManager.makeURL(img.getName()); } } else { fURL = imageNames.get(img.getName()); if (fURL == null) { fURL = ImagePath.find(img.getName()); } } if (fURL != null) { img.init(img.getName(), fURL, true); } } protected static Image get(URL imgURL) { return imageFiles.get(imgURL); } private Image(URL fURL) { if ("file".equals(fURL.getProtocol())) { init(fURL.getPath(), fURL, true); } else { init(getNameFromURL(fURL), fURL, true); } } private static String getNameFromURL(URL fURL) { //TODO add handling for http if ("jar".equals(fURL.getProtocol())) { int n = fURL.getPath().lastIndexOf(".jar!/"); int k = fURL.getPath().substring(0, n).lastIndexOf("/"); if (n > -1) { return "JAR:" + fURL.getPath().substring(k + 1, n) + fURL.getPath().substring(n + 5); } } return "???:" + fURL.getPath(); } /** * create a new image from a buffered image
* can only be reused with the object reference * * @param img BufferedImage */ public Image(BufferedImage img) { this(img, null); } /** * create a new image from a buffered image
* giving it a descriptive name for printout and logging
* can only be reused with the object reference * * @param img BufferedImage * @param name descriptive name */ public Image(BufferedImage img, String name) { imageName = isBImg; if (name != null) { imageName += name; } bimg = img; bwidth = bimg.getWidth(); bheight = bimg.getHeight(); log(lvl, "BufferedImage: (%d, %d)%s", bwidth, bheight, (name == null ? "" : " with name: " + name)); } /** * create a new image from a Sikuli ScreenImage (captured)
* can only be reused with the object reference * * @param img ScreenImage */ public Image(ScreenImage img) { this(img.getImage(), null); } /** * create a new image from a Sikuli ScreenImage (captured)
* giving it a descriptive name for printout and logging
* can only be reused with the object reference * * @param img ScreenImage * @param name descriptive name */ public Image(ScreenImage img, String name) { this(img.getImage(), name); } /** * INTERNAL USE: IDE: to get rid of cache entries at script save, close or * save as * * @param bundlePath absolute path for an image set in this folder */ public static void purge(String bundlePath) { if (imageFiles.isEmpty() || ImagePath.getPaths().get(0) == null) { return; } URL pathURL = FileManager.makeURL(bundlePath); if (!ImagePath.getPaths().get(0).pathURL.equals(pathURL)) { log(-1, "purge: not current bundlepath: " + pathURL); return; } purge(pathURL); } protected static void purge(ImagePath.PathEntry path) { if (path == null) { return; } purge(path.pathURL); } protected static synchronized void purge(URL pathURL) { List imagePurgeList = new ArrayList<>(); List imageNamePurgeList = new ArrayList<>(); URL imgURL; Image img; log(lvl, "purge: ImagePath: %s", pathURL.getPath()); Iterator> it = imageFiles.entrySet().iterator(); Map.Entry entry; while (it.hasNext()) { entry = it.next(); imgURL = entry.getKey(); if (imgURL.toString().startsWith(pathURL.toString())) { log(lvl + 1, "purge: URL: %s", imgURL.toString()); img = entry.getValue(); imagePurgeList.add(img); imageNamePurgeList.add(img.imageName); it.remove(); } } if (!imagePurgeList.isEmpty()) { Iterator bit = images.iterator(); while (bit.hasNext()) { img = bit.next(); if (imagePurgeList.contains(img)) { bit.remove(); log(lvl + 1, "purge: bimg: %s", img); currentMemoryDown(img.bsize); } } } for (String name : imageNamePurgeList) { imageNames.remove(name); } } public boolean isFile() { if (isValid()) { URL furl = getURL(); if ("file".equals(furl.getProtocol())) { return true; } } return false; } public File remove() { URL furl = null; if (isFile()) { furl = getURL(); unCacheImage(furl); return new File(furl.getPath()); } return null; } public void delete() { File fImg = remove(); if (null != fImg) FileManager.deleteFileOrFolder(fImg); } private String hasBackup = ""; protected boolean backup() { if (isValid()) { File fOrg = new File(fileURL.getPath()); File fBack = new File(fOrg.getParentFile(), "_BACKUP_" + fOrg.getName()); if (FileManager.xcopy(fOrg, fBack)) { hasBackup = fBack.getPath(); log(lvl, "backup: %s created", fBack.getName()); return true; } log(-1, "backup: %s did not work", fBack.getName()); } return false; } protected boolean restore() { if (!hasBackup.isEmpty()) { File fBack = new File(hasBackup); File fOrg = new File(hasBackup.replace("_BACKUP_", "")); if (FileManager.xcopy(fBack, fOrg)) { log(lvl, "restore: %s restored", fOrg.getName()); FileManager.deleteFileOrFolder(fBack); hasBackup = ""; return true; } log(-1, "restore: %s did not work", fBack.getName()); } return false; } /** * purge the given image file's in memory image data and remove it from cache. * @param imgFileName an absolute filename */ public static void unCacheBundledImage(String imgFileName) { URL imgURL = FileManager.makeURL(new File(imgFileName).getAbsolutePath()); unCacheImage(imgURL); } /** * purge the given image's in memory image data and remove it from cache. * @param imgURL URL of an image file */ public static void unCacheImage(URL imgURL) { Image img = imageFiles.get(imgURL); if (img == null) { return; } currentMemoryDown(img.bsize); img.setBimg(null); images.remove(img); } /** * Print the current state of the cache */ public static void dump() { dump(0); } /** * Print the current state of the cache, verbosity depends on debug level * @param lvl debug level used here */ public static void dump(int lvl) { log(lvl, "--- start of Image dump ---"); ImagePath.dump(lvl); log(lvl, "ImageFiles entries: %d", imageFiles.size()); Iterator> it = imageFiles.entrySet().iterator(); Map.Entry entry; while (it.hasNext()) { entry = it.next(); log(lvl, entry.getKey().toString()); } log(lvl, "ImageNames entries: %d", imageNames.size()); Iterator> nit = imageNames.entrySet().iterator(); Map.Entry name; while (nit.hasNext()) { name = nit.next(); log(lvl, "%s %d KB (%s)", new File(name.getKey()).getName(), imageFiles.get(name.getValue()).getKB(), name.getValue()); } if (Settings.getImageCache() == 0) { log(lvl, "Cache state: switched off!"); } else { log(lvl, "Cache state: Max %d MB (entries: %d used: %d %% %d KB)", Settings.getImageCache(), images.size(), (int) (100 * currentMemory / (Settings.getImageCache() * MB)), (int) (currentMemory / KB)); } log(lvl, "--- end of Image dump ---"); } /** * clears all caches (should only be needed for debugging) */ public static void reset() { clearCache(0); imageNames.clear(); imageFiles.clear(); } /** * Get the image's descriptive name * * @return the name */ public String getName() { if (isText()) { return imageNameGiven; } return imageName; } /** * * @return the current ImageGroup */ public ImageGroup getGroup() { return group; } /** * set the ImageGroup this image should belong to * * @param group ImageGroup */ public void setGroup(ImageGroup group) { this.group = group; } /** * check whether image is available for Finder.find()
* This is for backward compatibility
* The new ImageFinder uses isUsable() * * @return true if lodable from file or is an in memory image */ public boolean isValid() { return fileURL != null || getName().contains(isBImg); } /** * checks, wether the Image can be used with the new ImageFinder * @return true/false */ public boolean isUseable() { return isValid() || imageIsPattern; } /** * * @return the evaluated url for this image (might be null) */ public URL getURL() { return fileURL; } /** * @return the image's absolute filename or null if jar, http or in memory * image */ public String getFilename() { if (fileURL != null && "file".equals(fileURL.getProtocol())) { return new File(fileURL.getPath()).getAbsolutePath(); } else { return imageName; } } /** * return the image's BufferedImage (load it if not in cache) * * @return BufferedImage (might be null) */ public BufferedImage get() { return get(true); } protected BufferedImage get(boolean shouldLoad) { if (bimg != null) { if (fileURL == null) { log(lvl + 1, "getImage inMemory: %s", imageName); } else { log(lvl + 1, "getImage from cache: %s", imageName); } return bimg; } else { if (shouldLoad) { return load(); } else { return null; } } } /** * * @return size of image */ public Dimension getSize() { return new Dimension(bwidth, bheight); } private int getKB() { if (bimg == null) { return 0; } return (int) bsize / KB; } /** * resize the loaded image with factor using Graphics2D.drawImage * @param factor resize factor * @return a new BufferedImage resized (width*factor, height*factor) */ public BufferedImage resize(float factor) { int type; BufferedImage bufimg = get(); type = bufimg.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : bufimg.getType(); int width = (int) (getSize().getWidth() * factor); int height = (int) (getSize().getHeight() * factor); BufferedImage resizedImage = new BufferedImage(width, height, type); Graphics2D g = resizedImage.createGraphics(); g.drawImage(bufimg, 0, 0, width, height, null); g.dispose(); return resizedImage; } /** * create a sub image from this image * * @param x pixel column * @param y pixel row * @param w width * @param h height * @return the new image */ public Image getSub(int x, int y, int w, int h) { BufferedImage bi = createBufferedImage(w, h); Graphics2D g = bi.createGraphics(); g.drawImage(get().getSubimage(x, y, w, h), 0, 0, null); g.dispose(); return new Image(bi); } /** * create a sub image from this image * * @param part (the constants Region.XXX as used with {@link Region#get(int)}) * @return the sub image */ public Image getSub(int part) { Rectangle r = Region.getRectangle(new Rectangle(0, 0, getSize().width, getSize().height), part); return getSub(r.x, r.y, r.width, r.height); } /** * store info: this image is divided vertically into n even rows
* a preparation for using getRow() * * @param n number of rows * @return the top row */ public Image setRows(int n) { return setRaster(n, 0); } /** * store info: this image is divided horizontally into n even columns
* a preparation for using getCol() * * @param n number of Columns * @return the leftmost column */ public Image setCols(int n) { return setRaster(0, n); } /** * * @return number of eventually defined rows in this image or 0 */ public int getRows() { return rows; } /** * * @return height of eventually defined rows in this image or 0 */ public int getRowH() { return rowH; } /** * * @return number of eventually defined columns in this image or 0 */ public int getCols() { return cols; } /** * * @return width of eventually defined columns in this image or 0 */ public int getColW() { return colW; } /** * store info: this image is divided into a raster of even cells
* a preparation for using getCell() * * @param r number of rows * @param c number of columns * @return the top left cell */ public Image setRaster(int r, int c) { rows = r; cols = c; if (r > 0) { rowH = (int) (getSize().height / r); rowHd = getSize().height - r * rowH; } if (c > 0) { colW = (int) (getSize().width / c); colWd = getSize().width - c * colW; } return getCell(0, 0); } /** * get the specified row counting from 0, if rows or raster are setup
negative * counts reverse from the end (last = -1)
values outside range are 0 or last * respectively * * @param r row number * @return the row as new image or the image itself, if no rows are setup */ public Image getRow(int r) { if (rows == 0) { return this; } if (r < 0) { r = rows + r; } r = Math.max(0, r); r = Math.min(r, rows - 1); return getSub(0, r * rowH, getSize().width, rowH); } /** * get the specified column counting from 0, if columns or raster are setup
* negative counts reverse from the end (last = -1)
values outside range are 0 * or last respectively * * @param c column number * @return the column as new image or the image itself, if no columns are * setup */ public Image getCol(int c) { if (cols == 0) { return this; } if (c < 0) { c = cols + c; } c = Math.max(0, c); c = Math.min(c, cols - 1); return getSub(c * colW, 0, colW, getSize().height); } /** * get the specified cell counting from (0, 0), if a raster is setup
* negative counts reverse from the end (last = -1)
values outside range are 0 * or last respectively * * @param r row number * @param c column number * @return the cell as new image or the image itself, if no raster is setup */ public Image getCell(int r, int c) { if (rows == 0) { return getCol(c); } if (cols == 0) { return getRow(r); } if (rows == 0 && cols == 0) { return this; } if (r < 0) { r = rows - r; } if (c < 0) { c = cols - c; } r = Math.max(0, r); r = Math.min(r, rows - 1); c = Math.max(0, c); c = Math.min(c, cols - 1); return getSub(c * colW, r * rowH, colW, rowH); } /** * get the OpenCV Mat version of the image's BufferedImage * * @return OpenCV Mat */ public Mat getMat() { return createMat(get()); } protected static Mat createMat(BufferedImage img) { if (img != null) { Debug timer = Debug.startTimer("Mat create\t (%d x %d) from \n%s", img.getWidth(), img.getHeight(), img); Mat mat_ref = new Mat(img.getHeight(), img.getWidth(), CvType.CV_8UC4); timer.lap("init"); byte[] data; BufferedImage cvImg; ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); int[] nBits = {8, 8, 8, 8}; ColorModel cm = new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); SampleModel sm = cm.createCompatibleSampleModel(img.getWidth(), img.getHeight()); DataBufferByte db = new DataBufferByte(img.getWidth() * img.getHeight() * 4); WritableRaster r = WritableRaster.createWritableRaster(sm, db, new Point(0, 0)); cvImg = new BufferedImage(cm, r, false, null); timer.lap("empty"); Graphics2D g = cvImg.createGraphics(); g.drawImage(img, 0, 0, null); g.dispose(); timer.lap("created"); data = ((DataBufferByte) cvImg.getRaster().getDataBuffer()).getData(); mat_ref.put(0, 0, data); Mat mat = new Mat(); timer.lap("filled"); Imgproc.cvtColor(mat_ref, mat, Imgproc.COLOR_RGBA2BGR, 3); timer.end(); return mat; } else { return null; } } /** * to get old style OpenCV Mat for FindInput * * @return SWIG interfaced OpenCV Mat * @deprecated */ @Deprecated protected org.sikuli.natives.Mat getMatNative() { return convertBufferedImageToMat(get()); } protected static org.sikuli.natives.Mat convertBufferedImageToMat(BufferedImage img) { if (img != null) { long theMatTime = new Date().getTime(); byte[] data = convertBufferedImageToByteArray(img); org.sikuli.natives.Mat theMat = Vision.createMat(img.getHeight(), img.getWidth(), data); if (Settings.FindProfiling) { Debug.logp("[FindProfiling] createCVMat [%d x %d]: %d msec", img.getWidth(), img.getHeight(), new Date().getTime() - theMatTime); } return theMat; } else { return null; } } protected static byte[] convertBufferedImageToByteArray(BufferedImage img) { if (img != null) { BufferedImage cvImg = createBufferedImage(img.getWidth(), img.getHeight()); Graphics2D g = cvImg.createGraphics(); g.drawImage(img, 0, 0, null); g.dispose(); return ((DataBufferByte) cvImg.getRaster().getDataBuffer()).getData(); } else { return null; } } protected static BufferedImage createBufferedImage(int w, int h) { ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); int[] nBits = {8, 8, 8, 8}; ColorModel cm = new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); SampleModel sm = cm.createCompatibleSampleModel(w, h); DataBufferByte db = new DataBufferByte(w * h * 4); WritableRaster r = WritableRaster.createWritableRaster(sm, db, new Point(0, 0)); BufferedImage bm = new BufferedImage(cm, r, false, null); return bm; } // **************** for Tesseract4Java ******************** /** * Converts BufferedImage to ByteBuffer. * * @param bi Input image * @return pixel data */ public static ByteBuffer convertImageData(BufferedImage bi) { DataBuffer buff = bi.getRaster().getDataBuffer(); // ClassCastException thrown if buff not instanceof DataBufferByte because raster data is not necessarily bytes. // Convert the original buffered image to grayscale. if (!(buff instanceof DataBufferByte)) { bi = convertImageToGrayscale(bi); buff = bi.getRaster().getDataBuffer(); } byte[] pixelData = ((DataBufferByte) buff).getData(); // return ByteBuffer.wrap(pixelData); ByteBuffer buf = ByteBuffer.allocateDirect(pixelData.length); buf.order(ByteOrder.nativeOrder()); buf.put(pixelData); buf.flip(); return buf; } /** * A simple method to convert an image to gray scale. * * @param image input image * @return a monochrome image */ public static BufferedImage convertImageToGrayscale(BufferedImage image) { BufferedImage tmp = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); Graphics2D g2 = tmp.createGraphics(); g2.drawImage(image, 0, 0, null); g2.dispose(); return tmp; } /** * find an image in another image * @param img image * @return a Match or null */ public Match find(Image img) { log(-1, "find: not implemented yet"); return null; } /** * find all images in another image * @param img image * @return Match or null */ public Iterator findAll(Image img) { log(-1, "findAll: not implemented yet"); return null; } /** * OCR-read the text from the image * @return the text or null */ public String text() { //TODO: use Tess4J here already?? if (Settings.OcrTextRead) { TextRecognizer tr = TextRecognizer.getInstance(); if (tr == null) { Debug.error("text: text recognition is now switched off"); return null; } String textRead = tr.recognize(this.get()); log(lvl, "text: #(" + textRead + ")#"); return textRead; } Debug.error("text: text recognition is currently switched off"); return null; } /** * convenience method: get text from given image file * @param imgFile image filename * @return the text or null */ public static String text(String imgFile) { return create(imgFile).text(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/ImageFind.java000066400000000000000000000266151315726130400243440ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.MatOfDouble; import org.opencv.core.Rect; import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; /** * EXPERIMENTAL --- INTERNAL USE ONLY
* is not official API --- will not be in version 2 */ public class ImageFind implements Iterator{ private static String me = "ImageFind: "; private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private ImageFinder owner = null; private boolean isValid = false; private boolean isInnerFind = false; private Image pImage = null; private Mat probe = new Mat(); private boolean isPlainColor = false; private boolean isBlack = false; private double similarity = Settings.MinSimilarity; private double waitingTime = Settings.AutoWaitTimeout; private boolean shouldCheckLastSeen = Settings.CheckLastSeen; private Object[] findArgs = null; private int resizeMinDownSample = 12; private double resizeFactor; private float[] resizeLevels = new float[] {1f, 0.4f}; private int resizeMaxLevel = resizeLevels.length - 1; private double resizeMinSim = 0.9; private double resizeMinFactor = 1.5; private Core.MinMaxLocResult findDownRes = null; private int sorted; public static final int AS_ROWS = 0; public static final int AS_COLUMNS = 1; public static final int BEST_FIRST = 2; private int finding = -1; public static final int FINDING_ANY = 0; public static final int FINDING_SOME = 1; public static final int FINDING_ALL = 2; private int count = 0; public static int SOME_COUNT = 5; public static int ALL_MAX = 100; private int allMax = 0; private List matches = Collections.synchronizedList(new ArrayList()); private boolean repeating; private long lastFindTime = 0; private long lastSearchTime = 0; public ImageFind() { matches.add(null); } public boolean isValid() { return true; } public void setIsInnerFind() { isInnerFind = true; } void setSimilarity(double sim) { similarity = sim; } public void setFindTimeout(double t) { waitingTime = t; } public void setFinding(int ftyp) { finding = ftyp; } public void setSorted(int styp) { sorted = styp; } public void setCount(int c) { count = c; } public List getMatches() { return matches; } protected boolean checkFind(ImageFinder owner, Object pprobe, Object... args) { if (owner.isValid()) { this.owner = owner; } else { return false; } isValid = false; shouldCheckLastSeen = Settings.CheckLastSeen; if (pprobe instanceof String) { pImage = Image.create((String) pprobe); if (pImage.isValid()) { isValid = true; } } else if (pprobe instanceof Image) { if (((Image) pprobe).isValid()) { isValid = true; pImage = (Image) pprobe; } } else if (pprobe instanceof Pattern) { if (((Pattern) pprobe).getImage().isValid()) { isValid = true; pImage = ((Pattern) pprobe).getImage(); similarity = ((Pattern) pprobe).getSimilar(); } } else if (pprobe instanceof Mat) { isValid = true; probe = (Mat) pprobe; waitingTime = 0.0; shouldCheckLastSeen = false; } else { log(-1, "find(... some, any, all): probe invalid (not Pattern, String nor valid Image)"); return false; } if (probe.empty()) { probe = Image.createMat(pImage.get()); } checkProbe(); if (!owner.isImage()) { if (args.length > 0) { if (args[0] instanceof Integer) { waitingTime = 0.0 + (Integer) args[0]; } else if (args[0] instanceof Double) { waitingTime = (Double) args[0]; } } if (args.length > 1) { findArgs = Arrays.copyOfRange(args, 1, args.length); } else { findArgs = null; } } return isValid; } private void checkProbe() { MatOfDouble pMean = new MatOfDouble(); MatOfDouble pStdDev = new MatOfDouble(); Core.meanStdDev(probe, pMean, pStdDev); double min = 0.00001; isPlainColor = false; double sum = 0.0; double arr[] = pStdDev.toArray(); for (int i = 0; i < arr.length; i++) { sum += arr[i]; } if (sum < min) { isPlainColor = true; } sum = 0.0; arr = pMean.toArray(); for (int i = 0; i < arr.length; i++) { sum += arr[i]; } if (sum < min && isPlainColor) { isBlack = true; } resizeFactor = Math.min(((double) probe.width())/resizeMinDownSample, ((double) probe.height())/resizeMinDownSample); resizeFactor = Math.max(1.0, resizeFactor); } protected ImageFind doFind() { Debug.enter(me + ": doFind"); Core.MinMaxLocResult fres = null; repeating = false; long begin = (new Date()).getTime(); long lap; while (true) { lastFindTime = (new Date()).getTime(); if (shouldCheckLastSeen && !repeating && !owner.isImage && pImage.getLastSeen() != null) { log(3, "checkLastSeen: trying ..."); ImageFinder f = new ImageFinder(new Region(pImage.getLastSeen())); if (null != f.findInner(probe, pImage.getLastSeenScore() - 0.01)) { log(lvl, "checkLastSeen: success"); set(f.next()); if (pImage != null) { pImage.setLastSeen(get().getRect(), get().getScore()); } break; } log(lvl, "checkLastSeen: not found"); } if (!owner.isMultiFinder || owner.base.empty()) { if (owner.isRegion) { owner.setBase(owner.region.getScreen().capture(owner.region).getImage()); } else if (owner.isScreen) { owner.setBase(owner.screen.capture().getImage()); } } if (!isInnerFind && resizeFactor > resizeMinFactor) { log(3, "downsampling: trying ..."); doFindDown(0, resizeFactor); fres = findDownRes; } if (fres == null) { if (!isInnerFind) { log(3, "downsampling: not found with (%f) - trying original size", resizeFactor); } fres = doFindDown(0, 0.0); if(fres != null && fres.maxVal > similarity - 0.01) { set(new Match((int) fres.maxLoc.x + owner.offX, (int) fres.maxLoc.y + owner.offY, probe.width(), probe.height(), fres.maxVal, null, null)); } } else { log(lvl, "downsampling: success: adjusting match"); set(checkFound(fres)); } lastFindTime = (new Date()).getTime() - lastFindTime; if (hasNext()) { get().setTimes(lastFindTime, lastSearchTime); if (pImage != null) { pImage.setLastSeen(get().getRect(), get().getScore()); } break; } else { if (isInnerFind || owner.isImage()) { break; } else { if (waitingTime < 0.001 || (lap = (new Date()).getTime() - begin) > waitingTime * 1000) { break; } if (owner.MaxTimePerScan > lap) { try { Thread.sleep(owner.MaxTimePerScan - lap); } catch (Exception ex) { } } repeating = true; } } } return this; } private Match checkFound(Core.MinMaxLocResult res) { Match match = null; ImageFinder f; Rect r = null; if (owner.isImage()) { int off = ((int) resizeFactor) + 1; r = getSubMatRect(owner.base, (int) res.maxLoc.x, (int) res.maxLoc.y, probe.width(), probe.height(), off); f = new ImageFinder(owner.base.submat(r)); } else { f = new ImageFinder((new Region((int) res.maxLoc.x + owner.offX, (int) res.maxLoc.y + owner.offY, probe.width(), probe.height())).grow(((int) resizeFactor) + 1)); } if (null != f.findInner(probe, similarity)) { log(lvl, "check after downsampling: success"); match = f.next(); if (owner.isImage()) { match.x += r.x; match.y += r.y; } } return match; } private static Rect getSubMatRect(Mat mat, int x, int y, int w, int h, int margin) { x = Math.max(0, x - margin); y = Math.max(0, y - margin); w = Math.min(w + 2 * margin, mat.width() - x); h = Math.min(h + 2 * margin, mat.height()- y); return new Rect(x, y, w, h); } private Core.MinMaxLocResult doFindDown(int level, double factor) { Debug.enter(me + ": doFindDown (%d - 1/%.2f)", level, factor * resizeLevels[level]); Debug timer = Debug.startTimer("doFindDown"); Mat b = new Mat(); Mat p = new Mat(); Core.MinMaxLocResult dres = null; double rfactor; if (factor > 0.0) { rfactor = factor * resizeLevels[level]; if (rfactor < resizeMinFactor) return null; Size sb = new Size(owner.base.cols()/rfactor, owner.base.rows()/factor); Size sp = new Size(probe.cols()/rfactor, probe.rows()/factor); Imgproc.resize(owner.base, b, sb, 0, 0, Imgproc.INTER_AREA); Imgproc.resize(probe, p, sp, 0, 0, Imgproc.INTER_AREA); dres = doFindMatch(b, p); log(lvl, "doFindDown: score: %.2f at (%d, %d)", dres.maxVal, (int) (dres.maxLoc.x * rfactor), (int) (dres.maxLoc.y * rfactor)); } else { dres = doFindMatch(owner.base, probe); timer.end(); return dres; } if (dres.maxVal < resizeMinSim) { if (level == resizeMaxLevel) { timer.end(); return null; } if (level == 0) { findDownRes = null; } level++; doFindDown(level, factor); } else { dres.maxLoc.x *= rfactor; dres.maxLoc.y *= rfactor; findDownRes = dres; } timer.end(); return null; } private Core.MinMaxLocResult doFindMatch(Mat base, Mat probe) { Mat res = new Mat(); Mat bi = new Mat(); Mat pi = new Mat(); if (!isPlainColor) { Imgproc.matchTemplate(base, probe, res, Imgproc.TM_CCOEFF_NORMED); } else { if (isBlack) { Core.bitwise_not(base, bi); Core.bitwise_not(probe, pi); } else { bi = base; pi = probe; } Imgproc.matchTemplate(bi, pi, res, Imgproc.TM_SQDIFF_NORMED); Core.subtract(Mat.ones(res.size(), CvType.CV_32F), res, res); } return Core.minMaxLoc(res); } @Override public boolean hasNext() { if (matches.size() > 0) { return matches.get(0) != null; } return false; } @Override public Match next() { Match m = null; if (matches.size() > 0) { m = matches.get(0); remove(); } return m; } @Override public void remove() { if (matches.size() > 0) { matches.remove(0); } } public Match get() { return get(0); } public Match get(int n) { if (n < matches.size()) { return matches.get(n); } return null; } private Match add(Match m) { if (matches.add(m)) { return m; } return null; } private Match set(Match m) { if (matches.size() > 0) { matches.set(0, m); } else { matches.add(m); } return m; } public int getSize() { return matches.size(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/ImageFinder.java000066400000000000000000000222331315726130400246630ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint; import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; /** * EXPERIMENTAL --- INTERNAL USE ONLY
* is not official API --- will not be in version 2 */ public class ImageFinder extends Finder { static RunTime runTime = RunTime.get(); private static String me = "ImageFinder: "; private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private boolean isImageFinder = true; protected boolean isImage = false; protected Region region = null; protected boolean isRegion = false; protected IScreen screen = null; protected boolean isScreen = false; protected int offX, offY; protected long MaxTimePerScan; private Image bImage = null; protected Mat base = new Mat(); private double waitingTime = Settings.AutoWaitTimeout; private int minChanges; private ImageFind firstFind = null; private boolean isReusable = false; protected boolean isMultiFinder = false; public ImageFinder() { init(null, null, null); } public ImageFinder(Image base) { init(base, null, null); } public ImageFinder(IScreen scr) { init(null, scr, null); } public ImageFinder(Region reg) { init(null, null, reg); } protected ImageFinder(Mat base) { log(3, "init"); reset(); this.base = base; isImage = true; log(3, "search in: \n%s", base); } private void init(Image base, IScreen scr, Region reg) { log(3, "init"); if (base != null) { setImage(base); } else if (scr != null) { setScreen(scr); } else if (reg != null) { setRegion(reg); } } private void reset() { firstFind = null; isImage = false; isScreen = false; isRegion = false; screen = null; region = null; bImage = null; base = new Mat(); } @Override public void destroy() { reset(); } public void setIsMultiFinder() { base = new Mat(); isMultiFinder = true; } public boolean setImage(Image base) { reset(); if (base.isValid()) { bImage = base; this.base = Image.createMat(base.get()); isImage = true; log(3, "search in: \n%s", base.get()); } return isImage; } public boolean isImage() { return isImage; } protected void setBase(BufferedImage bImg) { log(3, "search in: \n%s", bImg); base = Image.createMat(bImg); } public boolean setScreen(IScreen scr) { reset(); if (scr != null) { screen = scr; isScreen = true; setScreenOrRegion(scr); } return isScreen; } public boolean setRegion(Region reg) { reset(); if (reg != null) { region = reg; isRegion = true; setScreenOrRegion(reg); } return isRegion; } private void setScreenOrRegion(Object reg) { Region r = (Region) reg; MaxTimePerScan = (int) (1000.0 / r.getWaitScanRate()); offX = r.x; offY = r.y; log(3, "search in: \n%s", r); } public void setFindTimeout(double t) { waitingTime = t; } public boolean isValid() { if (!isImage && !isScreen && !isRegion) { log(-1, "not yet initialized (not valid Image, Screen nor Region)"); return false; } return true; } @Override public String find(Image img) { if (null == imageFind(img)) { return null; } else { return "--fromImageFinder--"; } } @Override public String find(String filenameOrText) { if (null == imageFind(filenameOrText)) { return null; } else { return "--fromImageFinder--"; } } @Override public String find(Pattern pat) { if (null == imageFind(pat)) { return null; } else { return "--fromImageFinder--"; } } @Override public String findText(String text) { log(-1, "findText: not yet implemented"); return null; } public ImageFind search(PSI probe, Object... args) { isReusable = true; return imageFind(probe, args); } protected ImageFind findInner(PSI probe, double sim) { ImageFind newFind = new ImageFind(); newFind.setIsInnerFind(); newFind.setSimilarity(sim); if (!newFind.checkFind(this, probe)) { return null; } firstFind = newFind; if (newFind.isValid()) { return newFind.doFind(); } return null; } private ImageFind imageFind(PSI probe, Object... args) { Debug.enter(me + ": find: %s", probe); ImageFind newFind = new ImageFind(); newFind.setFindTimeout(waitingTime); if (!newFind.checkFind(this, probe, args)) { return null; } if (newFind.isValid() && !isReusable && firstFind == null) { firstFind = newFind; } ImageFind imgFind = newFind.doFind(); log(lvl, "find: success: %s", imgFind.get()); return imgFind; } public ImageFind searchAny(PSI probe, Object... args) { Debug.enter(me + ": findAny: %s", probe); ImageFind newFind = new ImageFind(); newFind.setFinding(ImageFind.FINDING_ANY); isReusable = true; if (!newFind.checkFind(this, probe, args)) { return null; } if (newFind.isValid() && !isReusable && firstFind == null) { firstFind = newFind; } ImageFind imgFind = newFind.doFind(); log(lvl, "find: success: %s", imgFind.get()); return imgFind; } public ImageFind searchSome(PSI probe, Object... args) { return searchSome(probe, ImageFind.SOME_COUNT, args); } public ImageFind searchSome(PSI probe, int count, Object... args) { isReusable = true; return imageFindAll(probe, ImageFind.BEST_FIRST, count, args); } @Override public String findAll(Image img) { if (null == imageFindAll(img, ImageFind.BEST_FIRST, 0)) { return null; } else { return "--fromImageFinder--"; } } @Override public String findAll(String filenameOrText) { if (null == imageFindAll(filenameOrText, ImageFind.BEST_FIRST, 0)) { return null; } else { return "--fromImageFinder--"; } } @Override public String findAll(Pattern pat) { if (null == imageFindAll(pat, ImageFind.BEST_FIRST, 0)) { return null; } else { return "--fromImageFinder--"; } } public ImageFind searchAll(PSI probe, Object... args) { isReusable = true; return imageFindAll(probe, ImageFind.BEST_FIRST, 0, args); } public ImageFind searchAll(PSI probe, int sorted, Object... args) { isReusable = true; return imageFindAll(probe, sorted, 0, args); } private ImageFind imageFindAll(PSI probe, int sorted, int count, Object... args) { Debug.enter(me + ": findAny: %s", probe); ImageFind newFind = new ImageFind(); newFind.setFinding(ImageFind.FINDING_ALL); newFind.setSorted(sorted); if (count > 0) { newFind.setCount(count); } if (!newFind.checkFind(this, probe, args)) { return null; } if (newFind.isValid() && !isReusable && firstFind == null) { firstFind = newFind; } ImageFind imgFind = newFind.doFind(); log(lvl, "find: success: %s", imgFind.get()); return imgFind; } public boolean hasChanges(Mat current) { int PIXEL_DIFF_THRESHOLD = 5; int IMAGE_DIFF_THRESHOLD = 5; Mat bg = new Mat(); Mat cg = new Mat(); Mat diff = new Mat(); Mat tdiff = new Mat(); Imgproc.cvtColor(base, bg, Imgproc.COLOR_BGR2GRAY); Imgproc.cvtColor(current, cg, Imgproc.COLOR_BGR2GRAY); Core.absdiff(bg, cg, diff); Imgproc.threshold(diff, tdiff, PIXEL_DIFF_THRESHOLD, 0.0, Imgproc.THRESH_TOZERO); if (Core.countNonZero(tdiff) <= IMAGE_DIFF_THRESHOLD) { return false; } Imgproc.threshold(diff, diff, PIXEL_DIFF_THRESHOLD, 255, Imgproc.THRESH_BINARY); Imgproc.dilate(diff, diff, new Mat()); Mat se = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5,5)); Imgproc.morphologyEx(diff, diff, Imgproc.MORPH_CLOSE, se); List points = new ArrayList(); Mat contours = new Mat(); Imgproc.findContours(diff, points, contours, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); int n = 0; for (Mat pm: points) { log(lvl, "(%d) %s", n++, pm); printMatI(pm); } log(lvl, "contours: %s", contours); printMatI(contours); return true; } private static void printMatI(Mat mat) { int[] data = new int[mat.channels()]; for (int r = 0; r < mat.rows(); r++) { for (int c = 0; c < mat.cols(); c++) { mat.get(r, c, data); log(lvl, "(%d, %d) %s", r, c, Arrays.toString(data)); } } } public void setMinChanges(int min) { minChanges = min; } @Override public boolean hasNext() { if (null != firstFind) { return firstFind.hasNext(); } return false; } @Override public Match next() { if (firstFind != null) { return firstFind.next(); } return null; } @Override public void remove() { } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/ImageGroup.java000066400000000000000000000050661315726130400245550ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.Rectangle; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * EXPERIMENTAL --- INTERNAL USE ONLY
* is not official API --- will not be in version 2 */ public class ImageGroup { static RunTime runTime = RunTime.get(); private static Map imageGroups = Collections.synchronizedMap(new HashMap()); private String name; private URL url; private String path; private String subSet; private Map images = Collections.synchronizedMap(new HashMap()); private boolean valid; /** * @return true if this group has been found and is loadable */ public boolean isValid() { return valid; } public static ImageGroup open(String name) { ImageGroup ig = imageGroups.get(name); if (ig != null && ig.isValid()) { return ig; } ig = new ImageGroup(name); if (!ig.isValid()) { ig = null; } return ig; } public static void close(ImageGroup ig) { ig.images.clear(); //take from ImagePath and purge } private ImageGroup(String name) { init(name, null); } private ImageGroup(String name, String subSet) { init(name, subSet); } private void init(String name, String subSet) { this.name = name; this.path = locate(name); url = null; this.subSet = subSet; valid = false; if (path != null) { valid = true; url = checkURL(path); imageGroups.put(name, this); use(subSet); } } private static String locate(String name) { // find the given folder name on current image path return null; } private static URL checkURL(String path) { // check wether path is an URL-string URL purl = null; return purl; } public boolean use(String sub) { if (sub == null) { // either no sub folders or use groupname as default sub } // add/replace in ImagePath (purge!) // save/load imagefacts? return true; } // triggered when lastSeen is stored protected int[] addImageFacts(Image img, Rectangle r, double score) { int[] facts = new int[5]; facts[0] = r.x; facts[1] = r.y; facts[2] = r.width; facts[3] = r.height; facts[4] = (int) (score*100); images.put(img.getName(), facts); return facts; } public boolean loadImageFacts() { return true; } public boolean saveImageFacts() { return true; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/ImageLocator.java000077500000000000000000000211751315726130400250660ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.basics.Settings; /** * This class was used to locate image files in the filesystem
* and in the internet (the files are cached locally)
* * @deprecated will be completely replaced by the classes Image and ImagePath * relevant functions are already redirected as needed */ @Deprecated public class ImageLocator { static RunTime runTime = RunTime.get(); static ArrayList pathList = new ArrayList(); static int firstEntries = 1; static File _cache_dir_global = new File(RunTime.get().fpBaseTempPath, "sikuli_cache/SIKULI_GLOBAL/"); static Map _cache = new HashMap(); static { pathList.add(""); resetImagePath(""); if (pathList.size() >= 1 && "".equals(pathList.get(0))) { pathList.set(0, System.getProperty("user.dir")); } if (!_cache_dir_global.exists()) { try { _cache_dir_global.mkdir(); } catch (Exception e) { Debug.error("ImageLocator: Local cache dir not possible: " + _cache_dir_global); _cache_dir_global = null; } } } /** * forwarded to ImagePath.getImagePath() * * @return an array of the imagepaths as added * @deprecated */ @Deprecated public static String[] getImagePath() { return ImagePath.get(); } /** * forwarded to ImagePath.add() * * @return null if not successful * @deprecated */ @Deprecated public static String addImagePath(String path) { if (!ImagePath.add(path)) { return null; } else { return path; } } /** * forwarded to ImagePath.remove() * * @deprecated */ @Deprecated public static void removeImagePath(String path) { ImagePath.remove(path); } /** * forwarded to ImagePath.setBundlePath() * * @deprecated */ @Deprecated public static void setBundlePath(String bundlePath) { ImagePath.setBundlePath(bundlePath); } /** * forwarded to ImagePath.getBundlePath() * * @return the current bundlepath * @deprecated */ @Deprecated public static String getBundlePath() { return ImagePath.getBundlePath(); } public static String locate(String filename) throws IOException { if (filename != null) { String ret; URL url = getURL(filename); if (url != null) { ret = getFileFromURL(url); if (ret != null) { return ret; } } File f = new File(filename); if (f.isAbsolute()) { if (f.exists()) { return f.getAbsolutePath(); } } else { ret = searchFile(filename); if (ret != null) { return ret; } } } else { filename = "*** not known ***"; } throw new FileNotFoundException("ImageLocator.locate: " + filename + " does not exist or cannot be found on ImagePath"); } /** * forwarded to Image.create(filename).get() * * @return a BufferedImage from the given filename or null * @deprecated */ @Deprecated public static BufferedImage getImage(String filename) { return Image.create(filename).get(); } /*************************** * methods below are obsolete ****************************/ private static String[] splitImagePath(String path) { if (path == null || "".equals(path)) { return new String[0]; } path = path.replaceAll("[Hh][Tt][Tt][Pp]://", "__http__//"); path = path.replaceAll("[Hh][Tt][Tt][Pp][Ss]://", "__https__//"); String[] pl = path.split(Settings.getPathSeparator()); File pathName; for (int i = 0; i < pl.length; i++) { boolean isURL = false; path = pl[i]; if (path.indexOf("__http__") >= 0) { path = path.replaceAll("__http__//", "http://"); isURL = true; } else if (path.indexOf("__https__") >= 0) { path = path.replaceAll("__https__//", "https://"); isURL = true; } if (isURL) { if ((path = getURL(path).getPath()) != null) { if (!path.endsWith("/")) { pl[i] = path + "/"; } } else { pl[i] = null; } } else { pathName = new File(path); if (pathName.exists()) { pl[i] = FileManager.slashify(pathName.getAbsolutePath(), true); } else { pathList.remove(pl[i]); pl[i] = null; } } } return pl; } private static URL getURL(String s) { try { URL url = new URL(s); return url; } catch (MalformedURLException e) { return null; } } private static String addImagePath(String[] pl, boolean first) { int addedAt = firstEntries; if (addedAt == pathList.size()) { first = false; } String epl; File fepl; for (int i = 0; i < pl.length; i++) { if (pl[i] == null) { continue; } epl = pl[i]; //fepl = new File(epl); //TODO handle relative paths if (!pathList.contains(epl)) { if (!first) { pathList.add(epl); } else { pathList.add(addedAt, epl); addedAt++; } } } if (pl.length > 0) { return pl[0]; } else { return null; } } private static String addImagePath(String path, boolean first) { String pl[] = splitImagePath(path); removeImagePath(pl); return addImagePath(pl, first); } private static String addImagePathFirst(String path) { return addImagePath(path, true); } private static String addImagePath(String[] pl) { return addImagePath(pl, false); } private static String addImagePathFirst(String[] pl) { return addImagePath(pl, true); } private static void removeImagePath(String[] pl) { for (int i = 0; i < pl.length; i++) { if (pl[i] != null) { pathList.remove(pl[i]); } } } private static void clearImagePath() { Iterator ip = pathList.listIterator(1); String p; while (ip.hasNext()) { p = ip.next(); if (!p.substring(0, p.length() - 1).endsWith(".sikuli")) { ip.remove(); } } if (firstEntries == pathList.size()) { addImagePath(System.getenv("SIKULI_IMAGE_PATH")); addImagePath(System.getProperty("SIKULI_IMAGE_PATH")); } else { addImagePathFirst(System.getProperty("SIKULI_IMAGE_PATH")); addImagePathFirst(System.getenv("SIKULI_IMAGE_PATH")); } } private static void resetImagePath(String path) { clearImagePath(); String pl[] = splitImagePath(path); if (pl.length > 0) { pathList.set(0, pl[0]); Settings.BundlePath = pl[0].substring(0, pl[0].length() - 1); pl[0] = null; addImagePath(pl); } } private static void resetImagePath(String[] pl) { clearImagePath(); addImagePath(pl); } private static String searchFile(String filename) { File f; String ret; for (Iterator it = pathList.iterator(); it.hasNext();) { String path = it.next(); URL url = getURL(path); if (url != null) { try { ret = getFileFromURL(new URL(url, filename)); if (ret != null) { return ret; } } catch (MalformedURLException ex) { } } f = new File(path, filename); if (f.exists()) { Debug.log(3, "ImageLocator: found " + filename + " in " + path); return f.getAbsolutePath(); } } return null; } private static String getFileFromURL(URL url) { if (_cache_dir_global == null) { Debug.error("ImageLocator.getFileFromURL: Local cache dir not available - cannot download from url" + url); return null; } try { URI uri = url.toURI(); if (_cache.containsKey(uri)) { Debug.log(2, "ImageLocator.getFileFromURL: " + uri + " taken from cache"); return _cache.get(uri); } String localFile = FileManager.downloadURL(url, _cache_dir_global.getPath()); if (localFile != null) { Debug.log(2, "ImageLocator.getFileFromURL: download " + uri + " to local: " + localFile); _cache.put(uri, localFile); } return localFile; } catch (java.net.URISyntaxException e) { Debug.log(2, "ImageLocator.getFileFromURL: URI syntax error: " + url + ", " + e.getMessage()); return null; } } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/ImagePath.java000066400000000000000000000437651315726130400243650ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.URISyntaxException; import java.net.URL; import java.security.CodeSource; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.basics.Settings; /** * runtain the path list of locations, where images will be searched. *
the first entry always is the bundlepath used on the scripting level
* Python import automatically adds a sikuli bundle here
* supported locations:
* - absolute filesystem paths
* - inside jars relative to root level given by a class found on classpath
* - a location in the web given as string starting with http[s]://
* - any location as a valid URL, from where image files can be loaded
*/ public class ImagePath { static RunTime runTime = RunTime.get(); private static final String me = "ImagePath: "; private static final int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } /** * represents an imagepath entry */ public static class PathEntry { public URL pathURL; public String path; /** * create a new image path entry * * @param givenName the given path relative or absolute * @param eqivalentURL the evaluated URL */ public PathEntry(String givenName, URL eqivalentURL) { path = FileManager.normalize(givenName); if (eqivalentURL != null) { pathURL = eqivalentURL; } else { pathURL = makePathURL(path, null).pathURL; } log(lvl+1, "PathEntry: %s \nas %s", path, pathURL); } public String getPath() { if (pathURL == null) { return "-- empty --"; } String uPath = pathURL.toExternalForm(); if (isFile() && uPath.startsWith("file:")) { uPath = uPath.substring(5); } return uPath; } public boolean isFile() { if (pathURL == null) { return false; } return "file".equals(pathURL.getProtocol()); } public boolean isJar() { if (pathURL == null) { return false; } return "jar".equals(pathURL.getProtocol()); } public boolean isHTTP() { if (pathURL == null) { return false; } return pathURL.getProtocol().startsWith("http"); } public boolean exists() { if (pathURL == null) { return false; } return new File(getPath()).exists(); } @Override public boolean equals(Object other) { if (pathURL == null) { return false; } if (! (other instanceof PathEntry)) { if (other instanceof URL) { if (pathURL.equals((URL) other)) { return true; } } else if (other instanceof String) { if (isFile()) { return FileManager.pathEquals(pathURL.getPath(), (String) other); } return false; } return false; } if (pathURL.equals(((PathEntry) other).pathURL)) { return true; } return false; } @Override public String toString() { return getPath(); } } private static final List imagePaths = Collections.synchronizedList(new ArrayList()); private static PathEntry bundlePath = null; static { imagePaths.add(null); } /** * get the list of path entries (as PathEntry) * * @return pathentries */ public static List getPaths() { return imagePaths; } private static int getCount() { int count = imagePaths.size(); for (PathEntry path : imagePaths) { if (path == null) { count--; } } return count; } /** * the path list as string array * * @return an array of the file path's currently in the path list */ public static String[] get() { int i = 0; for (PathEntry p : imagePaths) { if (p == null) { continue; } i++; } String[] paths = new String[i]; i = 0; for (PathEntry p : imagePaths) { if (p == null) { continue; } paths[i++] = p.getPath(); if (p.isFile()) { paths[i - 1] = new File(p.getPath()).getAbsolutePath(); } } return paths; } public static String getPath(int ix) { PathEntry pe = imagePaths.get(0); String path = null; if (pe != null) { path = pe.getPath(); } return path; } /** * print the list of path entries * * @param lvl debug level to use */ public static void dump(int lvl) { log(lvl, "ImagePath has %d entries (valid %d)", imagePaths.size(), getCount()); String bundle = "(taken as bundle path)"; for (PathEntry p : imagePaths) { if (p == null) { log(lvl, "Path: NULL %s", bundle); } else { log(lvl, "Path: given: %s\nis: %s", p.path, p.getPath()); } bundle = ""; } } private static boolean bundleEquals(Object path) { if (bundlePath != null) { return bundlePath.equals(path); } return false; } public static boolean isImageBundled(URL fURL) { if ("file".equals(fURL.getProtocol())) { return bundleEquals(new File(fURL.getPath()).getParent()); } return false; } /** * try to find the given relative image file name on the image path
* starting from entry 0, the first found existence is taken
* absolute file names are checked for existence * * @param fname relative or absolute filename * @return a valid URL or null if not found/exists */ public static URL find(String fname) { URL fURL = null; String proto = ""; fname = FileManager.normalize(fname); if (new File(fname).isAbsolute()) { if (new File(fname).exists()) { fURL = FileManager.makeURL(fname); } else { log(-1, "find: File does not exist: " + fname); } return fURL; } else { if (bundlePath == null) { setBundlePath(null); } for (PathEntry path : getPaths()) { if (path == null) { continue; } proto = path.pathURL.getProtocol(); if ("file".equals(proto)) { fURL = FileManager.makeURL(path.pathURL, fname); if (new File(fURL.getPath()).exists()) { break; } } else if ("jar".equals(proto) || proto.startsWith("http")) { fURL = FileManager.getURLForContentFromURL(path.pathURL, fname); if (fURL != null) { break; } } else { log(-1, "find: URL not supported: " + path.pathURL); return fURL; } } if (fURL == null) { log(-1, "find: not on image path: " + fname); dump(lvl); } return fURL; } } /** * given absolute or relative (searched on image path) file name
* is tried to open as a BufferedReader
* BE AWARE: use br.close() when finished * * @param fname relative or absolute filename * @return the BufferedReader to be used or null if not possible */ public static BufferedReader open(String fname) { log(lvl, "open: " + fname); URL furl = find(fname); if (furl != null) { BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(furl.openStream())); } catch (IOException ex) { log(-1, "open: %s", ex.getMessage()); return null; } try { br.mark(10); if (br.read() < 0) { br.close(); return null; } br.reset(); return br; } catch (IOException ex) { log(-1, "open: %s", ex.getMessage()); try { br.close(); } catch (IOException ex1) { log(-1, "open: %s", ex1.getMessage()); return null; } return null; } } return null; } /** * create a new PathEntry from the given absolute path name and add it to the * end of the current image path
* for usage with jars see; {@link #add(String, String)} * * @param mainPath relative or absolute path * @return true if successful otherwise false */ public static boolean add(String mainPath) { return add(mainPath, null); } /** * create a new PathEntry from the given net resource folder accessible via HTTP at * end of the current image path
* BE AWARE:
* Files stored in the given remote folder must allow HTTP HEAD-requests (checked)
* redirections are not followed (suppressed) * * @param pathHTTP folder address like siteaddress or siteaddress/folder/subfolder (e.g. download.sikuli.de/images) * @return true if successful otherwise false */ public static boolean addHTTP(String pathHTTP) { try { String proto = "http://"; String protos = "https://"; if (pathHTTP.startsWith(proto) || pathHTTP.startsWith(protos)) { proto = ""; } pathHTTP = FileManager.slashify(pathHTTP, false); URL aURL = new URL(proto + pathHTTP); if (0 != FileManager.isUrlUseabel(new URL(aURL.toString() + "/THIS_FILE_SHOULD_RETURN_404"))) { return false; } PathEntry path = new PathEntry(pathHTTP, aURL); if (hasPath(path) < 0) { log(lvl, "add: %s", path); imagePaths.add(path); } else { log(lvl, "duplicate not added: %s", path); } } catch (Exception ex) { log (-1, "addHTTP: not possible: %s\n%s", pathHTTP, ex); return false; } return true; } public static boolean removeHTTP(String pathHTTP) { try { String proto = "http://"; String protos = "https://"; if (pathHTTP.startsWith(proto) || pathHTTP.startsWith(protos)) { proto = ""; } pathHTTP = FileManager.slashify(pathHTTP, false); return remove(new URL(proto + pathHTTP)); } catch (Exception ex) { log (-1, "removeHTTP: not possible: %s\n%s", pathHTTP, ex); return false; } } /** * create a new PathEntry from the given absolute path name and add it to the * end of the current image path
* for images stored in jars:
* Set the primary image path to the top folder level of a jar based on the * given class name (must be found on class path). When not running from a jar (e.g. running in some IDE) the path will be the path to the compiled classes (for Maven based projects this is target/classes that contains all stuff copied from src/run/resources automatically)
* For situations, where the images cannot be found automatically in the non-jar situation, you * might give an alternative path either absolute or relative to the working folder. * @param mainPath absolute path name or a valid classname optionally followed by /subfolder... * @param altPath alternative image folder, when not running from jar * @return true if successful otherwise false */ public static boolean add(String mainPath, String altPath) { PathEntry path = null; File fPath = new File(mainPath); if (!fPath.isAbsolute() && mainPath.contains(":")) { return addHTTP(mainPath); } path = makePathURL(mainPath, altPath); if (path != null) { if (hasPath(path) < 0) { log(lvl, "add: %s", path); imagePaths.add(path); } else { log(lvl, "duplicate not added: %s", path); } return true; } else { log(-1, "add: not valid: %s %s", mainPath, (altPath == null ? "" : " / " + altPath)); } return false; } public static boolean addJar(String fpJar, String fpImage) { URL pathURL = null; if (new File(fpJar).exists()) { if (fpImage == null) { fpImage = ""; } pathURL = FileManager.makeURL(fpJar + "!/" + fpImage, "jar"); add(pathURL); } return true; } private static int hasPath(PathEntry path) { PathEntry pe = imagePaths.get(0); if (imagePaths.size() == 1 && pe == null) { return -1; } if (pe != null && pe.equals(path)) { return 0; } for (PathEntry p : imagePaths.subList(1, imagePaths.size())) { if (p != null && p.equals(path)) { return 1; } } return -1; } /** * add entry to end of list (the given URL is not checked) * * @param pURL a valid URL (not checked) */ public static void add(URL pURL) { imagePaths.add(new PathEntry("__PATH_URL__", pURL)); } /** * remove entry with given path (same as given with add) * * @param path relative or absolute path * @return true on success, false otherwise */ public static boolean remove(String path) { File fPath = new File(path); if (!fPath.isAbsolute() && path.contains(":")) { return removeHTTP(path); } return remove(makePathURL(FileManager.normalize(path), null).pathURL); } /** * remove entry with given URL
* bundlepath (entry 0) cannot be removed * loaded images are removed from cache * * @param pURL a valid URL (not checked) * @return true on success, false ozherwise */ private static boolean remove(URL pURL) { if (bundleEquals(pURL)) { Image.purge(pURL); bundlePath = null; Settings.BundlePath = null; imagePaths.set(0, null); } Iterator it = imagePaths.subList(1, imagePaths.size()).iterator(); PathEntry p, p0; p0 = imagePaths.get(0); while (it.hasNext()) { p = it.next(); if (!p.equals(pURL)) { continue; } it.remove(); Image.purge(p.pathURL); } return true; } /** * empty path list and add given path as first entry * Image cache is cleared completely * * @param path absolute path * @return true on success, false otherwise */ public static boolean reset(String path) { if (bundleEquals(path)) { return true; } reset(); return setBundlePath(path); } /** * empty path list and keep bundlePath (entry 0)
* Image cache is cleared completely * convenience for the scripting level * @return true */ public static boolean reset() { log(lvl, "reset"); if (imagePaths.isEmpty()) { return false; } for (PathEntry p : imagePaths) { if (p == null) { continue; } Image.purge(p.pathURL); } PathEntry bp = imagePaths.get(0); imagePaths.clear(); imagePaths.add(bp); return true; } /** * the given path replaces bundlepath (entry 0) * and Settings.bundlePath is set to given path * * @param bPath an absolute file path * @return true on success, false otherwise */ public static boolean setBundlePath(String bPath) { PathEntry path = null; if (bPath == null) { // called on first find, if bundlepath still null path = makePathURL(FileManager.normalizeAbsolute(Settings.BundlePath, false), null); } else { path = makePathURL(FileManager.normalizeAbsolute(bPath, false), null); } if (path != null && path.isFile()) { if (bundleEquals(path)) { return true; } Image.purge(bundlePath); if (path.exists()) { imagePaths.set(0, path); Settings.BundlePath = path.getPath(); bundlePath = path; log(lvl, "new BundlePath:\n%s", path); return true; } } if (getCount() ==0) { String wf = System.getProperty("user.dir"); log(-1, "setBundlePath: invalid BundlePath: %s \nusing working folder: %s", bPath, wf); if (!new File(wf).exists()) { log(-1, "setBundlePath: Fatal error: working folder does not exist --- terminating"); System.exit(1); } return setBundlePath(wf); } return true; } /** * no trailing path separator * @return the current bundle path (might be the fallback working folder) */ public static String getBundlePath() { if (bundlePath == null) { setBundlePath(null); } return new File(FileManager.slashify(bundlePath.getPath(), false)).getAbsolutePath(); } /** * no trailing path separator * @return the current bundle path (might be the fallback working folder) */ public static String getBundlePathSet() { if (bundlePath == null) { return null; } return new File(FileManager.slashify(bundlePath.getPath(), false)).getAbsolutePath(); } /** * no trailing path separator * @return the current bundle path (might be the fallback working folder) */ public static String getBundleFolder() { if (bundlePath == null) { setBundlePath(null); } return new File(FileManager.slashify(bundlePath.getPath(), true)).getAbsolutePath(); } private static PathEntry makePathURL(String fpMainPath, String fpAltPath) { if (fpMainPath == null || fpMainPath.isEmpty()) { return null; } URL pathURL = null; File fPath = new File(FileManager.normalizeAbsolute(fpMainPath, false)); if (fPath.exists()) { pathURL = FileManager.makeURL(fPath.getAbsolutePath()); } else { if (fpMainPath.contains("\\")) { return null; } Class cls = null; String klassName; String fpSubPath = ""; int n = fpMainPath.indexOf("/"); if (n > 0) { klassName = fpMainPath.substring(0, n); if (n < fpMainPath.length() - 2) { fpSubPath = fpMainPath.substring(n + 1); } } else { klassName = fpMainPath; } try { cls = Class.forName(klassName); } catch (ClassNotFoundException ex) { log(-1,"add: class %s not found on classpath.", klassName); } if (cls != null) { CodeSource codeSrc = cls.getProtectionDomain().getCodeSource(); if (codeSrc != null && codeSrc.getLocation() != null) { URL jarURL = codeSrc.getLocation(); if (runTime.runningWinApp || jarURL.getPath().endsWith(".jar")) { pathURL = FileManager.makeURL(jarURL.toString() + "!/" + fpSubPath, "jar"); } else { if (fpAltPath == null || fpAltPath.isEmpty()) { fpAltPath = jarURL.getPath(); } if (new File(FileManager.normalizeAbsolute(fpAltPath, false), fpSubPath).exists()) { File fAltPath = new File(FileManager.normalizeAbsolute(fpAltPath, false), fpSubPath); pathURL = FileManager.makeURL(fAltPath.getPath()); } } } } } if (pathURL != null) { return new PathEntry(fpMainPath, pathURL); } return null; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Key.java000066400000000000000000001144371315726130400232510ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.HotkeyManager; import org.sikuli.basics.HotkeyListener; import java.awt.Toolkit; import java.awt.event.KeyEvent; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; import org.sikuli.util.SysJNA; /** * this class implements an interface to the Java key system * as represented by java.awt.event.KeyEvent. * for the functions Region.type() and Region.write() * by translating key constants for special keys and plain text per character.
* for details consult the docs */ public class Key { /** * add a hotkey and listener * @param key respective key specifier according class Key * @param modifiers respective key specifier according class KeyModifiers * @param listener a HotKeyListener instance * @return true if ok, false otherwise */ public static boolean addHotkey(String key, int modifiers, HotkeyListener listener) { return HotkeyManager.getInstance().addHotkey(key, modifiers, listener); } /** * add a hotkey and listener * @param key respective key specifier according class Key * @param modifiers respective key specifier according class KeyModifiers * @param listener a HotKeyListener instance * @return true if ok, false otherwise */ public static boolean addHotkey(char key, int modifiers, HotkeyListener listener) { return HotkeyManager.getInstance().addHotkey(key, modifiers, listener); } /** * remove a hotkey and listener * @param key respective key specifier according class Key * @param modifiers respective key specifier according class KeyModifiers * @return true if ok, false otherwise */ public static boolean removeHotkey(String key, int modifiers) { return HotkeyManager.getInstance().removeHotkey(key, modifiers); } /** * remove a hotkey and listener * @param key respective key specifier according class Key * @param modifiers respective key specifier according class KeyModifiers * @return true if ok, false otherwise */ public static boolean removeHotkey(char key, int modifiers) { return HotkeyManager.getInstance().removeHotkey(key, modifiers); } static String[] keyVK = new String[] { // "VK_0","48", "VK_1","49", "VK_2","50", "VK_3","51", "VK_4","52", "VK_5","53", "VK_6","54", "VK_7","55", "VK_8","56", "VK_9","57", "VK_A","65", "VK_ACCEPT","30", "VK_ADD","107", "VK_AGAIN","65481", "VK_ALL_CANDIDATES","256", "VK_ALPHANUMERIC","240", "VK_ALT","18", "VK_ALT_GRAPH","65406", "VK_AMPERSAND","150", "VK_ASTERISK","151", "VK_AT","512", "VK_B","66", "VK_BACK_QUOTE","192", "VK_BACK_SLASH","92", "VK_BACK_SPACE","8", "VK_BEGIN","65368", "VK_BRACELEFT","161", "VK_BRACERIGHT","162", "VK_C","67", "VK_CANCEL","3", "VK_CAPS_LOCK","20", "VK_CIRCUMFLEX","514", "VK_CLEAR","12", "VK_CLOSE_BRACKET","93", "VK_CODE_INPUT","258", "VK_COLON","513", "VK_COMMA","44", "VK_COMPOSE","65312", "VK_CONTEXT_MENU","525", "VK_CONTROL","17", "VK_CONVERT","28", "VK_COPY","65485", "VK_CUT","65489", "VK_D","68", "VK_DEAD_ABOVEDOT","134", "VK_DEAD_ABOVERING","136", "VK_DEAD_ACUTE","129", "VK_DEAD_BREVE","133", "VK_DEAD_CARON","138", "VK_DEAD_CEDILLA","139", "VK_DEAD_CIRCUMFLEX","130", "VK_DEAD_DIAERESIS","135", "VK_DEAD_DOUBLEACUTE","137", "VK_DEAD_GRAVE","128", "VK_DEAD_IOTA","141", "VK_DEAD_MACRON","132", "VK_DEAD_OGONEK","140", "VK_DEAD_SEMIVOICED_SOUND","143", "VK_DEAD_TILDE","131", "VK_DEAD_VOICED_SOUND","142", "VK_DECIMAL","110", "VK_DELETE","127", "VK_DIVIDE","111", "VK_DOLLAR","515", "VK_DOWN","40", "VK_E","69", "VK_END","35", "VK_ENTER","10", "VK_EQUALS","61", "VK_ESCAPE","27", "VK_EURO_SIGN","516", "VK_EXCLAMATION_MARK","517", "VK_F","70", "VK_F1","112", "VK_F10","121", "VK_F11","122", "VK_F12","123", "VK_F13","61440", "VK_F14","61441", "VK_F15","61442", "VK_F16","61443", "VK_F17","61444", "VK_F18","61445", "VK_F19","61446", "VK_F2","113", "VK_F20","61447", "VK_F21","61448", "VK_F22","61449", "VK_F23","61450", "VK_F24","61451", "VK_F3","114", "VK_F4","115", "VK_F5","116", "VK_F6","117", "VK_F7","118", "VK_F8","119", "VK_F9","120", "VK_FINAL","24", "VK_FIND","65488", "VK_FULL_WIDTH","243", "VK_G","71", "VK_GREATER","160", "VK_H","72", "VK_HALF_WIDTH","244", "VK_HELP","156", "VK_HIRAGANA","242", "VK_HOME","36", "VK_I","73", "VK_INPUT_METHOD_ON_OFF","263", "VK_INSERT","155", "VK_INVERTED_EXCLAMATION_MARK","518", "VK_J","74", "VK_JAPANESE_HIRAGANA","260", "VK_JAPANESE_KATAKANA","259", "VK_JAPANESE_ROMAN","261", "VK_K","75", "VK_KANA","21", "VK_KANA_LOCK","262", "VK_KANJI","25", "VK_KATAKANA","241", "VK_KP_DOWN","225", "VK_KP_LEFT","226", "VK_KP_RIGHT","227", "VK_KP_UP","224", "VK_L","76", "VK_LEFT","37", "VK_LEFT_PARENTHESIS","519", "VK_LESS","153", "VK_M","77", "VK_META","157", "VK_MINUS","45", "VK_MODECHANGE","31", "VK_MULTIPLY","106", "VK_N","78", "VK_NONCONVERT","29", "VK_NUM_LOCK","144", "VK_NUMBER_SIGN","520", "VK_NUMPAD0","96", "VK_NUMPAD1","97", "VK_NUMPAD2","98", "VK_NUMPAD3","99", "VK_NUMPAD4","100", "VK_NUMPAD5","101", "VK_NUMPAD6","102", "VK_NUMPAD7","103", "VK_NUMPAD8","104", "VK_NUMPAD9","105", "VK_O","79", "VK_OPEN_BRACKET","91", "VK_P","80", "VK_PAGE_DOWN","34", "VK_PAGE_UP","33", "VK_PASTE","65487", "VK_PAUSE","19", "VK_PERIOD","46", "VK_PLUS","521", "VK_PREVIOUS_CANDIDATE","257", "VK_PRINTSCREEN","154", "VK_PROPS","65482", "VK_Q","81", "VK_QUOTE","222", "VK_QUOTEDBL","152", "VK_R","82", "VK_RIGHT","39", "VK_RIGHT_PARENTHESIS","522", "VK_ROMAN_CHARACTERS","245", "VK_S","83", "VK_SCROLL_LOCK","145", "VK_SEMICOLON","59", "VK_SEPARATER","108", "VK_SEPARATOR","108", "VK_SHIFT","16", "VK_SLASH","47", "VK_SPACE","32", "VK_STOP","65480", "VK_SUBTRACT","109", "VK_T","84", "VK_TAB","9", "VK_U","85", "VK_UNDEFINED","0", "VK_UNDERSCORE","523", "VK_UNDO","65483", "VK_UP","38", "VK_V","86", "VK_W","87", "VK_WINDOWS","524", "VK_X","88", "VK_Y","89", "VK_Z","90" // }; // non function keys on US-Keyboard per line top down without modifier keys public static String keyboardUS = "1234567890-=" + "qwertyuiop[]" + "asdfghjkl;'\\" + "`zxcvbnm,./"; // public static final String SPACE = " "; public static final String ENTER = "\r"; public static final String BACKSPACE = "\b"; public static final String TAB = "\t"; public static final String ESC = "\u001b"; public static final char C_ESC = '\u001b'; public static final String UP = "\ue000"; public static final char C_UP = '\ue000'; public static final String RIGHT = "\ue001"; public static final char C_RIGHT = '\ue001'; public static final String DOWN = "\ue002"; public static final char C_DOWN = '\ue002'; public static final String LEFT = "\ue003"; public static final char C_LEFT = '\ue003'; public static final String PAGE_UP = "\ue004"; public static final char C_PAGE_UP = '\ue004'; public static final String PAGE_DOWN = "\ue005"; public static final char C_PAGE_DOWN = '\ue005'; public static final String DELETE = "\ue006"; public static final char C_DELETE = '\ue006'; public static final String END = "\ue007"; public static final char C_END = '\ue007'; public static final String HOME = "\ue008"; public static final char C_HOME = '\ue008'; public static final String INSERT = "\ue009"; public static final char C_INSERT = '\ue009'; public static final String F1 = "\ue011"; public static final char C_F1 = '\ue011'; public static final String F2 = "\ue012"; public static final char C_F2 = '\ue012'; public static final String F3 = "\ue013"; public static final char C_F3 = '\ue013'; public static final String F4 = "\ue014"; public static final char C_F4 = '\ue014'; public static final String F5 = "\ue015"; public static final char C_F5 = '\ue015'; public static final String F6 = "\ue016"; public static final char C_F6 = '\ue016'; public static final String F7 = "\ue017"; public static final char C_F7 = '\ue017'; public static final String F8 = "\ue018"; public static final char C_F8 = '\ue018'; public static final String F9 = "\ue019"; public static final char C_F9 = '\ue019'; public static final String F10 = "\ue01A"; public static final char C_F10 = '\ue01A'; public static final String F11 = "\ue01B"; public static final char C_F11 = '\ue01B'; public static final String F12 = "\ue01C"; public static final char C_F12 = '\ue01C'; public static final String F13 = "\ue01D"; public static final char C_F13 = '\ue01D'; public static final String F14 = "\ue01E"; public static final char C_F14 = '\ue01E'; public static final String F15 = "\ue01F"; public static final char C_F15 = '\ue01F'; public static final String SHIFT = "\ue020"; public static final char C_SHIFT = '\ue020'; public static final String CTRL = "\ue021"; public static final char C_CTRL = '\ue021'; public static final String ALT = "\ue022"; public static final char C_ALT = '\ue022'; public static final String ALTGR = "\ue043"; public static final char C_ALTGR = '\ue043'; public static final String META = "\ue023"; public static final char C_META = '\ue023'; public static final String CMD = "\ue023"; public static final char C_CMD = '\ue023'; public static final String WIN = "\ue042"; public static final char C_WIN = '\ue042'; public static final String PRINTSCREEN = "\ue024"; public static final char C_PRINTSCREEN = '\ue024'; public static final String SCROLL_LOCK = "\ue025"; public static final char C_SCROLL_LOCK = '\ue025'; public static final String PAUSE = "\ue026"; public static final char C_PAUSE = '\ue026'; public static final String CAPS_LOCK = "\ue027"; public static final char C_CAPS_LOCK = '\ue027'; public static final String NUM0 = "\ue030"; public static final char C_NUM0 = '\ue030'; public static final String NUM1 = "\ue031"; public static final char C_NUM1 = '\ue031'; public static final String NUM2 = "\ue032"; public static final char C_NUM2 = '\ue032'; public static final String NUM3 = "\ue033"; public static final char C_NUM3 = '\ue033'; public static final String NUM4 = "\ue034"; public static final char C_NUM4 = '\ue034'; public static final String NUM5 = "\ue035"; public static final char C_NUM5 = '\ue035'; public static final String NUM6 = "\ue036"; public static final char C_NUM6 = '\ue036'; public static final String NUM7 = "\ue037"; public static final char C_NUM7 = '\ue037'; public static final String NUM8 = "\ue038"; public static final char C_NUM8 = '\ue038'; public static final String NUM9 = "\ue039"; public static final char C_NUM9 = '\ue039'; public static final String SEPARATOR = "\ue03A"; public static final char C_SEPARATOR = '\ue03A'; public static final String NUM_LOCK = "\ue03B"; public static final char C_NUM_LOCK = '\ue03B'; public static final String ADD = "\ue03C"; public static final char C_ADD = '\ue03C'; public static final String MINUS = "\ue03D"; public static final char C_MINUS = '\ue03D'; public static final String MULTIPLY = "\ue03E"; public static final char C_MULTIPLY = '\ue03E'; public static final String DIVIDE = "\ue03F"; public static final char C_DIVIDE = '\ue03F'; public static final String DECIMAL = "\ue040"; public static final char C_DECIMAL = '\ue040'; // VK_DECIMAL public static final String CONTEXT = "\ue041"; public static final char C_CONTEXT = '\ue041'; // VK_CONTEXT_MENU public static final String NEXT = "\ue044"; public static final char C_NEXT = '\ue044'; // VK_CONTEXT_MENU public static final char cMax = '\ue050'; public static final char cMin = '\ue000'; public static int keyMaxLength; // private static Map keyTexts = new HashMap(); private static Map keys = new HashMap(); static { // String sKey; keyMaxLength = 0; for (char c = cMin; c < cMax; c++) { sKey = toJavaKeyCodeText(c); if (!sKey.equals(""+c)) { keyTexts.put(sKey, toJavaKeyCode(c)[0]); keyMaxLength = sKey.length() > keyMaxLength ? sKey.length() : keyMaxLength; } } keyTexts.put("#ENTER.", toJavaKeyCode('\n')[0]); keyTexts.put("#N.", toJavaKeyCode('\n')[0]); keyTexts.put("#BACK.", toJavaKeyCode('\b')[0]); keyTexts.put("#B.", toJavaKeyCode('\b')[0]); keyTexts.put("#TAB.", toJavaKeyCode('\t')[0]); keyTexts.put("#T.", toJavaKeyCode('\t')[0]); keyTexts.put("#X.", toJavaKeyCode(C_NEXT)[0]); keyTexts.put("#ESC.", toJavaKeyCode(C_ESC)[0]); keyTexts.put("#U.", toJavaKeyCode(C_UP)[0]); keyTexts.put("#D.", toJavaKeyCode(C_DOWN)[0]); keyTexts.put("#L.", toJavaKeyCode(C_LEFT)[0]); keyTexts.put("#R.", toJavaKeyCode(C_RIGHT)[0]); keyTexts.put("#S.", toJavaKeyCode(C_SHIFT)[0]); keyTexts.put("#S+", toJavaKeyCode(C_SHIFT)[0]); keyTexts.put("#S-", toJavaKeyCode(C_SHIFT)[0]); keyTexts.put("#A.", toJavaKeyCode(C_ALT)[0]); keyTexts.put("#A+", toJavaKeyCode(C_ALT)[0]); keyTexts.put("#A-", toJavaKeyCode(C_ALT)[0]); keyTexts.put("#C.", toJavaKeyCode(C_CTRL)[0]); keyTexts.put("#C+", toJavaKeyCode(C_CTRL)[0]); keyTexts.put("#C-", toJavaKeyCode(C_CTRL)[0]); keyTexts.put("#M.", toJavaKeyCode(C_META)[0]); keyTexts.put("#M+", toJavaKeyCode(C_META)[0]); keyTexts.put("#M-", toJavaKeyCode(C_META)[0]); for (String k : keyTexts.keySet()) { if (Debug.getDebugLevel() > 3) { Debug.log(4, "Key: %s is: %s", k, KeyEvent.getKeyText(keyTexts.get(k))); } if (k.length() < 4) { continue; } keys.put(keyTexts.get(k), k); } // } // public static String getTextFromKeycode(int key) { return keys.get(key); } public static boolean isRepeatable(String token) { int key = toJavaKeyCodeFromText(token); switch (key) { case KeyEvent.VK_UP: return true; case KeyEvent.VK_DOWN: return true; case KeyEvent.VK_RIGHT: return true; case KeyEvent.VK_LEFT: return true; case -KeyEvent.VK_TAB: return true; case KeyEvent.VK_TAB: return true; case KeyEvent.VK_ENTER: return true; case KeyEvent.VK_BACK_SPACE: return true; } return false; } public static boolean isModifier(String token) { if (toJavaKeyCodeFromText(token) == toJavaKeyCodeFromText("#S.") || toJavaKeyCodeFromText(token) == toJavaKeyCodeFromText("#C.") || toJavaKeyCodeFromText(token) == toJavaKeyCodeFromText("#A.") || toJavaKeyCodeFromText(token) == toJavaKeyCodeFromText("#M.")) { return true; } return false; } public static int toJavaKeyCodeFromText(String key) { if (null == keyTexts.get(key)) { return -1; } else { return keyTexts.get(key).intValue(); } } public static void dump() { Map namesVK = new HashMap(); for (int i = 0; i < keyVK.length; i += 2) { namesVK.put(Integer.decode(keyVK[i+1]), keyVK[i].substring(3)); } Map sortedNames = new TreeMap(keyTexts); System.out.println("[info] Key: dump keynames (tokens) used with Region write"); System.out.println("[info] Token to use --- KeyEvent::keycode --- KeyEvent::keyname"); int keyN; for (String key : sortedNames.keySet()) { keyN = sortedNames.get(key); if (keyN < 1) { continue; } System.out.println(String.format("%s = %d (%s)", key, keyN, namesVK.get(keyN))); } } // /** * Convert Sikuli Key to Java virtual key code * @param key as String * @return the Java KeyCodes */ public static int[] toJavaKeyCode(String key) { if (key.length() > 0) { return toJavaKeyCode(key.charAt(0)); } return null; } /** * Convert Sikuli Key to Java virtual key code * @param key as Character * @return the Java KeyCodes */ public static int[] toJavaKeyCode(char key) { switch (key) { //Lowercase 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}; //Uppercase 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}; //Row 3 (below function keys) // case '§': return new int[]{192}; //not producable case '1': return new int[]{KeyEvent.VK_1}; case '!': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_1}; case '2': return new int[]{KeyEvent.VK_2}; case '@': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_2}; case '3': return new int[]{KeyEvent.VK_3}; case '#': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_3}; case '4': return new int[]{KeyEvent.VK_4}; case '$': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_4}; case '5': return new int[]{KeyEvent.VK_5}; case '%': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_5}; case '6': return new int[]{KeyEvent.VK_6}; case '^': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_6}; case '7': return new int[]{KeyEvent.VK_7}; case '&': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_7}; case '8': return new int[]{KeyEvent.VK_8}; case '*': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_8}; case '9': return new int[]{KeyEvent.VK_9}; case '(': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_9}; case '0': return new int[]{KeyEvent.VK_0}; case ')': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_0}; case '-': return new int[]{KeyEvent.VK_MINUS}; case '_': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_MINUS}; case '=': return new int[]{KeyEvent.VK_EQUALS}; case '+': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_EQUALS}; //Row 2 // q w e r t y u i o p case '[': return new int[]{KeyEvent.VK_OPEN_BRACKET}; case '{': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_OPEN_BRACKET}; case ']': return new int[]{KeyEvent.VK_CLOSE_BRACKET}; case '}': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_CLOSE_BRACKET}; //Row 1 // a s d f g h j k l case ';': return new int[]{KeyEvent.VK_SEMICOLON}; case ':': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_SEMICOLON}; case '\'': return new int[]{KeyEvent.VK_QUOTE}; case '"': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_QUOTE}; case '\\': return new int[]{KeyEvent.VK_BACK_SLASH}; case '|': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_SLASH}; //RETURN, BACKSPACE, TAB case '\b': return new int[]{KeyEvent.VK_BACK_SPACE}; case '\t': return new int[]{KeyEvent.VK_TAB}; case '\r': return new int[]{KeyEvent.VK_ENTER}; case '\n': return new int[]{KeyEvent.VK_ENTER}; //SPACE case ' ': return new int[]{KeyEvent.VK_SPACE}; //Row 0 (first above SPACE) case '`': return new int[]{KeyEvent.VK_BACK_QUOTE}; case '~': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_BACK_QUOTE}; // z x c v b n m case ',': return new int[]{KeyEvent.VK_COMMA}; case '<': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_COMMA}; case '.': return new int[]{KeyEvent.VK_PERIOD}; case '>': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_PERIOD}; case '/': return new int[]{KeyEvent.VK_SLASH}; case '?': return new int[]{KeyEvent.VK_SHIFT, KeyEvent.VK_SLASH}; //Modifier case Key.C_SHIFT: return new int[]{KeyEvent.VK_SHIFT}; case Key.C_CTRL: return new int[]{KeyEvent.VK_CONTROL}; case Key.C_ALT: return new int[]{KeyEvent.VK_ALT}; case Key.C_META: return new int[]{KeyEvent.VK_META}; //Cursor movement case Key.C_UP: return new int[]{KeyEvent.VK_UP}; case Key.C_RIGHT: return new int[]{KeyEvent.VK_RIGHT}; case Key.C_DOWN: return new int[]{KeyEvent.VK_DOWN}; case Key.C_LEFT: return new int[]{KeyEvent.VK_LEFT}; case Key.C_PAGE_UP: return new int[]{KeyEvent.VK_PAGE_UP}; case Key.C_PAGE_DOWN: return new int[]{KeyEvent.VK_PAGE_DOWN}; case Key.C_END: return new int[]{KeyEvent.VK_END}; case Key.C_HOME: return new int[]{KeyEvent.VK_HOME}; case Key.C_DELETE: return new int[]{KeyEvent.VK_DELETE}; //Function keys case Key.C_ESC: return new int[]{KeyEvent.VK_ESCAPE}; case Key.C_F1: return new int[]{KeyEvent.VK_F1}; case Key.C_F2: return new int[]{KeyEvent.VK_F2}; case Key.C_F3: return new int[]{KeyEvent.VK_F3}; case Key.C_F4: return new int[]{KeyEvent.VK_F4}; case Key.C_F5: return new int[]{KeyEvent.VK_F5}; case Key.C_F6: return new int[]{KeyEvent.VK_F6}; case Key.C_F7: return new int[]{KeyEvent.VK_F7}; case Key.C_F8: return new int[]{KeyEvent.VK_F8}; case Key.C_F9: return new int[]{KeyEvent.VK_F9}; case Key.C_F10: return new int[]{KeyEvent.VK_F10}; case Key.C_F11: return new int[]{KeyEvent.VK_F11}; case Key.C_F12: return new int[]{KeyEvent.VK_F12}; case Key.C_F13: return new int[]{KeyEvent.VK_F13}; case Key.C_F14: return new int[]{KeyEvent.VK_F14}; case Key.C_F15: return new int[]{KeyEvent.VK_F15}; //Toggling kezs case Key.C_SCROLL_LOCK: return new int[]{KeyEvent.VK_SCROLL_LOCK}; case Key.C_NUM_LOCK: return new int[]{KeyEvent.VK_NUM_LOCK}; case Key.C_CAPS_LOCK: return new int[]{KeyEvent.VK_CAPS_LOCK}; case Key.C_INSERT: return new int[]{KeyEvent.VK_INSERT}; //Windows special case Key.C_PAUSE: return new int[]{KeyEvent.VK_PAUSE}; case Key.C_PRINTSCREEN: return new int[]{KeyEvent.VK_PRINTSCREEN}; //Num pad case Key.C_NUM0: return new int[]{KeyEvent.VK_NUMPAD0}; case Key.C_NUM1: return new int[]{KeyEvent.VK_NUMPAD1}; case Key.C_NUM2: return new int[]{KeyEvent.VK_NUMPAD2}; case Key.C_NUM3: return new int[]{KeyEvent.VK_NUMPAD3}; case Key.C_NUM4: return new int[]{KeyEvent.VK_NUMPAD4}; case Key.C_NUM5: return new int[]{KeyEvent.VK_NUMPAD5}; case Key.C_NUM6: return new int[]{KeyEvent.VK_NUMPAD6}; case Key.C_NUM7: return new int[]{KeyEvent.VK_NUMPAD7}; case Key.C_NUM8: return new int[]{KeyEvent.VK_NUMPAD8}; case Key.C_NUM9: return new int[]{KeyEvent.VK_NUMPAD9}; //Num pad special case Key.C_SEPARATOR: return new int[]{KeyEvent.VK_SEPARATOR}; case Key.C_ADD: return new int[]{KeyEvent.VK_ADD}; case Key.C_MINUS: return new int[]{KeyEvent.VK_SUBTRACT}; case Key.C_MULTIPLY: return new int[]{KeyEvent.VK_MULTIPLY}; case Key.C_DIVIDE: return new int[]{KeyEvent.VK_DIVIDE}; case Key.C_DECIMAL: return new int[]{KeyEvent.VK_DECIMAL}; case Key.C_CONTEXT: return new int[]{KeyEvent.VK_CONTEXT_MENU}; case Key.C_WIN: return new int[]{KeyEvent.VK_WINDOWS}; //hack: alternative tab in GUI case Key.C_NEXT: return new int[]{-KeyEvent.VK_TAB}; default: throw new IllegalArgumentException("Key: Not supported character: " + key); } } /** * * @param key as Character * @return a printable version of a special key */ public static String toJavaKeyCodeText(char key) { switch (key) { //RETURN, BACKSPACE, TAB case '\b': return "#BACK."; case '\t': return "#TAB."; case C_NEXT: return "#TAB."; case '\r': return "#ENTER."; case '\n': return "#ENTER."; //Cursor movement case Key.C_UP: return "#UP."; case Key.C_RIGHT: return "#RIGHT."; case Key.C_DOWN: return "#DOWN."; case Key.C_LEFT: return "#LEFT."; case Key.C_PAGE_UP: return "#PUP."; case Key.C_PAGE_DOWN: return "#PDOWN."; case Key.C_END: return "#END."; case Key.C_HOME: return "#HOME."; case Key.C_DELETE: return "#DEL."; //Function keys case Key.C_ESC: return "#ESC."; case Key.C_F1: return "#F1."; case Key.C_F2: return "#F2."; case Key.C_F3: return "#F3."; case Key.C_F4: return "#F4."; case Key.C_F5: return "#F5."; case Key.C_F6: return "#F6."; case Key.C_F7: return "#F7."; case Key.C_F8: return "#F8."; case Key.C_F9: return "#F9."; case Key.C_F10: return "#F10."; case Key.C_F11: return "#F11."; case Key.C_F12: return "#F12."; case Key.C_F13: return "#F13."; case Key.C_F14: return "#F14."; case Key.C_F15: return "#F15."; //Toggling kezs case Key.C_SCROLL_LOCK: return "#SCROLL_LOCK."; case Key.C_NUM_LOCK: return "#NUM_LOCK."; case Key.C_CAPS_LOCK: return "#CAPS_LOCK."; case Key.C_INSERT: return "#INS."; //Windows special case Key.C_PAUSE: return "#PAUSE."; case Key.C_PRINTSCREEN: return "#PRINTSCREEN."; case Key.C_WIN: return "#WIN."; //Num pad case Key.C_NUM0: return "#NUM0."; case Key.C_NUM1: return "#NUM1."; case Key.C_NUM2: return "#NUM2."; case Key.C_NUM3: return "#NUM3."; case Key.C_NUM4: return "#NUM4."; case Key.C_NUM5: return "#NUM5."; case Key.C_NUM6: return "#NUM6."; case Key.C_NUM7: return "#NUM7."; case Key.C_NUM8: return "#NUM8."; case Key.C_NUM9: return "#NUM9."; //Num pad special case Key.C_SEPARATOR: return "#NSEP."; case Key.C_ADD: return "#NADD."; case Key.C_MINUS: return "#NSUB."; case Key.C_MULTIPLY: return "#NMUL"; case Key.C_DIVIDE: return "#NDIV."; case Key.C_DECIMAL: return "#NDEC."; case Key.C_CONTEXT: return "#NCON."; //KeyModifiers case Key.C_SHIFT: return "#SHIFT."; case Key.C_CTRL: return "#CTRL."; case Key.C_ALT: return "#ALT."; case Key.C_META: return "#META."; default: return "" + key; } } protected static int convertModifiers(String mod) { int modNew = 0; char key; for (int i = 0; i < mod.length(); i++) { key = mod.charAt(i); if (Key.C_CTRL == key) { modNew |= KeyModifier.CTRL; } else if (Key.C_ALT == key) { modNew |= KeyModifier.ALT; } else if (Key.C_SHIFT == key) { modNew |= KeyModifier.SHIFT; } else if (Key.C_CMD == key) { modNew |= KeyModifier.CMD; } else if (Key.C_META == key) { modNew |= KeyModifier.META; } else if (Key.C_ALTGR == key) { modNew |= KeyModifier.ALTGR; } else if (Key.C_WIN == key) { modNew |= KeyModifier.WIN; } } return modNew; } /** * get the lock state of the given key * * @param key as Character (scroll, caps, num) * @return true/false */ public static boolean isLockOn(char key) { // Toolkit tk = Toolkit.getDefaultToolkit(); // return tk.getLockingKeyState(KeyEvent.VK_SCROLL_LOCK); // return tk.getLockingKeyState(KeyEvent.VK_CAPS_LOCK); // return tk.getLockingKeyState(KeyEvent.VK_NUM_LOCK); if (!RunTime.get().runningWindows) { return false; } switch (key) { case '\ue025': return SysJNA.WinUser32.isScrollLockOn(); case '\ue027': return SysJNA.WinUser32.isCapsLockOn(); case '\ue03B': return SysJNA.WinUser32.isNumLockOn(); default: return false; } } /** * HotKey modifier to be used with Sikuli's HotKey feature * @return META(CMD) on Mac, CTRL otherwise */ public static int getHotkeyModifier() { if (Settings.isMac()) { return KeyEvent.VK_META; } else { return KeyEvent.VK_CONTROL; } } // static String[] result = new String[] {""}; /** * INTERNAL USE ONLY * create an alternate keycode table for a non-US keyboard */ // public static void keyBoardSetup() { // Map keyLocal = new HashMap(); // String[] keyXX = new String[]{"", "", "", "",}; // String keysx = Key.keyboardUS; // // try { // Screen s = new Screen(0); // ImagePath.add("org.sikuli.basics.SikuliX/Images"); // // ShowKeyBoardSetupWindow win = new ShowKeyBoardSetupWindow(); // win.start(); // s.wait(3.0F); // // Location btnOK = s.find("SikuliLogo").getCenter(); // Location txtArea = btnOK.offset(0, 400); // s.click(txtArea); // s.wait(1.0F); // String[] mods = new String[]{"", "S", "A", "SA"}; // Debug.setDebugLevel(0); // for (String modx : mods) { // s.paste((modx.isEmpty() ? "NO" : modx) + " modifier" + "\n"); // if (!modx.isEmpty()) { // if (modx.length() == 1) { // modx = "#" + modx + "."; // } else if (modx.length() == 2) { // modx = "#" + modx.substring(0, 1) + ".#" + modx.substring(1, 2) + "."; // } else { // break; // } // } // String c; // for (int n = 0; n < keysx.length(); n++) { // c = "" + keysx.charAt(n); // s.paste(c); // s.type(" "); // if (Mouse.hasMoved()) { // s.click(txtArea); // s.wait(0.3F); // } // s.write(String.format("%s%s ", modx, c)); // if ("=".equals(c) || "]".equals(c) || "\\".equals(c)) { // s.paste("\n"); // } // } // s.paste("\n"); // s.type(Key.ENTER); // } // Debug.setDebugLevel(3); // s.click(btnOK); // } catch (FindFailed e) { // Debug.error("KeyBoardSetup: Does not work - getting FindFailed"); // return; // } // while (true) { // try { // Thread.sleep(1000); // } catch (InterruptedException ex) { // } // Debug.log(3, "waiting for result\n" + result[0]); // if (!result[0].isEmpty()) { // break; // } // } // String[] keyNewx = result[0].split("\n"); // String keysNew; // String mod; // int nKDE = 0; // for (int n = 0; n < keyNewx.length; n += 6) { // mod = keyNewx[n].substring(0, 2); // keysNew = keyNewx[n + 1] + keyNewx[n + 2] + keyNewx[n + 3] + keyNewx[n + 4]; // keyXX[nKDE] = mod + ": " + keysNew; // Debug.log(3, "%s", keyXX[nKDE]); // nKDE++; // } // String kSet; // int offset = 4; // char keyOld, keyNew; // int[] codeA; // String codeS; // String modText; // int nOld; // for (int n = 0; n < 4; n++) { // kSet = keyXX[n].substring(4); // mod = keyXX[n].substring(0, 2); // modText = ""; // if ("S ".equals(mod)) { // modText = "SHIFT "; // } else if ("A ".equals(mod)) { // modText = "ALT "; // } else if ("SA".equals(mod)) { // modText = "SHIFT ALT "; // } // Debug.log(3, mod + "\n" + kSet); // nOld = 0; // for (int i = 0; i < kSet.length(); i++) { // if (i + 3 > kSet.length()) { // break; // } // if (!" ".equals("" + kSet.charAt(i + 3))) { // offset = 3; // } // keyOld = kSet.charAt(i); // keyNew = kSet.charAt(i + 2); // Integer[] codeI = new Integer[]{-1, -1, -1}; // try { // codeA = Key.toJavaKeyCode(keyNew); // codeS = ""; // for (int iK : codeA) { // codeS += KeyEvent.getKeyText(iK) + " "; // } // if (codeA.length == 1) { // codeI[2] = codeA[0]; // } else if (codeA.length == 2) { // codeI[2] = codeA[1]; // codeI[0] = codeA[0]; // } else if (codeA.length == 3) { // codeI[2] = codeA[2]; // codeI[0] = codeA[0]; // codeI[1] = codeA[1]; // } // } catch (Exception e) { // codeS = "UNKNOWN "; // codeI[2] = Key.toJavaKeyCode(keysx.charAt(nOld))[0]; // codeS += modText + KeyEvent.getKeyText(codeI[2]); // for (int ik = 0; ik < 2; ik++) { // if (mod.charAt(ik) == 'S') { // codeI[ik] = KeyEvent.VK_SHIFT; // } else if (mod.charAt(ik) == 'A') { // codeI[ik] = KeyEvent.VK_ALT; // } // } // } // if (keyLocal.get(keyNew) == null) { // keyLocal.put(keyNew, codeI); // } // Debug.log(3, "%s %c %c %s", mod, keyOld, keyNew, codeS); // i += offset - 1; // offset = 4; // nOld += 1; // } // } // // Debug.log(3, "--------------- keyLocal"); // Integer[] codeI; // for (Character kc : keyLocal.keySet()) { // codeI = keyLocal.get(kc); // Debug.log(3, "%c %s %s %s", kc.charValue(), // (codeI[0] == -1 ? "" : KeyEvent.getKeyText(codeI[0])), // (codeI[1] == -1 ? "" : KeyEvent.getKeyText(codeI[1])), // (codeI[2] == -1 ? "" : KeyEvent.getKeyText(codeI[2]))); // } // } /** * INTERNAL USE ONLY * * @param code keycode * @param mod modifier * @return readable key text */ public static String convertKeyToText(int code, int mod) { String txtMod = KeyEvent.getKeyModifiersText(mod); String txtCode = KeyEvent.getKeyText(code); String ret; if (code == KeyEvent.VK_ALT || code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT) { ret = txtMod; } else { ret = txtMod + " " + txtCode; } return ret; } // static class ShowKeyBoardSetupWindow extends Thread { // // JFrame kbSetup; // // @Override // public void run() { // BufferedImage img = Image.create("SikuliLogo").get(); // Debug.log(3, "KBSetup: %s", img); // Image.dump(); // kbSetup = new JFrame("Localized Keyboard Setup"); // Container mpwinCP = kbSetup.getContentPane(); // mpwinCP.setLayout(new BorderLayout()); // KeyBoardSetupWindow win = new KeyBoardSetupWindow(result); // mpwinCP.add(win, BorderLayout.CENTER); // kbSetup.pack(); // kbSetup.setAlwaysOnTop(true); // kbSetup.setDefaultCloseOperation(DISPOSE_ON_CLOSE); // win.setLogo(new ImageIcon(img)); // kbSetup.setVisible(true); // } // } /** * INTERNAL USE ONLY * create a table containing all relevant key, keycode and keytext settings for VK_xxx */ public static void createKeyTable() { Map namesVK = new HashMap(); for (int i = 0; i < keyVK.length; i += 2) { namesVK.put(keyVK[i+1], keyVK[i].substring(3)); } String keyText; int upper = 66000; String form = "%05d: %-25s %-20s %-15s %15s %s"; for (int key = 0; key < upper; key++) { char ckey = (char) key; String k1 = "..."; String k2 = "..."; try { if (toJavaKeyCode((ckey)).length > 1) { k1 = namesVK.get(new Integer(toJavaKeyCode(ckey)[0]).toString()); k2 = namesVK.get(new Integer(toJavaKeyCode(ckey)[1]).toString()); } else if (key > 32) { k2 = namesVK.get(new Integer(toJavaKeyCode(ckey)[0]).toString()); } } catch (Exception e) { } keyText = KeyEvent.getKeyText(key); if (keyText.startsWith("Unknown") && ("...".equals(k2) || key > 255)) { continue; } if (keyText.startsWith("Unknown")) { keyText = "???"; } if (keyText.length() == 1) { if (key < 256 ) { if (key < 32) { key = ' '; } Debug.log(3, form, key, keyText, "...", "...", k2, k1); } continue; } keyText = keyText.replaceAll(" ", "_"); String vkey = namesVK.get(new Integer(key).toString()); if (vkey == null || vkey.equals(keyText.toUpperCase())) { vkey = "..."; } if (null == getTextFromKeycode(key)) { Debug.log(3, form, key, keyText.toUpperCase(), vkey, "...", k2, k1); } else { Debug.log(3, form, key, keyText.toUpperCase(), vkey, getTextFromKeycode(key), k2, k1); } } } // } sikulix-1.1.1/API/src/main/java/org/sikuli/script/KeyModifier.java000077500000000000000000000022501315726130400247200ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.event.InputEvent; /** * complementing class Key with the constants for the modifier keys
* only still there for backward compatibility (is already duplicated in Key) */ public class KeyModifier { public static final int CTRL = InputEvent.CTRL_MASK; public static final int SHIFT = InputEvent.SHIFT_MASK; public static final int ALT = InputEvent.ALT_MASK; public static final int ALTGR = InputEvent.ALT_GRAPH_MASK; public static final int META = InputEvent.META_MASK; public static final int CMD = InputEvent.META_MASK; public static final int WIN = InputEvent.META_MASK; @Deprecated public static final int KEY_CTRL = InputEvent.CTRL_MASK; @Deprecated public static final int KEY_SHIFT = InputEvent.SHIFT_MASK; @Deprecated public static final int KEY_ALT = InputEvent.ALT_MASK; @Deprecated public static final int KEY_META = InputEvent.META_MASK; @Deprecated public static final int KEY_CMD = InputEvent.META_MASK; @Deprecated public static final int KEY_WIN = InputEvent.META_MASK; } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Location.java000077500000000000000000000273731315726130400242760ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Debug; import java.awt.Color; import java.awt.Point; import java.awt.Rectangle; /** * A point like AWT.Point using global coordinates (x, y). * hence modifications might move location out of * any screen (not checked as is done with region) * */ public class Location implements Comparable{ static RunTime runTime = RunTime.get(); public int x; public int y; private IScreen otherScreen = null; /** * to allow calculated x and y that might not be integers * @param x column * @param y row * truncated to the integer part */ public Location(double x, double y) { this.x = (int) x; this.y = (int) y; } /** * a new point at the given coordinates * @param x column * @param y row */ public Location(int x, int y) { this.x = x; this.y = y; } /** * duplicates the point * @param loc other Location */ public Location(Location loc) { x = loc.x; y = loc.y; if (loc.isOtherScreen()) { otherScreen = loc.getScreen(); } } /** * create from AWT point * @param point a Point */ public Location(Point point) { x = (int) point.x; y = (int) point.y; } /** * * @return x value */ public int getX() { return x; } /** * * @return y value */ public int getY() { return y; } /** * get as AWT point * @return Point */ public Point getPoint() { return new Point(x,y); } /** * sets the coordinates to the given values (moves it) * @param x new x * @param y new y * @return this */ public Location setLocation(int x, int y) { this.x = x; this.y = y; return this; } /** * sets the coordinates to the given values (moves it) * @param x new x might be non-int * @param y new y might be non-int * @return this */ public Location setLocation(double x, double y) { this.x = (int) x; this.y = (int) y; return this; } /** * Returns null, if outside of any screen and not contained in a non-Desktop Screen instance (e.g. remote screen)
* subsequent actions WILL crash if not tested for null return * * @return the screen, that contains the given point */ public IScreen getScreen() { Rectangle r; if (otherScreen != null) { return otherScreen; } for (int i = 0; i < Screen.getNumberScreens(); i++) { r = Screen.getScreen(i).getBounds(); if (r.contains(this.x, this.y)) { return Screen.getScreen(i); } } Debug.error("Location: outside any screen (%s, %s) - subsequent actions might not work as expected", x, y); return null; } /** * Returns primary screen, if outside of any screen or not contained in a non-Desktop Screen instance (e.g. remote screen)
* * @return the real screen, that contains the given point */ public Screen getMonitor() { Rectangle r; Screen scr = null; if (otherScreen == null) { for (int i = 0; i < Screen.getNumberScreens(); i++) { r = Screen.getScreen(i).getBounds(); if (r.contains(this.x, this.y)) { scr = Screen.getScreen(i); break; } } } else { Debug.error("Location: getMonitor: (%s, %s) not on real screen - using primary", x, y); scr = Screen.getPrimaryScreen(); } if (scr == null) { Debug.error("Location: getMonitor: (%s, %s) outside any screen - using primary", x, y); scr = Screen.getPrimaryScreen(); } return scr; } /** * INTERNAL USE * reveals wether the containing screen is a DeskTopScreen or not * @return null if DeskTopScreen */ public boolean isOtherScreen() { return (otherScreen != null); } /** * INTERNAL USE * identifies the point as being on a non-desktop-screen * @param scr Screen * @return this */ public Location setOtherScreen(IScreen scr) { otherScreen = scr; return this; } /** * INTERNAL USE * identifies the point as being on a non-desktop-screen * if this is true for the given location * @return this */ private Location setOtherScreen(Location loc) { if (loc.isOtherScreen()) { setOtherScreen(loc.getScreen()); } return this; } // TODO Location.getColor() implement more support and make it useable /** * Get the color at the given Point for details: see java.awt.Robot and ...Color * * @return The Color of the Point */ public Color getColor() { if (getScreen() == null) { return null; } return getScreen().getRobot().getColorAt(x, y); } /** * the offset of given point to this Location * * @param loc the other Location * @return relative offset */ public Location getOffset(Location loc) { return new Location(loc.x - x, loc.y - y); } /** * create a region with this point as center and the given size * * @param w the width * @param h the height * @return the new region */ public Region grow(int w, int h) { return Region.grow(this, w, h); } /** * create a region with this point as center and the given size * * @param wh the width and height * @return the new region */ public Region grow(int wh) { return grow(wh, wh); } /** * create a region with a corner at this point
as specified with x y
0 0 top left
* 0 1 bottom left
1 0 top right
1 1 bottom right
* * @param CREATE_X_DIRECTION == 0 is left side !=0 is right side, see {@link Region#CREATE_X_DIRECTION_LEFT}, {@link Region#CREATE_X_DIRECTION_RIGHT} * @param CREATE_Y_DIRECTION == 0 is top side !=0 is bottom side, see {@link Region#CREATE_Y_DIRECTION_TOP}, {@link Region#CREATE_Y_DIRECTION_BOTTOM} * @param w the width * @param h the height * @return the new region */ public Region grow(int CREATE_X_DIRECTION, int CREATE_Y_DIRECTION, int w, int h) { return Region.create(this, CREATE_X_DIRECTION, CREATE_Y_DIRECTION, w, h); } /** * moves the point the given amounts in the x and y direction, might be negative
might move * point outside of any screen, not checked * * @param dx x offset * @param dy y offset * @return the location itself modified * @deprecated use {@link #translate(int, int)} */ @Deprecated public Location moveFor(int dx, int dy) { x += dx; y += dy; return this; } /** * convenience: like awt point * @param dx x offset * @param dy y offset * @return the location itself modified */ public Location translate(int dx, int dy) { return moveFor(dx, dy); } /** * changes the locations x and y value to the given values (moves it)
might move point * outside of any screen, not checked * * @param X new x * @param Y new y * @return the location itself modified * @deprecated use {@link #move(int, int)} */ @Deprecated public Location moveTo(int X, int Y) { x = X; y = Y; return this; } /** * convenience: like awt point * @param X new x * @param Y new y * @return the location itself modified */ public Location move(int X, int Y) { return moveTo(X, Y); } /** * creates a point at the given offset, might be negative
might create a point outside of * any screen, not checked * * @param dx x offset * @param dy y offset * @return new location */ public Location offset(int dx, int dy) { return new Location(x + dx, y + dy); } /** * creates a point at the given offset, might be negative
might create a point outside of * any screen, not checked * * @param loc offset given as Location * @return new location */ public Location offset(Location loc) { return new Location(x + loc.x, y + loc.y); } /** * creates a point at the given offset to the left, might be negative
might create a point * outside of any screen, not checked * * @param dx x offset * @return new location */ public Location left(int dx) { return new Location(x - dx, y).setOtherScreen(this); } /** * creates a point at the given offset to the right, might be negative
might create a point * outside of any screen, not checked * * @param dx x offset * @return new location */ public Location right(int dx) { return new Location(x + dx, y).setOtherScreen(this); } /** * creates a point at the given offset above, might be negative
might create a point outside * of any screen, not checked * * @param dy y offset * @return new location */ public Location above(int dy) { return new Location(x, y - dy).setOtherScreen(this); } /** * creates a point at the given offset below, might be negative
might create a point outside * of any screen, not checked * * @param dy y offset * @return new location */ public Location below(int dy) { return new Location(x, y + dy).setOtherScreen(this); } /** * new point with same offset to current screen's top left on given screen * * @param scrID number of screen * @return new location */ public Location copyTo(int scrID) { return copyTo(Screen.getScreen(scrID)); } /** * New point with same offset to current screen's top left on given screen * * @param screen new parent screen * @return new location */ public Location copyTo(IScreen screen) { IScreen s = getScreen(); s = (s == null ? Screen.getPrimaryScreen() : s); Location o = new Location(s.getBounds().getLocation()); Location n = new Location(screen.getBounds().getLocation()); return new Location(n.x + x - o.x, n.y + y - o.y); } /** * Move the mouse to this location point * * @return this */ public Location hover() { Mouse.move(this); return this; } /** * Move the mouse to this location point and click left * * @return this */ public Location click() { Mouse.click(this, "L"); return this; } /** * Move the mouse to this location point and double click left * * @return this */ public Location doubleClick() { Mouse.click(this, "LD"); return this; } /** * Move the mouse to this location point and click right * * @return this */ public Location rightClick() { Mouse.click(this, "R"); return this; } @Override public boolean equals(Object oThat) { if (this == oThat) { return true; } if (!(oThat instanceof Location)) { return false; } Location that = (Location) oThat; return x == that.x && y == that.y; } /** * {@inheritDoc} * @param loc other Location * @return -1 if given point is more above and/or left, 1 otherwise (0 is equal) */ @Override public int compareTo(Location loc) { if (equals(loc)) { return 0; } if (loc.x > x) { return 1; } else if (loc.x == x) { if (loc.y > y) { return 1; } } return -1; } /** * {@inheritDoc} * @return the description */ @Override public String toString() { IScreen s = getScreen(); if(s instanceof Screen){ return "L(" + x + "," + y + ")" + "@" + ((Screen) s).toStringShort(); } else{ return "L(" + x + "," + y + ")" + ((s == null) ? "" : "@" + s.toString()); } } /** * * @return a shorter description */ public String toStringShort() { return "L(" + x + "," + y + ")"; } public String toJSON() { return String.format("[\"L\", %d, %d]", x, y); } protected IRobot getRobotForPoint(String action) { if (getScreen() == null) { Debug.error("Point %s outside any screen for %s - might not work", this, action); return Screen.getGlobalRobot(); } if (!getScreen().isOtherScreen()) { getScreen().showTarget(this); } return getScreen().getRobot(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Match.java000077500000000000000000000147751315726130400235640ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Settings; import org.sikuli.natives.FindResult; /** * holds the result of a find operation, is itself the region on the screen, * where the image was found and hence inherits all methods from {@link Region}. *
* attributes:
the match score (0 ... 1.0)
the click target (e.g. * from {@link Pattern})
a ref to the image used for search
or the text used for * find text
and elapsed times for debugging */ public class Match extends Region implements Comparable { private double simScore; private Location target = null; private Image image = null; private String ocrText = null; private long lastSearchTime = -1; private long lastFindTime = -1; private int index = -1; private boolean onScreen = true; public void setOnScreen(boolean state) { onScreen = state; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } /** * INTERNAL USE * set the elapsed times from search * @param ftime time * @param stime time */ public void setTimes(long ftime, long stime) { lastFindTime = ftime; lastSearchTime = stime; } /** * * @return this Match's actual waiting time from last successful find */ public long getTime() { return lastFindTime; } /** * create a copy of Match object
* to e.g. set another TargetOffset for same match * * @param m other Match */ public Match(Match m) { init(m.x, m.y, m.w, m.h, m.getScreen()); copy(m); } /** * create a Match from a region with given SimScore * @param reg Region * @param sc SimScore */ public Match(Region reg, double sc) { init(reg.x, reg.y, reg.w, reg.h, reg.getScreen()); simScore = sc; } private Match(Match m, IScreen parent) { init(m.x, m.y, m.w, m.h, parent); copy(m); } /** * internally used constructor by TextRecognizer.listText() * * @param x x * @param y y * @param w width * @param h height * @param Score SimScore * @param parent Screen * @param text given text */ protected Match(int x, int y, int w, int h, double Score, IScreen parent, String text) { init(x, y, w, h, parent); simScore = Score; ocrText = text; } private Match(int _x, int _y, int _w, int _h, double score, IScreen _parent) { init(_x, _y, _w, _h, _parent); simScore = score; } /** * internally used constructor used by findX image * * @param f * @param _parent */ protected Match(FindResult f, IScreen _parent) { init(f.getX(), f.getY(), f.getW(), f.getH(), _parent); simScore = f.getScore(); } private void init(int X, int Y, int W, int H, IScreen parent) { x = X; y = Y; w = W; h = H; setScreen(parent); } private void copy(Match m) { simScore = m.simScore; ocrText = m.ocrText; image = m.image; target = null; if (m.target != null) { target = new Location(m.target); } lastFindTime = m.lastFindTime; lastSearchTime = m.lastSearchTime; } /** * the match score * * @return a decimal value between 0 (no match) and 1 (exact match) */ public double getScore() { return simScore; } /** * {@inheritDoc} * @return the point defined by target offset (if set) or the center */ @Override public Location getTarget() { if (target != null) { return target; } return getCenter(); } /** * like {@link Pattern#targetOffset(org.sikuli.script.Location) Pattern.targetOffset} * sets the click target by offset relative to the center * * @param offset as a Location */ public void setTargetOffset(Location offset) { target = new Location(getCenter()); target.translate(offset.x, offset.y); } /** * like {@link Pattern#targetOffset(int, int) Pattern.targetOffset} * sets the click target relative to the center * @param x x offset * @param y y offset */ public void setTargetOffset(int x, int y) { setTargetOffset(new Location(x,y)); } /** * convenience - same as {@link Pattern#getTargetOffset()} * * @return the relative offset to the center */ public Location getTargetOffset() { return (getCenter().getOffset(getTarget())); } /** * set the image after finding with success * @param img Image */ protected void setImage(Image img) { image = img; if (Settings.Highlight) { highlight(Settings.DefaultHighlightTime); } } /** * get the image used for searching * @return image or null */ public Image getImage() { return image; } /** * get the filename of the image used for searching * @return filename */ public String getImageFilename() { return image.getFilename(); } /** * * @return the text used for searching */ public String getText() { return ocrText; } @Override public int compareTo(Match m) { if (simScore != m.simScore) { return simScore < m.simScore ? -1 : 1; } if (x != m.x) { return x - m.x; } if (y != m.y) { return y - m.y; } if (w != m.w) { return w - m.w; } if (h != m.h) { return h - m.h; } if (equals(m)) { return 0; } return -1; } @Override public boolean equals(Object oThat) { if (this == oThat) { return true; } if (!(oThat instanceof Match)) { return false; } Match that = (Match) oThat; return x == that.x && y == that.y && w == that.w && h == that.h && Math.abs(simScore - that.simScore) < 1e-5 && getTarget().equals(that.getTarget()); } @Override public String toString() { String starget; Location c = getCenter(); if (target != null && !c.equals(target)) { starget = String.format("T:%d,%d", target.x, target.y); } else { starget = String.format("C:%d,%d", c.x, c.y); } String findTimes = String.format("[%d msec]", lastFindTime); return String.format("M[%d,%d %dx%d]@S(%s) S:%.2f %s %s", x, y, w, h, ((getScreen()== null || !onScreen) ? "?" : getScreen().toStringShort()), simScore, starget, findTimes); } @Override public String toStringShort() { return String.format("M[%d,%d %dx%d]@S(%s)", x, y, w, h, (getScreen()== null ? "?" : getScreen().getID())); } /** * INTERNAL USE * * @param tx x * @param ty y */ public void setTarget(int tx, int ty) { target = new Location(tx, ty); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Mouse.java000066400000000000000000000300241315726130400235760ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; /** * Main pupose is to coordinate the mouse usage among threads
* At any one time, the mouse has one owner (usually a Region object)
* who exclusively uses the mouse, all others wait for the mouse to be free again
* if more than one possible owner is waiting, the next owner is uncertain
* It is detected, when the mouse is moved external from the workflow, which can be used for * appropriate actions (e.g. pause a script)
* the mouse can be blocked for a longer time, so only this owner can use the mouse (like some * transactional processing)
* Currently deadlocks and infinite waits are not detected, but should not happen ;-)
* Contained are methods to use the mouse (click, move, button down/up) as is */ public class Mouse { private static String me = "Mouse: "; private static final int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private static Mouse mouse = null; private Device device = null; protected Location mousePos; protected boolean clickDouble; protected int buttons; protected int beforeWait; protected int innerWait; protected int afterWait; public static final int LEFT = InputEvent.BUTTON1_MASK; public static final int MIDDLE = InputEvent.BUTTON2_MASK; public static final int RIGHT = InputEvent.BUTTON3_MASK; public static final int WHEEL_UP = -1; public static int WHEEL_DOWN = 1; public static final int WHEEL_STEP_DELAY = 50; private Mouse() { } public static void init() { if (mouse == null) { log(3, "init start"); mouse = new Mouse(); mouse.device = new Device(mouse); mouse.device.isMouse = true; Location loc = at(); move(loc); mouse.device.lastPos = null; log(3, "init end"); } } private static Mouse get() { if (mouse == null) { init(); } return mouse; } protected static boolean use() { return get().device.use(null); } protected static boolean use(Object owner) { return get().device.use(owner); } protected static boolean keep(Object owner) { return get().device.keep(owner); } protected static boolean let() { return get().device.let(null); } protected static boolean let(Object owner) { return get().get().device.let(owner); } public static Location at() { return get().device.getLocation(); } public static void reset() { if (mouse == null) { return; } get().device.unblock(get().device.owner); mouse.get().device.let(get().device.owner); get().device.let(get().device.owner); get().device.mouseMovedResponse = get().device.MouseMovedIgnore; get().device.mouseMovedCallback = null; get().device.callback = null; get().device.lastPos = null; Screen.getPrimaryScreen().getRobot().mouseReset(); } /** * current setting what to do if mouse is moved outside Sikuli's mouse protection * * @return current setting see {@link #setMouseMovedAction(int)} */ public static int getMouseMovedResponse() { return get().device.mouseMovedResponse; } /** * what to do if mouse is moved outside Sikuli's mouse protection
* - Mouse.MouseMovedIgnore (0) ignore it (default)
* - Mouse.MouseMovedShow (1) show and ignore it
* - Mouse.MouseMovedPause (2) show it and pause until user says continue
* (2 not implemented yet - 1 is used) * * @param movedAction value */ public static void setMouseMovedAction(int movedAction) { if (movedAction > -1 && movedAction < 3) { get().device.mouseMovedResponse = movedAction; get().device.mouseMovedCallback = null; log(lvl, "setMouseMovedAction: %d", get().device.mouseMovedResponse); } } /** * what to do if mouse is moved outside Sikuli's mouse protection
* only 3 is honored:
* in case of event the user provided callBack.happened is called * * @param callBack ObserverCallBack */ public static void setMouseMovedCallback(Object callBack) { if (callBack != null) { get().device.mouseMovedResponse = 3; get().device.mouseMovedCallback = new ObserverCallBack(callBack, ObserveEvent.Type.GENERIC); } } public static void setMouseMovedHighlight(boolean state) { get().device.MouseMovedHighlight = state; } /** * check if mouse was moved since last mouse action * * @return true/false */ public static boolean hasMoved() { Location pos = get().device.getLocation(); if (get().device.lastPos.x != pos.x || get().device.lastPos.y != pos.y) { return true; } return false; } /** * to click (left, right, middle - single or double) at the given location using the given button * only useable for local screens * * timing parameters:
* - one value
* < 0 wait before mouse down
* > 0 wait after mouse up
* - 2 or 3 values 1st wait before mouse down
* 2nd wait after mouse up
* 3rd inner wait (milli secs, cut to 1000): pause between mouse down and up (Settings.ClickDelay) * * wait before and after: > 9 taken as milli secs - 1 ... 9 are seconds * * @param loc where to click * @param action L,R,M left, right, middle - D means double click * @param args timing parameters * @return the location */ public static Location click(Location loc, String action, Integer... args) { if (get().device.isSuspended() || loc.isOtherScreen()) { return null; } getArgsClick(loc, action, args); get().device.use(); Device.delay(mouse.beforeWait); Settings.ClickDelay = mouse.innerWait / 1000; click(loc, mouse.buttons, 0, ((Mouse) get()).clickDouble, null); Device.delay(mouse.afterWait); get().device.let(); return loc; } private static void getArgsClick(Location loc, String action, Integer... args) { mouse.mousePos = loc; mouse.clickDouble = false; action = action.toUpperCase(); if (action.contains("D")) { mouse.clickDouble = true; } mouse.buttons = 0; if (action.contains("L")) { mouse.buttons += LEFT; } if (action.contains("M")) { mouse.buttons += MIDDLE; } if (action.contains("R")) { mouse.buttons += RIGHT; } if (mouse.buttons == 0) { mouse.buttons = LEFT; } mouse.beforeWait = 0; mouse.innerWait = (int) (Settings.ClickDelay * 1000); mouse.afterWait = 0; if (args.length > 0) { if (args.length == 1) { if (args[0] < 0) { mouse.beforeWait = -args[0]; } else { mouse.afterWait = args[0]; } } mouse.beforeWait = args[0]; if (args.length > 1) { mouse.afterWait = args[1]; if (args.length > 2) { mouse.innerWait = args[2]; } } } } protected static int click(Location loc, int buttons, Integer modifiers, boolean dblClick, Region region) { if (modifiers == null) { modifiers = 0; } Debug profiler = Debug.startTimer("Mouse.click"); boolean shouldMove = true; if (loc == null) { shouldMove = false; loc = at(); } IRobot r = null; IScreen s = loc.getScreen(); if (s == null) { profiler.end(); return 0; } r = s.getRobot(); if (r == null) { profiler.end(); return 0; } get().device.use(region); profiler.lap("after use"); if (shouldMove) { r.smoothMove(loc); profiler.lap("after move"); } r.clickStarts(); if (modifiers > 0) { r.pressModifiers(modifiers); } int pause = Settings.ClickDelay > 1 ? 1 : (int) (Settings.ClickDelay * 1000); Settings.ClickDelay = 0.0; profiler.lap("before Down"); if (dblClick) { r.mouseDown(buttons); profiler.lap("before Up"); r.mouseUp(buttons); profiler.lap("before Down"); r.delay(pause); r.mouseDown(buttons); profiler.lap("before Up"); r.mouseUp(buttons); } else { r.mouseDown(buttons); r.delay(pause); profiler.lap("before Up"); r.mouseUp(buttons); } profiler.lap("after click"); if (modifiers > 0) { r.releaseModifiers(modifiers); } r.clickEnds(); r.waitForIdle(); profiler.lap("before let"); get().device.let(region); long duration = profiler.end(); Debug.action(getClickMsg(loc, buttons, modifiers, dblClick, duration)); return 1; } private static String getClickMsg(Location loc, int buttons, int modifiers, boolean dblClick, long duration) { String msg = ""; if (modifiers != 0) { msg += KeyEvent.getKeyModifiersText(modifiers) + "+"; } if (buttons == InputEvent.BUTTON1_MASK && !dblClick) { msg += "CLICK"; } if (buttons == InputEvent.BUTTON1_MASK && dblClick) { msg += "DOUBLE CLICK"; } if (buttons == InputEvent.BUTTON3_MASK) { msg += "RIGHT CLICK"; } else if (buttons == InputEvent.BUTTON2_MASK) { msg += "MID CLICK"; } msg += String.format(" on %s (%d msec)", loc, duration); return msg; } /** * move the mouse to the given location (local and remote) * * @param loc Location * @return 1 for success, 0 otherwise */ public static int move(Location loc) { return move(loc, null); } /** * move the mouse from the current position to the offset position given by the parameters * @param xoff horizontal offset (< 0 left, > 0 right) * @param yoff vertical offset (< 0 up, > 0 down) * @return 1 for success, 0 otherwise */ public static int move(int xoff, int yoff) { return move(at().offset(xoff, yoff)); } protected static int move(Location loc, Region region) { if (get().device.isSuspended()) { return 0; } if (loc != null) { IRobot r = null; IScreen s = loc.getScreen(); if (s == null) { return 0; } r = s.getRobot(); if (r == null) { return 0; } if (!r.isRemote()) { get().device.use(region); } r.smoothMove(loc); r.waitForIdle(); if (!r.isRemote()) { get().device.let(region); } return 1; } return 0; } /** * press and hold the given buttons {@link Button} * * @param buttons value */ public static void down(int buttons) { down(buttons, null); } protected static void down(int buttons, Region region) { if (get().device.isSuspended()) { return; } get().device.use(region); Screen.getRobot(region).mouseDown(buttons); } /** * release all buttons * */ public static void up() { up(0, null); } /** * release the given buttons {@link Button} * * @param buttons (0 releases all buttons) */ public static void up(int buttons) { up(buttons, null); } protected static void up(int buttons, Region region) { if (get().device.isSuspended()) { return; } Screen.getRobot(region).mouseUp(buttons); if (region != null) { get().device.let(region); } } /** * move mouse using mouse wheel in the given direction the given steps
* the result is system dependent * * @param direction {@link Button} * @param steps value */ public static void wheel(int direction, int steps) { wheel(direction, steps, null); } protected static void wheel(int direction, int steps, Region region) { wheel(direction,steps,region, WHEEL_STEP_DELAY); } protected static void wheel(int direction, int steps, Region region, int stepDelay) { if (get().device.isSuspended()) { return; } IRobot r = Screen.getRobot(region); get().device.use(region); Debug.log(3, "Region: wheel: %s steps: %d", (direction == WHEEL_UP ? "WHEEL_UP" : "WHEEL_DOWN"), steps); for (int i = 0; i < steps; i++) { r.mouseWheel(direction); r.delay(stepDelay); } get().device.let(region); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/ObserveEvent.java000077500000000000000000000143131315726130400251230ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * provides information about the observed event being in the {@link ObserverCallBack} */ public class ObserveEvent { public enum Type { APPEAR, VANISH, CHANGE, GENERIC, FINDFAILED, MISSING } /** * the event's type as ObserveEvent.APPEAR, .VANISH, .CHANGE, ... */ private Type type; private Region region = null; private Object pattern = null; private Match match = null; private Image image = null; private FindFailedResponse response = FindFailed.defaultFindFailedResponse; private int index = -1; private List changes = null; private long time; private String name; private Object[] vals = new Object[] {null, null, null}; protected ObserveEvent() { } /** * INTERNAL USE ONLY: creates an observed event */ protected ObserveEvent(String name, Type type, Object v1, Object v2, Object v3, long now) { init(name, type, v1, v2, v3, now); } private void init(String name, Type type, Object v1, Object v2, Object v3, long now) { this.name = name; this.type = type; if (now > 0) { time = now; } else { time = new Date().getTime(); } if (Type.GENERIC.equals(type)) { setVals(v1, v2, v3); } else if (Type.FINDFAILED.equals(type) || Type.MISSING.equals(type)) { setRegion(v3); setImage(v2); setPattern(v1); } else { setRegion(v3); setMatch(v2); setPattern(v1); } } /** * get the observe event type * @return a string containing either APPEAR, VANISH, CHANGE or GENERIC */ public String getType() { return type.toString(); } /** * check the observe event type * @return true if it is APPEAR, false otherwise */ public boolean isAppear() { return Type.APPEAR.equals(type); } /** * check the observe event type * @return true if it is VANISH, false otherwise */ public boolean isVanish() { return Type.VANISH.equals(type); } /** * check the observe event type * @return true if it is CHANGE, false otherwise */ public boolean isChange() { return Type.CHANGE.equals(type); } /** * check the observe event type * @return true if it is GENERIC, false otherwise */ public boolean isGeneric() { return Type.GENERIC.equals(type); } /** * check the observe event type * @return true if it is FINDFAILED, false otherwise */ public boolean isFindFailed() { return Type.FINDFAILED.equals(type); } /** * check the observe event type * @return true if it is MISSING, false otherwise */ public boolean isMissing() { return Type.MISSING.equals(type); } /** * for type GENERIC: 3 values can be stored in the event * (the value's type is known by creator and user of getVals as some private protocol) * @param v1 * @param v2 * @param v3 */ protected void setVals(Object v1, Object v2, Object v3) { vals[0] = v1; vals[1] = v2; vals[2] = v3; } /** * for type GENERIC: (the value's type is known by creator and user of getVals as some private protocol) * @return an array with the 3 stored values (might be null) */ public Object[] getVals() { return vals; } /** * * @return the observer name of this event */ public String getName() { return name; } /** * * @return this event's observer's region */ public Region getRegion() { return region; } protected void setRegion(Object r) { if (r instanceof Region) { region = (Region) r; } } /** * * @return the observed match (APEAR, VANISH) */ public Match getMatch() { return match; } protected void setMatch(Object m) { if (null != m && m instanceof Match) { match = new Match((Match) m); } } protected void setIndex(int index) { this.index = index; } /** * * @return a list of observed changes as matches (CHANGE) */ public List getChanges() { return changes; } protected void setChanges(List c) { if (c != null) { changes = new ArrayList(); changes.addAll(c); } } /** * * @return the used pattern for this event's observing */ public Pattern getPattern() { return (Pattern) pattern; } public void setPattern(Object p) { if (null != p) { if (p.getClass().isInstance("")) { pattern = new Pattern((String) p); } else if (p instanceof Pattern) { pattern = new Pattern((Pattern) p); } else if (p instanceof Image) { pattern = new Pattern((Image) p); } } } public Image getImage() { return image; } public void setImage(Object img) { image = (Image) img; } public void setResponse(FindFailedResponse resp) { response = resp; } public FindFailedResponse getResponse() { return response; } public long getTime() { return time; } /** * tell the observer to repeat this event's observe action immediately * after returning from this handler (APPEAR, VANISH) */ public void repeat() { repeat(0); } /** * tell the observer to repeat this event's observe action after given time in secs * after returning from this handler (APPEAR, VANISH) * @param secs seconds */ public void repeat(long secs) { region.getObserver().repeat(name, secs); } /** * @return the number how often this event has already been triggered until now */ public int getCount() { return region.getObserver().getCount(name); } /** * stops the observer */ public void stopObserver() { region.stopObserver(); } /** * stops the observer and prints the given text * @param text text */ public void stopObserver(String text) { region.stopObserver(text); } @Override public String toString() { if (type == Type.CHANGE) { return String.format("Event(%s) %s on: %s with: %d count: %d", type, name, region, index, getCount()); } else { return String.format("Event(%s) %s on: %s with: %s\nmatch: %s count: %d", type, name, region, pattern, match, getCount()); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Observer.java000077500000000000000000000325541315726130400243120ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.sikuli.basics.Settings; import org.sikuli.basics.Debug; import org.sikuli.natives.FindInput; import org.sikuli.natives.FindResult; import org.sikuli.natives.FindResults; import org.sikuli.natives.Mat; import org.sikuli.natives.Vision; /** * INTERNAL USE implements the observe action for a region and calls the ObserverCallBacks */ public class Observer { private static String me = "Observer: "; private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } protected enum State { FIRST, UNKNOWN, MISSING, REPEAT, HAPPENED, INACTIVE } private Region observedRegion = null; private Mat lastImgMat = null; private org.opencv.core.Mat lastImageMat = null; private Map eventStates = null; private Map eventRepeatWaitTimes = null; private Map eventMatches = null; private Map eventNames = null; private Map eventTypes = null; private Map eventCallBacks = null; private Map eventCounts = null; private int minChanges = 0; private int numChangeCallBacks = 0; private int numChangeObservers = 0; private static boolean shouldStopOnFirstEvent = false; private Observer() { } protected Observer(Region region) { observedRegion = region; eventStates = Collections.synchronizedMap(new HashMap()); eventRepeatWaitTimes = Collections.synchronizedMap(new HashMap()); eventCounts = Collections.synchronizedMap(new HashMap()); eventMatches = Collections.synchronizedMap(new HashMap()); eventNames = Collections.synchronizedMap(new HashMap()); eventTypes = Collections.synchronizedMap(new HashMap()); eventCallBacks = Collections.synchronizedMap(new HashMap()); } protected void initialize() { log(3, "resetting observe states for " + observedRegion.toStringShort()); synchronized (eventNames) { for (String name : eventNames.keySet()) { eventStates.put(name, State.FIRST); eventCounts.put(name, 0); eventMatches.put(name, null); } } shouldStopOnFirstEvent = false; if (Observing.getStopOnFirstEvent()) { log(lvl, "requested to stop on first event"); shouldStopOnFirstEvent = true; } } protected void setStopOnFirstEvent() { shouldStopOnFirstEvent = true; } protected String[] getNames() { return eventNames.keySet().toArray(new String[0]); } protected void setActive(String name, boolean state) { if (eventNames.containsKey(me)) { if (state) { eventStates.put(name, State.FIRST); } else { eventStates.put(name, State.INACTIVE); } } } protected int getCount(String name) { return eventCounts.get(name); } private float getSimiliarity(PSC ptn) { float similarity = -1f; if (ptn instanceof Pattern) { similarity = ((Pattern) ptn).getSimilar(); } if (similarity < 0) { similarity = (float) Settings.MinSimilarity; } return similarity; } protected void addObserver(PSC ptn, ObserverCallBack ob, String name, ObserveEvent.Type type) { eventCallBacks.put(name, ob); eventStates.put(name, State.FIRST); eventNames.put(name, ptn); eventTypes.put(name, type); if (type == ObserveEvent.Type.CHANGE) { minChanges = getMinChanges(); numChangeObservers++; if (eventCallBacks.get(name) != null) { numChangeCallBacks++; } } } protected void removeObserver(String name) { Observing.remove(name); if (eventTypes.get(name) == ObserveEvent.Type.CHANGE) { if (eventCallBacks.get(name) != null) { numChangeCallBacks--; } numChangeObservers--; } eventNames.remove(name); eventCallBacks.remove(name); eventStates.remove(name); eventTypes.remove(name); eventCounts.remove(name); eventMatches.remove(name); eventRepeatWaitTimes.remove(name); } protected boolean hasObservers() { return eventNames.size() > 0; } private void callEventObserver(String name, Match match, long time) { Object ptn = eventNames.get(name); ObserveEvent.Type obsType = eventTypes.get(name); log(lvl, "%s: %s with: %s at: %s", obsType, name, ptn, match); ObserveEvent observeEvent = new ObserveEvent(name, obsType, ptn, match, observedRegion, time); Object callBack = eventCallBacks.get(name); Observing.addEvent(observeEvent); if (callBack != null && callBack instanceof ObserverCallBack) { log(lvl, "running call back: %s", obsType); if (obsType == ObserveEvent.Type.APPEAR) { ((ObserverCallBack) callBack).appeared(observeEvent); } else if (obsType == ObserveEvent.Type.VANISH) { ((ObserverCallBack) callBack).vanished(observeEvent); } else if (obsType == ObserveEvent.Type.CHANGE) { ((ObserverCallBack) callBack).changed(observeEvent); } else if (obsType == ObserveEvent.Type.GENERIC) { ((ObserverCallBack) callBack).happened(observeEvent); } } } private boolean checkPatterns(ScreenImage simg) { log(lvl + 1, "update: checking patterns"); if (!observedRegion.isObserving()) { return false; } Finder finder = null; for (String name : eventStates.keySet()) { if (!patternsToCheck()) { continue; } if (eventStates.get(name) == State.REPEAT) { if ((new Date()).getTime() < eventRepeatWaitTimes.get(name)) { continue; } else { eventStates.put(name, State.UNKNOWN); } } Object ptn = eventNames.get(name); Image img = Image.getImageFromTarget(ptn); if (img == null || !img.isUseable()) { Debug.error("EventMgr: checkPatterns: Image not valid", ptn); eventStates.put(name, State.MISSING); continue; } Match match = null; boolean hasMatch = false; long lastSearchTime; long now = 0; if (!Settings.UseImageFinder && Settings.CheckLastSeen && null != img.getLastSeen()) { Region r = Region.create(img.getLastSeen()); if (observedRegion.contains(r)) { lastSearchTime = (new Date()).getTime(); Finder f = new Finder(new Screen().capture(r), r); f.find(new Pattern(img).similar(Settings.CheckLastSeenSimilar)); if (f.hasNext()) { log(lvl + 1, "checkLastSeen: still there"); match = new Match(new Region(img.getLastSeen()), img.getLastSeenScore()); match.setTimes(0, (new Date()).getTime() - lastSearchTime); hasMatch = true; } else { log(lvl + 1, "checkLastSeen: not there"); } } } if (match == null) { if (finder == null) { if (Settings.UseImageFinder) { finder = new ImageFinder(observedRegion); ((ImageFinder) finder).setIsMultiFinder(); } else { finder = new Finder(simg, observedRegion); } } lastSearchTime = (new Date()).getTime(); now = (new Date()).getTime(); finder.find(img); if (finder.hasNext()) { match = finder.next(); match.setTimes(0, now - lastSearchTime); if (match.getScore() >= getSimiliarity(ptn)) { hasMatch = true; img.setLastSeen(match.getRect(), match.getScore()); } } } if (hasMatch) { eventMatches.put(name, match); log(lvl + 1, "(%s): %s match: %s in:%s", eventTypes.get(name), ptn.toString(), match.toStringShort(), observedRegion.toStringShort()); } else if (eventStates.get(ptn) == State.FIRST) { log(lvl + 1, "(%s): %s match: %s in:%s", eventTypes.get(name), ptn.toString(), match.toStringShort(), observedRegion.toStringShort()); eventStates.put(name, State.UNKNOWN); } if (eventStates.get(name) != State.HAPPENED) { if (hasMatch && eventTypes.get(name) == ObserveEvent.Type.VANISH) { eventMatches.put(name, match); } if ((hasMatch && eventTypes.get(name) == ObserveEvent.Type.APPEAR) || (!hasMatch && eventTypes.get(name) == ObserveEvent.Type.VANISH)) { eventStates.put(name, State.HAPPENED); eventCounts.put(name, eventCounts.get(name) + 1); callEventObserver(name, eventMatches.get(name), now); if (shouldStopOnFirstEvent) { observedRegion.stopObserver(); } } } if (!observedRegion.isObserving()) { return false; } } return patternsToCheck(); } private boolean patternsToCheck () { for (String name : eventNames.keySet()) { if (eventTypes.get(name) == ObserveEvent.Type.CHANGE) { continue; } State s = eventStates.get(name); if (s == State.FIRST || s == State.UNKNOWN || s == State.REPEAT) { return true; } } return false; } protected void repeat(String name, long secs) { eventStates.put(name, State.REPEAT); if (secs <= 0) { secs = (long) observedRegion.getRepeatWaitTime(); } eventRepeatWaitTimes.put(name, (new Date()).getTime() + 1000 * secs); log(lvl, "repeat (%s): %s after %d seconds", eventTypes.get(name), name, secs); } private int getMinChanges() { int min = Integer.MAX_VALUE; int n; for (String name : eventNames.keySet()) { if (eventTypes.get(name) != ObserveEvent.Type.CHANGE) continue; n = (Integer) eventNames.get(name); if (n < min) { min = n; } } return min; } private boolean checkChanges(ScreenImage img) { if (numChangeObservers == 0) { return false; } boolean leftToDo = false; if (lastImgMat == null) { if (Settings.UseImageFinder) { lastImageMat = new org.opencv.core.Mat(); } else { lastImgMat = Image.convertBufferedImageToMat(img.getImage()); } return true; } if (Settings.UseImageFinder && lastImageMat.empty()) { lastImageMat = Image.createMat(img.getImage()); return true; } for (String name : eventNames.keySet()) { if (eventTypes.get(name) != ObserveEvent.Type.CHANGE) { continue; } if (eventStates.get(name) == State.REPEAT) { if ((new Date()).getTime() < eventRepeatWaitTimes.get(name)) { continue; } } leftToDo = true; } if (leftToDo) { leftToDo = false; log(lvl + 1, "update: checking changes"); if (Settings.UseImageFinder) { ImageFinder f = new ImageFinder(lastImageMat); f.setMinChanges(minChanges); org.opencv.core.Mat current = Image.createMat(img.getImage()); if (f.hasChanges(current)) { //TODO implement ChangeObserver: processing changes log(lvl, "TODO: processing changes"); } lastImageMat = current; } else { FindInput fin = new FindInput(); fin.setSource(lastImgMat); Mat target = Image.convertBufferedImageToMat(img.getImage()); fin.setTarget(target); fin.setSimilarity(minChanges); FindResults results = Vision.findChanges(fin); if (results.size() > 0) { callChangeObserver(results); if (shouldStopOnFirstEvent) { observedRegion.stopObserver(); } } else { leftToDo = true; } lastImgMat = target; } } return leftToDo |= numChangeCallBacks > 0; } private void callChangeObserver(FindResults results) { int n; log(lvl, "changes: %d in: %s", results.size(), observedRegion); for (String name : eventNames.keySet()) { if (eventTypes.get(name) != ObserveEvent.Type.CHANGE) { continue; } n = (Integer) eventNames.get(name); List changes = new ArrayList(); for (int i = 0; i < results.size(); i++) { FindResult r = results.get(i); if (r.getW() * r.getH() >= n) { changes.add(observedRegion.toGlobalCoord(new Match(r, observedRegion.getScreen()))); } } if (changes.size() > 0) { long now = (new Date()).getTime(); eventCounts.put(name, eventCounts.get(name) + 1); ObserveEvent observeEvent = new ObserveEvent(name, ObserveEvent.Type.CHANGE, null, null, observedRegion, now); observeEvent.setChanges(changes); observeEvent.setIndex(n); Observing.addEvent(observeEvent); Object callBack = eventCallBacks.get(name); if (callBack != null) { log(lvl, "running call back"); ((ObserverCallBack) callBack).changed(observeEvent); } } } } protected boolean update(ScreenImage simg) { boolean fromPatterns = checkPatterns(simg); log(lvl, "update result: Patterns: %s", fromPatterns); if (!observedRegion.isObserving()) { return false; } boolean fromChanges = checkChanges(simg); log(lvl, "update result: Changes: %s", fromChanges); if (!observedRegion.isObserving()) { return false; } return false || fromPatterns || fromChanges; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/ObserverCallBack.java000066400000000000000000000061701315726130400256570ustar00rootroot00000000000000/* * Copyright 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. */ package org.sikuli.script; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.EventListener; import org.sikuli.basics.Debug; import org.sikuli.util.JLangHelperInterface; import org.sikuli.util.JRubyHelper; import org.sikuli.util.JythonHelper; /** * Use this class to implement call back methods for the Region observers * onAppear, onVanish and onChange.
* by overriding the respective method appeared, vanished or changed *
 * example:
 * aRegion.onAppear(anImageOrPattern,
 *   new ObserverCallBack() {
 *     appeared(ObserveEvent e) {
 *       // do something
 *     }
 *   }
 * );
 * 
* when the image appears, your above call back appeared() will be called
* see {@link ObserveEvent} about the features available in the callback function */ public class ObserverCallBack implements EventListener { private Object callback = null; private ObserveEvent.Type obsType = ObserveEvent.Type.GENERIC; private JLangHelperInterface scriptHelper = null; private String scriptRunnerType = null; public ObserverCallBack() { } public ObserverCallBack(Object callback, ObserveEvent.Type obsType) { this.callback = callback; this.obsType = obsType; if (callback.getClass().getName().contains("org.python")) { scriptRunnerType = "jython"; scriptHelper = JythonHelper.get(); } else if (callback.getClass().getName().contains("org.jruby")) { scriptRunnerType = "jruby"; scriptHelper = JRubyHelper.get(); } else { Debug.error("ObserverCallBack: %s init: ScriptRunner not available for class %s", obsType, callback.getClass().getName()); } } public ObserveEvent.Type getType() { return obsType; } public void appeared(ObserveEvent e) { if (scriptHelper != null && ObserveEvent.Type.APPEAR.equals(obsType)) { run(e); } } public void vanished(ObserveEvent e) { if (scriptHelper != null && ObserveEvent.Type.VANISH.equals(obsType)) { run(e); } } public void changed(ObserveEvent e) { if (scriptHelper != null && ObserveEvent.Type.CHANGE.equals(obsType)) { run(e); } } public void happened(ObserveEvent e) { if (scriptHelper != null && ObserveEvent.Type.GENERIC.equals(obsType)) { run(e); } } public void findfailed(ObserveEvent e) { if (scriptHelper != null && ObserveEvent.Type.FINDFAILED.equals(obsType)) { run(e); } } public void missing(ObserveEvent e) { if (scriptHelper != null && ObserveEvent.Type.MISSING.equals(obsType)) { run(e); } } private void run(ObserveEvent e) { boolean success = true; Object[] args = new Object[]{callback, e}; if (scriptHelper != null) { success = scriptHelper.runObserveCallback(args); if (!success) { Debug.error("ObserverCallBack: problem with scripting handler: %s\n%s", scriptHelper.getClass().getName(), callback.getClass().getName()); } } } public void setType(ObserveEvent.Type givenType) { obsType = givenType; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Observing.java000066400000000000000000000141731315726130400244530ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.sikuli.basics.Debug; /** * INTERNAL USE ONLY --- NOT part of the official API * This class globally collects * all running observations and tracks the created events.
*/ public class Observing { private static final String me = "Observing: "; private static final int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private Observing() { } private static final Map observers = Collections.synchronizedMap(new HashMap()); private static final Map events = Collections.synchronizedMap(new HashMap()); private static final List runningObservers = Collections.synchronizedList(new ArrayList()); private static long lastName = 0; private static boolean shouldStopOnFirstEvent = false; /** * tell the next starting observer, to stop on the first event */ public static void setStopOnFirstEvent() { shouldStopOnFirstEvent = true; } protected static boolean getStopOnFirstEvent() { boolean val = shouldStopOnFirstEvent; shouldStopOnFirstEvent = false; return val; } protected static void addRunningObserver(Region r) { if (shouldStopOnFirstEvent) { shouldStopOnFirstEvent = false; r.getObserver().setStopOnFirstEvent(); } runningObservers.add(r); log(lvl,"add observer: now running %d observer(s)", runningObservers.size()); } protected static void removeRunningObserver(Region r) { runningObservers.remove(r); log(lvl, "remove observer: now running %d observer(s)", runningObservers.size()); } protected static synchronized String add(Region reg, ObserverCallBack obs, ObserveEvent.Type type, Object target) { String name; long now = new Date().getTime(); while (now <= lastName) { now++; } lastName = now; name = "" + now; observers.put(name, reg); reg.getObserver().addObserver(target, (ObserverCallBack) obs, name, type); return name; } /** * set the observer with the given name inactive (not checked while observing) * @param name */ public void setInactive(String name) { setActive(name, false); } /** * set the observer with the given name active (checked while observing) * @param name */ public void setActive(String name) { setActive(name, true); } protected static void setActive(String name, boolean state) { if (observers.containsKey(name)) { observers.get(name).getObserver().setActive(name, state); } } /** * remove the observer from the list, a region observer will be stopped
* events for that observer are removed as well * * @param name name of observer */ public static void remove(String name) { if (observers.containsKey(name)) { observers.get(name).stopObserver(); observers.remove(name); events.remove(name); } } /** * stop and remove all observers registered for this region from the list
* events for those observers are removed as well * @param reg */ public static void remove(Region reg) { for (String name : reg.getObserver().getNames()) { remove(name); } } /** * stop and remove all observers and their registered events * */ public static void cleanUp() { String[] names; synchronized (observers) { names = new String[observers.size()]; int i = 0; for (String name : observers.keySet()) { Region reg = observers.get(name); if (reg.isObserving()) { reg.stopObserver(); } events.remove(name); names[i++] = name; } } runningObservers.clear(); for (String name : names) { observers.remove(name); } log(lvl + 1, "as requested: removed all observers"); } /** * are their any happened events * * @return true if yes */ public static boolean hasEvents() { return events.size() > 0; } /** * are their any happened events for this region? * * @param reg * @return true if yes */ public static boolean hasEvents(Region reg) { for (String name : reg.getObserver().getNames()) { if (events.containsKey(name)) { return true; } } return false; } /** * are their any happened events for the observer having this name? * * @param name * @return true if yes */ public static boolean hasEvent(String name) { return events.containsKey(name); } protected static void addEvent(ObserveEvent evt) { events.put(evt.getName(), evt); } /** * return the events for that region
* events are removed from the list * * @param reg * @return the array of events or size 0 array if none */ public static ObserveEvent[] getEvents(Region reg) { List evts = new ArrayList(); ObserveEvent evt; for (String name : reg.getObserver().getNames()) { evt = events.get(name); if (evt != null) { evts.add(evt); } events.remove(name); } return evts.toArray(new ObserveEvent[0]); } /** * return all events (they are preserved)
* * @return the array of events or size 0 array if none */ public static ObserveEvent[] getEvents() { List evts = new ArrayList(); ObserveEvent evt; synchronized (events) { for (String name : events.keySet()) { evt = events.get(name); if (evt == null) { evts.add(evt); } } } return evts.toArray(new ObserveEvent[0]); } /** * retrieves and removes the requested event * @param name of event * @return the event or null */ public static ObserveEvent getEvent(String name) { return events.remove(name); } /** * the event list is purged */ public static void clearEvents() { events.clear(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Pattern.java000077500000000000000000000134211315726130400241300ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Settings; import java.awt.image.BufferedImage; import java.net.URL; /** * to define a more complex search target
* - non-standard minimum similarity
* - click target other than center
* - image as in-memory image */ public class Pattern { static RunTime runTime = RunTime.get(); private Image image = null; private float similarity = (float) Settings.MinSimilarity; private Location offset = new Location(0, 0); private int waitAfter = 0; private boolean imagePattern = false; /** * creates empty Pattern object at least setFilename() or setBImage() must be used before the * Pattern object is ready for anything */ public Pattern() { } /** * create a new Pattern from another (attribs are copied) * * @param p other Pattern */ public Pattern(Pattern p) { image = p.getImage(); similarity = p.similarity; offset.x = p.offset.x; offset.y = p.offset.y; imagePattern = image.isPattern(); } /** * create a Pattern with given image
* * @param img Image */ public Pattern(Image img) { image = img.create(img); image.setIsPattern(false); imagePattern = true; } /** * true if Pattern was created from Image * @return true/false */ public boolean isImagePattern() { return imagePattern; } /** * create a Pattern based on an image file name
* * @param imgpath image filename */ public Pattern(String imgpath) { image = Image.create(imgpath); } /** * Pattern from a Java resource (Object.class.getResource) * * @param url image file URL */ public Pattern(URL url) { image = Image.create(url); } /** * A Pattern from a BufferedImage * * @param bimg BufferedImage */ public Pattern(BufferedImage bimg) { image = new Image(bimg); } /** * A Pattern from a ScreenImage * * @param simg ScreenImage */ public Pattern(ScreenImage simg) { image = new Image(simg.getImage()); } /** * check wether the image is valid * * @return true if image is useable */ public boolean isValid() { return image.isValid() || imagePattern; } /** * set a new image for this pattern * * @param fileName image filename * @return the Pattern itself */ public Pattern setFilename(String fileName) { image = Image.create(fileName); return this; } /** * set a new image for this pattern * * @param fileURL image file URL * @return the Pattern itself */ public Pattern setFilename(URL fileURL) { image = Image.create(fileURL); return this; } /** * set a new image for this pattern * * @param img Image * @return the Pattern itself */ public Pattern setFilename(Image img) { image = img; return this; } /** * the current image's absolute filepath *
will return null, if image is in jar or in web *
use getFileURL in this case * * @return might be null */ public String getFilename() { return image.getFilename(); } /** * the current image's URL * * @return might be null */ public URL getFileURL() { return image.getURL(); } /** * sets the minimum Similarity to use with findX * * @param sim value 0 to 1 * @return the Pattern object itself */ public Pattern similar(float sim) { similarity = sim; return this; } /** * sets the minimum Similarity to 0.99 which means exact match * * @return the Pattern object itself */ public Pattern exact() { similarity = 0.99f; return this; } /** * * @return the current minimum similarity */ public float getSimilar() { return this.similarity; } /** * set the offset from the match's center to be used with mouse actions * * @param dx x offset * @param dy y offset * @return the Pattern object itself */ public Pattern targetOffset(int dx, int dy) { offset.x = dx; offset.y = dy; return this; } /** * set the offset from the match's center to be used with mouse actions * * @param loc Location * @return the Pattern object itself */ public Pattern targetOffset(Location loc) { offset.x = loc.x; offset.y = loc.y; return this; } /** * * @return the current offset */ public Location getTargetOffset() { return offset; } /** * ONLY FOR INTERNAL USE! Might vanish without notice! * * @return might be null */ public BufferedImage getBImage() { return image.get(); } /** * ONLY FOR INTERNAL USE! Might vanish without notice! * * @param bimg BufferedImage * @return the Pattern object itself */ public Pattern setBImage(BufferedImage bimg) { image = new Image(bimg); return this; } /** * sets the Pattern's image * * @param img Image * @return the Pattern object itself */ public Pattern setImage(Image img) { image = img; return this; } /** * get the Pattern's image * * @return Image */ public Image getImage() { return image; } /** * set the seconds to wait, after this pattern is acted on * * @param secs seconds */ public void setTimeAfter(int secs) { waitAfter = secs; } /** *
TODO: Usage to be implemented! * get the seconds to wait, after this pattern is acted on * @return time in seconds */ public int getTimeAfter() { return waitAfter; } @Override public String toString() { String ret = "P(" + image.getName() + (isValid() ? "" : " -- not valid!") + ")"; ret += " S: " + similarity; if (offset.x != 0 || offset.y != 0) { ret += " T: " + offset.x + "," + offset.y; } return ret; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Region.java000066400000000000000000004213021315726130400237340ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.Rectangle; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.List; import org.sikuli.android.ADBDevice; import org.sikuli.android.ADBScreen; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; import org.sikuli.util.ScreenHighlighter; /** * A Region is a rectengular area and lies always completely inside its parent screen * */ public class Region { static RunTime runTime = RunTime.get(); private static String me = "Region: "; private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } // /** * The Screen containing the Region */ private IScreen scr; protected boolean otherScreen = false; /** * The ScreenHighlighter for this Region */ private ScreenHighlighter overlay = null; /** * X-coordinate of the Region */ public int x; /** * Y-coordinate of the Region */ public int y; /** * Width of the Region */ public int w; /** * Height of the Region */ public int h; /** * Setting, how to react if an image is not found {@link FindFailed} */ private FindFailedResponse findFailedResponse = FindFailed.defaultFindFailedResponse; private Object findFailedHandler = FindFailed.getFindFailedHandler(); private Object imageMissingHandler = FindFailed.getImageMissingHandler(); /** * Setting {@link Settings}, if exception is thrown if an image is not found */ private boolean throwException = Settings.ThrowException; /** * Default time to wait for an image {@link Settings} */ private double autoWaitTimeout = Settings.AutoWaitTimeout; private float waitScanRate = Settings.WaitScanRate; /** * Flag, if an observer is running on this region {@link Settings} */ private boolean observing = false; private float observeScanRate = Settings.ObserveScanRate; private int repeatWaitTime = Settings.RepeatWaitTime; /** * The {@link Observer} Singleton instance */ private Observer regionObserver = null; /** * The last found {@link Match} in the Region */ private Match lastMatch = null; /** * The last found {@link Match}es in the Region */ private Iterator lastMatches = null; private long lastSearchTime = -1; private long lastFindTime = -1; private boolean isScreenUnion = false; private boolean isVirtual = false; private long lastSearchTimeRepeat = -1; /** * the area constants for use with get() */ public static final int NW = 300, NORTH_WEST = NW, TL = NW; public static final int NM = 301, NORTH_MID = NM, TM = NM; public static final int NE = 302, NORTH_EAST = NE, TR = NE; public static final int EM = 312, EAST_MID = EM, RM = EM; public static final int SE = 322, SOUTH_EAST = SE, BR = SE; public static final int SM = 321, SOUTH_MID = SM, BM = SM; public static final int SW = 320, SOUTH_WEST = SW, BL = SW; public static final int WM = 310, WEST_MID = WM, LM = WM; public static final int MM = 311, MIDDLE = MM, M3 = MM; public static final int TT = 200; public static final int RR = 201; public static final int BB = 211; public static final int LL = 210; public static final int NH = 202, NORTH = NH, TH = NH; public static final int EH = 221, EAST = EH, RH = EH; public static final int SH = 212, SOUTH = SH, BH = SH; public static final int WH = 220, WEST = WH, LH = WH; public static final int MV = 441, MID_VERTICAL = MV, CV = MV; public static final int MH = 414, MID_HORIZONTAL = MH, CH = MH; public static final int M2 = 444, MIDDLE_BIG = M2, C2 = M2; public static final int EN = NE, EAST_NORTH = NE, RT = TR; public static final int ES = SE, EAST_SOUTH = SE, RB = BR; public static final int WN = NW, WEST_NORTH = NW, LT = TL; public static final int WS = SW, WEST_SOUTH = SW, LB = BL; /** * to support a raster over the region */ private int rows; private int cols = 0; private int rowH = 0; private int colW = 0; private int rowHd = 0; private int colWd = 0; /** * {@inheritDoc} * * @return the description */ @Override public String toString() { String scrText = getScreen() == null ? "?" : "" + (-1 == getScreen().getID() ? "Union" : "" + getScreen().getID()); return String.format("R[%d,%d %dx%d]@S(%s) E:%s, T:%.1f", x, y, w, h, scrText, throwException ? "Y" : "N", autoWaitTimeout); } /** * INTERNAL USE ONLY * * @return text */ public String getIDString() { return "NonLocal"; } /** * * @return a compact description */ public String toStringShort() { if (isOtherScreen()) { return String.format("%s, %dx%d", getScreen().getIDString(), w, h); } else { String scrText = getScreen() == null ? "?" : "" + (-1 == getScreen().getID() ? "Union" : getScreen().getID()); return String.format("R[%d,%d %dx%d]@S(%s)", x, y, w, h, scrText); } } // // /* public Object __enter__() { Debug.error("Region: with(__enter__): Trying to make it a Jython Region for with: usage"); IScriptRunner runner = Settings.getScriptRunner("jython", null, null); if (runner != null) { Object[] jyreg = new Object[]{this}; if (runner.doSomethingSpecial("createRegionForWith", jyreg)) { if (jyreg[0] != null) { return jyreg[0]; } } } Debug.error("Region: with(__enter__): Sorry, not possible"); return null; } public void __exit__(Object type, Object value, Object traceback) { Debug.error("Region: with(__exit__): Sorry, not a Jython Region and not posssible!"); } */ // // /** * INTERNAL USE * * @param iscr screen */ public void initScreen(IScreen iscr) { // check given screen first Rectangle rect, screenRect; IScreen screen, screenOn; if (iscr != null) { if (iscr.isOtherScreen()) { if (x < 0) { w = w + x; x = 0; } if (y < 0) { h = h + y; y = 0; } this.scr = iscr; this.otherScreen = true; return; } if (iscr.getID() > -1) { rect = regionOnScreen(iscr); if (rect != null) { x = rect.x; y = rect.y; w = rect.width; h = rect.height; this.scr = iscr; return; } } else { // is ScreenUnion return; } } // check all possible screens if no screen was given or the region is not on given screen // crop to the screen with the largest intersection screenRect = new Rectangle(0, 0, 0, 0); screenOn = null; if (scr == null || !scr.isOtherScreen()) { for (int i = 0; i < Screen.getNumberScreens(); i++) { screen = Screen.getScreen(i); rect = regionOnScreen(screen); if (rect != null) { if (rect.width * rect.height > screenRect.width * screenRect.height) { screenRect = rect; screenOn = screen; } } } } else { rect = regionOnScreen(scr); if (rect != null) { if (rect.width * rect.height > screenRect.width * screenRect.height) { screenRect = rect; screenOn = scr; } } } if (screenOn != null) { x = screenRect.x; y = screenRect.y; w = screenRect.width; h = screenRect.height; this.scr = screenOn; } else { // no screen found this.scr = null; Debug.error("Region(%d,%d,%d,%d) outside any screen - subsequent actions might not work as expected", x, y, w, h); } } private Location checkAndSetRemote(Location loc) { if (!isOtherScreen()) { return loc; } return loc.setOtherScreen(scr); } /** * INTERNAL USE - EXPERIMENTAL if true: this region is not bound to any screen * * @param rect rectangle * @return the current state */ public static Region virtual(Rectangle rect) { Region reg = new Region(); reg.x = rect.x; reg.y = rect.y; reg.w = rect.width; reg.h = rect.height; reg.setVirtual(true); reg.scr = Screen.getPrimaryScreen(); return reg; } /** * INTERNAL USE - EXPERIMENTAL if true: this region is not bound to any screen * * @return the current state */ public boolean isVirtual() { return isVirtual; } /** * INTERNAL USE - EXPERIMENTAL * * @param state if true: this region is not bound to any screen */ public void setVirtual(boolean state) { isVirtual = state; } /** * INTERNAL USE: checks wether this region belongs to a non-Desktop screen * * @return true/false */ public boolean isOtherScreen() { return otherScreen; } /** * INTERNAL USE: flags this region as belonging to a non-Desktop screen */ public void setOtherScreen() { otherScreen = true; } /** * INTERNAL USE: flags this region as belonging to a non-Desktop screen * * @param aScreen screen */ public void setOtherScreen(IScreen aScreen) { scr = aScreen; setOtherScreen(); } /** * Checks if the Screen contains the Region. * * @param screen The Screen in which the Region might be * @return True, if the Region is on the Screen. False if the Region is not inside the Screen */ protected Rectangle regionOnScreen(IScreen screen) { if (screen == null) { return null; } // get intersection of Region and Screen Rectangle rect = screen.getRect().intersection(getRect()); // no Intersection, Region is not on the Screen if (rect.isEmpty()) { return null; } return rect; } /** * Check wether thie Region is contained by any of the available screens * * @return true if yes, false otherwise */ public boolean isValid() { if (this instanceof Screen) { return true; } return scr != null && w != 0 && h != 0; } // // /** * Create a region with the provided coordinate / size and screen * * @param X X position * @param Y Y position * @param W width * @param H heigth * @param screenNumber The number of the screen containing the Region */ public Region(int X, int Y, int W, int H, int screenNumber) { this(X, Y, W, H, Screen.getScreen(screenNumber)); this.rows = 0; } /** * Create a region with the provided coordinate / size and screen * * @param X X position * @param Y Y position * @param W width * @param H heigth * @param parentScreen the screen containing the Region */ public Region(int X, int Y, int W, int H, IScreen parentScreen) { this.rows = 0; this.x = X; this.y = Y; this.w = W > 1 ? W : 1; this.h = H > 1 ? H : 1; initScreen(parentScreen); } /** * Convenience: a minimal Region to be used as a Point (backport from Version 2)
* is always on primary screen * @param X * @param Y */ public Region (int X, int Y) { this(X, Y, 1, 1, null); } /** * Create a region with the provided coordinate / size * * @param X X position * @param Y Y position * @param W width * @param H heigth */ public Region(int X, int Y, int W, int H) { this(X, Y, W, H, null); this.rows = 0; log(lvl, "init: (%d, %d, %d, %d)", X, Y, W, H); } /** * Create a region from a Rectangle * * @param r the Rectangle */ public Region(Rectangle r) { this(r.x, r.y, r.width, r.height, null); this.rows = 0; } /** * Create a new region from another region
including the region's settings * * @param r the region */ public Region(Region r) { init(r); } private void init(Region r) { if (!r.isValid()) { return; } x = r.x; y = r.y; w = r.w; h = r.h; scr = r.getScreen(); otherScreen = r.isOtherScreen(); rows = 0; autoWaitTimeout = r.autoWaitTimeout; findFailedResponse = r.findFailedResponse; throwException = r.throwException; waitScanRate = r.waitScanRate; observeScanRate = r.observeScanRate; repeatWaitTime = r.repeatWaitTime; } //
// /** * internal use only, used for new Screen objects to get the Region behavior */ protected Region() { this.rows = 0; } /** * internal use only, used for new Screen objects to get the Region behavior */ protected Region(boolean isScreenUnion) { this.isScreenUnion = isScreenUnion; this.rows = 0; } /** * Create a region with the provided top left corner and size * * @param X top left X position * @param Y top left Y position * @param W width * @param H heigth * @return then new region */ public static Region create(int X, int Y, int W, int H) { return Region.create(X, Y, W, H, null); } /** * Create a region with the provided top left corner and size * * @param X top left X position * @param Y top left Y position * @param W width * @param H heigth * @param scr the source screen * @return the new region */ private static Region create(int X, int Y, int W, int H, IScreen scr) { return new Region(X, Y, W, H, scr); } /** * Create a region with the provided top left corner and size * * @param loc top left corner * @param w width * @param h height * @return then new region */ public static Region create(Location loc, int w, int h) { int _x = loc.x; int _y = loc.y; IScreen s = loc.getScreen(); if (s == null) { _x = _y = 0; s = Screen.getPrimaryScreen(); } return Region.create(_x, _y, w, h, s); } /** * Flag for the {@link #create(Location, int, int, int, int)} method. Sets the Location to be on the left corner of * the new Region. */ public final static int CREATE_X_DIRECTION_LEFT = 0; /** * Flag for the {@link #create(Location, int, int, int, int)} method. Sets the Location to be on the right corner of * the new Region. */ public final static int CREATE_X_DIRECTION_RIGHT = 1; /** * Flag for the {@link #create(Location, int, int, int, int)} method. Sets the Location to be on the top corner of the * new Region. */ public final static int CREATE_Y_DIRECTION_TOP = 0; /** * Flag for the {@link #create(Location, int, int, int, int)} method. Sets the Location to be on the bottom corner of * the new Region. */ public final static int CREATE_Y_DIRECTION_BOTTOM = 1; /** * create a region with a corner at the given point
as specified with x y
0 0 top left
0 1 bottom left
* 1 0 top right
1 1 bottom right
* * @param loc the refence point * @param create_x_direction == 0 is left side !=0 is right side * @param create_y_direction == 0 is top side !=0 is bottom side * @param w the width * @param h the height * @return the new region */ public static Region create(Location loc, int create_x_direction, int create_y_direction, int w, int h) { int _x = loc.x; int _y = loc.y; IScreen s = loc.getScreen(); if (s == null) { _x = _y = 0; s = Screen.getPrimaryScreen(); } int X; int Y; int W = w; int H = h; if (create_x_direction == CREATE_X_DIRECTION_LEFT) { if (create_y_direction == CREATE_Y_DIRECTION_TOP) { X = _x; Y = _y; } else { X = _x; Y = _y - h; } } else { if (create_y_direction == CREATE_Y_DIRECTION_TOP) { X = _x - w; Y = _y; } else { X = _x - w; Y = _y - h; } } return Region.create(X, Y, W, H, s); } /** * create a region with a corner at the given point
as specified with x y
0 0 top left
0 1 bottom left
* 1 0 top right
1 1 bottom right
same as the corresponding create method, here to be naming compatible with * class Location * * @param loc the refence point * @param x ==0 is left side !=0 is right side * @param y ==0 is top side !=0 is bottom side * @param w the width * @param h the height * @return the new region */ public static Region grow(Location loc, int x, int y, int w, int h) { return Region.create(loc, x, y, w, h); } /** * Create a region from a Rectangle * * @param r the Rectangle * @return the new region */ public static Region create(Rectangle r) { return Region.create(r.x, r.y, r.width, r.height, null); } /** * Create a region from a Rectangle on a given Screen * * @param r the Rectangle * @param parentScreen the new parent screen * @return the new region */ protected static Region create(Rectangle r, IScreen parentScreen) { return Region.create(r.x, r.y, r.width, r.height, parentScreen); } /** * Create a region from another region
including the region's settings * * @param r the region * @return then new region */ public static Region create(Region r) { Region reg = Region.create(r.x, r.y, r.w, r.h, r.getScreen()); reg.autoWaitTimeout = r.autoWaitTimeout; reg.findFailedResponse = r.findFailedResponse; reg.throwException = r.throwException; return reg; } /** * create a region with the given point as center and the given size * * @param loc the center point * @param w the width * @param h the height * @return the new region */ public static Region grow(Location loc, int w, int h) { int _x = loc.x; int _y = loc.y; IScreen s = loc.getScreen(); if (s == null) { _x = _y = 0; s = Screen.getPrimaryScreen(); } int X = _x - (int) w / 2; int Y = _y - (int) h / 2; return Region.create(X, Y, w, h, s); } /** * create a minimal region at given point with size 1 x 1 * * @param loc the point * @return the new region */ public static Region grow(Location loc) { int _x = loc.x; int _y = loc.y; IScreen s = loc.getScreen(); if (s == null) { _x = _y = 0; s = Screen.getPrimaryScreen(); } return Region.create(_x, _y, 1, 1, s); } //
// /** * check if current region contains given point * * @param point Point * @return true/false */ public boolean contains(Location point) { return getRect().contains(point.x, point.y); } /** * check if mouse pointer is inside current region * * @return true/false */ public boolean containsMouse() { return contains(Mouse.at()); } /** * new region with same offset to current screen's top left on given screen * * @param scrID number of screen * @return new region */ public Region copyTo(int scrID) { return copyTo(Screen.getScreen(scrID)); } /** * new region with same offset to current screen's top left on given screen * * @param screen new parent screen * @return new region */ public Region copyTo(IScreen screen) { Location o = new Location(getScreen().getBounds().getLocation()); Location n = new Location(screen.getBounds().getLocation()); return Region.create(n.x + x - o.x, n.y + y - o.y, w, h, screen); } /** * used in Observer.callChangeObserving, Finder.next to adjust region relative coordinates of matches to screen * coordinates * * @param m * @return the modified match */ protected Match toGlobalCoord(Match m) { m.x += x; m.y += y; return m; } // // //TODO should be possible to reset to current global value resetXXX() /** * true - (initial setting) should throw exception FindFailed if findX unsuccessful in this region
false - do not * abort script on FindFailed (might leed to null pointer exceptions later) * * @param flag true/false */ public void setThrowException(boolean flag) { throwException = flag; if (throwException) { findFailedResponse = FindFailedResponse.ABORT; } else { findFailedResponse = FindFailedResponse.SKIP; } } /** * current setting for this region (see setThrowException) * * @return true/false */ public boolean getThrowException() { return throwException; } /** * the time in seconds a find operation should wait for the appearence of the target in this region
initial value * is the global AutoWaitTimeout setting at time of Region creation * * @param sec seconds */ public void setAutoWaitTimeout(double sec) { autoWaitTimeout = sec; } /** * current setting for this region (see setAutoWaitTimeout) * * @return value of seconds */ public double getAutoWaitTimeout() { return autoWaitTimeout; } /** * FindFailedResponse.
* ABORT - (initial value) abort script on FindFailed (= setThrowException(true) )
* SKIP - ignore FindFailed (same as setThrowException(false) )
* PROMPT - display prompt on FindFailed to let user decide how to proceed
* RETRY - continue to wait for appearence after FindFailed (caution: endless loop) * * @param response the FindFailedResponse */ public void setFindFailedResponse(FindFailedResponse response) { findFailedResponse = response; } public void setFindFailedHandler(Object handler) { findFailedHandler = setHandler(handler, ObserveEvent.Type.FINDFAILED); log(lvl, "Setting FindFailedHandler"); } public void setImageMissingHandler(Object handler) { imageMissingHandler = setHandler(handler, ObserveEvent.Type.MISSING); log(lvl, "Setting ImageMissingHandler"); } private Object setHandler(Object handler, ObserveEvent.Type type) { findFailedResponse = FindFailedResponse.HANDLE; if (handler != null && (handler.getClass().getName().contains("org.python") || handler.getClass().getName().contains("org.jruby"))) { handler = new ObserverCallBack(handler, type); } else { ((ObserverCallBack) handler).setType(type); } return handler; } /** * * @return the current setting (see setFindFailedResponse) */ public FindFailedResponse getFindFailedResponse() { return findFailedResponse; } /** * * @return the regions current WaitScanRate */ public float getWaitScanRate() { return waitScanRate; } /** * set the regions individual WaitScanRate * * @param waitScanRate decimal number */ public void setWaitScanRate(float waitScanRate) { this.waitScanRate = waitScanRate; } /** * * @return the regions current ObserveScanRate */ public float getObserveScanRate() { return observeScanRate; } /** * set the regions individual ObserveScanRate * * @param observeScanRate decimal number */ public void setObserveScanRate(float observeScanRate) { this.observeScanRate = observeScanRate; } /** * INTERNAL USE: Observe * * @return the regions current RepeatWaitTime time in seconds */ public int getRepeatWaitTime() { return repeatWaitTime; } /** * INTERNAL USE: Observe set the regions individual WaitForVanish * * @param time in seconds */ public void setRepeatWaitTime(int time) { repeatWaitTime = time; } //
// /** * * @return the Screen object containing the region */ public IScreen getScreen() { return scr; } // to avoid NPE for Regions being outside any screen private IRobot getRobotForRegion() { if (getScreen() == null || isScreenUnion) { return Screen.getPrimaryScreen().getRobot(); } return getScreen().getRobot(); } /** * * @return the screen, that contains the top left corner of the region. Returns primary screen if outside of any * screen. * @deprecated Only for compatibility, to get the screen containing this region, use {@link #getScreen()} */ @Deprecated public IScreen getScreenContaining() { return getScreen(); } /** * Sets a new Screen for this region. * * @param scr the containing screen object * @return the region itself */ protected Region setScreen(IScreen scr) { initScreen(scr); return this; } /** * Sets a new Screen for this region. * * @param id the containing screen object's id * @return the region itself */ protected Region setScreen(int id) { return setScreen(Screen.getScreen(id)); } /** * synonym for showMonitors */ public void showScreens() { Screen.showMonitors(); } /** * synonym for resetMonitors */ public void resetScreens() { Screen.resetMonitors(); } // ************************************************ /** * * @return the center pixel location of the region */ public Location getCenter() { return checkAndSetRemote(new Location(getX() + getW() / 2, getY() + getH() / 2)); } /** * convenience method * * @return the region's center */ public Location getTarget() { return getCenter(); } /** * Moves the region to the area, whose center is the given location * * @param loc the location which is the new center of the region * @return the region itself */ public Region setCenter(Location loc) { Location c = getCenter(); x = x - c.x + loc.x; y = y - c.y + loc.y; initScreen(null); return this; } /** * * @return top left corner Location */ public Location getTopLeft() { return checkAndSetRemote(new Location(x, y)); } /** * Moves the region to the area, whose top left corner is the given location * * @param loc the location which is the new top left point of the region * @return the region itself */ public Region setTopLeft(Location loc) { return setLocation(loc); } /** * * @return top right corner Location */ public Location getTopRight() { return checkAndSetRemote(new Location(x + w - 1, y)); } /** * Moves the region to the area, whose top right corner is the given location * * @param loc the location which is the new top right point of the region * @return the region itself */ public Region setTopRight(Location loc) { Location c = getTopRight(); x = x - c.x + loc.x; y = y - c.y + loc.y; initScreen(null); return this; } /** * * @return bottom left corner Location */ public Location getBottomLeft() { return checkAndSetRemote(new Location(x, y + h - 1)); } /** * Moves the region to the area, whose bottom left corner is the given location * * @param loc the location which is the new bottom left point of the region * @return the region itself */ public Region setBottomLeft(Location loc) { Location c = getBottomLeft(); x = x - c.x + loc.x; y = y - c.y + loc.y; initScreen(null); return this; } /** * * @return bottom right corner Location */ public Location getBottomRight() { return checkAndSetRemote(new Location(x + w - 1, y + h - 1)); } /** * Moves the region to the area, whose bottom right corner is the given location * * @param loc the location which is the new bottom right point of the region * @return the region itself */ public Region setBottomRight(Location loc) { Location c = getBottomRight(); x = x - c.x + loc.x; y = y - c.y + loc.y; initScreen(null); return this; } // ************************************************ /** * * @return x of top left corner */ public int getX() { return x; } /** * * @return y of top left corner */ public int getY() { return y; } /** * * @return width of region */ public int getW() { return w; } /** * * @return height of region */ public int getH() { return h; } /** * * @param X new x position of top left corner */ public void setX(int X) { x = X; initScreen(null); } /** * * @param Y new y position of top left corner */ public void setY(int Y) { y = Y; initScreen(null); } /** * * @param W new width */ public void setW(int W) { w = W > 1 ? W : 1; initScreen(null); } /** * * @param H new height */ public void setH(int H) { h = H > 1 ? H : 1; initScreen(null); } // ************************************************ /** * * @param W new width * @param H new height * @return the region itself */ public Region setSize(int W, int H) { w = W > 1 ? W : 1; h = H > 1 ? H : 1; initScreen(null); return this; } /** * * @return the AWT Rectangle of the region */ public Rectangle getRect() { return new Rectangle(x, y, w, h); } /** * set the regions position/size
this might move the region even to another screen * * @param r the AWT Rectangle to use for position/size * @return the region itself */ public Region setRect(Rectangle r) { return setRect(r.x, r.y, r.width, r.height); } /** * set the regions position/size
this might move the region even to another screen * * @param X new x of top left corner * @param Y new y of top left corner * @param W new width * @param H new height * @return the region itself */ public Region setRect(int X, int Y, int W, int H) { x = X; y = Y; w = W > 1 ? W : 1; h = H > 1 ? H : 1; initScreen(null); return this; } /** * set the regions position/size
this might move the region even to another screen * * @param r the region to use for position/size * @return the region itself */ public Region setRect(Region r) { return setRect(r.x, r.y, r.w, r.h); } // **************************************************** /** * resets this region (usually a Screen object) to the coordinates of the containing screen * * Because of the wanted side effect for the containing screen, this should only be used with screen objects. For * Region objects use setRect() instead. */ public void setROI() { setROI(getScreen().getBounds()); } /** * resets this region to the given location, and size
this might move the region even to another screen * *
Because of the wanted side effect for the containing screen, this should only be used with screen objects. *
For Region objects use setRect() instead. * * @param X new x * @param Y new y * @param W new width * @param H new height */ public void setROI(int X, int Y, int W, int H) { x = X; y = Y; w = W > 1 ? W : 1; h = H > 1 ? H : 1; initScreen(null); } /** * resets this region to the given rectangle
this might move the region even to another screen * *
Because of the wanted side effect for the containing screen, this should only be used with screen objects. *
For Region objects use setRect() instead. * * @param r AWT Rectangle */ public void setROI(Rectangle r) { setROI(r.x, r.y, r.width, r.height); } /** * resets this region to the given region
this might move the region even to another screen * *
Because of the wanted side effect for the containing screen, this should only be used with screen objects. *
For Region objects use setRect() instead. * * @param reg Region */ public void setROI(Region reg) { setROI(reg.getX(), reg.getY(), reg.getW(), reg.getH()); } /** * A function only for backward compatibility - Only makes sense with Screen objects * * @return the Region being the current ROI of the containing Screen */ public Region getROI() { IScreen screen = getScreen(); Rectangle screenRect = screen.getRect(); return new Region(screenRect.x, screenRect.y, screenRect.width, screenRect.height, screen); } // **************************************************** /** * * @return the region itself * @deprecated only for backward compatibility */ @Deprecated public Region inside() { return this; } /** * set the regions position
this might move the region even to another screen * * @param loc new top left corner * @return the region itself * @deprecated to be like AWT Rectangle API use setLocation() */ @Deprecated public Region moveTo(Location loc) { return setLocation(loc); } /** * set the regions position
this might move the region even to another screen * * @param loc new top left corner * @return the region itself */ public Region setLocation(Location loc) { x = loc.x; y = loc.y; initScreen(null); return this; } /** * set the regions position/size
this might move the region even to another screen * * @param r Region * @return the region itself * @deprecated to be like AWT Rectangle API use setRect() instead */ @Deprecated public Region morphTo(Region r) { return setRect(r); } /** * resize the region using the given padding values
might be negative * * @param l padding on left side * @param r padding on right side * @param t padding at top side * @param b padding at bottom side * @return the region itself */ public Region add(int l, int r, int t, int b) { x = x - l; y = y - t; w = w + l + r; if (w < 1) { w = 1; } h = h + t + b; if (h < 1) { h = 1; } initScreen(null); return this; } /** * extend the region, so it contains the given region
but only the part inside the current screen * * @param r the region to include * @return the region itself */ public Region add(Region r) { Rectangle rect = getRect(); rect.add(r.getRect()); setRect(rect); initScreen(null); return this; } /** * extend the region, so it contains the given point
but only the part inside the current screen * * @param loc the point to include * @return the region itself */ public Region add(Location loc) { Rectangle rect = getRect(); rect.add(loc.x, loc.y); setRect(rect); initScreen(null); return this; } //
// // ************************************************ /** * a find operation saves its match on success in the used region object
unchanged if not successful * * @return the Match object from last successful find in this region */ public Match getLastMatch() { return lastMatch; } // ************************************************ /** * a searchAll operation saves its matches on success in the used region object
unchanged if not successful * * @return a Match-Iterator of matches from last successful searchAll in this region */ public Iterator getLastMatches() { return lastMatches; } //
// public String saveScreenCapture() { return getScreen().capture(this).save(); } public String saveScreenCapture(String path) { return getScreen().capture(this).save(path); } public String saveScreenCapture(String path, String name) { return getScreen().capture(this).save(path, name); } // ************************************************ /** * get the last image taken on this regions screen * * @return the stored ScreenImage */ public ScreenImage getLastScreenImage() { return getScreen().getLastScreenImageFromScreen(); } /** * stores the lastScreenImage in the current bundle path with a created unique name * * @return the absolute file name * @throws java.io.IOException if not possible */ public String getLastScreenImageFile() throws IOException { return getScreen().getLastScreenImageFile(ImagePath.getBundlePath(), null); } /** * stores the lastScreenImage in the current bundle path with the given name * * @param name file name (.png is added if not there) * @return the absolute file name * @throws java.io.IOException if not possible */ public String getLastScreenImageFile(String name) throws IOException { return getScreen().getLastScreenImageFromScreen().getFile(ImagePath.getBundlePath(), name); } /** * stores the lastScreenImage in the given path with the given name * * @param path path to use * @param name file name (.png is added if not there) * @return the absolute file name * @throws java.io.IOException if not possible */ public String getLastScreenImageFile(String path, String name) throws IOException { return getScreen().getLastScreenImageFromScreen().getFile(path, name); } public void saveLastScreenImage() { ScreenImage simg = getScreen().getLastScreenImageFromScreen(); if (simg != null) { simg.saveLastScreenImage(runTime.fSikulixStore); } } // // /** * check if current region contains given region * * @param region the other Region * @return true/false */ public boolean contains(Region region) { return getRect().contains(region.getRect()); } /** * create a Location object, that can be used as an offset taking the width and hight of this Region * * @return a new Location object with width and height as x and y */ public Location asOffset() { return new Location(w, h); } /** * create region with same size at top left corner offset * * @param loc use its x and y to set the offset * @return the new region */ public Region offset(Location loc) { return Region.create(x + loc.x, y + loc.y, w, h, scr); } /** * create region with same size at top left corner offset * * @param x horizontal offset * @param y vertical offset * @return the new region */ public Region offset(int x, int y) { return Region.create(this.x + x, this.y + y, w, h, scr); } /** * create a region enlarged Settings.DefaultPadding pixels on each side * * @return the new region * @deprecated to be like AWT Rectangle API use grow() instead */ @Deprecated public Region nearby() { return grow(Settings.DefaultPadding, Settings.DefaultPadding); } /** * create a region enlarged range pixels on each side * * @param range the margin to be added around * @return the new region * @deprecated to be like AWT Rectangle API use grow() instaed */ @Deprecated public Region nearby(int range) { return grow(range, range); } /** * create a region enlarged n pixels on each side (n = Settings.DefaultPadding = 50 default) * * @return the new region */ public Region grow() { return grow(Settings.DefaultPadding, Settings.DefaultPadding); } /** * create a region enlarged range pixels on each side * * @param range the margin to be added around * @return the new region */ public Region grow(int range) { return grow(range, range); } /** * create a region enlarged w pixels on left and right side and h pixels at top and bottom * * @param w pixels horizontally * @param h pixels vertically * @return the new region */ public Region grow(int w, int h) { Rectangle r = getRect(); r.grow(w, h); return Region.create(r.x, r.y, r.width, r.height, scr); } /** * create a region enlarged l pixels on left and r pixels right side and t pixels at top side and b pixels a bottom * side. negative values go inside (shrink) * * @param l add to the left * @param r add to right * @param t add above * @param b add beneath * @return the new region */ public Region grow(int l, int r, int t, int b) { return Region.create(x - l, y - t, w + l + r, h + t + b, scr); } /** * point middle on right edge * * @return point middle on right edge */ public Location rightAt() { return rightAt(0); } /** * positive offset goes to the right. might be off current screen * * @param offset pixels * @return point with given offset horizontally to middle point on right edge */ public Location rightAt(int offset) { return checkAndSetRemote(new Location(x + w + offset, y + h / 2)); } /** * create a region right of the right side with same height. the new region extends to the right screen border
* use grow() to include the current region * * @return the new region */ public Region right() { int distToRightScreenBorder = getScreen().getX() + getScreen().getW() - (getX() + getW()); return right(distToRightScreenBorder); } /** * create a region right of the right side with same height and given width. negative width creates the right part * with width inside the region
* use grow() to include the current region * * @param width pixels * @return the new region */ public Region right(int width) { int _x; if (width < 0) { _x = x + w + width; } else { _x = x + w; } return Region.create(_x, y, Math.abs(width), h, scr); } /** * * @return point middle on left edge */ public Location leftAt() { return leftAt(0); } /** * negative offset goes to the left
might be off current screen * * @param offset pixels * @return point with given offset horizontally to middle point on left edge */ public Location leftAt(int offset) { return checkAndSetRemote(new Location(x + offset, y + h / 2)); } /** * create a region left of the left side with same height
the new region extends to the left screen border
use * grow() to include the current region * * @return the new region */ public Region left() { int distToLeftScreenBorder = getX() - getScreen().getX(); return left(distToLeftScreenBorder); } /** * create a region left of the left side with same height and given width
* negative width creates the left part with width inside the region use grow() to include the current region
* * @param width pixels * @return the new region */ public Region left(int width) { int _x; if (width < 0) { _x = x; } else { _x = x - width; } return Region.create(getScreen().getBounds().intersection(new Rectangle(_x, y, Math.abs(width), h)), scr); } /** * * @return point middle on top edge */ public Location aboveAt() { return aboveAt(0); } /** * negative offset goes towards top of screen
might be off current screen * * @param offset pixels * @return point with given offset vertically to middle point on top edge */ public Location aboveAt(int offset) { return checkAndSetRemote(new Location(x + w / 2, y + offset)); } /** * create a region above the top side with same width
the new region extends to the top screen border
use * grow() to include the current region * * @return the new region */ public Region above() { int distToAboveScreenBorder = getY() - getScreen().getY(); return above(distToAboveScreenBorder); } /** * create a region above the top side with same width and given height
* negative height creates the top part with height inside the region use grow() to include the current region * * @param height pixels * @return the new region */ public Region above(int height) { int _y; if (height < 0) { _y = y; } else { _y = y - height; } return Region.create(getScreen().getBounds().intersection(new Rectangle(x, _y, w, Math.abs(height))), scr); } /** * * @return point middle on bottom edge */ public Location belowAt() { return belowAt(0); } /** * positive offset goes towards bottom of screen
might be off current screen * * @param offset pixels * @return point with given offset vertically to middle point on bottom edge */ public Location belowAt(int offset) { return checkAndSetRemote(new Location(x + w / 2, y + h - offset)); } /** * create a region below the bottom side with same width
the new region extends to the bottom screen border
* use grow() to include the current region * * @return the new region */ public Region below() { int distToBelowScreenBorder = getScreen().getY() + getScreen().getH() - (getY() + getH()); return below(distToBelowScreenBorder); } /** * create a region below the bottom side with same width and given height
* negative height creates the bottom part with height inside the region use grow() to include the current region * * @param height pixels * @return the new region */ public Region below(int height) { int _y; if (height < 0) { _y = y + h + height; } else { _y = y + h; } return Region.create(x, _y, w, Math.abs(height), scr); } /** * create a new region containing both regions * * @param ur region to unite with * @return the new region */ public Region union(Region ur) { Rectangle r = getRect().union(ur.getRect()); return Region.create(r.x, r.y, r.width, r.height, scr); } /** * create a region that is the intersection of the given regions * * @param ir the region to intersect with like AWT Rectangle API * @return the new region */ public Region intersection(Region ir) { Rectangle r = getRect().intersection(ir.getRect()); return Region.create(r.x, r.y, r.width, r.height, scr); } //
// /** * select the specified part of the region. * *
Constants for the top parts of a region (Usage: Region.CONSTANT)
* shown in brackets: possible shortcuts for the part constant
* NORTH (NH, TH) - upper half
* NORTH_WEST (NW, TL) - left third in upper third
* NORTH_MID (NM, TM) - middle third in upper third
* NORTH_EAST (NE, TR) - right third in upper third
* ... similar for the other directions:
* right side: EAST (Ex, Rx)
* bottom part: SOUTH (Sx, Bx)
* left side: WEST (Wx, Lx)
*
* specials for quartered:
* TT top left quarter
* RR top right quarter
* BB bottom right quarter
* LL bottom left quarter
*
* specials for the center parts:
* MID_VERTICAL (MV, CV) half of width vertically centered
* MID_HORIZONTAL (MH, CH) half of height horizontally centered
* MID_BIG (M2, C2) half of width / half of height centered
* MID_THIRD (MM, CC) third of width / third of height centered
*
* Based on the scheme behind these constants there is another possible usage:
* specify part as e 3 digit integer where the digits xyz have the following meaning
* 1st x: use a raster of x rows and x columns
* 2nd y: the row number of the wanted cell
* 3rd z: the column number of the wanted cell
* y and z are counting from 0
* valid numbers: 200 up to 999 (< 200 are invalid and return the region itself)
* example: get(522) will use a raster of 5 rows and 5 columns and return the cell in the middle
* special cases:
* if either y or z are == or > x: returns the respective row or column
* example: get(525) will use a raster of 5 rows and 5 columns and return the row in the middle
*
* internally this is based on {@link #setRaster(int, int) setRaster} and {@link #getCell(int, int) getCell}
*
* If you need only one row in one column with x rows or only one column in one row with x columns you can use * {@link #getRow(int, int) getRow} or {@link #getCol(int, int) getCol} * * @param part the part to get (Region.PART long or short) * @return new region */ public Region get(int part) { return Region.create(getRectangle(getRect(), part)); } protected static Rectangle getRectangle(Rectangle rect, int part) { if (part < 200 || part > 999) { return rect; } Region r = Region.create(rect); int pTyp = (int) (part / 100); int pPos = part - pTyp * 100; int pRow = (int) (pPos / 10); int pCol = pPos - pRow * 10; r.setRaster(pTyp, pTyp); if (pTyp == 3) { // NW = 300, NORTH_WEST = NW; // NM = 301, NORTH_MID = NM; // NE = 302, NORTH_EAST = NE; // EM = 312, EAST_MID = EM; // SE = 322, SOUTH_EAST = SE; // SM = 321, SOUTH_MID = SM; // SW = 320, SOUTH_WEST = SW; // WM = 310, WEST_MID = WM; // MM = 311, MIDDLE = MM, M3 = MM; return r.getCell(pRow, pCol).getRect(); } if (pTyp == 2) { // NH = 202, NORTH = NH; // EH = 221, EAST = EH; // SH = 212, SOUTH = SH; // WH = 220, WEST = WH; if (pRow > 1) { return r.getCol(pCol).getRect(); } else if (pCol > 1) { return r.getRow(pRow).getRect(); } return r.getCell(pRow, pCol).getRect(); } if (pTyp == 4) { // MV = 441, MID_VERTICAL = MV; // MH = 414, MID_HORIZONTAL = MH; // M2 = 444, MIDDLE_BIG = M2; if (pRow > 3) { if (pCol > 3) { return r.getCell(1, 1).union(r.getCell(2, 2)).getRect(); } return r.getCell(0, 1).union(r.getCell(3, 2)).getRect(); } else if (pCol > 3) { return r.getCell(1, 0).union(r.getCell(2, 3)).getRect(); } return r.getCell(pRow, pCol).getRect(); } return rect; } /** * store info: this region is divided vertically into n even rows
* a preparation for using getRow() * * @param n number of rows * @return the top row */ public Region setRows(int n) { return setRaster(n, 0); } /** * store info: this region is divided horizontally into n even columns
* a preparation for using getCol() * * @param n number of columns * @return the leftmost column */ public Region setCols(int n) { return setRaster(0, n); } /** * * @return the number of rows or null */ public int getRows() { return rows; } /** * * @return the row height or 0 */ public int getRowH() { return rowH; } /** * * @return the number of columns or 0 */ public int getCols() { return cols; } /** * * @return the columnwidth or 0 */ public int getColW() { return colW; } /** * Can be used to check, wether the Region currently has a valid raster * * @return true if it has a valid raster (either getCols or getRows or both would return > 0) false otherwise */ public boolean isRasterValid() { return (rows > 0 || cols > 0); } /** * store info: this region is divided into a raster of even cells
* a preparation for using getCell()
* * @param r number of rows * @param c number of columns * @return the topleft cell */ public Region setRaster(int r, int c) { rows = Math.max(r, h); cols = Math.max(c, w); if (r > 0) { rowH = (int) (h / r); rowHd = h - r * rowH; } if (c > 0) { colW = (int) (w / c); colWd = w - c * colW; } return getCell(0, 0); } /** * get the specified row counting from 0, if rows or raster are setup negative counts reverse from the end (last = -1) * values outside range are 0 or last respectively * * @param r row number * @return the row as new region or the region itself, if no rows are setup */ public Region getRow(int r) { if (rows == 0) { return this; } if (r < 0) { r = rows + r; } r = Math.max(0, r); r = Math.min(r, rows - 1); return Region.create(x, y + r * rowH, w, rowH); } public Region getRow(int r, int n) { return this; } /** * get the specified column counting from 0, if columns or raster are setup negative counts reverse from the end (last * = -1) values outside range are 0 or last respectively * * @param c column number * @return the column as new region or the region itself, if no columns are setup */ public Region getCol(int c) { if (cols == 0) { return this; } if (c < 0) { c = cols + c; } c = Math.max(0, c); c = Math.min(c, cols - 1); return Region.create(x + c * colW, y, colW, h); } /** * divide the region in n columns and select column c as new Region * * @param c the column to select counting from 0 or negative to count from the end * @param n how many columns to devide in * @return the selected part or the region itself, if parameters are invalid */ public Region getCol(int c, int n) { Region r = new Region(this); r.setCols(n); return r.getCol(c); } /** * get the specified cell counting from (0, 0), if a raster is setup
* negative counts reverse from the end (last = -1) values outside range are 0 or last respectively * * @param r row number * @param c column number * @return the cell as new region or the region itself, if no raster is setup */ public Region getCell(int r, int c) { if (rows == 0) { return getCol(c); } if (cols == 0) { return getRow(r); } if (rows == 0 && cols == 0) { return this; } if (r < 0) { r = rows - r; } if (c < 0) { c = cols - c; } r = Math.max(0, r); r = Math.min(r, rows - 1); c = Math.max(0, c); c = Math.min(c, cols - 1); return Region.create(x + c * colW, y + r * rowH, colW, rowH); } //
// protected void updateSelf() { if (overlay != null) { highlight(false, null); highlight(true, null); } } protected Region silentHighlight(boolean onOff) { if (onOff && overlay == null) { return doHighlight(true, null, true); } if (!onOff && overlay != null) { return doHighlight(false, null, true); } return this; } /** * Toggle the regions Highlight visibility (red frame) * * @return the region itself */ public Region highlight() { // Pass true if overlay is null, false otherwise highlight(overlay == null, null); return this; } /** * Toggle the regions Highlight visibility (frame of specified color)
* allowed color specifications for frame color:
* - a color name out of: black, blue, cyan, gray, green, magenta, orange, pink, red, white, yellow (lowercase and * uppercase can be mixed, internally transformed to all uppercase)
* - these colornames exactly written: lightGray, LIGHT_GRAY, darkGray and DARK_GRAY
* - a hex value like in HTML: #XXXXXX (max 6 hex digits) - an RGB specification as: #rrrgggbbb where rrr, ggg, bbb * are integer values in range 0 - 255 padded with leading zeros if needed (hence exactly 9 digits) * * @param color Color of frame * @return the region itself */ public Region highlight(String color) { // Pass true if overlay is null, false otherwise highlight(overlay == null, color); return this; } /** * Sets the regions Highlighting border * * @param toEnable set overlay enabled or disabled * @param color Color of frame (see method highlight(color)) */ private Region highlight(boolean toEnable, String color) { return doHighlight(toEnable, color, false); } private Region doHighlight(boolean toEnable, String color, boolean silent) { if (isOtherScreen()) { return this; } if (!silent) { Debug.action("toggle highlight " + toString() + ": " + toEnable + (color != null ? " color: " + color : "")); } if (toEnable) { overlay = new ScreenHighlighter(getScreen(), color); overlay.setWaitAfter(silent); overlay.highlight(this); } else { if (overlay != null) { overlay.close(); overlay = null; } } return this; } /** * show the regions Highlight for the given time in seconds (red frame) if 0 - use the global Settings.SlowMotionDelay * * @param secs time in seconds * @return the region itself */ public Region highlight(float secs) { return highlight(secs, null); } /** * show the regions Highlight for the given time in seconds (frame of specified color) if 0 - use the global * Settings.SlowMotionDelay * * @param secs time in seconds * @param color Color of frame (see method highlight(color)) * @return the region itself */ public Region highlight(float secs, String color) { if (getScreen() == null || isOtherScreen() || isScreenUnion) { Debug.error("highlight: not possible for %s", getScreen()); return this; } if (secs < 0.1) { return highlight((int) secs, color); } Debug.action("highlight " + toStringShort() + " for " + secs + " secs" + (color != null ? " color: " + color : "")); ScreenHighlighter _overlay = new ScreenHighlighter(getScreen(), color); _overlay.highlight(this, secs); return this; } /** * hack to implement the getLastMatch() convenience 0 means same as highlight() < 0 same as highlight(secs) if * available the last match is highlighted * * @param secs seconds * @return this region */ public Region highlight(int secs) { return highlight(secs, null); } /** * Show highlight in selected color * * @param secs time in seconds * @param color Color of frame (see method highlight(color)) * @return this region */ public Region highlight(int secs, String color) { if (getScreen() == null || isOtherScreen() || isScreenUnion) { Debug.error("highlight: not possible for %s", getScreen()); return this; } if (secs > 0) { return highlight((float) secs, color); } if (lastMatch != null) { if (secs < 0) { return lastMatch.highlight((float) -secs, color); } return lastMatch.highlight(Settings.DefaultHighlightTime, color); } return this; } //
// /** * WARNING: wait(long timeout) is taken by Java Object as final. This method catches any interruptedExceptions * * @param timeout The time to wait */ public void wait(double timeout) { try { Thread.sleep((long) (timeout * 1000L)); } catch (InterruptedException e) { } } /** * return false to skip
* return true to try again
* throw FindFailed to abort * * @param img Handles a failed find action */ private Boolean handleFindFailed(PSI target, Image img, boolean isExists) { log(lvl, "handleFindFailed: %s", target); Boolean state = null; ObserveEvent evt = null; FindFailedResponse response = findFailedResponse; if (FindFailedResponse.HANDLE.equals(response)) { ObserveEvent.Type type = ObserveEvent.Type.FINDFAILED; if (findFailedHandler != null && ((ObserverCallBack) findFailedHandler).getType().equals(type)) { log(lvl, "handleFindFailed: Response.HANDLE: calling handler"); evt = new ObserveEvent("", type, target, img, this, 0); ((ObserverCallBack) findFailedHandler).findfailed(evt); response = evt.getResponse(); } } if (FindFailedResponse.ABORT.equals(response)) { state = null; if (isExists) { state = false; } } else if (FindFailedResponse.SKIP.equals(response)) { state = false; } else if (FindFailedResponse.RETRY.equals(response)) { state = true; } if (FindFailedResponse.PROMPT.equals(response)) { response = handleFindFailedShowDialog(img, false); } else { return state; } if (FindFailedResponse.ABORT.equals(response)) { state = null; } else if (FindFailedResponse.SKIP.equals(response)) { // TODO HACK to allow recapture on FindFailed PROMPT if (img.backup()) { img.delete(); state = handleImageMissing(img, true); if (state == null || !state) { if (!img.restore()) { state = null; } else { img.get(); } } } } else if (FindFailedResponse.RETRY.equals(response)) { state = true; } return state; } private Boolean handleImageMissing(Image img, boolean recap) { log(lvl, "handleImageMissing: %s", img.getName()); ObserveEvent evt = null; FindFailedResponse response = findFailedResponse; if (FindFailedResponse.HANDLE.equals(response)) { ObserveEvent.Type type = ObserveEvent.Type.MISSING; if (imageMissingHandler != null && ((ObserverCallBack) imageMissingHandler).getType().equals(type)) { log(lvl, "handleImageMissing: Response.HANDLE: calling handler"); evt = new ObserveEvent("", type, null, img, this, 0); ((ObserverCallBack) imageMissingHandler).missing(evt); response = evt.getResponse(); } else { response = FindFailedResponse.PROMPT; } } if (FindFailedResponse.PROMPT.equals(response)) { log(lvl, "handleImageMissing: Response.PROMPT"); response = handleFindFailedShowDialog(img, true); } if (findFailedResponse.RETRY.equals(response)) { log(lvl, "handleImageMissing: Response.RETRY: %s", (recap?"recapture ":"capture missing ")); getRobotForRegion().delay(500); ScreenImage simg = getScreen().userCapture( (recap?"recapture ":"capture missing ") + img.getName()); if (simg != null) { String path = ImagePath.getBundlePath(); if (path == null) { log(-1, "handleImageMissing: no bundle path - aborting"); return null; } simg.getFile(path, img.getImageName()); Image.set(img); if (img.isValid()) { log(lvl, "handleImageMissing: %scaptured: %s", (recap?"re":""), img); Image.setIDEshouldReload(img); return true; } } return null; } else if (findFailedResponse.ABORT.equals(response)) { log(lvl, "handleImageMissing: Response.ABORT: aborting"); return null; } log(lvl, "handleImageMissing: skip requested on %s", (recap?"recapture ":"capture missing ")); return false; } private FindFailedResponse handleFindFailedShowDialog(Image img, boolean shouldCapture) { log(lvl, "handleFindFailedShowDialog: requested %s", (shouldCapture?"(with capture)":"")); FindFailedResponse response; FindFailedDialog fd = new FindFailedDialog(img, shouldCapture); fd.setVisible(true); response = fd.getResponse(); fd.dispose(); wait(0.5); log(lvl, "handleFindFailedShowDialog: answer is %s", response); return response; } /** * finds the given Pattern, String or Image in the region and returns the best match. If AutoWaitTimeout is set, this * is equivalent to wait(). Otherwise only one search attempt will be done. * * @param Pattern, String or Image * @param target A search criteria * @return If found, the element. null otherwise * @throws FindFailed if the Find operation failed */ public Match find(PSI target) throws FindFailed { if (autoWaitTimeout > 0) { return wait(target, autoWaitTimeout); } lastMatch = null; Image img = Image.getImageFromTarget(target); Boolean response = true; if (!img.isText() && !img.isValid() && img.hasIOException()) { response = handleImageMissing(img, false); if (response == null) { runTime.abortScripting("Find: Abort:", "ImageMissing: " + target.toString()); } } String targetStr = img.getName(); while (null != response && response) { log(lvl, "find: waiting 0 secs for %s to appear in %s", targetStr, this.toStringShort()); lastMatch = doFind(target, img, null); if (lastMatch != null) { lastMatch.setImage(img); if (isOtherScreen()) { lastMatch.setOtherScreen(); } else if (img != null) { img.setLastSeen(lastMatch.getRect(), lastMatch.getScore()); } log(lvl, "find: %s appeared (%s)", targetStr, lastMatch); break; } log(lvl, "find: %s did not appear [%d msec]", targetStr, new Date().getTime() - lastFindTime); if (null == lastMatch) { response = handleFindFailed(target, img, false); } } if (null == response) { throw new FindFailed(FindFailed.createdefault(this, img)); } return lastMatch; } /** * Check if target exists (with the default autoWaitTimeout) * * @param Pattern, String or Image * @param target Pattern, String or Image * @return the match (null if not found or image file missing) */ public Match exists(PSI target) { return exists(target, autoWaitTimeout); } /** * Check if target exists with a specified timeout
* timout = 0: returns immediately after first search * * @param Pattern, String or Image * @param target The target to search for * @param timeout Timeout in seconds * @return the match (null if not found or image file missing) */ public Match exists(PSI target, double timeout) { lastMatch = null; String shouldAbort = ""; RepeatableFind rf = new RepeatableFind(target, null); Image img = rf._image; Boolean response = true; if (!img.isText() && !img.isValid() && img.hasIOException()) { response = handleImageMissing(img, false); if (response == null) { runTime.abortScripting("Exists: Abort:", "ImageMissing: " + target.toString()); } } String targetStr = img.getName(); while (null != response && response) { log(lvl, "exists: waiting %.1f secs for %s to appear in %s", timeout, targetStr, this.toStringShort()); if (rf.repeat(timeout)) { lastMatch = rf.getMatch(); lastMatch.setImage(img); if (isOtherScreen()) { lastMatch.setOtherScreen(); } else if (img != null) { img.setLastSeen(lastMatch.getRect(), lastMatch.getScore()); } log(lvl, "exists: %s has appeared (%s)", targetStr, lastMatch); return lastMatch; } else { response = handleFindFailed(target, img, true); if (null == response) { shouldAbort = FindFailed.createdefault(this, img); } else if (response) { if (img.isRecaptured()) { rf = new RepeatableFind(target, img); } continue; } break; } } if (!shouldAbort.isEmpty()) { runTime.abortScripting("Exists: Abort:", "FindFailed: " + shouldAbort); } else { log(lvl, "exists: %s did not appear [%d msec]", targetStr, new Date().getTime() - lastFindTime); } return null; } /** * finds all occurences of the given Pattern, String or Image in the region and returns an Iterator of Matches. * * * @param Pattern, String or Image * @param target A search criteria * @return All elements matching * @throws FindFailed if the Find operation failed */ public Iterator findAll(PSI target) throws FindFailed { lastMatches = null; RepeatableFindAll rf = new RepeatableFindAll(target, null); Image img = rf._image; String targetStr = img.getName(); Boolean response = true; if (!img.isValid() && img.hasIOException()) { response = handleImageMissing(img, false); if (response == null) { runTime.abortScripting("FindAll: Abort:", "ImageMissing: " + target.toString()); } } while (null != response && response) { log(lvl, "findAll: waiting %.1f secs for (multiple) %s to appear in %s", autoWaitTimeout, targetStr, this.toStringShort()); if (autoWaitTimeout > 0) { rf.repeat(autoWaitTimeout); lastMatches = rf.getMatches(); } else { lastMatches = doFindAll(target, null); } if (lastMatches != null) { log(lvl, "findAll: %s has appeared", targetStr); break; } else { log(lvl, "findAll: %s did not appear", targetStr); response = handleFindFailed(target, img, false); } } if (null == response) { throw new FindFailed(FindFailed.createdefault(this, img)); } return lastMatches; } public Match[] findAllByRow(PSI target) { Match[] matches = new Match[0]; List mList = findAllCollect(target); if (mList.isEmpty()) { return null; } Collections.sort(mList, new Comparator() { @Override public int compare(Match m1, Match m2) { if (m1.y == m2.y) { return m1.x - m2.x; } return m1.y - m2.y; } }); return mList.toArray(matches); } public Match[] findAllByColumn(PSI target) { Match[] matches = new Match[0]; List mList = findAllCollect(target); if (mList.isEmpty()) { return null; } Collections.sort(mList, new Comparator() { @Override public int compare(Match m1, Match m2) { if (m1.x == m2.x) { return m1.y - m2.y; } return m1.x - m2.x; } }); return mList.toArray(matches); } private List findAllCollect(PSI target) { Iterator mIter = null; try { mIter = findAll(target); } catch (Exception ex) { Debug.error("findAllByRow: %s", ex.getMessage()); return null; } List mList = new ArrayList(); while (mIter.hasNext()) { mList.add(mIter.next()); } return mList; } public Match findBest(Object... args) { Debug.log(lvl, "findBest: enter"); Match mResult = null; List mList = findAnyCollect(args); if (mList != null) { Collections.sort(mList, new Comparator() { @Override public int compare(Match m1, Match m2) { double ms = m2.getScore() - m1.getScore(); if (ms < 0) { return -1; } else if (ms > 0) { return 1; } return 0; } }); mResult = mList.get(0); } return mResult; } private List findAnyCollect(Object... args) { if (args == null) { return null; } List mList = new ArrayList(); Match[] mArray = new Match[args.length]; SubFindRun[] theSubs = new SubFindRun[args.length]; int nobj = 0; ScreenImage base = getScreen().capture(this); for (Object obj : args) { mArray[nobj] = null; if (obj instanceof Pattern || obj instanceof String || obj instanceof Image) { theSubs[nobj] = new SubFindRun(mArray, nobj, base, obj, this); new Thread(theSubs[nobj]).start(); } nobj++; } Debug.log(lvl, "findAnyCollect: waiting for SubFindRuns"); nobj = 0; boolean all = false; while (!all) { all = true; for (SubFindRun sub : theSubs) { all &= sub.hasFinished(); } } Debug.log(lvl, "findAnyCollect: SubFindRuns finished"); nobj = 0; for (Match match : mArray) { if (match != null) { match.setIndex(nobj); mList.add(match); } else { } nobj++; } return mList; } private class SubFindRun implements Runnable { Match[] mArray; ScreenImage base; Object target; Region reg; boolean finished = false; int subN; public SubFindRun(Match[] pMArray, int pSubN, ScreenImage pBase, Object pTarget, Region pReg) { subN = pSubN; base = pBase; target = pTarget; reg = pReg; mArray = pMArray; } @Override public void run() { try { mArray[subN] = reg.findInImage(base, target); } catch (Exception ex) { log(-1, "findAnyCollect: image file not found:\n", target); } hasFinished(true); } public boolean hasFinished() { return hasFinished(false); } public synchronized boolean hasFinished(boolean state) { if (state) { finished = true; } return finished; } } private Match findInImage(ScreenImage base, Object target) throws IOException { Finder finder = null; Match match = null; boolean findingText = false; Image img = null; if (target instanceof String) { if (((String) target).startsWith("\t") && ((String) target).endsWith("\t")) { findingText = true; } else { img = Image.create((String) target); if (img.isValid()) { finder = doCheckLastSeenAndCreateFinder(base, img, 0.0, null); if (!finder.hasNext()) { runFinder(finder, img); } } else if (img.isText()) { findingText = true; } else { throw new IOException("Region: findInImage: Image not loadable: " + target.toString()); } } if (findingText) { if (TextRecognizer.getInstance() != null) { log(lvl, "findInImage: Switching to TextSearch"); finder = new Finder(getScreen().capture(x, y, w, h), this); finder.findText((String) target); } } } else if (target instanceof Pattern) { if (((Pattern) target).isValid()) { img = ((Pattern) target).getImage(); finder = doCheckLastSeenAndCreateFinder(base, img, 0.0, (Pattern) target); if (!finder.hasNext()) { runFinder(finder, target); } } else { throw new IOException("Region: findInImage: Image not loadable: " + target.toString()); } } else if (target instanceof Image) { if (((Image) target).isValid()) { img = ((Image) target); finder = doCheckLastSeenAndCreateFinder(base, img, 0.0, null); if (!finder.hasNext()) { runFinder(finder, img); } } else { throw new IOException("Region: findInImage: Image not loadable: " + target.toString()); } } else { log(-1, "findInImage: invalid parameter: %s", target); return null; } if (finder.hasNext()) { match = finder.next(); match.setImage(img); img.setLastSeen(match.getRect(), match.getScore()); } return match; } /** * Waits for the Pattern, String or Image to appear until the AutoWaitTimeout value is exceeded. * * @param Pattern, String or Image * @param target The target to search for * @return The found Match * @throws FindFailed if the Find operation finally failed */ public Match wait(PSI target) throws FindFailed { if (target instanceof Float || target instanceof Double) { wait(0.0 + ((Double) target)); return null; } return wait(target, autoWaitTimeout); } /** * Waits for the Pattern, String or Image to appear or timeout (in second) is passed * * @param Pattern, String or Image * @param target The target to search for * @param timeout Timeout in seconds * @return The found Match * @throws FindFailed if the Find operation finally failed */ public Match wait(PSI target, double timeout) throws FindFailed { lastMatch = null; String shouldAbort = ""; RepeatableFind rf = new RepeatableFind(target, null); Image img = rf._image; String targetStr = img.getName(); Boolean response = true; if (!img.isText() && !img.isValid() && img.hasIOException()) { response = handleImageMissing(img, false); if (response == null) { runTime.abortScripting("Wait: Abort:", "ImageMissing: " + target.toString()); } } while (null != response && response) { log(lvl, "wait: waiting %.1f secs for %s to appear in %s", timeout, targetStr, this.toStringShort()); if (rf.repeat(timeout)) { lastMatch = rf.getMatch(); lastMatch.setImage(img); if (isOtherScreen()) { lastMatch.setOtherScreen(); } else if (img != null) { img.setLastSeen(lastMatch.getRect(), lastMatch.getScore()); } log(lvl, "wait: %s appeared (%s)", targetStr, lastMatch); return lastMatch; } else { response = handleFindFailed(target, img, false); if (null == response) { shouldAbort = FindFailed.createdefault(this, img); break; } else if (response) { if (img.isRecaptured()) { rf = new RepeatableFind(target, img); } continue; } break; } } log(lvl, "wait: %s did not appear [%d msec]", targetStr, new Date().getTime() - lastFindTime); if (!shouldAbort.isEmpty()) { throw new FindFailed(shouldAbort); } return lastMatch; } /** * waits until target vanishes or timeout (in seconds) is passed (AutoWaitTimeout) * * @param Pattern, String or Image * @param target The target to wait for it to vanish * @return true if the target vanishes, otherwise returns false. */ public boolean waitVanish(PSI target) { return waitVanish(target, autoWaitTimeout); } /** * waits until target vanishes or timeout (in seconds) is passed * * @param Pattern, String or Image * @param target Pattern, String or Image * @param timeout time in seconds * @return true if target vanishes, false otherwise and if imagefile is missing. */ public boolean waitVanish(PSI target, double timeout) { RepeatableVanish rv = new RepeatableVanish(target); Image img = rv._image; String targetStr = img.getName(); Boolean response = true; if (!img.isValid() && img.hasIOException()) { response = handleImageMissing(img, false); } if (null != response && response) { log(lvl, "waiting for " + targetStr + " to vanish within %.1f secs", timeout); if (rv.repeat(timeout)) { log(lvl, "%s vanished", targetStr); return true; } log(lvl, "%s did not vanish before timeout", targetStr); return false; } return false; } //TODO 1.2.0 Region.compare as time optimized Region.exists /** * time optimized Region.exists, when image-size == region-size
* 1.1.x: just using exists(img, 0), sizes not checked * * @param img image file name * @return the match or null if not equal */ public Match compare(String img) { return compare(Image.create(img)); } /** * time optimized Region.exists, when image-size == region-size
* 1.1.x: just using exists(img, 0), sizes not checked * * @param img Image object * @return the match or null if not equal */ public Match compare(Image img) { return exists(img, 0); } /** * Use findText() instead of find() in cases where the given string could be misinterpreted as an image filename * * @param text text * @param timeout time * @return the matched region containing the text * @throws org.sikuli.script.FindFailed if not found */ public Match findText(String text, double timeout) throws FindFailed { // the leading/trailing tab is used to internally switch to text search directly return wait("\t" + text + "\t", timeout); } /** * Use findText() instead of find() in cases where the given string could be misinterpreted as an image filename * * @param text text * @return the matched region containing the text * @throws org.sikuli.script.FindFailed if not found */ public Match findText(String text) throws FindFailed { return findText(text, autoWaitTimeout); } /** * Use findAllText() instead of findAll() in cases where the given string could be misinterpreted as an image filename * * @param text text * @return the matched region containing the text * @throws org.sikuli.script.FindFailed if not found */ public Iterator findAllText(String text) throws FindFailed { // the leading/trailing tab is used to internally switch to text search directly return findAll("\t" + text + "\t"); } //
// /** * Match doFind( Pattern/String/Image ) finds the given pattern on the screen and returns the best match without * waiting. */ private Match doFind(PSI ptn, Image img, RepeatableFind repeating) { Finder f = null; Match m = null; IScreen s = null; boolean findingText = false; ScreenImage simg; double findTimeout = autoWaitTimeout; String someText = ""; if (repeating != null) { findTimeout = repeating.getFindTimeOut(); } if (repeating != null && repeating._finder != null) { simg = getScreen().capture(this); f = repeating._finder; f.setScreenImage(simg); f.setRepeating(); if (Settings.FindProfiling) { Debug.logp("[FindProfiling] Region.doFind repeat: %d msec", new Date().getTime() - lastSearchTimeRepeat); } lastSearchTime = (new Date()).getTime(); f.findRepeat(); } else { s = getScreen(); lastFindTime = (new Date()).getTime(); if (ptn instanceof String) { if (((String) ptn).startsWith("\t") && ((String) ptn).endsWith("\t")) { findingText = true; someText = ((String) ptn).replaceAll("\\t", ""); } else { if (img.isValid()) { lastSearchTime = (new Date()).getTime(); f = checkLastSeenAndCreateFinder(img, findTimeout, null); if (!f.hasNext()) { runFinder(f, img); } } else if (img.isText()) { findingText = true; someText = img.getText(); } } if (findingText) { log(lvl, "doFind: Switching to TextSearch"); if (TextRecognizer.getInstance() != null) { f = new Finder(getScreen().capture(x, y, w, h), this); lastSearchTime = (new Date()).getTime(); f.findText(someText); } } } else if (ptn instanceof Pattern) { if (img.isValid()) { lastSearchTime = (new Date()).getTime(); f = checkLastSeenAndCreateFinder(img, findTimeout, (Pattern) ptn); if (!f.hasNext()) { runFinder(f, ptn); } } } else if (ptn instanceof Image) { if (img.isValid()) { lastSearchTime = (new Date()).getTime(); f = checkLastSeenAndCreateFinder(img, findTimeout, null); if (!f.hasNext()) { runFinder(f, img); } } } else { runTime.abortScripting("aborting script at:", String.format("find, wait, exists: invalid parameter: %s", ptn)); } if (repeating != null) { repeating._finder = f; repeating._image = img; } } if (f != null) { lastSearchTimeRepeat = lastSearchTime; lastSearchTime = (new Date()).getTime() - lastSearchTime; if (f.hasNext()) { lastFindTime = (new Date()).getTime() - lastFindTime; m = f.next(); m.setTimes(lastFindTime, lastSearchTime); if (Settings.FindProfiling) { Debug.logp("[FindProfiling] Region.doFind final: %d msec", lastSearchTime); } } } return m; } private void runFinder(Finder f, Object target) { if (Debug.shouldHighlight()) { if (this.scr.getW() > w + 20 && this.scr.getH() > h + 20) { highlight(2, "#000255000"); } } if (target instanceof Image) { f.find((Image) target); } else if (target instanceof Pattern) { f.find((Pattern) target); } } private Finder checkLastSeenAndCreateFinder(Image img, double findTimeout, Pattern ptn) { return doCheckLastSeenAndCreateFinder(null, img, findTimeout, ptn); } private Finder doCheckLastSeenAndCreateFinder(ScreenImage base, Image img, double findTimeout, Pattern ptn) { if (base == null) { base = getScreen().capture(this); } boolean shouldCheckLastSeen = false; float score = 0; if (!Settings.UseImageFinder && Settings.CheckLastSeen && null != img.getLastSeen()) { score = (float) (img.getLastSeenScore() - 0.01); if (ptn != null) { if (!(ptn.getSimilar() > score)) { shouldCheckLastSeen = true; } } } if (shouldCheckLastSeen) { Region r = Region.create(img.getLastSeen()); if (this.contains(r)) { Finder f = new Finder(base.getSub(r.getRect()), r); if (Debug.shouldHighlight()) { if (this.scr.getW() > w + 10 && this.scr.getH() > h + 10) { highlight(2, "#000255000"); } } if (ptn == null) { f.find(new Pattern(img).similar(score)); } else { f.find(new Pattern(ptn).similar(score)); } if (f.hasNext()) { log(lvl, "checkLastSeen: still there"); return f; } log(lvl, "checkLastSeen: not there"); } } if (Settings.UseImageFinder) { ImageFinder f = new ImageFinder(this); f.setFindTimeout(findTimeout); return f; } else { return new Finder(base, this); } } /** * Match findAllNow( Pattern/String/Image ) finds all the given pattern on the screen and returns the best matches * without waiting. */ private Iterator doFindAll(PSI ptn, RepeatableFindAll repeating) { boolean findingText = false; Finder f; ScreenImage simg = getScreen().capture(x, y, w, h); if (repeating != null && repeating._finder != null) { f = repeating._finder; f.setScreenImage(simg); f.setRepeating(); f.findAllRepeat(); } else { f = new Finder(simg, this); Image img = null; if (ptn instanceof String) { if (((String) ptn).startsWith("\t") && ((String) ptn).endsWith("\t")) { findingText = true; } else { img = Image.create((String) ptn); if (img.isValid()) { f.findAll(img); } else if (img.isText()) { findingText = true; } } if (findingText) { if (TextRecognizer.getInstance() != null) { log(lvl, "doFindAll: Switching to TextSearch"); f.findAllText((String) ptn); } } } else if (ptn instanceof Pattern) { if (((Pattern) ptn).isValid()) { img = ((Pattern) ptn).getImage(); f.findAll((Pattern) ptn); } } else if (ptn instanceof Image) { if (((Image) ptn).isValid()) { img = ((Image) ptn); f.findAll((Image) ptn); } } else { log(-1, "doFind: invalid parameter: %s", ptn); Sikulix.terminate(999); } if (repeating != null) { repeating._finder = f; repeating._image = img; } } if (f.hasNext()) { return f; } return null; } // Repeatable Find //////////////////////////////// private abstract class Repeatable { private double findTimeout; abstract void run(); abstract boolean ifSuccessful(); double getFindTimeOut() { return findTimeout; } // return TRUE if successful before timeout // return FALSE if otherwise // throws Exception if any unexpected error occurs boolean repeat(double timeout) { findTimeout = timeout; int MaxTimePerScan = (int) (1000.0 / waitScanRate); int timeoutMilli = (int) (timeout * 1000); long begin_t = (new Date()).getTime(); do { long before_find = (new Date()).getTime(); run(); if (ifSuccessful()) { return true; } else if (timeoutMilli < MaxTimePerScan || Settings.UseImageFinder) { // instant return on first search failed if timeout very small or 0 // or when using new ImageFinder return false; } long after_find = (new Date()).getTime(); if (after_find - before_find < MaxTimePerScan) { getRobotForRegion().delay((int) (MaxTimePerScan - (after_find - before_find))); } else { getRobotForRegion().delay(10); } } while (begin_t + timeout * 1000 > (new Date()).getTime()); return false; } } private class RepeatableFind extends Repeatable { Object _target; Match _match = null; Finder _finder = null; Image _image = null; public RepeatableFind(PSI target, Image img) { _target = target; if (img == null) { _image = Image.getImageFromTarget(target); } else { _image = img; } } public Match getMatch() { if (_finder != null) { _finder.destroy(); } return (_match == null) ? _match : new Match(_match); } @Override public void run() { _match = doFind(_target, _image, this); } @Override boolean ifSuccessful() { return _match != null; } } private class RepeatableVanish extends RepeatableFind { public RepeatableVanish(PSI target) { super(target, null); } @Override boolean ifSuccessful() { return _match == null; } } private class RepeatableFindAll extends Repeatable { Object _target; Iterator _matches = null; Finder _finder = null; Image _image = null; public RepeatableFindAll(PSI target, Image img) { _target = target; if (img == null) { _image = Image.getImageFromTarget(target); } else { _image = img; } } public Iterator getMatches() { return _matches; } @Override public void run() { _matches = doFindAll(_target, this); } @Override boolean ifSuccessful() { return _matches != null; } } // // // private Region getRegionFromTarget(PatternStringRegionMatch target) throws FindFailed { // if (target instanceof Pattern || target instanceof String || target instanceof Image) { // Match m = find(target); // if (m != null) { // return m.setScreen(scr); // } // return null; // } // if (target instanceof Region) { // return ((Region) target).setScreen(scr); // } // return null; // } protected Location getLocationFromTarget(PSIMRL target) throws FindFailed { if (target instanceof Pattern || target instanceof String || target instanceof Image) { Match m = find(target); if (m != null) { if (isOtherScreen()) { return m.getTarget().setOtherScreen(scr); } else { return m.getTarget(); } } return null; } if (target instanceof Match) { return ((Match) target).getTarget(); } if (target instanceof Region) { return ((Region) target).getCenter(); } if (target instanceof Location) { return new Location((Location) target); } return null; } // // protected Observer getObserver() { if (regionObserver == null) { regionObserver = new Observer(this); } return regionObserver; } /** * evaluate if at least one event observer is defined for this region (the observer need not be running) * * @return true, if the region has an observer with event observers */ public boolean hasObserver() { if (regionObserver != null) { return regionObserver.hasObservers(); } return false; } /** * * @return true if an observer is running for this region */ public boolean isObserving() { return observing; } /** * * @return true if any events have happened for this region, false otherwise */ public boolean hasEvents() { return Observing.hasEvents(this); } /** * the region's events are removed from the list * * @return the region's happened events as array if any (size might be 0) */ public ObserveEvent[] getEvents() { return Observing.getEvents(this); } /** * the event is removed from the list * * @param name event's name * @return the named event if happened otherwise null */ public ObserveEvent getEvent(String name) { return Observing.getEvent(name); } /** * set the observer with the given name inactive (not checked while observing) * * @param name observers name */ public void setInactive(String name) { if (!hasObserver()) { return; } Observing.setActive(name, false); } /** * set the observer with the given name active (checked while observing) * * @param name observers name */ public void setActive(String name) { if (!hasObserver()) { return; } Observing.setActive(name, true); } /** * a subsequently started observer in this region should wait for target and notify the given observer about this * event
* for details about the observe event handler: {@link ObserverCallBack}
* for details about APPEAR/VANISH/CHANGE events: {@link ObserveEvent}
* * @param Pattern, String or Image * @param target Pattern, String or Image * @param observer ObserverCallBack * @return the event's name */ public String onAppear(PSI target, Object observer) { return onEvent(target, observer, ObserveEvent.Type.APPEAR); } /** * a subsequently started observer in this region should wait for target success and details about the event can be * obtained using @{link Observing}
* for details about APPEAR/VANISH/CHANGE events: {@link ObserveEvent}
* * @param Pattern, String or Image * @param target Pattern, String or Image * @return the event's name */ public String onAppear(PSI target) { return onEvent(target, null, ObserveEvent.Type.APPEAR); } private String onEvent(PSIC targetThreshhold, Object observer, ObserveEvent.Type obsType) { if (observer != null && (observer.getClass().getName().contains("org.python") || observer.getClass().getName().contains("org.jruby"))) { observer = new ObserverCallBack(observer, obsType); } if (! (targetThreshhold instanceof Integer)) { Image img = Image.getImageFromTarget(targetThreshhold); Boolean response = true; if (!img.isValid() && img.hasIOException()) { response = handleImageMissing(img, false); if (response == null) { runTime.abortScripting("onEvent(" + obsType.name() + "): Abort:", "ImageMissing: " + targetThreshhold.toString()); } } } String name = Observing.add(this, (ObserverCallBack) observer, obsType, targetThreshhold); log(lvl, "%s: observer %s %s: %s with: %s", toStringShort(), obsType, (observer == null ? "" : " with callback"), name, targetThreshhold); return name; } /** * a subsequently started observer in this region should wait for the target to vanish and notify the given observer * about this event
* for details about the observe event handler: {@link ObserverCallBack}
* for details about APPEAR/VANISH/CHANGE events: {@link ObserveEvent}
* * @param Pattern, String or Image * @param target Pattern, String or Image * @param observer ObserverCallBack * @return the event's name */ public String onVanish(PSI target, Object observer) { return onEvent(target, observer, ObserveEvent.Type.VANISH); } /** * a subsequently started observer in this region should wait for the target to vanish success and details about the * event can be obtained using @{link Observing}
* for details about APPEAR/VANISH/CHANGE events: {@link ObserveEvent}
* * @param Pattern, String or Image * @param target Pattern, String or Image * @return the event's name */ public String onVanish(PSI target) { return onEvent(target, null, ObserveEvent.Type.VANISH); } /** * a subsequently started observer in this region should wait for changes in the region and notify the given observer * about this event for details about the observe event handler: {@link ObserverCallBack} for details about * APPEAR/VANISH/CHANGE events: {@link ObserveEvent} * * @param threshold minimum size of changes (rectangle threshhold x threshold) * @param observer ObserverCallBack * @return the event's name */ public String onChange(Integer threshold, Object observer) { return onEvent((threshold > 0 ? threshold : Settings.ObserveMinChangedPixels), observer, ObserveEvent.Type.CHANGE); } /** * a subsequently started observer in this region should wait for changes in the region success and details about the * event can be obtained using @{link Observing}
* for details about APPEAR/VANISH/CHANGE events: {@link ObserveEvent} * * @param threshold minimum size of changes (rectangle threshhold x threshold) * @return the event's name */ public String onChange(Integer threshold) { return onEvent((threshold > 0 ? threshold : Settings.ObserveMinChangedPixels), null, ObserveEvent.Type.CHANGE); } /** * a subsequently started observer in this region should wait for changes in the region and notify the given observer * about this event
* minimum size of changes used: Settings.ObserveMinChangedPixels for details about the observe event handler: * {@link ObserverCallBack} for details about APPEAR/VANISH/CHANGE events: {@link ObserveEvent} * * @param observer ObserverCallBack * @return the event's name */ public String onChange(Object observer) { return onEvent(Settings.ObserveMinChangedPixels, observer, ObserveEvent.Type.CHANGE); } /** * a subsequently started observer in this region should wait for changes in the region success and details about the * event can be obtained using @{link Observing}
* minimum size of changes used: Settings.ObserveMinChangedPixels for details about APPEAR/VANISH/CHANGE events: * {@link ObserveEvent} * * @return the event's name */ public String onChange() { return onEvent(Settings.ObserveMinChangedPixels, null, ObserveEvent.Type.CHANGE); } // // /** // *INTERNAL USE ONLY: for use with scripting API bridges // * @param Pattern, String or Image // * @param target Pattern, String or Image // * @param observer ObserverCallBack // * @return the event's name // */ // public String onAppearJ(PSI target, Object observer) { // return onEvent(target, observer, ObserveEvent.Type.APPEAR); // } // // /** // *INTERNAL USE ONLY: for use with scripting API bridges // * @param Pattern, String or Image // * @param target Pattern, String or Image // * @param observer ObserverCallBack // * @return the event's name // */ // public String onVanishJ(PSI target, Object observer) { // return onEvent(target, observer, ObserveEvent.Type.VANISH); // } // // /** // *INTERNAL USE ONLY: for use with scripting API bridges // * @param threshold min pixel size - 0 = ObserveMinChangedPixels // * @param observer ObserverCallBack // * @return the event's name // */ // public String onChangeJ(int threshold, Object observer) { // return onEvent( (threshold > 0 ? threshold : Settings.ObserveMinChangedPixels), // observer, ObserveEvent.Type.CHANGE); // } // // public String onChangeDo(Integer threshold, Object observer) { String name = Observing.add(this, (ObserverCallBack) observer, ObserveEvent.Type.CHANGE, threshold); log(lvl, "%s: onChange%s: %s minSize: %d", toStringShort(), (observer == null ? "" : " with callback"), name, threshold); return name; } /** * start an observer in this region that runs forever (use stopObserving() in handler) for details about the observe * event handler: {@link ObserverCallBack} for details about APPEAR/VANISH/CHANGE events: {@link ObserveEvent} * * @return false if not possible, true if events have happened */ public boolean observe() { return observe(Float.POSITIVE_INFINITY); } /** * start an observer in this region for the given time for details about the observe event handler: * {@link ObserverCallBack} for details about APPEAR/VANISH/CHANGE events: {@link ObserveEvent} * * @param secs time in seconds the observer should run * @return false if not possible, true if events have happened */ public boolean observe(double secs) { return observeDo(secs); } /** * INTERNAL USE ONLY: for use with scripting API bridges * * @param secs time in seconds the observer should run * @return false if not possible, true if events have happened */ public boolean observeInLine(double secs) { return observeDo(secs); } private boolean observeDo(double secs) { if (regionObserver == null) { Debug.error("Region: observe: Nothing to observe (Region might be invalid): " + this.toStringShort()); return false; } if (observing) { Debug.error("Region: observe: already running for this region. Only one allowed!"); return false; } log(lvl, "observe: starting in " + this.toStringShort() + " for " + secs + " seconds"); int MaxTimePerScan = (int) (1000.0 / observeScanRate); long begin_t = (new Date()).getTime(); long stop_t; if (secs > Long.MAX_VALUE) { stop_t = Long.MAX_VALUE; } else { stop_t = begin_t + (long) (secs * 1000); } regionObserver.initialize(); observing = true; Observing.addRunningObserver(this); while (observing && stop_t > (new Date()).getTime()) { long before_find = (new Date()).getTime(); ScreenImage simg = getScreen().capture(x, y, w, h); if (!regionObserver.update(simg)) { observing = false; break; } if (!observing) { break; } long after_find = (new Date()).getTime(); try { if (after_find - before_find < MaxTimePerScan) { Thread.sleep((int) (MaxTimePerScan - (after_find - before_find))); } } catch (Exception e) { } } boolean observeSuccess = false; if (observing) { observing = false; log(lvl, "observe: stopped due to timeout in " + this.toStringShort() + " for " + secs + " seconds"); } else { log(lvl, "observe: ended successfully: " + this.toStringShort()); observeSuccess = Observing.hasEvents(this); } return observeSuccess; } /** * start an observer in this region for the given time that runs in background for details about the observe event * handler: {@link ObserverCallBack} for details about APPEAR/VANISH/CHANGE events: {@link ObserveEvent} * * @param secs time in seconds the observer should run * @return false if not possible, true otherwise */ public boolean observeInBackground(double secs) { if (observing) { Debug.error("Region: observeInBackground: already running for this region. Only one allowed!"); return false; } Thread observeThread = new Thread(new ObserverThread(secs)); observeThread.start(); log(lvl, "observeInBackground now running"); return true; } private class ObserverThread implements Runnable { private double time; ObserverThread(double time) { this.time = time; } @Override public void run() { observeDo(time); } } /** * stops a running observer */ public void stopObserver() { log(lvl, "observe: request to stop observer for " + this.toStringShort()); observing = false; } /** * stops a running observer printing an info message * * @param message text */ public void stopObserver(String message) { Debug.info(message); stopObserver(); } //
// protected Location checkMatch() { if (lastMatch != null) { return lastMatch.getTarget(); } return getTarget(); } /** * move the mouse pointer to region's last successful match
use center if no lastMatch
* if region is a match: move to targetOffset
same as mouseMove * * @return 1 if possible, 0 otherwise */ public int hover() { try { // needed to cut throw chain for FindFailed return hover(checkMatch()); } catch (FindFailed ex) { } return 0; } /** * move the mouse pointer to the given target location
same as mouseMove
Pattern or Filename - do a find * before and use the match
Region - position at center
Match - position at match's targetOffset
Location * - position at that point
* * @param to search: Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @return 1 if possible, 0 otherwise * @throws FindFailed for Pattern or Filename */ public int hover(PFRML target) throws FindFailed { log(lvl, "hover: " + target); return mouseMove(target); } /** * left click at the region's last successful match
use center if no lastMatch
if region is a match: click * targetOffset * * @return 1 if possible, 0 otherwise */ public int click() { try { // needed to cut throw chain for FindFailed return click(checkMatch(), 0); } catch (FindFailed ex) { return 0; } } /** * left click at the given target location
Pattern or Filename - do a find before and use the match
Region - * position at center
Match - position at match's targetOffset
* Location - position at that point
* * @param to search: Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @return 1 if possible, 0 otherwise * @throws FindFailed for Pattern or Filename */ public int click(PFRML target) throws FindFailed { return click(target, 0); } /** * left click at the given target location
holding down the given modifier keys
* Pattern or Filename - do a find before and use the match
Region - position at center
* Match - position at match's targetOffset
Location - position at that point
* * @param to search: Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @param modifiers the value of the resulting bitmask (see KeyModifier) * @return 1 if possible, 0 otherwise * @throws FindFailed for Pattern or Filename */ public int click(PFRML target, Integer modifiers) throws FindFailed { Location loc = getLocationFromTarget(target); int ret = Mouse.click(loc, InputEvent.BUTTON1_MASK, modifiers, false, this); //TODO SikuliActionManager.getInstance().clickTarget(this, target, _lastScreenImage, _lastMatch); return ret; } /** * double click at the region's last successful match
use center if no lastMatch
if region is a match: click * targetOffset * * @return 1 if possible, 0 otherwise */ public int doubleClick() { try { // needed to cut throw chain for FindFailed return doubleClick(checkMatch(), 0); } catch (FindFailed ex) { return 0; } } /** * double click at the given target location
Pattern or Filename - do a find before and use the match
Region - * position at center
Match - position at match's targetOffset
* Location - position at that point
* * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @return 1 if possible, 0 otherwise * @throws FindFailed for Pattern or Filename */ public int doubleClick(PFRML target) throws FindFailed { return doubleClick(target, 0); } /** * double click at the given target location
holding down the given modifier keys
* Pattern or Filename - do a find before and use the match
Region - position at center
Match - position at * match's targetOffset
Location - position at that point
* * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @param modifiers the value of the resulting bitmask (see KeyModifier) * @return 1 if possible, 0 otherwise * @throws FindFailed for Pattern or Filename */ public int doubleClick(PFRML target, Integer modifiers) throws FindFailed { Location loc = getLocationFromTarget(target); int ret = Mouse.click(loc, InputEvent.BUTTON1_MASK, modifiers, true, this); //TODO SikuliActionManager.getInstance().doubleClickTarget(this, target, _lastScreenImage, _lastMatch); return ret; } /** * right click at the region's last successful match
use center if no lastMatch
if region is a match: click * targetOffset * * @return 1 if possible, 0 otherwise */ public int rightClick() { try { // needed to cut throw chain for FindFailed return rightClick(checkMatch(), 0); } catch (FindFailed ex) { return 0; } } /** * right click at the given target location
Pattern or Filename - do a find before and use the match
Region - * position at center
Match - position at match's targetOffset
Location - position at that point
* * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @return 1 if possible, 0 otherwise * @throws FindFailed for Pattern or Filename */ public int rightClick(PFRML target) throws FindFailed { return rightClick(target, 0); } /** * right click at the given target location
holding down the given modifier keys
* Pattern or Filename - do a find before and use the match
Region - position at center
Match - position at * match's targetOffset
Location - position at that point
* * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @param modifiers the value of the resulting bitmask (see KeyModifier) * @return 1 if possible, 0 otherwise * @throws FindFailed for Pattern or Filename */ public int rightClick(PFRML target, Integer modifiers) throws FindFailed { Location loc = getLocationFromTarget(target); int ret = Mouse.click(loc, InputEvent.BUTTON3_MASK, modifiers, false, this); //TODO SikuliActionManager.getInstance().rightClickTarget(this, target, _lastScreenImage, _lastMatch); return ret; } /** * time in milliseconds to delay between button down/up at next click only (max 1000) * * @param millisecs value */ public void delayClick(int millisecs) { Settings.ClickDelay = millisecs; } //
// /** * Drag from region's last match and drop at given target
applying Settings.DelayAfterDrag and DelayBeforeDrop *
using left mouse button * * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @return 1 if possible, 0 otherwise * @throws FindFailed if the Find operation failed */ public int dragDrop(PFRML target) throws FindFailed { return dragDrop(lastMatch, target); } /** * Drag from a position and drop to another using left mouse button
applying Settings.DelayAfterDrag and * DelayBeforeDrop * * @param Pattern, Filename, Text, Region, Match or Location * @param t1 source position * @param t2 destination position * @return 1 if possible, 0 otherwise * @throws FindFailed if the Find operation failed */ public int dragDrop(PFRML t1, PFRML t2) throws FindFailed { Location loc1 = getLocationFromTarget(t1); Location loc2 = getLocationFromTarget(t2); int retVal = 0; if (loc1 != null && loc2 != null) { IRobot r1 = loc1.getRobotForPoint("drag"); IRobot r2 = loc2.getRobotForPoint("drop"); if (r1 != null && r2 != null) { Mouse.use(this); r1.smoothMove(loc1); r1.delay((int) (Settings.DelayBeforeMouseDown * 1000)); r1.mouseDown(InputEvent.BUTTON1_MASK); double DelayBeforeDrag = Settings.DelayBeforeDrag; if (DelayBeforeDrag < 0.0) { DelayBeforeDrag = Settings.DelayAfterDrag; } r1.delay((int) (DelayBeforeDrag * 1000)); r2.smoothMove(loc2); r2.delay((int) (Settings.DelayBeforeDrop * 1000)); r2.mouseUp(InputEvent.BUTTON1_MASK); Mouse.let(this); retVal = 1; } } Settings.DelayBeforeMouseDown = Settings.DelayValue; Settings.DelayAfterDrag = Settings.DelayValue; Settings.DelayBeforeDrag = -Settings.DelayValue; Settings.DelayBeforeDrop = Settings.DelayValue; return retVal; } /** * Prepare a drag action: move mouse to given target
press and hold left mouse button
wait * Settings.DelayAfterDrag * * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @return 1 if possible, 0 otherwise * @throws FindFailed if not found */ public int drag(PFRML target) throws FindFailed { Location loc = getLocationFromTarget(target); int retVal = 0; if (loc != null) { IRobot r = loc.getRobotForPoint("drag"); if (r != null) { Mouse.use(this); r.smoothMove(loc); r.delay((int) (Settings.DelayBeforeMouseDown * 1000)); r.mouseDown(InputEvent.BUTTON1_MASK); double DelayBeforeDrag = Settings.DelayBeforeDrag; if (DelayBeforeDrag < 0.0) { DelayBeforeDrag = Settings.DelayAfterDrag; } r.delay((int) (DelayBeforeDrag * 1000)); r.waitForIdle(); Mouse.let(this); retVal = 1; } } Settings.DelayBeforeMouseDown = Settings.DelayValue; Settings.DelayAfterDrag = Settings.DelayValue; Settings.DelayBeforeDrag = -Settings.DelayValue; Settings.DelayBeforeDrop = Settings.DelayValue; return retVal; } /** * finalize a drag action with a drop: move mouse to given target
* wait Settings.DelayBeforeDrop
* before releasing the left mouse button * * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @return 1 if possible, 0 otherwise * @throws FindFailed if not found */ public int dropAt(PFRML target) throws FindFailed { Location loc = getLocationFromTarget(target); int retVal = 0; if (loc != null) { IRobot r = loc.getRobotForPoint("drag"); if (r != null) { Mouse.use(this); r.smoothMove(loc); r.delay((int) (Settings.DelayBeforeDrop * 1000)); r.mouseUp(InputEvent.BUTTON1_MASK); r.waitForIdle(); Mouse.let(this); retVal = 1; } } Settings.DelayBeforeMouseDown = Settings.DelayValue; Settings.DelayAfterDrag = Settings.DelayValue; Settings.DelayBeforeDrag = -Settings.DelayValue; Settings.DelayBeforeDrop = Settings.DelayValue; return retVal; } //
// /** * press and hold the specified buttons - use + to combine Button.LEFT left mouse button Button.MIDDLE middle mouse * button Button.RIGHT right mouse button * * @param buttons spec */ public void mouseDown(int buttons) { Mouse.down(buttons, this); } /** * release all currently held buttons */ public void mouseUp() { Mouse.up(0, this); } /** * release the specified mouse buttons (see mouseDown) if buttons==0, all currently held buttons are released * * @param buttons spec */ public void mouseUp(int buttons) { Mouse.up(buttons, this); } /** * move the mouse pointer to the region's last successful match
same as hover
* * @return 1 if possible, 0 otherwise */ public int mouseMove() { if (lastMatch != null) { try { return mouseMove(lastMatch); } catch (FindFailed ex) { return 0; } } return 0; } /** * move the mouse pointer to the given target location
same as hover
Pattern or Filename - do a find before * and use the match
Region - position at center
Match - position at match's targetOffset
* Location - position at that point
* * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @return 1 if possible, 0 otherwise * @throws FindFailed for Pattern or Filename */ public int mouseMove(PFRML target) throws FindFailed { Location loc = getLocationFromTarget(target); return Mouse.move(loc, this); } /** * move the mouse from the current position to the offset position given by the parameters * * @param xoff horizontal offset (< 0 left, > 0 right) * @param yoff vertical offset (< 0 up, > 0 down) * @return 1 if possible, 0 otherwise */ public int mouseMove(int xoff, int yoff) { try { return mouseMove(Mouse.at().offset(xoff, yoff)); } catch (Exception ex) { return 0; } } /** * Move the wheel at the current mouse position
the given steps in the given direction:
Button.WHEEL_DOWN, * Button.WHEEL_UP * * @param direction to move the wheel * @param steps the number of steps * @return 1 in any case */ public int wheel(int direction, int steps) { Mouse.wheel(direction, steps, this); return 1; } /** * move the mouse pointer to the given target location
and move the wheel the given steps in the given direction: *
Button.WHEEL_DOWN, Button.WHEEL_UP * * @param Pattern, Filename, Text, Region, Match or Location target * @param target Pattern, Filename, Text, Region, Match or Location * @param direction to move the wheel * @param steps the number of steps * @return 1 if possible, 0 otherwise * @throws FindFailed if the Find operation failed */ public int wheel(PFRML target, int direction, int steps) throws FindFailed { return wheel(target, direction, steps, Mouse.WHEEL_STEP_DELAY); } /** * move the mouse pointer to the given target location
and move the wheel the given steps in the given direction: *
Button.WHEEL_DOWN, Button.WHEEL_UP * * @param Pattern, Filename, Text, Region, Match or Location target * @param target Pattern, Filename, Text, Region, Match or Location * @param direction to move the wheel * @param steps the number of steps * @param stepDelay number of miliseconds to wait when incrementing the step value * @return 1 if possible, 0 otherwise * @throws FindFailed if the Find operation failed */ public int wheel(PFRML target, int direction, int steps, int stepDelay) throws FindFailed { Location loc = getLocationFromTarget(target); if (loc != null) { Mouse.use(this); Mouse.keep(this); Mouse.move(loc, this); Mouse.wheel(direction, steps, this, stepDelay); Mouse.let(this); return 1; } return 0; } /** * * @return current location of mouse pointer * @deprecated use {@link Mouse#at()} instead */ @Deprecated public static Location atMouse() { return Mouse.at(); } //
// /** * press and hold the given key use a constant from java.awt.event.KeyEvent which might be special in the current * machine/system environment * * @param keycode Java KeyCode */ public void keyDown(int keycode) { getRobotForRegion().keyDown(keycode); } /** * press and hold the given keys including modifier keys
use the key constants defined in class Key,
which * only provides a subset of a US-QWERTY PC keyboard layout
might be mixed with simple characters *
use + to concatenate Key constants * * @param keys valid keys */ public void keyDown(String keys) { getRobotForRegion().keyDown(keys); } /** * release all currently pressed keys */ public void keyUp() { getRobotForRegion().keyUp(); } /** * release the given keys (see keyDown(keycode) ) * * @param keycode Java KeyCode */ public void keyUp(int keycode) { getRobotForRegion().keyUp(keycode); } /** * release the given keys (see keyDown(keys) ) * * @param keys valid keys */ public void keyUp(String keys) { getRobotForRegion().keyUp(keys); } /** * Compact alternative for type() with more options
* - special keys and options are coded as #XN. or #X+ or #X-
* where X is a refrence for a special key and N is an optional repeat factor
* A modifier key as #X. modifies the next following key
* the trailing . ends the special key, the + (press and hold) or - (release) does the same,
* but signals press-and-hold or release additionally.
* except #W / #w all special keys are not case-sensitive
* a #wn. inserts a wait of n millisecs or n secs if n less than 60
* a #Wn. sets the type delay for the following keys (must be > 60 and denotes millisecs) - otherwise taken as * normal wait
* Example: wait 2 secs then type CMD/CTRL - N then wait 1 sec then type DOWN 3 times
* Windows/Linux: write("#w2.#C.n#W1.#d3.")
* Mac: write("#w2.#M.n#W1.#D3.")
* for more details about the special key codes and examples consult the docs
* * @param text a coded text interpreted as a series of key actions (press/hold/release) * @return 0 for success 1 otherwise */ public int write(String text) { Debug.info("Write: " + text); char c; String token, tokenSave; String modifier = ""; int k; IRobot robot = getRobotForRegion(); int pause = 20 + (Settings.TypeDelay > 1 ? 1000 : (int) (Settings.TypeDelay * 1000)); Settings.TypeDelay = 0.0; robot.typeStarts(); for (int i = 0; i < text.length(); i++) { log(lvl + 1, "write: (%d) %s", i, text.substring(i)); c = text.charAt(i); token = null; boolean isModifier = false; if (c == '#') { if (text.charAt(i + 1) == '#') { log(lvl, "write at: %d: %s", i, c); i += 1; continue; } if (text.charAt(i + 2) == '+' || text.charAt(i + 2) == '-') { token = text.substring(i, i + 3); isModifier = true; } else if (-1 < (k = text.indexOf('.', i))) { if (k > -1) { token = text.substring(i, k + 1); if (token.length() > Key.keyMaxLength || token.substring(1).contains("#")) { token = null; } } } } Integer key = -1; if (token == null) { log(lvl + 1, "write: %d: %s", i, c); } else { log(lvl + 1, "write: token at %d: %s", i, token); int repeat = 0; if (token.toUpperCase().startsWith("#W")) { if (token.length() > 3) { i += token.length() - 1; int t = 0; try { t = Integer.parseInt(token.substring(2, token.length() - 1)); } catch (NumberFormatException ex) { } if ((token.startsWith("#w") && t > 60)) { pause = 20 + (t > 1000 ? 1000 : t); log(lvl + 1, "write: type delay: " + t); } else { log(lvl + 1, "write: wait: " + t); robot.delay((t < 60 ? t * 1000 : t)); } continue; } } tokenSave = token; token = token.substring(0, 2).toUpperCase() + "."; if (Key.isRepeatable(token)) { try { repeat = Integer.parseInt(tokenSave.substring(2, tokenSave.length() - 1)); } catch (NumberFormatException ex) { token = tokenSave; } } else if (tokenSave.length() == 3 && Key.isModifier(tokenSave.toUpperCase())) { i += tokenSave.length() - 1; modifier += tokenSave.substring(1, 2).toUpperCase(); continue; } else { token = tokenSave; } if (-1 < (key = Key.toJavaKeyCodeFromText(token))) { if (repeat > 0) { log(lvl + 1, "write: %s Repeating: %d", token, repeat); } else { log(lvl + 1, "write: %s", tokenSave); repeat = 1; } i += tokenSave.length() - 1; if (isModifier) { if (tokenSave.endsWith("+")) { robot.keyDown(key); } else { robot.keyUp(key); } continue; } if (repeat > 1) { for (int n = 0; n < repeat; n++) { robot.typeKey(key.intValue()); } continue; } } } if (!modifier.isEmpty()) { log(lvl + 1, "write: modifier + " + modifier); for (int n = 0; n < modifier.length(); n++) { robot.keyDown(Key.toJavaKeyCodeFromText(String.format("#%s.", modifier.substring(n, n + 1)))); } } if (key > -1) { robot.typeKey(key.intValue()); } else { robot.typeChar(c, IRobot.KeyMode.PRESS_RELEASE); } if (!modifier.isEmpty()) { log(lvl + 1, "write: modifier - " + modifier); for (int n = 0; n < modifier.length(); n++) { robot.keyUp(Key.toJavaKeyCodeFromText(String.format("#%s.", modifier.substring(n, n + 1)))); } } robot.delay(pause); modifier = ""; } robot.typeEnds(); robot.waitForIdle(); return 0; } /** * enters the given text one character/key after another using keyDown/keyUp *
about the usable Key constants see keyDown(keys)
Class Key only provides a subset of a US-QWERTY PC * keyboard layout
the text is entered at the current position of the focus/carret * * @param text containing characters and/or Key constants * @return 1 if possible, 0 otherwise */ public int type(String text) { try { return keyin(null, text, 0); } catch (FindFailed ex) { return 0; } } /** * enters the given text one character/key after another using keyDown/keyUp
while holding down the given modifier * keys
about the usable Key constants see keyDown(keys)
Class Key only provides a subset of a US-QWERTY PC * keyboard layout
the text is entered at the current position of the focus/carret * * @param text containing characters and/or Key constants * @param modifiers constants according to class KeyModifiers * @return 1 if possible, 0 otherwise */ public int type(String text, int modifiers) { try { return keyin(null, text, modifiers); } catch (FindFailed findFailed) { return 0; } } /** * enters the given text one character/key after another using * * keyDown/keyUp
while holding down the given modifier keys
about the usable Key constants see keyDown(keys) *
Class Key only provides a subset of a US-QWERTY PC keyboard layout
the text is entered at the current * position of the focus/carret * * * @param text containing characters and/or Key constants * @param modifiers constants according to class Key - combine using + * @return 1 if possible, 0 otherwise */ public int type(String text, String modifiers) { String target = null; int modifiersNew = Key.convertModifiers(modifiers); if (modifiersNew == 0) { target = text; text = modifiers; } try { return keyin(target, text, modifiersNew); } catch (FindFailed findFailed) { return 0; } } /** * first does a click(target) at the given target position to gain focus/carret
enters the given text one * character/key after another using keyDown/keyUp
about the usable Key constants see keyDown(keys) *
Class Key only provides a subset of a US-QWERTY PC keyboard layout * * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @param text containing characters and/or Key constants * @return 1 if possible, 0 otherwise * @throws FindFailed if not found */ public int type(PFRML target, String text) throws FindFailed { return keyin(target, text, 0); } /** * first does a click(target) at the given target position to gain focus/carret
enters the given text one * character/key after another using keyDown/keyUp
while holding down the given modifier keys
about the usable * Key constants see keyDown(keys)
Class Key only provides a subset of a US-QWERTY PC keyboard layout * * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @param text containing characters and/or Key constants * @param modifiers constants according to class KeyModifiers * @return 1 if possible, 0 otherwise * @throws FindFailed if not found */ public int type(PFRML target, String text, int modifiers) throws FindFailed { return keyin(target, text, modifiers); } /** * first does a click(target) at the given target position to gain focus/carret
enters the given text one * character/key after another using keyDown/keyUp
while holding down the given modifier keys
about the usable * Key constants see keyDown(keys)
Class Key only provides a subset of a US-QWERTY PC keyboard layout * * @param Pattern, Filename, Text, Region, Match or Location * @param target Pattern, Filename, Text, Region, Match or Location * @param text containing characters and/or Key constants * @param modifiers constants according to class Key - combine using + * @return 1 if possible, 0 otherwise * @throws FindFailed if not found */ public int type(PFRML target, String text, String modifiers) throws FindFailed { int modifiersNew = Key.convertModifiers(modifiers); return keyin(target, text, modifiersNew); } private int keyin(PFRML target, String text, int modifiers) throws FindFailed { if (target != null && 0 == click(target, 0)) { return 0; } Debug profiler = Debug.startTimer("Region.type"); if (text != null && !"".equals(text)) { String showText = ""; for (int i = 0; i < text.length(); i++) { showText += Key.toJavaKeyCodeText(text.charAt(i)); } String modText = ""; String modWindows = null; if ((modifiers & KeyModifier.WIN) != 0) { modifiers -= KeyModifier.WIN; modifiers |= KeyModifier.META; log(lvl, "Key.WIN as modifier"); modWindows = "Windows"; } if (modifiers != 0) { modText = String.format("( %s ) ", KeyEvent.getKeyModifiersText(modifiers)); if (modWindows != null) { modText = modText.replace("Meta", modWindows); } } Debug.action("%s TYPE \"%s\"", modText, showText); log(lvl, "%s TYPE \"%s\"", modText, showText); profiler.lap("before getting Robot"); IRobot r = getRobotForRegion(); int pause = 20 + (Settings.TypeDelay > 1 ? 1000 : (int) (Settings.TypeDelay * 1000)); Settings.TypeDelay = 0.0; profiler.lap("before typing"); r.typeStarts(); for (int i = 0; i < text.length(); i++) { r.pressModifiers(modifiers); r.typeChar(text.charAt(i), IRobot.KeyMode.PRESS_RELEASE); r.releaseModifiers(modifiers); r.delay(pause); } r.typeEnds(); profiler.lap("after typing, before waitForIdle"); r.waitForIdle(); profiler.end(); return 1; } return 0; } /** * time in milliseconds to delay between each character at next type only (max 1000) * * @param millisecs value */ public void delayType(int millisecs) { Settings.TypeDelay = millisecs; } /** * pastes the text at the current position of the focus/carret
using the clipboard and strg/ctrl/cmd-v (paste * keyboard shortcut) * * @param text a string, which might contain unicode characters * @return 0 if possible, 1 otherwise */ public int paste(String text) { try { return paste(null, text); } catch (FindFailed ex) { return 1; } } /** * first does a click(target) at the given target position to gain focus/carret
and then pastes the text
* using the clipboard and strg/ctrl/cmd-v (paste keyboard shortcut) * * @param Pattern, Filename, Text, Region, Match or Location target * @param target Pattern, Filename, Text, Region, Match or Location * @param text a string, which might contain unicode characters * @return 0 if possible, 1 otherwise * @throws FindFailed if not found */ public int paste(PFRML target, String text) throws FindFailed { if (target != null) { click(target, 0); } if (text != null) { App.setClipboard(text); int mod = Key.getHotkeyModifier(); IRobot r = getRobotForRegion(); r.keyDown(mod); r.keyDown(KeyEvent.VK_V); r.keyUp(KeyEvent.VK_V); r.keyUp(mod); return 0; } return 1; } //
// private ADBDevice adbDevice = null; private ADBScreen adbScreen = null; private boolean isAndroid() { if (isOtherScreen()) { IScreen scr = getScreen(); if (scr instanceof ADBScreen) { adbScreen = (ADBScreen) scr; adbDevice = adbScreen.getDevice(); return true; } } return false; } /** * EXPERIMENTAL: for Android over ADB * * @param Pattern, String, Image, Match, Region or Location * @param target PFRML * @throws FindFailed image not found */ public void aTap(PFRML target) throws FindFailed { if (isAndroid() && adbDevice != null) { Location loc = getLocationFromTarget(target); if (loc != null) { adbDevice.tap(loc.x, loc.y); RunTime.pause(adbScreen.waitAfterAction); } } } /** * EXPERIMENTAL: for Android over ADB * * @param text text */ public void aInput(String text) { if (isAndroid() && adbDevice != null) { adbDevice.input(text); } } /** * EXPERIMENTAL: for Android over ADB * * @param key key */ public void aKey(int key) { if (isAndroid() && adbDevice != null) { adbDevice.inputKeyEvent(key); } } /** * EXPERIMENTAL: for Android over ADB * * @param Pattern, String, Image, Match, Region or Location * @param from PFRML * @param to PFRML * @throws FindFailed image not found */ public void aSwipe(PFRML from, PFRML to) throws FindFailed { if (isAndroid() && adbDevice != null) { Location locFrom = getLocationFromTarget(from); Location locTo = getLocationFromTarget(to); if (locFrom != null && locTo != null) { adbDevice.swipe(locFrom.x, locFrom.y, locTo.x, locTo.y); RunTime.pause(adbScreen.waitAfterAction); } } } /** * EXPERIMENTAL: for Android over ADB */ public void aSwipeUp() { int midX = (int) (w/2); int swipeStep = (int) (h/5); try { aSwipe(new Location(midX, h - swipeStep), new Location(midX, swipeStep)); } catch (FindFailed findFailed) { } } /** * EXPERIMENTAL: for Android over ADB */ public void aSwipeDown() { int midX = (int) (w/2); int swipeStep = (int) (h/5); try { aSwipe(new Location(midX, swipeStep), new Location(midX, h - swipeStep)); } catch (FindFailed findFailed) { } } /** * EXPERIMENTAL: for Android over ADB */ public void aSwipeLeft() { int midY = (int) (h/2); int swipeStep = (int) (w/5); try { aSwipe(new Location(w - swipeStep, midY), new Location(swipeStep, midY)); } catch (FindFailed findFailed) { } } /** * EXPERIMENTAL: for Android over ADB */ public void aSwipeRight() { int midY = (int) (h/2); int swipeStep = (int) (w/5); try { aSwipe(new Location(swipeStep, midY), new Location(w - swipeStep, midY)); } catch (FindFailed findFailed) { } } // // /** * STILL EXPERIMENTAL: tries to read the text in this region
might contain misread characters, NL characters and * other stuff, when interpreting contained grafics as text
* Best results: one line of text with no grafics in the line * * @return the text read (utf8 encoded) */ public String text() { if (Settings.OcrTextRead) { ScreenImage simg = getScreen().capture(x, y, w, h); TextRecognizer tr = TextRecognizer.getInstance(); if (tr == null) { Debug.error("text: text recognition is now switched off"); return "--- no text ---"; } String textRead = tr.recognize(simg); log(lvl, "text: #(" + textRead + ")#"); return textRead; } Debug.error("text: text recognition is currently switched off"); return "--- no text ---"; } /** * VERY EXPERIMENTAL: returns a list of matches, that represent single words, that have been found in this region
* the match's x,y,w,h the region of the word
Match.getText() returns the word (utf8) at this match
* Match.getScore() returns a value between 0 ... 1, that represents some OCR-confidence value
(the higher, the * better the OCR engine thinks the result is) * * @return a list of matches */ public List listText() { if (Settings.OcrTextRead) { ScreenImage simg = getScreen().capture(x, y, w, h); TextRecognizer tr = TextRecognizer.getInstance(); if (tr == null) { Debug.error("text: text recognition is now switched off"); return null; } log(lvl, "listText: scanning %s", this); return tr.listText(simg, this); } Debug.error("text: text recognition is currently switched off"); return null; } //
} sikulix-1.1.1/API/src/main/java/org/sikuli/script/RobotDesktop.java000066400000000000000000000265371315726130400251430ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Animator; import org.sikuli.basics.AnimatorOutQuarticEase; import org.sikuli.basics.AnimatorTimeBased; import org.sikuli.basics.Settings; import org.sikuli.basics.Debug; import java.awt.AWTException; import java.awt.Color; import java.awt.MouseInfo; import java.awt.Point; import java.awt.PointerInfo; import java.awt.Rectangle; import java.awt.Robot; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Date; /** * INTERNAL USE Implementation of IRobot making a DesktopRobot using java.awt.Robot */ public class RobotDesktop extends Robot implements IRobot { final static int MAX_DELAY = 60000; private static int heldButtons = 0; private static String heldKeys = ""; private static final ArrayList heldKeyCodes = new ArrayList(); public static int stdAutoDelay = 0; public static int stdDelay = 10; public static int stdMaxElapsed = 1000; private Screen scr = null; private static RunTime runTime = RunTime.get(); private long start; private static boolean alwaysNewRobot = false; private static boolean isMouseInitialized = false; private void logRobot(int delay, String msg) { start = new Date().getTime(); int theDelay = getAutoDelay(); if (theDelay > 0 && theDelay > delay) { Debug.log(0, msg, isAutoWaitForIdle(), theDelay); } } private void logRobot(String msg, int maxElapsed) { long elapsed = new Date().getTime() - start; if (elapsed > maxElapsed) { Debug.log(0, msg, elapsed); setAutoDelay(stdAutoDelay); setAutoWaitForIdle(false); } } private void doMouseMove(int x, int y) { mouseMove(x, y); } private void doMouseDown(int buttons) { if (Settings.RobotFake && runTime.needsRobotFake()) { Screen.getFakeRegion().silentHighlight(true); } logRobot(stdAutoDelay, "MouseDown: WaitForIdle: %s - Delay: %d"); setAutoDelay(stdAutoDelay); setAutoWaitForIdle(Settings.ClickFast); if (Settings.RobotFake && runTime.needsRobotFake()) { delay(20); Screen.getFakeRegion().silentHighlight(false); delay(20); } mousePress(buttons); setAutoWaitForIdle(false); if (!Settings.ClickFast && stdAutoDelay == 0) { delay(stdDelay); } logRobot("MouseDown: extended delay: %d", stdMaxElapsed); } private void doMouseUp(int buttons) { logRobot(stdAutoDelay, "MouseUp: WaitForIdle: %s - Delay: %d"); setAutoDelay(stdAutoDelay); setAutoWaitForIdle(Settings.ClickFast); mouseRelease(buttons); setAutoWaitForIdle(false); if (!Settings.ClickFast && stdAutoDelay == 0) { delay(stdDelay); } logRobot("MouseUp: extended delay: %d", stdMaxElapsed); } private void doKeyPress(int keyCode) { logRobot(stdAutoDelay, "KeyPress: WaitForIdle: %s - Delay: %d"); setAutoDelay(stdAutoDelay); setAutoWaitForIdle(false); keyPress(keyCode); if (stdAutoDelay == 0) { delay(stdDelay); } logRobot("KeyPress: extended delay: %d", stdMaxElapsed); } private void doKeyRelease(int keyCode) { logRobot(stdAutoDelay, "KeyRelease: WaitForIdle: %s - Delay: %d"); setAutoDelay(stdAutoDelay); setAutoWaitForIdle(false); keyRelease(keyCode); if (stdAutoDelay == 0) { delay(stdDelay); } logRobot("KeyRelease: extended delay: %d", stdMaxElapsed); } private Robot getRobot() { return null; } @Override public boolean isRemote() { return false; } @Override public Screen getScreen() { return scr; } public RobotDesktop(Screen screen) throws AWTException { super(runTime.getGraphicsDevice(screen.getcurrentID())); scr = screen; } public RobotDesktop() throws AWTException { super(); setAutoDelay(stdAutoDelay); setAutoWaitForIdle(false); } @Override public void smoothMove(Location dest) { smoothMove(Mouse.at(), dest, (long) (Settings.MoveMouseDelay * 1000L)); } @Override public void smoothMove(Location src, Location dest, long ms) { Debug.log(4, "RobotDesktop: smoothMove (%.1f): " + src.toString() + "---" + dest.toString(), ms/1000f); if (ms == 0) { doMouseMove(dest.x, dest.y); waitForIdle(); checkMousePosition(dest); return; } Animator aniX = new AnimatorTimeBased( new AnimatorOutQuarticEase(src.x, dest.x, ms)); Animator aniY = new AnimatorTimeBased( new AnimatorOutQuarticEase(src.y, dest.y, ms)); float x = 0, y = 0; while (aniX.running()) { x = aniX.step(); y = aniY.step(); doMouseMove((int) x, (int) y); } checkMousePosition(new Location((int) x, (int) y)); } private void checkMousePosition(Location p) { PointerInfo mp = MouseInfo.getPointerInfo(); Point pc; if (mp == null) { Debug.error("RobotDesktop: checkMousePosition: MouseInfo.getPointerInfo invalid\nafter move to %s", p); } else { pc = mp.getLocation(); if (pc.x != p.x || pc.y != p.y) { if (isMouseInitialized) { Debug.error("RobotDesktop: checkMousePosition: should be %s\nbut after move is %s" + "\nPossible cause in case you did not touch the mouse while script was running:\n" + " Mouse actions are blocked generally or by the frontmost application." + (Settings.isWindows() ? "\nYou might try to run the SikuliX stuff as admin." : ""), p, new Location(pc)); } } } if (!isMouseInitialized) { isMouseInitialized = true; } } @Override public void mouseDown(int buttons) { if (heldButtons != 0) { heldButtons |= buttons; } else { heldButtons = buttons; } doMouseDown(heldButtons); } @Override public int mouseUp(int buttons) { if (buttons == 0) { doMouseUp(heldButtons); heldButtons = 0; } else { doMouseUp(buttons); heldButtons &= ~buttons; } return heldButtons; } @Override public void mouseReset() { if (heldButtons != 0) { setAutoWaitForIdle(false); mouseRelease(heldButtons); heldButtons = 0; } } @Override public void clickStarts() { } @Override public void clickEnds() { } @Override public void delay(int ms) { if (ms < 0) { return; } while (ms > MAX_DELAY) { super.delay(MAX_DELAY); ms -= MAX_DELAY; } super.delay(ms); } @Override public ScreenImage captureScreen(Rectangle rect) { // Rectangle s = scr.getBounds(); Rectangle cRect = new Rectangle(rect); // cRect.translate(-s.x, -s.y); BufferedImage img = createScreenCapture(rect); Debug.log(4, "RobotDesktop: captureScreen: [%d,%d, %dx%d]", rect.x, rect.y, rect.width, rect.height); return new ScreenImage(rect, img); } @Override public Color getColorAt(int x, int y) { return getPixelColor(x, y); } @Override public void pressModifiers(int modifiers) { if ((modifiers & KeyModifier.SHIFT) != 0) { doKeyPress(KeyEvent.VK_SHIFT); } if ((modifiers & KeyModifier.CTRL) != 0) { doKeyPress(KeyEvent.VK_CONTROL); } if ((modifiers & KeyModifier.ALT) != 0) { doKeyPress(KeyEvent.VK_ALT); } if ((modifiers & KeyModifier.META) != 0) { if (Settings.isWindows()) { doKeyPress(KeyEvent.VK_WINDOWS); } else { doKeyPress(KeyEvent.VK_META); } } } @Override public void releaseModifiers(int modifiers) { if ((modifiers & KeyModifier.SHIFT) != 0) { doKeyRelease(KeyEvent.VK_SHIFT); } if ((modifiers & KeyModifier.CTRL) != 0) { doKeyRelease(KeyEvent.VK_CONTROL); } if ((modifiers & KeyModifier.ALT) != 0) { doKeyRelease(KeyEvent.VK_ALT); } if ((modifiers & KeyModifier.META) != 0) { if (Settings.isWindows()) { doKeyRelease(KeyEvent.VK_WINDOWS); } else { doKeyRelease(KeyEvent.VK_META); } } } @Override public void keyDown(String keys) { if (keys != null && !"".equals(keys)) { for (int i = 0; i < keys.length(); i++) { if (heldKeys.indexOf(keys.charAt(i)) == -1) { Debug.log(4, "press: " + keys.charAt(i)); typeChar(keys.charAt(i), IRobot.KeyMode.PRESS_ONLY); heldKeys += keys.charAt(i); } } } } @Override public void keyDown(int code) { if (!heldKeyCodes.contains(code)) { doKeyPress(code); heldKeyCodes.add(code); } } @Override public void keyUp(String keys) { if (keys != null && !"".equals(keys)) { for (int i = 0; i < keys.length(); i++) { int pos; if ((pos = heldKeys.indexOf(keys.charAt(i))) != -1) { Debug.log(4, "release: " + keys.charAt(i)); typeChar(keys.charAt(i), IRobot.KeyMode.RELEASE_ONLY); heldKeys = heldKeys.substring(0, pos) + heldKeys.substring(pos + 1); } } } } @Override public void keyUp(int code) { if (heldKeyCodes.contains(code)) { doKeyRelease(code); heldKeyCodes.remove((Object) code); } } @Override public void keyUp() { keyUp(heldKeys); for (int code : heldKeyCodes) { keyUp(code); } } private void doType(KeyMode mode, int... keyCodes) { waitForIdle(); if (mode == KeyMode.PRESS_ONLY) { for (int i = 0; i < keyCodes.length; i++) { doKeyPress(keyCodes[i]); } } else if (mode == KeyMode.RELEASE_ONLY) { for (int i = 0; i < keyCodes.length; i++) { doKeyRelease(keyCodes[i]); } } else { for (int i = 0; i < keyCodes.length; i++) { doKeyPress(keyCodes[i]); } for (int i = 0; i < keyCodes.length; i++) { doKeyRelease(keyCodes[i]); } } waitForIdle(); } @Override public void typeChar(char character, KeyMode mode) { Debug.log(4, "Robot: doType: %s ( %d )", KeyEvent.getKeyText(Key.toJavaKeyCode(character)[0]).toString(), Key.toJavaKeyCode(character)[0]); doType(mode, Key.toJavaKeyCode(character)); } @Override public void typeKey(int key) { Debug.log(4, "Robot: doType: %s ( %d )", KeyEvent.getKeyText(key), key); if (Settings.isMac()) { if (key == Key.toJavaKeyCodeFromText("#N.")) { doType(KeyMode.PRESS_ONLY, Key.toJavaKeyCodeFromText("#C.")); doType(KeyMode.PRESS_RELEASE, key); doType(KeyMode.RELEASE_ONLY, Key.toJavaKeyCodeFromText("#C.")); return; } else if (key == Key.toJavaKeyCodeFromText("#T.")) { doType(KeyMode.PRESS_ONLY, Key.toJavaKeyCodeFromText("#C.")); doType(KeyMode.PRESS_ONLY, Key.toJavaKeyCodeFromText("#A.")); doType(KeyMode.PRESS_RELEASE, key); doType(KeyMode.RELEASE_ONLY, Key.toJavaKeyCodeFromText("#A.")); doType(KeyMode.RELEASE_ONLY, Key.toJavaKeyCodeFromText("#C.")); return; } else if (key == Key.toJavaKeyCodeFromText("#X.")) { key = Key.toJavaKeyCodeFromText("#T."); doType(KeyMode.PRESS_ONLY, Key.toJavaKeyCodeFromText("#A.")); doType(KeyMode.PRESS_RELEASE, key); doType(KeyMode.RELEASE_ONLY, Key.toJavaKeyCodeFromText("#A.")); return; } } doType(KeyMode.PRESS_RELEASE, key); } @Override public void typeStarts() { } @Override public void typeEnds() { } @Override public void cleanup() { } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Run.java000066400000000000000000000106121315726130400232530ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.Scanner; /** * EXPERIMENTAL --- NOT official API
* not in version 2 */ public class Run { private static Scanner in = null; private static PrintWriter out = null; private static Socket socket = null; private static boolean socketValid = false; private static String ip = null; private static int port = -1; private static boolean keepAlive = false; private static boolean disconnecting = false; private static void log(String message, Object... args) { System.out.println(String.format("[debug] Run: " + message, args)); } private static void error(String message, Object... args) { System.out.println(String.format("[error] Run: " + message, args)); } private Run() { } public static void main(String[] args) { String adr = ""; String p = "-1"; if (!init(adr, p)) { error("not possible"); } } public static boolean connect() { keepAlive = true; return init(); } private static Boolean init() { if (socketValid) { if (!close()) { return false; } } return init(ip, "" + port); } private static Boolean init(String adr, String p) { socketValid = true; ip = getAddress(adr); port = getPort(p); if (ip == null || port < 0) { error("target not valid: " + adr + " / " + p); System.exit(1); } try { socket = new Socket(ip, port); } catch (Exception ex) { error("no connection: " + adr + " / " + p); socketValid = false; } try { if (socketValid) { out = new PrintWriter(socket.getOutputStream()); in = new Scanner(socket.getInputStream()); log("connection at: " + socket); } } catch (Exception ex) { error("problem starting connection:\n", ex.getMessage()); socketValid = false; } return socketValid; } public static String getAddress(String adr) { try { if (adr == null || adr.isEmpty()) { return InetAddress.getLocalHost().getHostAddress(); } return InetAddress.getByName(adr).getHostAddress(); } catch (UnknownHostException ex) { return null; } } public static int getPort(String p) { int port; int pDefault = 50001; if (p == null || p.isEmpty()) { return pDefault; } else { try { port = Integer.parseInt(p); } catch (NumberFormatException ex) { return pDefault; } } if (port < 0) { port = pDefault; } if (port < 1024) { port += pDefault; } return port; } public static boolean isValid() { return (socketValid && socket != null); } public static String send(String command) { if (keepAlive) { command = "X" + command; } else { init(); } if (!isValid()) { error("connection not valid - send not possible"); return null; } String res; try { out.println(command); out.flush(); log("send: " + command); res = in.nextLine(); while (in.hasNextLine()) { String line = in.nextLine(); if (line.contains("###+++###")) { break; } res += "\n" + line; } } catch (Exception ex) { error("error while processing:\n" + ex.getMessage()); res = "fail: reason unknown"; } if (!keepAlive && !disconnecting) { close(); } return res; } public static boolean close() { return close(false); } public static boolean stop() { return close(true); } private static boolean close(boolean stopServer) { disconnecting = true; if (stopServer) { send("STOP"); } else if (keepAlive) { send("EXIT"); } if (socket != null) { try { in.close(); out.close(); socket.close(); } catch (IOException ex) { log("error on close: %s\n" + ex.getMessage(), socket); socket = null; } } in = null; out = null; socketValid = false; if (socket == null) { return false; } socket = null; keepAlive = false; return true; } public static String show() { return String.format("%s:%d %s", ip, port, isValid()); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/RunServer.java000066400000000000000000000451161315726130400244510ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.*; import java.util.regex.Matcher; import javax.script.ScriptEngine; import org.sikuli.basics.Debug; /** * EXPERIMENTAL --- NOT official API
* not as is in version 2 */ public class RunServer { private static ServerSocket server = null; private static PrintWriter out = null; private static Scanner in = null; private static boolean isHandling = false; private static boolean shouldStop = false; //TODO set loglevel at runtime private static int logLevel = 0; private static void log(int lvl, String message, Object... args) { if (lvl < 0 || lvl >= logLevel) { System.out.println((lvl < 0 ? "[error] " : "[info] ") + String.format("RunServer: " + message, args)); } } private static void log(String message, Object... args) { log(0, message, args); } private RunServer() { } static File isRunning = null; static FileOutputStream isRunningFile = null; public static boolean run(String[] args) { if (args == null) { args = new String[0]; } String userArgs = ""; for (String userArg : RunTime.get().getArgs()) { userArgs += userArg + " "; } if (!userArgs.isEmpty()) { userArgs = "\nWith User parameters: " + userArgs; } int port = getPort(args.length > 0 ? args[0] : null); try { try { if (port > 0) { log(3, "Starting: trying port: %d %s", port, userArgs); server = new ServerSocket(port); } } catch (Exception ex) { log(-1, "Starting: " + ex.getMessage()); } if (server == null) { log(-1, "could not be started"); return false; } String theIP = InetAddress.getLocalHost().getHostAddress(); String theServer = String.format("%s %d", theIP, port); isRunning = new File(RunTime.get().fSikulixStore, "RunServer.txt"); try { isRunning.createNewFile(); isRunningFile = new FileOutputStream(isRunning); if (null == isRunningFile.getChannel().tryLock()) { log(-1, "Terminating on FatalError: already running"); return false; } isRunningFile.write(theServer.getBytes()); } catch (Exception ex) { log(-1, "Terminating on FatalError: cannot access to lock for/n" + isRunning); return false; } Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { log(3, "final cleanup"); if (isRunning != null) { try { isRunningFile.close(); } catch (IOException ex) { } isRunning.delete(); } } }); while (true) { log("now waiting on port: %d at %s", port, theIP); Socket socket = server.accept(); out = new PrintWriter(socket.getOutputStream()); in = new Scanner(socket.getInputStream()); HandleClient client = new HandleClient(socket); isHandling = true; while (true) { if (socket.isClosed()) { shouldStop = client.getShouldStop(); break; } try { Thread.sleep(1000); } catch (InterruptedException ex) { } } if (shouldStop) { break; } } } catch (Exception e) { } if (!isHandling) { log(-1, "start handling not possible: " + port); return false; } log("now stopped on port: " + port); return true; } private static int getPort(String p) { int port; int pDefault = 50001; if (p != null) { try { port = Integer.parseInt(p); } catch (NumberFormatException ex) { log(-1, "given port not useable: %s --- using default", p); return pDefault; } } else { return pDefault; } if (port < 1024) { port += pDefault; } return port; } static ScriptEngine jsRunner = null; static File scriptFolder = null; static String scriptFolderNet = null; static File imageFolder = null; static String imageFolderNet = null; private static class HandleClient implements Runnable { private volatile boolean keepRunning; private boolean shouldKeep = false; Thread thread; Socket socket; Boolean shouldStop = false; public HandleClient(Socket sock) { init(sock); } private void init(Socket sock) { socket = sock; if (in == null || out == null) { RunServer.log(-1, "communication not established"); System.exit(1); } thread = new Thread(this, "HandleClient"); keepRunning = true; thread.start(); } public boolean getShouldStop() { return shouldStop; } boolean isHTTP = false; String request; String rCommand; String rRessource; String rVersion = "HTTP/1.1"; String rQuery; String[] rArgs; String rMessage = ""; String rStatus; String rStatusOK = "200 OK"; String rStatusBadRequest = "400 Bad Request"; String rStatusNotFound = "404 Not Found"; String rStatusServerError = "500 Internal Server Error"; String rStatusServiceNotAvail = "503 Service Unavailable"; Object evalReturnObject; String runTypeJS = "JavaScript"; String runTypePY = "Python"; String runTypeRB = "Ruby"; String runType = runTypeJS; @Override public void run() { Debug.on(3); RunServer.log("now handling client: " + socket); while (keepRunning) { try { String inLine = in.nextLine(); if (inLine != null) { if (!isHTTP) { RunServer.log("processing: <%s>", inLine); } boolean success = true; if (inLine.startsWith("GET /") && inLine.contains("HTTP/")) { isHTTP = true; request = inLine; continue; } if (isHTTP) { if (!inLine.isEmpty()) { continue; } } if (!isHTTP) { request = "GET /" + inLine + " HTTP/1.1"; } success = checkRequest(request); if (success) { // STOP if (rCommand.contains("STOP")) { rMessage = "stopping server"; shouldStop = true; shouldKeep = false; } else if (rCommand.contains("EXIT")) { rMessage = "stopping client"; shouldKeep = false; // START } else if (rCommand.startsWith("START")) { runType = runTypeJS; if (rCommand.length() > 5) { if ("P".equals(rCommand.substring(5, 6))) { runType = runTypePY; } else if ("R".equals(rCommand.substring(5, 6))) { runType = runTypeRB; } } success = startRunner(runType, null, null); rMessage = "startRunner for: " + runType; if (!success) { rMessage = "startRunner: not possible for: " + runType; rStatus = rStatusServiceNotAvail; } // SCRIPTS } else if (rCommand.startsWith("SCRIPTS")) { if (rRessource.isEmpty()) { rMessage = "no scriptFolder given "; rStatus = rStatusBadRequest; success = false; } else { scriptFolder = getFolder(rRessource); if (scriptFolder.getPath().startsWith("__NET/")) { scriptFolderNet = "http://" + scriptFolder.getPath().substring(6); rMessage = "scriptFolder now: " + scriptFolderNet; } else { scriptFolderNet = null; rMessage = "scriptFolder now: " + scriptFolder.getAbsolutePath(); if (!scriptFolder.exists()) { rMessage = "scriptFolder not found: " + scriptFolder.getAbsolutePath(); rStatus = rStatusNotFound; success = false; } } } // IMAGES } else if (rCommand.startsWith("IMAGES")) { String asImagePath; if (rRessource.isEmpty()) { rMessage = "no imageFolder given "; rStatus = rStatusBadRequest; success = false; } else { imageFolder = getFolder(rRessource); if (imageFolder.getPath().startsWith("__NET/")) { imageFolderNet = "http://" + imageFolder.getPath().substring(6); rMessage = "imageFolder now: " + imageFolderNet; asImagePath = imageFolderNet; } else { String fpGiven = imageFolder.getAbsolutePath(); if (!imageFolder.exists()) { imageFolder = new File(imageFolder.getAbsolutePath() + ".sikuli"); if (!imageFolder.exists()) { rMessage = "imageFolder not found: " + fpGiven; rStatus = rStatusNotFound; success = false; } } asImagePath = imageFolder.getAbsolutePath(); } rMessage = "imageFolder now: " + asImagePath; ImagePath.add(asImagePath); } // RUN } else if (rCommand.startsWith("RUN")) { String script = rRessource; File fScript = null; File fScriptScript = null; if (scriptFolderNet != null) { rMessage = "runScript from net not yet supported"; rStatus = rStatusServiceNotAvail; success = false; } if (success) { Debug.log("Using script folder: " + RunServer.scriptFolder); fScript = new File(RunServer.scriptFolder, script); if (!fScript.exists()) { if (script.endsWith(".sikuli")) { script = script.replace(".sikuli", ""); } else { script = script + ".sikuli"; } fScript = new File(scriptFolder, script); } String scriptScript = script.replace(".sikuli", ""); fScriptScript = new File(fScript, scriptScript + ".js"); success = fScriptScript.exists(); if (!success) { fScriptScript = new File(fScript, scriptScript + ".py"); success = fScript.exists() && fScriptScript.exists(); if (!success) { RunServer.log("Script folder path: " + fScript.getAbsolutePath()); RunServer.log("Script file path: " + fScriptScript.getAbsolutePath()); rMessage = "runScript: script not found, not valid or not supported " + fScriptScript.toString(); } runType = runTypePY; } } if (success) { ImagePath.setBundlePath(fScript.getAbsolutePath()); List args = new ArrayList(); if (this.rQuery != null && this.rQuery.length() > 0) { String[] params = this.rQuery.split("[;&]"); for (String param : params) { String[] pair = param.split("[=]"); if (pair != null && pair.length == 2) { // Needs both a variable name and value, and supports repeated parameters String arg = String.format("--%1$s=%2$s", pair[0], pair[1]); args.add(arg); } } } success = this.startRunner(this.runType, fScript, fScriptScript, args.toArray(new String[0])); } } else if (rCommand.startsWith("EVAL")) { if (jsRunner != null) { String line = rQuery; try { evalReturnObject = jsRunner.eval(line); rMessage = "runStatement: returned: " + (evalReturnObject == null ? "null" : evalReturnObject.toString()); success = true; } catch (Exception ex) { rMessage = "runStatement: raised exception on eval: " + ex.toString(); success = false; } } else { rMessage = "runStatement: not possible --- no runner"; rStatus = rStatusServiceNotAvail; success = false; } } } String retVal = ""; if (isHTTP) { retVal = "HTTP/1.1 " + rStatus; String state = (success ? "PASS " : "FAIL ") + rStatus.substring(0,3) + " "; retVal += "\r\n\r\n" + state + rMessage + "\r"; } else { retVal = (success ? "isok:\n" : "fail:\n") + rMessage + "\n###+++###"; } try { out.println(retVal); out.flush(); RunServer.log("returned:\n" + retVal.replace("###+++###", "")); } catch (Exception ex) { RunServer.log(-1, "write response: Exception:\n" + ex.getMessage()); } stopRunning(); } } catch (Exception ex) { RunServer.log(-1, "while processing: Exception:\n" + ex.getMessage()); shouldKeep = false; stopRunning(); } } try { Thread.sleep(100); } catch (InterruptedException ex) { shouldKeep = false; stopRunning(); } } public void stopRunning() { if (!shouldKeep) { in.close(); out.close(); try { socket.close(); } catch (IOException ex) { RunServer.log(-1, "fatal: socket not closeable"); System.exit(1); } keepRunning = false; } } private File getFolder(String path) { File aFolder = new File(path); Debug.log("Original path: " + aFolder); if (path.toLowerCase().startsWith("/home/")) { path = path.substring(6); aFolder = new File(RunTime.get().fUserDir, path); } else if (path.toLowerCase().startsWith("/net/")) { path = "__NET/" + path.substring(5); aFolder = new File(path); } else if (RunTime.get().runningWindows) { Matcher matcher = java.util.regex.Pattern.compile("(?ix: ^ (?: / ([a-z]) [:]? /) (.*) $)").matcher(path); // Assume specified drive exists or fallback on the default/required drive String newPath = matcher.matches() ? matcher.replaceAll("$1:/$2") : ("c:" + path); aFolder = new File(newPath); } Debug.log("Transformed path: " + aFolder); return aFolder; } private boolean checkRequest(String request) { shouldKeep = false; rCommand = "NOOP"; rMessage = "invalid: " + request; rStatus = rStatusBadRequest; String[] parts = request.split("\\s"); if (parts.length != 3 || !"GET".equals(parts[0]) || !parts[1].startsWith("/")) { return false; } if (!rVersion.equals(parts[2])) { return false; } String cmd = parts[1].substring(1); if (cmd.startsWith("X")) { cmd = cmd.substring(1); shouldKeep = true; } parts = cmd.split("\\?"); cmd = parts[0]; rQuery = ""; if (parts.length > 1) { rQuery = parts[1]; } parts = cmd.split("/"); if (!"START,STARTP,STOP,EXIT,SCRIPTS,IMAGES,RUN,EVAL,".contains((parts[0]+",").toUpperCase())) { rMessage = "invalid command: " + request; return false; } rCommand = parts[0].toUpperCase(); rMessage = ""; rStatus = rStatusOK; rRessource = ""; if (parts.length > 1) { rRessource = cmd.substring(rCommand.length()); } return true; } private boolean startRunner(String runType, File fScript, File fScriptScript) { return this.startRunner(runType, fScript, fScriptScript, new String[0]); } private boolean startRunner(String runType, File fScript, File fScriptScript, String[] args) { if (runTypeJS.equals(runType)) { if (jsRunner == null) { try { jsRunner = Runner.initjs(); String prolog = ""; prolog = Runner.prologjs(prolog); prolog = Runner.prologjs(prolog); jsRunner.eval(prolog); } catch (Exception ex) { rMessage = "startRunner JavaScript: not possible"; rStatus = rStatusServiceNotAvail; return false; } } if (fScript == null) { return true; } if (jsRunner != null) { try { evalReturnObject = jsRunner.eval(new java.io.FileReader(fScriptScript)); rMessage = "runScript: returned: " + (evalReturnObject == null ? "null" : evalReturnObject.toString()); return evalReturnObject != null; } catch (Exception ex) { rMessage = "runScript: script raised exception on run: " + ex.toString(); return false; } } else { return false; } } else if (runTypePY.equals(runType)) { Integer retval = 0; if (!Runner.initpy()) { retval = -1; } if (fScript != null && retval == 0) { // Arguments are passed to Python in the long format: --name=value evalReturnObject = Runner.run(fScript.getAbsolutePath(), args); try { retval = Integer.parseInt(evalReturnObject.toString()); if (retval == -999) { retval = 0; } } catch (Exception ex) { retval = 0; } } if (retval < 0) { rMessage = "startRunner Python: not possible or crashed with exception"; rStatus = rStatusServiceNotAvail; return false; } if (fScript != null) { rMessage = "runScript: returned: " + retval.toString(); } } return true; } } }sikulix-1.1.1/API/src/main/java/org/sikuli/script/RunTime.java000066400000000000000000002765451315726130400241150ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.basics.Settings; import org.sikuli.util.JythonHelper; import org.sikuli.util.LinuxSupport; import org.sikuli.util.SysJNA; import java.awt.*; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.security.CodeSource; import java.util.*; import java.util.List; import java.util.prefs.Preferences; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * INTERNAL USE --- NOT official API
* not as is in version 2 * * Intended to concentrate all, that is needed at startup of sikulix or sikulixapi and may be at runtime by SikuliX or * any caller */ public class RunTime { public static File scriptProject = null; public static URL uScriptProject = null; public static boolean shouldRunServer = false; private static boolean isTerminating = false; public static void resetProject() { scriptProject = null; uScriptProject = null; } public static String appDataMsg = ""; public static void pause(int time) { try { Thread.sleep(time * 1000); } catch (InterruptedException ex) { } } public static void pause(float time) { try { Thread.sleep((int) (time * 1000)); } catch (InterruptedException ex) { } } protected void abortScripting(String msg1, String msg2) { Thread current = Thread.currentThread(); String where = "unknown"; if (Region.runTime.isJythonReady) { where = JythonHelper.get().getCurrentLine(); } log(-1, msg1 + " %s", where); log(-1, msg2); current.interrupt(); current.stop(); } // private final String me = "RunTime%s: "; private int lvl = 3; private int minLvl = lvl; private static String preLogMessages = ""; public final static String runCmdError = "*****error*****"; public static String NL = "\n"; public boolean runningInteractive = false; public boolean runningTests = false; public String interactiveRunner; public File fLibsProvided; public File fLibsLocal; public boolean useLibsProvided = false; private String lastResult = ""; public boolean shouldCleanDownloads = false; public boolean isJythonReady = false; private boolean shouldExport = false; private void log(int level, String message, Object... args) { Debug.logx(level, String.format(me, runType) + message, args); } private void logp(String message, Object... args) { Debug.logx(-3, message, args); } private void logp(int level, String message, Object... args) { if (level <= Debug.getDebugLevel()) { logp(message, args); } } public void terminate(int retval, String message, Object... args) { log(-1, " *** terminating: " + message, args); System.exit(retval); } // // /** * INTERNAL USE */ private RunTime() { } public static synchronized RunTime get(Type typ) { return get(typ, null); } /** * INTERNAL USE to initialize the runtime environment for SikuliX
* for public use: use RunTime.get() to get the existing instance * * @param typ IDE or API * @return the RunTime singleton instance */ public static synchronized RunTime get(Type typ, String[] clArgs) { if (runTime == null) { runTime = new RunTime(); int debugLevel = 0; if (null != clArgs) { debugLevel = checkArgs(clArgs, typ); if (Type.IDE.equals(typ)) { if (debugLevel == -1) { Debug.on(3); Debug.log(3, "RunTime: option -d detected --- log goes to SikulixLog.txt"); Debug.setLogFile(""); Settings.LogTime = true; System.setProperty("sikuli.console", "false"); } else if (debugLevel == 999) { runTime.runningScripts = true; } else if (debugLevel == -3) { //if (Type.IDE.equals(typ) && "runserver".equals(opt)) { shouldRunServer = true; } } } if (Type.API.equals(typ)) { Debug.init(); } // String vJava = System.getProperty("java.runtime.version"); String vVM = System.getProperty("java.vm.version"); String vClass = System.getProperty("java.class.version"); String vSysArch = System.getProperty("sikuli.arch"); if (null == vSysArch) { vSysArch = System.getProperty("os.arch"); } else { runTime.log(runTime.lvl, "SystemProperty given: sikuli.arch=%s", vSysArch); } if (vSysArch != null) { if (vSysArch.contains("64")) { runTime.javaArch = 64; } } else { runTime.log(runTime.lvl, "Java arch (32 or 64 Bit) not detected nor given - using %d Bit", runTime.javaArch); } try { runTime.javaVersion = Integer.parseInt(vJava.substring(2, 3)); runTime.javaShow = String.format("java %d-%d version %s vm %s class %s arch %s", runTime.javaVersion, runTime.javaArch, vJava, vVM, vClass, vSysArch); } catch (Exception ex) { runTime.log(-1, "Java version not detected (using 7): %s", vJava); runTime.javaVersion = 7; runTime.javaShow = String.format("java ?7?-%d version %s vm %s class %s arch %s", runTime.javaArch, vJava, vVM, vClass, vSysArch); runTime.logp(runTime.javaShow); runTime.dumpSysProps(); } if (Debug.getDebugLevel() > runTime.minLvl) { runTime.dumpSysProps(); } if (!runTime.isJava7()) { runTime.terminate(-1, "Java version must be 1.7 or later!"); } runTime.osVersion = runTime.osVersionSysProp; String os = runTime.osNameSysProp.toLowerCase(); if (os.startsWith("windows")) { runTime.runningOn = theSystem.WIN; runTime.sysName = "windows"; runTime.osName = "Windows"; runTime.runningWindows = true; runTime.NL = "\r\n"; } else if (os.startsWith("mac")) { runTime.runningOn = theSystem.MAC; runTime.sysName = "mac"; runTime.osName = "Mac OSX"; runTime.runningMac = true; } else if (os.startsWith("linux")) { runTime.runningOn = theSystem.LUX; runTime.sysName = "linux"; runTime.osName = "Linux"; runTime.runningLinux = true; String result = runTime.runcmd("lsb_release -i -r -s"); if (result.contains("*** error ***")) { runTime.log(-1, "command returns error: lsb_release -i -r -s\n%s", result); } else { runTime.linuxDistro = result.replaceAll("\n", " ").trim(); } } else { // Presume Unix -- pretend to be Linux runTime.runningOn = theSystem.LUX; runTime.sysName = os; runTime.osName = runTime.osNameSysProp; runTime.runningLinux = true; runTime.linuxDistro = runTime.osNameSysProp; } runTime.fpJarLibs += runTime.sysName + "/libs" + runTime.javaArch; runTime.fpSysLibs = runTime.fpJarLibs.substring(1); String aFolder = System.getProperty("user.home"); if (aFolder == null || aFolder.isEmpty() || !(runTime.fUserDir = new File(aFolder)).exists()) { runTime.terminate(-1, "JavaSystemProperty::user.home not valid"); } aFolder = System.getProperty("user.dir"); if (aFolder == null || aFolder.isEmpty() || !(runTime.fWorkDir = new File(aFolder)).exists()) { runTime.terminate(-1, "JavaSystemProperty::user.dir not valid"); } runTime.fSikulixAppPath = new File("SikulixAppDataNotAvailable"); if (runTime.runningWindows) { appDataMsg = "init: Windows: %APPDATA% not valid (null or empty) or is not accessible:\n%s"; String tmpdir = System.getenv("APPDATA"); if (tmpdir != null && !tmpdir.isEmpty()) { runTime.fAppPath = new File(tmpdir); runTime.fSikulixAppPath = new File(runTime.fAppPath, "Sikulix"); } } else if (runTime.runningMac) { appDataMsg = "init: Mac: SikulxAppData does not exist or is not accessible:\n%s"; runTime.fAppPath = new File(runTime.fUserDir, "Library/Application Support"); runTime.fSikulixAppPath = new File(runTime.fAppPath, "Sikulix"); } else if (runTime.runningLinux) { runTime.fAppPath = runTime.fUserDir; runTime.fSikulixAppPath = new File(runTime.fAppPath, ".Sikulix"); appDataMsg = "init: Linux: SikulxAppData does not exist or is not accessible:\n%s"; } runTime.fSikulixStore = new File(runTime.fSikulixAppPath, "SikulixStore"); runTime.fSikulixStore.mkdirs(); // debugLevelSaved = Debug.getDebugLevel(); debugLogfileSaved = Debug.logfile; File fDebug = new File(runTime.fSikulixStore, "SikulixDebug.txt"); if (fDebug.exists()) { if (Debug.getDebugLevel() == 0) { Debug.setDebugLevel(3); } Debug.setLogFile(fDebug.getAbsolutePath()); if (Type.IDE.equals(typ)) { System.setProperty("sikuli.console", "false"); } runTime.logp("auto-debugging with level %d into:\n%s", Debug.getDebugLevel(), fDebug); } runTime.fTestFolder = new File(runTime.fUserDir, "SikulixTest"); runTime.fTestFile = new File(runTime.fTestFolder, "SikulixTest.txt"); runTime.loadOptions(typ); int dl = runTime.getOptionNumber("Debug.level"); if (dl > 0 && Debug.getDebugLevel() < 2) { Debug.setDebugLevel(dl); } if (Debug.getDebugLevel() == 2) { runTime.dumpOptions(); } if (Type.SETUP.equals(typ) && debugLevel != -2) { Debug.setDebugLevel(3); } Settings.init(); // force Settings initialization runTime.initSikulixOptions(); runTime.init(typ); if (Type.IDE.equals(typ)) { runTime.initIDEbefore(); runTime.initAPI(); runTime.initIDEafter(); } else { runTime.initAPI(); if (Type.SETUP.equals(typ)) { runTime.initSetup(); } } } if (testingWinApp && !runTime.runningWindows) { runTime.terminate(1, "***** for testing winapp: not running on Windows"); } return runTime; } /** * get the initialized RunTime singleton instance * * @return */ public static synchronized RunTime get() { if (runTime == null) { return get(Type.API); } return runTime; } /** * INTERNAL USE get a new initialized RunTime singleton instance * * @return */ public static synchronized RunTime reset(Type typ) { if (runTime != null) { preLogMessages += "RunTime: resetting RunTime instance;"; if (Sikulix.testNumber == 1) { Debug.setDebugLevel(debugLevelSaved); } Debug.setLogFile(debugLogfileSaved); runTime = null; } return get(typ); } /** * INTERNAL USE get a new initialized RunTime singleton instance * * @return */ public static synchronized RunTime reset() { return reset(Type.API); } //
// public enum Type { IDE, API, SETUP, INIT } private enum theSystem { WIN, MAC, LUX, FOO } private static RunTime runTime = null; private static int debugLevelSaved; private static String debugLogfileSaved; public static boolean testing = false; public static boolean testingWinApp = false; public Type runType = Type.INIT; public String sxBuild = ""; public String sxBuildStamp = ""; public String jreVersion = java.lang.System.getProperty("java.runtime.version"); public Preferences optionsIDE = null; public ClassLoader classLoader = RunTime.class.getClassLoader(); public String baseJar = ""; public String userName = ""; public String fpBaseTempPath = ""; private Class clsRef = RunTime.class; private Class clsRefBase = clsRef; private List classPath = new ArrayList(); public File fTempPath = null; public File fBaseTempPath = null; public File fLibsFolder = null; public String fpJarLibs = "/sikulixlibs/"; public String fpSysLibs = null; boolean areLibsExported = false; private Map libsLoaded = new HashMap(); public File fUserDir = null; public File fWorkDir = null; public File fTestFolder = null; public File fTestFile = null; public File fAppPath = null; public File fSikulixAppPath = null; public File fSikulixExtensions = null; public String[] standardExtensions = new String[]{"selenium4sikulix"}; public File fSikulixLib = null; public File fSikulixStore; public File fSikulixDownloadsGeneric = null; public File fSikulixDownloadsBuild = null; public File fSikulixSetup; private File fOptions = null; private Properties options = null; private String fnOptions = "SikulixOptions.txt"; private String fnPrefs = "SikulixPreferences.txt"; public File fSxBase = null; public File fSxBaseJar = null; public File fSxProject = null; public File fSxProjectTestScriptsJS = null; public File fSxProjectTestScripts = null; public String fpContent = "sikulixcontent"; public boolean runningJar = true; public boolean runningInProject = false; public boolean runningWindows = false; public boolean runningMac = false; public boolean runningLinux = false; public boolean runningWinApp = false; public boolean runningMacApp = false; private theSystem runningOn = theSystem.FOO; private final String osNameSysProp = System.getProperty("os.name"); private final String osVersionSysProp = System.getProperty("os.version"); public String javaShow = "not-set"; public int javaArch = 32; public int javaVersion = 0; public String javahome = FileManager.slashify(System.getProperty("java.home"), false); public String osName = "NotKnown"; public String sysName = "NotKnown"; public String osVersion = ""; private String appType = null; public int debuglevelAPI = -1; private boolean runningScripts = false; public String linuxDistro = "???LINUX???"; public String linuxNeededLibs = ""; // GraphicsEnvironment genv = null; GraphicsDevice[] gdevs; public Rectangle[] monitorBounds = null; Rectangle rAllMonitors; int mainMonitor = -1; int nMonitors = 0; Point pNull = new Point(0, 0); // private void init(Type typ) { // if ("winapp".equals(getOption("testing"))) { log(lvl, "***** for testing: simulating WinApp"); testingWinApp = true; } for (String line : preLogMessages.split(";")) { if (!line.isEmpty()) { log(lvl, line); } } log(lvl, "global init: entering as: %s", typ); sxBuild = SikuliVersionBuild; sxBuildStamp = sxBuild.replace("_", "").replace("-", "").replace(":", "").substring(0, 12); if (System.getProperty("user.name") != null) { userName = System.getProperty("user.name"); } if (userName.isEmpty()) { userName = "unknown"; } String tmpdir = System.getProperty("java.io.tmpdir"); if (tmpdir != null && !tmpdir.isEmpty()) { fTempPath = new File(tmpdir); } else { terminate(1, "init: java.io.tmpdir not valid (null or empty"); } fBaseTempPath = new File(fTempPath, String.format("Sikulix_%d", FileManager.getRandomInt())); fpBaseTempPath = fBaseTempPath.getAbsolutePath(); fBaseTempPath.mkdirs(); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { isTerminating = true; log(lvl, "final cleanup"); if (isRunning != null) { try { isRunningFile.close(); } catch (IOException ex) { } isRunning.delete(); } if (shouldCleanDownloads) { FileManager.deleteFileOrFolder(fSikulixDownloadsBuild); } for (File f : fTempPath.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { File aFile = new File(dir, name); boolean isObsolete = false; long lastTime = aFile.lastModified(); if (lastTime == 0) { return false; } if (lastTime < ((new Date().getTime()) - 7 * 24 * 60 * 60 * 1000)) { isObsolete = true; } if (name.contains("BridJExtractedLibraries") && isObsolete) { return true; } if (name.toLowerCase().contains("sikuli")) { if (name.contains("Sikulix_")) { if (isObsolete || aFile.equals(fBaseTempPath)) { return true; } } else { return true; } } return false; } })) { Debug.log(4, "cleanTemp: " + f.getName()); FileManager.deleteFileOrFolder(f.getAbsolutePath()); } } }); if (Type.IDE.equals(typ) && !runningScripts) { isRunning = new File(fTempPath, isRunningFilename); boolean shouldTerminate = false; try { isRunning.createNewFile(); isRunningFile = new FileOutputStream(isRunning); if (null == isRunningFile.getChannel().tryLock()) { Sikulix.popError("Terminating: IDE already running"); shouldTerminate = true; } } catch (Exception ex) { Sikulix.popError("Terminating on FatalError: cannot access IDE lock for/n" + isRunning); shouldTerminate = true; } if (shouldTerminate) { System.exit(1); } } for (String aFile : fTempPath.list()) { if ((aFile.startsWith("Sikulix") && (new File(aFile).isFile())) || (aFile.startsWith("jffi") && aFile.endsWith(".tmp"))) { FileManager.deleteFileOrFolder(new File(fTempPath, aFile)); } } try { if (!fSikulixAppPath.exists()) { fSikulixAppPath.mkdirs(); } if (!fSikulixAppPath.exists()) { terminate(1, appDataMsg, fSikulixAppPath); } fSikulixExtensions = new File(fSikulixAppPath, "Extensions"); fSikulixLib = new File(fSikulixAppPath, "Lib"); fSikulixDownloadsGeneric = new File(fSikulixAppPath, "SikulixDownloads"); fSikulixSetup = new File(fSikulixAppPath, "SikulixSetup"); fLibsProvided = new File(fSikulixAppPath, fpSysLibs); fLibsLocal = fLibsProvided.getParentFile().getParentFile(); fSikulixExtensions.mkdir(); fSikulixDownloadsGeneric.mkdir(); } catch (Exception ex) { terminate(1, appDataMsg + "\n" + ex.toString(), fSikulixAppPath); } // // if (!isHeadless()) { genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); gdevs = genv.getScreenDevices(); nMonitors = gdevs.length; if (nMonitors == 0) { terminate(1, "GraphicsEnvironment has no ScreenDevices"); } monitorBounds = new Rectangle[nMonitors]; rAllMonitors = null; Rectangle currentBounds; for (int i = 0; i < nMonitors; i++) { currentBounds = gdevs[i].getDefaultConfiguration().getBounds(); if (null != rAllMonitors) { rAllMonitors = rAllMonitors.union(currentBounds); } else { rAllMonitors = currentBounds; } if (currentBounds.contains(pNull)) { if (mainMonitor < 0) { mainMonitor = i; log(lvl, "ScreenDevice %d has (0,0) --- will be primary Screen(0)", i); } else { log(lvl, "ScreenDevice %d too contains (0,0)!", i); } } log(lvl, "Monitor %d: (%d, %d) %d x %d", i, currentBounds.x, currentBounds.y, currentBounds.width, currentBounds.height); monitorBounds[i] = currentBounds; } if (mainMonitor < 0) { log(lvl, "No ScreenDevice has (0,0) --- using 0 as primary: %s", monitorBounds[0]); mainMonitor = 0; } } else { log(lvl, "running in headless environment"); } // // try { if (Type.IDE.equals(typ)) { clsRef = Class.forName("org.sikuli.ide.SikuliIDE"); } else if (Type.SETUP.equals(typ)) { clsRef = Class.forName("org.sikuli.setup.RunSetup"); } } catch (Exception ex) { } CodeSource codeSrc = clsRef.getProtectionDomain().getCodeSource(); String base = null; if (codeSrc != null && codeSrc.getLocation() != null) { base = FileManager.slashify(codeSrc.getLocation().getPath(), false); } appType = "from a jar"; if (base != null) { fSxBaseJar = new File(base); String jn = fSxBaseJar.getName(); fSxBase = fSxBaseJar.getParentFile(); log(lvl, "runs as %s in: %s", jn, fSxBase.getAbsolutePath()); if (jn.contains("classes")) { runningJar = false; fSxProject = fSxBase.getParentFile().getParentFile(); log(lvl, "not jar - supposing Maven project: %s", fSxProject); appType = "in Maven project from classes"; runningInProject = true; } else if ("target".equals(fSxBase.getName())) { fSxProject = fSxBase.getParentFile().getParentFile(); log(lvl, "folder target detected - supposing Maven project: %s", fSxProject); appType = "in Maven project from some jar"; runningInProject = true; } else { if (runningWindows) { if (jn.endsWith(".exe")) { runningWinApp = true; runningJar = false; appType = "as application .exe"; } } else if (runningMac) { if (fSxBase.getAbsolutePath().contains("SikuliX.app/Content")) { runningMacApp = true; appType = "as application .app"; if (!fSxBase.getAbsolutePath().startsWith("/Applications")) { appType += " (not from /Applications folder)"; } } } } } else { terminate(1, "no valid Java context for SikuliX available " + "(java.security.CodeSource.getLocation() is null)"); } if (runningInProject) { fSxProjectTestScriptsJS = new File(fSxProject, "StuffContainer/testScripts/testJavaScript"); fSxProjectTestScripts = new File(fSxProject, "StuffContainer/testScripts"); } List items = new ArrayList(); if (Type.API.equals(typ)) { String optJython = getOption("jython"); if (!optJython.isEmpty()) { items.add(optJython); } } if (!Type.SETUP.equals(typ)) { String optClasspath = getOption("classpath"); if (!optClasspath.isEmpty()) { items.addAll(Arrays.asList(optClasspath.split(System.getProperty("path.separator")))); } items.addAll(Arrays.asList(standardExtensions)); if (items.size() > 0) { String[] fList = fSikulixExtensions.list(); for (String item : items) { item = item.trim(); if (new File(item).isAbsolute()) { addToClasspath(item); } else { for (String fpFile : fList) { File fFile = new File(fSikulixExtensions, fpFile); if (fFile.length() > 0) { if (fpFile.startsWith(item)) { addToClasspath(fFile.getAbsolutePath()); break; } } else { fFile.delete(); } } } } } } // if (runningWinApp || testingWinApp) { runTime.fpJarLibs += "windows"; runTime.fpSysLibs = runTime.fpJarLibs.substring(1) + "/libs" + runTime.javaArch; } if (!Type.SETUP.equals(typ)) { libsExport(typ); } else { fSikulixDownloadsBuild = new File(fSikulixAppPath, "SikulixDownloads_" + sxBuildStamp); String[] fpList = fSikulixAppPath.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { if (name.contains("SikulixDownloads_")) { if (name.contains(sxBuildStamp)) { return false; } return true; } return false; } }); if (fpList.length > 0) { log(lvl, "deleting versioned downloads folder in AppPath (%s)", fSikulixDownloadsBuild.getName()); for (String entry : fpList) { //new File(fSikulixAppPath, entry).renameTo(fSikulixDownloadsBuild); FileManager.deleteFileOrFolder(new File(fSikulixAppPath, entry)); } } } runType = typ; if (Debug.getDebugLevel() == minLvl) { show(); } log(lvl, "global init: leaving"); } class LibsFilter implements FilenameFilter { String sAccept = ""; public LibsFilter(String toAccept) { sAccept = toAccept; } @Override public boolean accept(File dir, String name) { if (dir.getPath().contains(sAccept)) { return true; } return false; } } // // public void makeFolders() { fLibsFolder = new File(fSikulixAppPath, "SikulixLibs_" + sxBuildStamp); if (testing) { logp("***** for testing: delete folders SikulixLibs/ and Lib/"); FileManager.deleteFileOrFolder(fLibsFolder); FileManager.deleteFileOrFolder(fSikulixLib); } if (!fLibsFolder.exists()) { fLibsFolder.mkdirs(); if (!fLibsFolder.exists()) { terminate(1, "libs folder not available: " + fLibsFolder.toString()); } log(lvl, "new libs folder at: %s", fLibsFolder); } else { log(lvl, "exists libs folder at: %s", fLibsFolder); } String[] fpList = fTempPath.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { if (name.contains("SikulixLibs")) { return true; } return false; } }); if (fpList.length > 0) { log(lvl, "deleting obsolete libs folders in Temp"); for (String entry : fpList) { if (entry.endsWith(sxBuildStamp)) { continue; } FileManager.deleteFileOrFolder(new File(fTempPath, entry)); } } fpList = fSikulixAppPath.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { if (name.contains("SikulixLibs")) { return true; } return false; } }); if (fpList.length > 1) { log(lvl, "deleting obsolete libs folders in AppPath"); for (String entry : fpList) { if (entry.endsWith(sxBuildStamp)) { continue; } FileManager.deleteFileOrFolder(new File(fSikulixAppPath, entry)); } } } private boolean libsLoad(String libName) { if (!areLibsExported) { libsExport(runType); } if (!areLibsExported) { terminate(1, "loadLib: deferred exporting of libs did not work"); } if (runningWindows) { libName += ".dll"; } else if (runningMac) { libName = "lib" + libName + ".dylib"; } else if (runningLinux) { libName = "lib" + libName + ".so"; } File fLib = new File(fLibsFolder, libName); Boolean vLib = libsLoaded.get(libName); if (vLib == null || !fLib.exists()) { terminate(1, String.format("loadlib: %s not available in %s", libName, fLibsFolder)); } String msg = "loadLib: %s"; int level = lvl; if (vLib) { level++; msg += " already loaded"; } if (vLib) { log(level, msg, libName); return true; } boolean shouldTerminate = false; Error loadError = null; while (!shouldTerminate) { shouldTerminate = true; loadError = null; try { System.load(new File(fLibsFolder, libName).getAbsolutePath()); } catch (Error e) { loadError = e; if (runningLinux) { log(-1, msg + " not usable: \n%s", libName, loadError); shouldTerminate = !LinuxSupport.checkAllLibs(); } } } if (loadError != null) { log(-1, "Problematic lib: %s (...TEMP...)", fLib); log(-1, "%s loaded, but it might be a problem with needed dependent libraries\nERROR: %s", libName, loadError.getMessage().replace(fLib.getAbsolutePath(), "...TEMP...")); if (Settings.runningSetup) { return false; } else { terminate(1, "problem with native library: " + libName); } } libsLoaded.put(libName, true); log(level, msg, libName); return true; } private boolean libsCheck(File flibsFolder) { // 1.1-MadeForSikuliX64M.txt String name = String.format("1.1-MadeForSikuliX%d%s.txt", javaArch, runningOn.toString().substring(0, 1)); if (!new File(flibsFolder, name).exists()) { log(lvl, "libs folder empty or has wrong content"); return false; } return true; } private void libsExport(Type typ) { shouldExport = false; makeFolders(); URL uLibsFrom = null; if (!libsCheck(fLibsFolder)) { FileManager.deleteFileOrFolder(fLibsFolder); fLibsFolder.mkdirs(); shouldExport = true; if (!fLibsFolder.exists()) { terminate(1, "libs folder not available: " + fLibsFolder.toString()); } } if (shouldExport) { String sysShort = "win"; boolean shouldAddLibsJar = false; if (!runningWinApp && !testingWinApp) { sysShort = runningOn.toString().toLowerCase(); } String fpLibsFrom = ""; if (runningJar) { fpLibsFrom = fSxBaseJar.getAbsolutePath(); if (fpLibsFrom.contains("forsetup")) { shouldAddLibsJar = true; } } else { String fSrcFolder = typ.toString(); if (Type.SETUP.toString().equals(fSrcFolder)) { fSrcFolder = "Setup"; } fpLibsFrom = fSxBaseJar.getPath().replace(fSrcFolder, "Libs" + sysShort) + "/"; } if (testing && !runningJar) { if (testingWinApp || testSwitch()) { logp("***** for testing: exporting from classes"); } else { logp("***** for testing: exporting from jar"); shouldAddLibsJar = true; } } if (!shouldAddLibsJar && (null != isJarOnClasspath("sikulix.jar") || null != isJarOnClasspath("sikulixapi.jar"))) { shouldAddLibsJar = false; fpLibsFrom = ""; } if (shouldAddLibsJar) { fpLibsFrom = new File(fSxProject, String.format("Libs%s/target/sikulixlibs%s-1.1.2.jar", sysShort, sysShort)).getAbsolutePath(); } log(lvl, "now exporting libs"); if (!fpLibsFrom.isEmpty()) { addToClasspath(fpLibsFrom); } uLibsFrom = clsRef.getResource(fpJarLibs); if (testing || uLibsFrom == null) { dumpClassPath(); } if (uLibsFrom == null) { terminate(1, "libs to export not found on above classpath: " + fpJarLibs); } log(lvl, "libs to export are at:\n%s", uLibsFrom); if (runningWinApp || testingWinApp) { String libsAccepted = "libs" + javaArch; extractResourcesToFolder(fpJarLibs, fLibsFolder, new LibsFilter(libsAccepted)); File fCurrentLibs = new File(fLibsFolder, libsAccepted); if (FileManager.xcopy(fCurrentLibs, fLibsFolder)) { FileManager.deleteFileOrFolder(fCurrentLibs); } else { terminate(1, "could not create libs folder for Windows --- see log"); } } else { extractResourcesToFolder(fpJarLibs, fLibsFolder, null); } } for (String aFile : fLibsFolder.list()) { libsLoaded.put(aFile, false); } if (useLibsProvided) { log(lvl, "Linux: requested to use provided libs - copying"); LinuxSupport.copyProvidedLibs(fLibsFolder); } if (runningWindows) { addToWindowsSystemPath(fLibsFolder); if (!checkJavaUsrPath(fLibsFolder)) { log(-1, "Problems setting up on Windows - see errors - might not work and crash later"); } String lib = "jawt.dll"; File fJawtDll = new File(fLibsFolder, lib); FileManager.deleteFileOrFolder(fJawtDll); FileManager.xcopy(new File(javahome + "/bin/" + lib), fJawtDll); if (!fJawtDll.exists()) { terminate(1, "problem copying %s", fJawtDll); } } areLibsExported = true; } // // /** * INTERNAL USE: load a native library from the libs folder * * @param libname name of library without prefix/suffix/ending */ public static void loadLibrary(String libname) { if (isTerminating) { return; } RunTime.get().libsLoad(libname); } /** * INTERNAL USE: load a native library from the libs folder * * @param libname name of library without prefix/suffix/ending */ public static boolean loadLibrary(String libname, boolean useLibsProvided) { RunTime rt = RunTime.get(); rt.useLibsProvided = useLibsProvided; return rt.libsLoad(libname); } private void addToWindowsSystemPath(File fLibsFolder) { String syspath = SysJNA.WinKernel32.getEnvironmentVariable("PATH"); if (syspath == null) { terminate(1, "addToWindowsSystemPath: cannot access system path"); } else { String libsPath = (fLibsFolder.getAbsolutePath()).replaceAll("/", "\\"); if (!syspath.toUpperCase().contains(libsPath.toUpperCase())) { if (SysJNA.WinKernel32.setEnvironmentVariable("PATH", libsPath + ";" + syspath)) { syspath = SysJNA.WinKernel32.getEnvironmentVariable("PATH"); if (!syspath.toUpperCase().contains(libsPath.toUpperCase())) { log(-1, "addToWindowsSystemPath: adding to system path did not work:\n%s", syspath); terminate(1, "addToWindowsSystemPath: did not work - see error"); } } log(lvl, "addToWindowsSystemPath: added to systempath:\n%s", libsPath); } } } private boolean checkJavaUsrPath(File fLibsFolder) { String fpLibsFolder = fLibsFolder.getAbsolutePath(); Field usrPathsField = null; boolean contained = false; try { usrPathsField = ClassLoader.class.getDeclaredField("usr_paths"); } catch (NoSuchFieldException ex) { log(-1, "checkJavaUsrPath: get\n%s", ex); } catch (SecurityException ex) { log(-1, "checkJavaUsrPath: get\n%s", ex); } if (usrPathsField != null) { usrPathsField.setAccessible(true); try { //get array of paths String[] javapaths = (String[]) usrPathsField.get(null); //check if the path to add is already present for (String p : javapaths) { if (new File(p).equals(fLibsFolder)) { contained = true; break; } } //add the new path if (!contained) { final String[] newPaths = Arrays.copyOf(javapaths, javapaths.length + 1); newPaths[newPaths.length - 1] = fpLibsFolder; usrPathsField.set(null, newPaths); log(lvl, "checkJavaUsrPath: added to ClassLoader.usrPaths"); contained = true; } } catch (IllegalAccessException ex) { log(-1, "checkJavaUsrPath: set\n%s", ex); } catch (IllegalArgumentException ex) { log(-1, "checkJavaUsrPath: set\n%s", ex); } return contained; } return false; } // // File isRunning = null; FileOutputStream isRunningFile = null; String isRunningFilename = "s_i_k_u_l_i-ide-isrunning"; private void initIDEbefore() { log(lvl, "initIDEbefore: entering"); optionsIDE = Preferences.userNodeForPackage(Sikulix.class); if (jreVersion.startsWith("1.6")) { String jyversion = ""; Properties prop = new Properties(); String fp = "org/python/version.properties"; InputStream ifp = null; try { ifp = classLoader.getResourceAsStream(fp); if (ifp != null) { prop.load(ifp); ifp.close(); jyversion = prop.getProperty("jython.version"); } } catch (IOException ex) { } if (!jyversion.isEmpty() && !jyversion.startsWith("2.5")) { Sikulix.popError(String.format("The bundled Jython %s\n" + "cannot be used on Java 6!\n" + "Run setup again in this environment.\n" + "Click OK to terminate now", jyversion)); System.exit(1); } } Settings.isRunningIDE = true; if (runningMac) { System.setProperty("apple.laf.useScreenMenuBar", "true"); if (!runningMacApp && !runningInProject) { if (!Sikulix.popAsk("This use of SikuliX is not supported\n" + "and might lead to misbehavior!\n" + "Click YES to continue (you should be sure)\n" + "Click NO to terminate and check the situation.")) { System.exit(1); } } } log(lvl, "initIDEbefore: leaving"); } private void initIDEafter() { // log(lvl, "initIDEafter: entering"); // log(lvl, "initIDEafter: leaving"); } // // private void initAPI() { log(lvl, "initAPI: entering"); if (shouldExport || !fSikulixLib.exists() || !new File(fSikulixLib, "robot").exists() || !new File(fSikulixLib, "sikuli").exists()) { fSikulixLib.mkdir(); extractResourcesToFolder("Lib", fSikulixLib, null); } else { extractResourcesToFolder("Lib/sikuli", new File(fSikulixLib, "sikuli"), null); } log(lvl, "initAPI: leaving"); } // // private void initSetup() { // log(lvl, "initSetup: entering"); // log(lvl, "initSetup: leaving"); } // // /** * INTERNAL USE: to check whether we are running in compiled classes context * * @return true if the code source location is a folder ending with classes (Maven convention) */ public boolean isRunningFromJar() { return runningJar; } /** * @return return true if Java version > 7 */ public boolean isJava8() { return javaVersion > 7; } /** * @return return true if Java version > 6 */ public boolean isJava7() { return javaVersion > 6; } public boolean isOSX10() { return osVersion.startsWith("10.10.") || osVersion.startsWith("10.11.") || osVersion.startsWith("10.12."); } public boolean needsRobotFake() { return !Settings.ClickFast && runningMac && isOSX10(); } /** * print out some basic information about the current runtime environment */ public void show() { if (hasOptions()) { dumpOptions(); } logp("***** show environment for %s (build %s)", runType, sxBuildStamp); logp("user.home: %s", fUserDir); logp("user.dir (work dir): %s", fWorkDir); logp("user.name: %s", userName); logp("java.io.tmpdir: %s", fTempPath); logp("running %dBit on %s (%s) %s", javaArch, osName, (linuxDistro.contains("???") ? osVersion : linuxDistro), appType); logp(javaShow); logp("app data folder: %s", fSikulixAppPath); logp("libs folder: %s", fLibsFolder); if (runningJar) { logp("executing jar: %s", fSxBaseJar); } if (Debug.getDebugLevel() > minLvl - 1 || isJythonReady) { dumpClassPath("sikulix"); if (isJythonReady) { int saveLvl = Debug.getDebugLevel(); Debug.setDebugLevel(lvl); JythonHelper.get().showSysPath(); Screen.showMonitors(); Debug.setDebugLevel(saveLvl); } } logp("***** show environment end"); } public boolean testSwitch() { if (0 == (new Date().getTime() / 10000) % 2) { return true; } return false; } public String getVersionShortBasic() { return sversion.substring(0, 3); } public String getVersionShort() { if (SikuliVersionBetaN > 0 && SikuliVersionBetaN < 99) { return bversion; } else { return sversion; } } public String getSystemInfo() { return String.format("%s/%s/%s", SikuliVersionLong, SikuliSystemVersion, SikuliJavaVersion); } public boolean isVersionRelease() { return SikuliVersionType.isEmpty(); } public String getVersion() { return SikuliVersion; } public void getStatus() { System.out.println("***** System Information Dump *****"); System.out.println(String.format("*** SystemInfo\n%s", getSystemInfo())); System.getProperties().list(System.out); System.out.println("*** System Environment"); for (String key : System.getenv().keySet()) { System.out.println(String.format("%s = %s", key, System.getenv(key))); } System.out.println("*** Java Class Path"); URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); URL[] urls = sysLoader.getURLs(); for (int i = 0; i < urls.length; i++) { System.out.println(String.format("%d: %s", i, urls[i])); } System.out.println("***** System Information Dump ***** end *****"); } // // private void loadOptions(Type typ) { for (File aFile : new File[]{fWorkDir, fUserDir, fSikulixStore}) { log(lvl, "loadOptions: check: %s", aFile); fOptions = new File(aFile, fnOptions); if (fOptions.exists()) { break; } else { fOptions = null; } } if (fOptions != null) { options = new Properties(); try { InputStream is; is = new FileInputStream(fOptions); options.load(is); is.close(); } catch (Exception ex) { log(-1, "while checking Options file:\n%s", fOptions); fOptions = null; options = null; } testing = isOption("testing", false); if (testing) { Debug.setDebugLevel(3); } log(lvl, "found Options file at: %s", fOptions); } if (hasOptions()) { for (Object oKey : options.keySet()) { String sKey = (String) oKey; String[] parts = sKey.split("\\."); if (parts.length == 1) { continue; } String sClass = parts[0]; String sAttr = parts[1]; Class cClass = null; Field cField = null; Class ccField = null; if (sClass.contains("Settings")) { try { cClass = Class.forName("org.sikuli.basics.Settings"); cField = cClass.getField(sAttr); ccField = cField.getType(); if (ccField.getName() == "boolean") { cField.setBoolean(null, isOption(sKey)); } else if (ccField.getName() == "int") { cField.setInt(null, getOptionNumber(sKey)); } else if (ccField.getName() == "float") { cField.setInt(null, getOptionNumber(sKey)); } else if (ccField.getName() == "double") { cField.setInt(null, getOptionNumber(sKey)); } else if (ccField.getName() == "String") { cField.set(null, getOption(sKey)); } } catch (Exception ex) { log(-1, "loadOptions: not possible: %s = %s", sKey, options.getProperty(sKey)); } } } } } /** * CONVENIENCE: look into the option file if any (if no option file is found, the option is taken as not existing) * * @param pName the option key (case-sensitive) * @return true only if option exists and has yes or true (not case-sensitive), in all other cases false */ public boolean isOption(String pName) { return isOption(pName, false); } /** * CONVENIENCE: look into the option file if any (if no option file is found, the option is taken as not existing) * * @param pName the option key (case-sensitive) * @param bDefault the default to be returned if option absent or empty * @return true if option has yes or no, false for no or false (not case-sensitive) */ public boolean isOption(String pName, Boolean bDefault) { if (options == null) { return bDefault; } String pVal = options.getProperty(pName, bDefault.toString()).toLowerCase(); if (pVal.isEmpty()) { return bDefault; } else if (pVal.contains("yes") || pVal.contains("true") || pVal.contains("on")) { return true; } else if (pVal.contains("no") || pVal.contains("false") || pVal.contains("off")) { return false; } return true; } /** * look into the option file if any (if no option file is found, the option is taken as not existing) * * @param pName the option key (case-sensitive) * @return the associated value, empty string if absent */ public String getOption(String pName) { if (options == null) { return ""; } String pVal = options.getProperty(pName, ""); return pVal; } /** * look into the option file if any (if no option file is found, the option is taken as not existing)
* side-effect: if no options file is there, an options store will be created in memory
* in this case and when the option is absent or empty, the given default will be stored
* you might later save the options store to a file with storeOptions() * * @param pName the option key (case-sensitive) * @param sDefault the default to be returned if option absent or empty * @return the associated value, the default value if absent or empty */ public String getOption(String pName, String sDefault) { if (options == null) { options = new Properties(); options.setProperty(pName, sDefault); return sDefault; } String pVal = options.getProperty(pName, sDefault); if (pVal.isEmpty()) { options.setProperty(pName, sDefault); return sDefault; } return pVal; } /** * store an option key-value pair, overwrites existing value
* new option store is created if necessary and can later be saved to a file * * @param pName * @param sValue */ public void setOption(String pName, String sValue) { if (options == null) { options = new Properties(); } options.setProperty(pName, sValue); } /** * CONVENIENCE: look into the option file if any (if no option file is found, the option is taken as not existing)
* tries to convert the stored string value into an integer number (gives 0 if not possible)
* * @param pName the option key (case-sensitive) * @return the converted integer number, 0 if absent or not possible */ public int getOptionNumber(String pName) { if (options == null) { return 0; } String pVal = options.getProperty(pName, "0"); int nVal = 0; try { nVal = Integer.decode(pVal); } catch (Exception ex) { } return nVal; } /** * CONVENIENCE: look into the option file if any (if no option file is found, the option is taken as not existing)
* tries to convert the stored string value into an integer number (gives 0 if not possible)
* * @param pName the option key (case-sensitive) * @param nDefault the default to be returned if option absent, empty or not convertable * @return the converted integer number, default if absent, empty or not possible */ public int getOptionNumber(String pName, Integer nDefault) { if (options == null) { return nDefault; } String pVal = options.getProperty(pName, nDefault.toString()); int nVal = nDefault; try { nVal = Integer.decode(pVal); } catch (Exception ex) { } return nVal; } /** * all options and their values * * @return a map of key-value pairs containing the found options, empty if no options file found */ public Map getOptions() { Map mapOptions = new HashMap(); if (options != null) { Enumeration optionNames = options.propertyNames(); String optionName; while (optionNames.hasMoreElements()) { optionName = (String) optionNames.nextElement(); mapOptions.put(optionName, getOption(optionName)); } } return mapOptions; } /** * check whether options are defined * * @return true if at lest one option defined else false */ public boolean hasOptions() { return options != null && options.size() > 0; } /** * all options and their values written to sysout as key = value */ public void dumpOptions() { if (hasOptions()) { logp("*** options dump:\n%s", (fOptions == null ? "" : fOptions)); for (String sOpt : getOptions().keySet()) { logp("%s = %s", sOpt, getOption(sOpt)); } logp("*** options dump end"); } } //
// public int SikuliVersionMajor; public int SikuliVersionMinor; public int SikuliVersionSub; public int SikuliVersionBetaN; public String SikuliProjectVersionUsed = ""; public String SikuliProjectVersion = ""; public String SikuliVersionBuild; public String SikuliVersionType; public String SikuliVersionTypeText; public String downloadBaseDirBase; public String downloadBaseDirWeb; public String downloadBaseDir; // used for download of production versions private final String dlProdLink = "https://launchpad.net/raiman/sikulix2013+/"; private final String dlProdLink1 = ".0"; private final String dlProdLink2 = "/+download/"; // used for download of development versions (nightly builds) private final String dlDevLink = "http://nightly.sikuli.de/"; public String SikuliRepo; public String SikuliLocalRepo = ""; public String[] ServerList = {}; private String sversion; private String bversion; public String SikuliVersionDefault; public String SikuliVersionBeta; public String SikuliVersionDefaultIDE; public String SikuliVersionBetaIDE; public String SikuliVersionDefaultScript; public String SikuliVersionBetaScript; public String SikuliVersion; public String SikuliVersionIDE; public String SikuliVersionScript; public String SikuliJythonVersion; public String SikuliJythonVersion25 = "2.5.4-rc1"; public String SikuliJythonMaven; public String SikuliJythonMaven25; public String SikuliJython; public String SikuliJython25; public String SikuliJRubyVersion; public String SikuliJRuby; public String SikuliJRubyMaven; public String dlMavenRelease = "https://repo1.maven.org/maven2/"; public String dlMavenSnapshot = "https://oss.sonatype.org/content/groups/public/"; public Map tessData = new HashMap(); //TODO needed ??? public final String libOpenCV = "libopencv_java248"; public String SikuliVersionLong; public String SikuliSystemVersion; public String SikuliJavaVersion; private void initSikulixOptions() { SikuliRepo = null; Properties prop = new Properties(); String svf = "sikulixversion.txt"; try { InputStream is; is = clsRef.getClassLoader().getResourceAsStream("Settings/" + svf); if (is == null) { terminate(1, "initSikulixOptions: not found on classpath: %s", "Settings/" + svf); } prop.load(is); is.close(); String svt = prop.getProperty("sikulixdev"); SikuliVersionMajor = Integer.decode(prop.getProperty("sikulixvmaj")); SikuliVersionMinor = Integer.decode(prop.getProperty("sikulixvmin")); SikuliVersionSub = Integer.decode(prop.getProperty("sikulixvsub")); SikuliVersionBetaN = Integer.decode(prop.getProperty("sikulixbeta")); String ssxbeta = ""; if (SikuliVersionBetaN > 0) { ssxbeta = String.format("-Beta%d", SikuliVersionBetaN); } SikuliVersionBuild = prop.getProperty("sikulixbuild"); log(lvl + 1, "%s version from %s: %d.%d.%d%s build: %s", svf, SikuliVersionMajor, SikuliVersionMinor, SikuliVersionSub, ssxbeta, SikuliVersionBuild, svt); sversion = String.format("%d.%d.%d", SikuliVersionMajor, SikuliVersionMinor, SikuliVersionSub); bversion = String.format("%d.%d.%d-Beta%d", SikuliVersionMajor, SikuliVersionMinor, SikuliVersionSub, SikuliVersionBetaN); SikuliVersionDefault = "SikuliX " + sversion; SikuliVersionBeta = "Sikuli " + bversion; SikuliVersionDefaultIDE = "SikulixIDE " + sversion; SikuliVersionBetaIDE = "SikulixIDE " + bversion; SikuliVersionDefaultScript = "SikulixScript " + sversion; SikuliVersionBetaScript = "SikulixScript " + bversion; SikuliVersionTypeText = ""; if ("release".equals(svt)) { downloadBaseDirBase = dlProdLink; downloadBaseDirWeb = downloadBaseDirBase + getVersionShortBasic() + dlProdLink1; downloadBaseDir = downloadBaseDirWeb + dlProdLink2; SikuliVersionType = ""; } else { downloadBaseDirBase = dlDevLink; downloadBaseDirWeb = dlDevLink; downloadBaseDir = dlDevLink; //TODO switch on for 1.1.2 //SikuliVersionTypeText = "nightly"; SikuliVersionBuild += SikuliVersionTypeText; SikuliVersionType = svt; } if (SikuliVersionBetaN > 0) { SikuliVersion = SikuliVersionBeta; SikuliVersionIDE = SikuliVersionBetaIDE; SikuliVersionScript = SikuliVersionBetaScript; SikuliVersionLong = bversion + "(" + SikuliVersionBuild + ")"; } else { SikuliVersion = SikuliVersionDefault; SikuliVersionIDE = SikuliVersionDefaultIDE; SikuliVersionScript = SikuliVersionDefaultScript; SikuliVersionLong = sversion + "(" + SikuliVersionBuild + ")"; } SikuliProjectVersionUsed = prop.getProperty("sikulixvused"); SikuliProjectVersion = prop.getProperty("sikulixvproject"); String osn = "UnKnown"; String os = System.getProperty("os.name").toLowerCase(); if (os.startsWith("mac")) { osn = "Mac"; } else if (os.startsWith("windows")) { osn = "Windows"; } else if (os.startsWith("linux")) { osn = "Linux"; } SikuliLocalRepo = FileManager.slashify(prop.getProperty("sikulixlocalrepo"), true); SikuliJythonVersion = prop.getProperty("sikulixvjython"); SikuliJythonMaven = "org/python/jython-standalone/" + SikuliJythonVersion + "/jython-standalone-" + SikuliJythonVersion + ".jar"; SikuliJythonMaven25 = "org/python/jython-standalone/" + SikuliJythonVersion25 + "/jython-standalone-" + SikuliJythonVersion25 + ".jar"; SikuliJython = SikuliLocalRepo + SikuliJythonMaven; SikuliJython25 = SikuliLocalRepo + SikuliJythonMaven25; SikuliJRubyVersion = prop.getProperty("sikulixvjruby"); SikuliJRubyMaven = "org/jruby/jruby-complete/" + SikuliJRubyVersion + "/jruby-complete-" + SikuliJRubyVersion + ".jar"; SikuliJRuby = SikuliLocalRepo + SikuliJRubyMaven; SikuliSystemVersion = osn + System.getProperty("os.version"); SikuliJavaVersion = "Java" + javaVersion + "(" + javaArch + ")" + jreVersion; //TODO this should be in RunSetup only //TODO debug version: where to do in sikulixapi.jar //TODO need a function: reveal all environment and system information // log(lvl, "%s version: downloading from %s", svt, downloadBaseDir); } catch (Exception e) { Debug.error("Settings: load version file %s did not work", svf); Sikulix.endError(999); } // tessData.put("eng", "http://tesseract-ocr.googlecode.com/files/tesseract-ocr-3.02.eng.tar.gz"); tessData.put("eng", "http://download.sikulix.com/tesseract-ocr-3.02.eng.tar.gz"); Env.setSikuliVersion(SikuliVersion); } // // private static String optThisComingFromFile = "thisOptions.comingFromWhatFile"; private static String optThisWhatIsANumber = "thisOptions.whatIsAnumber"; private static String whatIsANumber = "#"; private static boolean optIsNumber(Properties props, String pName) { String prefix = getOpt(props, pName, whatIsANumber); if (pName.contains(prefix)) { return true; } return false; } /** * load a properties file * * @param fpOptions path to a file containing options * @return the Properties store or null */ public Properties loadOpts(String fpOptions) { if (fpOptions == null) { log(-1, "loadOptions: (error: no file)"); return null; } File fOptions = new File(fpOptions); if (!fOptions.isFile()) { log(-1, "loadOptions: (error: not found) %s", fOptions); return null; } Properties pOptions = new Properties(); try { fpOptions = fOptions.getCanonicalPath(); InputStream is; is = new FileInputStream(fOptions); pOptions.load(is); is.close(); } catch (Exception ex) { log(-1, "loadOptions: %s (error %s)", fOptions, ex.getMessage()); return null; } log(lvl, "loadOptions: ok (%d): %s", pOptions.size(), fOptions.getName()); pOptions.setProperty(optThisComingFromFile, fpOptions); return pOptions; } public static Properties makeOpts() { return new Properties(); } /** * save a properties store to a file (prop: this.comingfrom = abs. filepath) * * @param pOptions the prop store * @return success */ public boolean saveOpts(Properties pOptions) { String fpOptions = pOptions.getProperty(optThisComingFromFile); if (null == fpOptions) { log(-1, "saveOptions: no prop %s", optThisComingFromFile); return false; } return saveOpts(pOptions, fpOptions); } /** * save a properties store to the given file * * @param pOptions the prop store * @param fpOptions path to a file * @return success */ public boolean saveOpts(Properties pOptions, String fpOptions) { pOptions.remove(optThisComingFromFile); File fOptions = new File(fpOptions); try { fpOptions = fOptions.getCanonicalPath(); OutputStream os; os = new FileOutputStream(fOptions); pOptions.store(os, ""); os.close(); } catch (Exception ex) { log(-1, "saveOptions: %s (error %s)", fOptions, ex.getMessage()); return false; } log(lvl, "saveOptions: saved: %s", fpOptions); return true; } public static boolean hasOpt(Properties props, String pName) { return null != props && null != props.getProperty(pName); } public static String getOpt(Properties props, String pName) { return getOpt(props, pName, ""); } public static String getOpt(Properties props, String pName, String deflt) { String retVal = deflt; if (hasOpt(props, pName)) { retVal = props.getProperty(pName); } return retVal; } public static String setOpt(Properties props, String pName, String pVal) { String retVal = ""; if (hasOpt(props, pName)) { retVal = props.getProperty(pName); } props.setProperty(pName, pVal); return retVal; } public static double getOptNum(Properties props, String pName) { return getOptNum(props, pName, 0d); } public static double getOptNum(Properties props, String pName, double deflt) { double retVal = deflt; if (hasOpt(props, pName)) { try { retVal = Double.parseDouble(props.getProperty(pName)); } catch (Exception ex) { } } return retVal; } public static double setOptNum(Properties props, String pName, double pVal) { double retVal = 0d; if (hasOpt(props, pName)) { try { retVal = Double.parseDouble(props.getProperty(pName)); } catch (Exception ex) { } } props.setProperty(pName, ((Double) pVal).toString()); return retVal; } public static String delOpt(Properties props, String pName) { String retVal = ""; if (hasOpt(props, pName)) { retVal = props.getProperty(pName); } props.remove(pName); return retVal; } public static Map getOpts(Properties props) { Map mapOptions = new HashMap(); if (props != null) { Enumeration optionNames = props.propertyNames(); String optionName; while (optionNames.hasMoreElements()) { optionName = (String) optionNames.nextElement(); mapOptions.put(optionName, props.getProperty(optionName)); } } return mapOptions; } public static int setOpts(Properties props, Map aMap) { int n = 0; for (String key : aMap.keySet()) { props.setProperty(key, aMap.get(key)); n++; } return n; } public static boolean delOpts(Properties props) { if (null != props) { props.clear(); return true; } return false; } public static int hasOpts(Properties props) { if (null != props) { return props.size(); } return 0; } // // protected List extractTessData(File folder) { List files = new ArrayList(); String tessdata = "/sikulixtessdata"; URL uContentList = clsRef.getResource(tessdata + "/" + fpContent); if (uContentList != null) { files = doResourceListWithList(tessdata, files, null); if (files.size() > 0) { files = doExtractToFolderWithList(tessdata, folder, files); } } else { files = extractResourcesToFolder("/sikulixtessdata", folder, null); } return (files.size() == 0 ? null : files); } /** * export all resource files from the given subtree on classpath to the given folder retaining the subtree
* to export a specific file from classpath use extractResourceToFile or extractResourceToString * * @param fpRessources path of the subtree relative to root * @param fFolder folder where to export (if null, only list - no export) * @param filter implementation of interface FilenameFilter or null for no filtering * @return the filtered list of files (compact sikulixcontent format) */ public List extractResourcesToFolder(String fpRessources, File fFolder, FilenameFilter filter) { List content = null; content = resourceList(fpRessources, filter); if (content == null) { return null; } if (fFolder == null) { return content; } return doExtractToFolderWithList(fpRessources, fFolder, content); } private List doExtractToFolderWithList(String fpRessources, File fFolder, List content) { int count = 0; int ecount = 0; String subFolder = ""; if (content != null && content.size() > 0) { for (String eFile : content) { if (eFile == null) { continue; } if (eFile.endsWith("/")) { subFolder = eFile.substring(0, eFile.length() - 1); continue; } if (!subFolder.isEmpty()) { eFile = new File(subFolder, eFile).getPath(); } if (extractResourceToFile(fpRessources, eFile, fFolder)) { log(lvl + 1, "extractResourceToFile done: %s", eFile); count++; } else { ecount++; } } } if (ecount > 0) { log(lvl, "files exported: %d - skipped: %d from %s to:\n %s", count, ecount, fpRessources, fFolder); } else { log(lvl, "files exported: %d from: %s to:\n %s", count, fpRessources, fFolder); } return content; } /** * export all resource files from the given subtree in given jar to the given folder retaining the subtree * * @param aJar absolute path to an existing jar or a string identifying the jar on classpath (no leading /) * @param fpRessources path of the subtree or file relative to root * @param fFolder folder where to export (if null, only list - no export) * @param filter implementation of interface FilenameFilter or null for no filtering * @return the filtered list of files (compact sikulixcontent format) */ public List extractResourcesToFolderFromJar(String aJar, String fpRessources, File fFolder, FilenameFilter filter) { List content = new ArrayList(); File faJar = new File(aJar); URL uaJar = null; fpRessources = FileManager.slashify(fpRessources, false); if (faJar.isAbsolute()) { if (!faJar.exists()) { log(-1, "extractResourcesToFolderFromJar: does not exist:\n%s", faJar); return null; } try { uaJar = new URL("jar", null, "file:" + aJar); logp("%s", uaJar); } catch (MalformedURLException ex) { log(-1, "extractResourcesToFolderFromJar: bad URL for:\n%s", faJar); return null; } } else { uaJar = fromClasspath(aJar); if (uaJar == null) { log(-1, "extractResourcesToFolderFromJar: not on classpath: %s", aJar); return null; } try { String sJar = "file:" + uaJar.getPath() + "!/"; uaJar = new URL("jar", null, sJar); } catch (MalformedURLException ex) { log(-1, "extractResourcesToFolderFromJar: bad URL for:\n%s", uaJar); return null; } } content = doResourceListJar(uaJar, fpRessources, content, filter); if (fFolder == null) { return content; } copyFromJarToFolderWithList(uaJar, fpRessources, content, fFolder); return content; } /** * store a resource found on classpath to a file in the given folder with same filename * * @param inPrefix a subtree found in classpath * @param inFile the filename combined with the prefix on classpath * @param outDir a folder where to export * @return success */ public boolean extractResourceToFile(String inPrefix, String inFile, File outDir) { return extractResourceToFile(inPrefix, inFile, outDir, ""); } /** * store a resource found on classpath to a file in the given folder * * @param inPrefix a subtree found in classpath * @param inFile the filename combined with the prefix on classpath * @param outDir a folder where to export * @param outFile the filename for export * @return success */ public boolean extractResourceToFile(String inPrefix, String inFile, File outDir, String outFile) { InputStream aIS; FileOutputStream aFileOS; String content = inPrefix + "/" + inFile; try { content = runningWindows ? content.replace("\\", "/") : content; if (!content.startsWith("/")) { content = "/" + content; } aIS = (InputStream) clsRef.getResourceAsStream(content); if (aIS == null) { throw new IOException("resource not accessible"); } File out = outFile.isEmpty() ? new File(outDir, inFile) : new File(outDir, inFile); if (!out.getParentFile().exists()) { out.getParentFile().mkdirs(); } aFileOS = new FileOutputStream(out); copy(aIS, aFileOS); aIS.close(); aFileOS.close(); } catch (Exception ex) { log(-1, "extractResourceToFile: %s\n%s", content, ex); return false; } return true; } /** * store the content of a resource found on classpath in the returned string * * @param inPrefix a subtree from root found in classpath (leading /) * @param inFile the filename combined with the prefix on classpath * @param encoding * @return file content */ public String extractResourceToString(String inPrefix, String inFile, String encoding) { InputStream aIS = null; String out = null; String content = inPrefix + "/" + inFile; if (!content.startsWith("/")) { content = "/" + content; } try { content = runningWindows ? content.replace("\\", "/") : content; aIS = (InputStream) clsRef.getResourceAsStream(content); if (aIS == null) { throw new IOException("resource not accessible"); } if (encoding == null) { encoding = "UTF-8"; out = new String(copy(aIS)); } else if (encoding.isEmpty()) { out = new String(copy(aIS), "UTF-8"); } else { out = new String(copy(aIS), encoding); } aIS.close(); aIS = null; } catch (Exception ex) { log(-1, "extractResourceToString as %s from:\n%s\n%s", encoding, content, ex); } try { if (aIS != null) { aIS.close(); } } catch (Exception ex) { } return out; } public URL resourceLocation(String folderOrFile) { log(lvl, "resourceLocation: (%s) %s", clsRef, folderOrFile); if (!folderOrFile.startsWith("/")) { folderOrFile = "/" + folderOrFile; } return clsRef.getResource(folderOrFile); } private List resourceList(String folder, FilenameFilter filter) { log(lvl, "resourceList: enter"); List files = new ArrayList(); if (!folder.startsWith("/")) { folder = "/" + folder; } URL uFolder = resourceLocation(folder); if (uFolder == null) { log(lvl, "resourceList: not found: %s", folder); return files; } try { uFolder = new URL(uFolder.toExternalForm().replaceAll(" ", "%20")); } catch (Exception ex) { } URL uContentList = clsRef.getResource(folder + "/" + fpContent); if (uContentList != null) { return doResourceListWithList(folder, files, filter); } File fFolder = null; try { fFolder = new File(uFolder.toURI()); log(lvl, "resourceList: having folder: %s", fFolder); String sFolder = FileManager.normalizeAbsolute(fFolder.getPath(), false); if (":".equals(sFolder.substring(2, 3))) { sFolder = sFolder.substring(1); } files.add(sFolder); files = doResourceListFolder(new File(sFolder), files, filter); files.remove(0); return files; } catch (Exception ex) { if (!"jar".equals(uFolder.getProtocol())) { log(lvl, "resourceList:\n%s", folder); log(-1, "resourceList: URL neither folder nor jar:\n%s", ex); return null; } } String[] parts = uFolder.getPath().split("!"); if (parts.length < 2 || !parts[0].startsWith("file:")) { log(lvl, "resourceList:\n%s", folder); log(-1, "resourceList: not a valid jar URL: " + uFolder.getPath()); return null; } String fpFolder = parts[1]; log(lvl, "resourceList: having jar: %s", uFolder); return doResourceListJar(uFolder, fpFolder, files, filter); } /** * write the list as it is produced by calling extractResourcesToFolder to the given file with system line * separator
* non-compact format: every file with full path * * @param folder path of the subtree relative to root with leading / * @param target the file to write the list (if null, only list - no file) * @param filter implementation of interface FilenameFilter or null for no filtering * @return success */ public String[] resourceListAsFile(String folder, File target, FilenameFilter filter) { String content = resourceListAsString(folder, filter); if (content == null) { log(-1, "resourceListAsFile: did not work: %s", folder); return null; } if (target != null) { try { FileManager.deleteFileOrFolder(target.getAbsolutePath()); target.getParentFile().mkdirs(); PrintWriter aPW = new PrintWriter(target); aPW.write(content); aPW.close(); } catch (Exception ex) { log(-1, "resourceListAsFile: %s:\n%s", target, ex); } } return content.split(System.getProperty("line.separator")); } /** * write the list as it is produced by calling extractResourcesToFolder to the given file with system line * separator
* compact sikulixcontent format * * @param folder path of the subtree relative to root with leading / * @param targetFolder the folder where to store the file sikulixcontent (if null, only list - no export) * @param filter implementation of interface FilenameFilter or null for no filtering * @return success */ public String[] resourceListAsSikulixContent(String folder, File targetFolder, FilenameFilter filter) { List contentList = resourceList(folder, filter); if (contentList == null) { log(-1, "resourceListAsSikulixContent: did not work: %s", folder); return null; } File target = null; String arrString[] = new String[contentList.size()]; try { PrintWriter aPW = null; if (targetFolder != null) { target = new File(targetFolder, fpContent); FileManager.deleteFileOrFolder(target); target.getParentFile().mkdirs(); aPW = new PrintWriter(target); } int n = 0; for (String line : contentList) { arrString[n++] = line; if (targetFolder != null) { aPW.println(line); } } if (targetFolder != null) { aPW.close(); } } catch (Exception ex) { log(-1, "resourceListAsFile: %s:\n%s", target, ex); } return arrString; } /** * write the list as it is produced by calling extractResourcesToFolder to the given file with system line * separator
* compact sikulixcontent format * * @param aJar absolute path to an existing jar or a string identifying the jar on classpath (no leading /) * @param folder path of the subtree relative to root with leading / * @param targetFolder the folder where to store the file sikulixcontent (if null, only list - no export) * @param filter implementation of interface FilenameFilter or null for no filtering * @return success */ public String[] resourceListAsSikulixContentFromJar(String aJar, String folder, File targetFolder, FilenameFilter filter) { List contentList = extractResourcesToFolderFromJar(aJar, folder, null, filter); if (contentList == null || contentList.size() == 0) { log(-1, "resourceListAsSikulixContentFromJar: did not work: %s", folder); return null; } File target = null; String arrString[] = new String[contentList.size()]; try { PrintWriter aPW = null; if (targetFolder != null) { target = new File(targetFolder, fpContent); FileManager.deleteFileOrFolder(target); target.getParentFile().mkdirs(); aPW = new PrintWriter(target); } int n = 0; for (String line : contentList) { arrString[n++] = line; if (targetFolder != null) { aPW.println(line); } } if (targetFolder != null) { aPW.close(); } } catch (Exception ex) { log(-1, "resourceListAsFile: %s:\n%s", target, ex); } return arrString; } /** * write the list produced by calling extractResourcesToFolder to the returned string with system line separator
* non-compact format: every file with full path * * @param folder path of the subtree relative to root with leading / * @param filter implementation of interface FilenameFilter or null for no filtering * @return the resulting string */ public String resourceListAsString(String folder, FilenameFilter filter) { return resourceListAsString(folder, filter, null); } /** * write the list produced by calling extractResourcesToFolder to the returned string with given separator
* non-compact format: every file with full path * * @param folder path of the subtree relative to root with leading / * @param filter implementation of interface FilenameFilter or null for no filtering * @param separator to be used to separate the entries * @return the resulting string */ public String resourceListAsString(String folder, FilenameFilter filter, String separator) { List aList = resourceList(folder, filter); if (aList == null) { return null; } if (separator == null) { separator = System.getProperty("line.separator"); } String out = ""; String subFolder = ""; if (aList != null && aList.size() > 0) { for (String eFile : aList) { if (eFile == null) { continue; } if (eFile.endsWith("/")) { subFolder = eFile.substring(0, eFile.length() - 1); continue; } if (!subFolder.isEmpty()) { eFile = new File(subFolder, eFile).getPath(); } out += eFile.replace("\\", "/") + separator; } } return out; } private List doResourceListFolder(File fFolder, List files, FilenameFilter filter) { int localLevel = testing ? lvl : lvl + 1; String subFolder = ""; if (fFolder.isDirectory()) { if (!FileManager.pathEquals(fFolder.getPath(), files.get(0))) { subFolder = fFolder.getPath().substring(files.get(0).length() + 1).replace("\\", "/") + "/"; if (filter != null && !filter.accept(new File(files.get(0), subFolder), "")) { return files; } } else { logp(localLevel, "scanning folder:\n%s", fFolder); subFolder = "/"; files.add(subFolder); } String[] subList = fFolder.list(); for (String entry : subList) { File fEntry = new File(fFolder, entry); if (fEntry.isDirectory()) { files.add(fEntry.getAbsolutePath().substring(1 + files.get(0).length()).replace("\\", "/") + "/"); doResourceListFolder(fEntry, files, filter); files.add(subFolder); } else { if (filter != null && !filter.accept(fFolder, entry)) { continue; } logp(localLevel, "from %s adding: %s", (subFolder.isEmpty() ? "." : subFolder), entry); files.add(fEntry.getAbsolutePath().substring(1 + fFolder.getPath().length())); } } } return files; } private List doResourceListWithList(String folder, List files, FilenameFilter filter) { String content = extractResourceToString(folder, fpContent, ""); String[] contentList = content.split(content.indexOf("\r") != -1 ? "\r\n" : "\n"); if (filter == null) { files.addAll(Arrays.asList(contentList)); } else { for (String fpFile : contentList) { if (filter.accept(new File(fpFile), "")) { files.add(fpFile); } } } return files; } private List doResourceListJar(URL uJar, String fpResource, List files, FilenameFilter filter) { ZipInputStream zJar; String fpJar = uJar.getPath().split("!")[0]; int localLevel = testing ? lvl : lvl + 1; String fileSep = "/"; if (!fpJar.endsWith(".jar")) { return files; } logp(localLevel, "scanning jar:\n%s", uJar); fpResource = (fpResource.startsWith("/") ? fpResource.substring(1) : fpResource) + "/"; File fFolder = new File(fpResource); File fSubFolder = null; ZipEntry zEntry; String subFolder = ""; boolean skip = false; try { zJar = new ZipInputStream(new URL(fpJar).openStream()); while ((zEntry = zJar.getNextEntry()) != null) { if (zEntry.getName().endsWith("/")) { continue; } String zePath = zEntry.getName(); if (zePath.startsWith(fpResource)) { // if (fpResource.length() == zePath.length()) { // files.add(zePath); // return files; // } String zeName = zePath.substring(fpResource.length()); int nSep = zeName.lastIndexOf(fileSep); String zefName = zeName.substring(nSep + 1, zeName.length()); String zeSub = ""; if (nSep > -1) { zeSub = zeName.substring(0, nSep + 1); if (!subFolder.equals(zeSub)) { subFolder = zeSub; fSubFolder = new File(fFolder, subFolder); skip = false; if (filter != null && !filter.accept(fSubFolder, "")) { skip = true; continue; } files.add(zeSub); } if (skip) { continue; } } else { if (!subFolder.isEmpty()) { subFolder = ""; fSubFolder = fFolder; files.add("/"); } } if (filter != null && !filter.accept(fSubFolder, zefName)) { continue; } files.add(zefName); logp(localLevel, "from %s adding: %s", (zeSub.isEmpty() ? "." : zeSub), zefName); } } } catch (Exception ex) { log(-1, "doResourceListJar: %s", ex); return files; } return files; } private boolean copyFromJarToFolderWithList(URL uJar, String fpRessource, List files, File fFolder) { if (files == null || files.isEmpty()) { log(lvl, "copyFromJarToFolderWithList: list of files is empty"); return false; } String fpJar = uJar.getPath().split("!")[0]; if (!fpJar.endsWith(".jar")) { return false; } int localLevel = testing ? lvl : lvl + 1; logp(localLevel, "scanning jar:\n%s", uJar); fpRessource = fpRessource.startsWith("/") ? fpRessource.substring(1) : fpRessource; String subFolder = ""; int maxFiles = files.size() - 1; int nFiles = 0; ZipEntry zEntry; ZipInputStream zJar; String zPath; int prefix = fpRessource.length(); fpRessource += !fpRessource.isEmpty() ? "/" : ""; String current = "/"; boolean shouldStop = false; try { zJar = new ZipInputStream(new URL(fpJar).openStream()); while ((zEntry = zJar.getNextEntry()) != null) { zPath = zEntry.getName(); if (zPath.endsWith("/")) { continue; } while (current.endsWith("/")) { if (nFiles > maxFiles) { shouldStop = true; break; } subFolder = current.length() == 1 ? "" : current; current = files.get(nFiles++); if (!current.endsWith("/")) { current = fpRessource + subFolder + current; break; } } if (shouldStop) { break; } if (zPath.startsWith(current)) { if (zPath.length() == fpRessource.length() - 1) { log(-1, "extractResourcesToFolderFromJar: only ressource folders allowed - use filter"); return false; } logp(localLevel, "copying: %s", zPath); File out = new File(fFolder, zPath.substring(prefix)); if (!out.getParentFile().exists()) { out.getParentFile().mkdirs(); } FileOutputStream aFileOS = new FileOutputStream(out); copy(zJar, aFileOS); aFileOS.close(); if (nFiles > maxFiles) { break; } current = files.get(nFiles++); if (!current.endsWith("/")) { current = fpRessource + subFolder + current; } } } zJar.close(); } catch (Exception ex) { log(-1, "doResourceListJar: %s", ex); return false; } return true; } private void copy(InputStream in, OutputStream out) throws IOException { byte[] tmp = new byte[8192]; int len; while (true) { len = in.read(tmp); if (len <= 0) { break; } out.write(tmp, 0, len); } out.flush(); } private byte[] copy(InputStream inputStream) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int length = 0; while ((length = inputStream.read(buffer)) != -1) { baos.write(buffer, 0, length); } return baos.toByteArray(); } public class oneFileFilter implements FilenameFilter { String aFile; public oneFileFilter(String aFileGiven) { aFile = aFileGiven; } @Override public boolean accept(File dir, String name) { if (name.contains(aFile)) { return true; } return false; } } //
// private void storeClassPath() { //TODO Java9 URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); classPath = Arrays.asList(sysLoader.getURLs()); } /** * print the current classpath entries to sysout */ public void dumpClassPath() { dumpClassPath(null); } /** * print the current classpath entries to sysout whose path name contain the given string * * @param filter the fileter string */ public void dumpClassPath(String filter) { filter = filter == null ? "" : filter; logp("*** classpath dump %s", filter); storeClassPath(); String sEntry; filter = filter.toUpperCase(); int n = 0; for (URL uEntry : classPath) { sEntry = uEntry.getPath(); if (!filter.isEmpty()) { if (!sEntry.toUpperCase().contains(filter)) { n++; continue; } } logp("%3d: %s", n, sEntry); n++; } logp("*** classpath dump end"); } /** * check whether a classpath entry contains the given identifying string, stops on first match * * @param artefact the identifying string * @return the absolute path of the entry found - null if not found */ private String isOnClasspath(String artefact, boolean isJar) { artefact = FileManager.slashify(artefact, false); String cpe = null; if (classPath.isEmpty()) { storeClassPath(); } for (URL entry : classPath) { String sEntry = FileManager.slashify(new File(entry.getPath()).getPath(), false); if (sEntry.contains(artefact)) { if (isJar) { if (!sEntry.endsWith(".jar")) { continue; } if (!new File(sEntry).getName().contains(artefact)) { continue; } if (new File(sEntry).getName().contains("4" + artefact)) { continue; } } cpe = new File(entry.getPath()).getPath(); break; } } return cpe; } public String isJarOnClasspath(String artefact) { return isOnClasspath(artefact, true); } public String isOnClasspath(String artefact) { return isOnClasspath(artefact, false); } public URL fromClasspath(String artefact) { artefact = FileManager.slashify(artefact, false).toUpperCase(); URL cpe = null; if (classPath.isEmpty()) { storeClassPath(); } for (URL entry : classPath) { String sEntry = FileManager.slashify(new File(entry.getPath()).getPath(), false); if (sEntry.toUpperCase().contains(artefact)) { return entry; } } return cpe; } /** * check wether a the given URL is on classpath * * @param path URL to look for * @return true if found else otherwise */ public boolean isOnClasspath(URL path) { if (classPath.isEmpty()) { storeClassPath(); } for (URL entry : classPath) { if (new File(path.getPath()).equals(new File(entry.getPath()))) { return true; } } return false; } /** * adds the given folder or jar to the end of the current classpath * * @param jarOrFolder absolute path to a folder or jar * @return success */ public boolean addToClasspath(String jarOrFolder) { URL uJarOrFolder = FileManager.makeURL(jarOrFolder); if (!new File(jarOrFolder).exists()) { log(-1, "addToClasspath: does not exist - not added:\n%s", jarOrFolder); return false; } if (isOnClasspath(uJarOrFolder)) { return true; } log(lvl, "addToClasspath:\n%s", uJarOrFolder); Method method; URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); Class sysclass = URLClassLoader.class; try { method = sysclass.getDeclaredMethod("addURL", new Class[]{URL.class}); method.setAccessible(true); method.invoke(sysLoader, new Object[]{uJarOrFolder}); } catch (Exception ex) { log(-1, "Did not work: %s", ex.getMessage()); return false; } storeClassPath(); return true; } public File asExtension(String fpJar) { File fJarFound = new File(FileManager.normalizeAbsolute(fpJar, false)); if (!fJarFound.exists()) { String fpCPEntry = runTime.isOnClasspath(fJarFound.getName()); if (fpCPEntry == null) { fJarFound = new File(runTime.fSikulixExtensions, fpJar); if (!fJarFound.exists()) { fJarFound = new File(runTime.fSikulixLib, fpJar); if (!fJarFound.exists()) { fJarFound = null; } } } else { fJarFound = new File(fpCPEntry, fJarFound.getName()); } } else { return null; } return fJarFound; } // // /** * print the current java system properties key-value pairs sorted by key */ public void dumpSysProps() { dumpSysProps(null); } /** * print the current java system properties key-value pairs sorted by key but only keys containing filter * * @param filter the filter string */ public void dumpSysProps(String filter) { filter = filter == null ? "" : filter; logp("*** system properties dump " + filter); Properties sysProps = System.getProperties(); ArrayList keysProp = new ArrayList(); Integer nL = 0; String entry; for (Object e : sysProps.keySet()) { entry = (String) e; if (entry.length() > nL) { nL = entry.length(); } if (filter.isEmpty() || !filter.isEmpty() && entry.contains(filter)) { keysProp.add(entry); } } Collections.sort(keysProp); String form = "%-" + nL.toString() + "s = %s"; for (Object e : keysProp) { logp(form, e, sysProps.get(e)); } logp("*** system properties dump end" + filter); } /** * checks, whether Java runs with a valid GraphicsEnvironment (usually means real screens connected) * * @return false if Java thinks it has access to screen(s), true otherwise */ public boolean isHeadless() { return GraphicsEnvironment.isHeadless(); } public boolean isMultiMonitor() { return nMonitors > 1; } public Rectangle getMonitor(int n) { if (isHeadless()) { return new Rectangle(0, 0, 1, 1); } if (null == monitorBounds) { return null; } n = (n < 0 || n >= nMonitors) ? mainMonitor : n; return monitorBounds[n]; } public Rectangle getAllMonitors() { if (isHeadless()) { return new Rectangle(0, 0, 1, 1); } return rAllMonitors; } public Rectangle hasPoint(Point aPoint) { if (isHeadless()) { return new Rectangle(0, 0, 1, 1); } for (Rectangle rMon : monitorBounds) { if (rMon.contains(aPoint)) { return rMon; } } return null; } public Rectangle hasRectangle(Rectangle aRect) { if (isHeadless()) { return new Rectangle(0, 0, 1, 1); } return hasPoint(aRect.getLocation()); } public GraphicsDevice getGraphicsDevice(int id) { return gdevs[id]; } // // /** * run a system command finally using Java::Runtime.getRuntime().exec(args) and waiting for completion * * @param cmd the command as it would be given on command line, quoting is preserved * @return the output produced by the command (sysout [+ "*** error ***" + syserr] if the syserr part is present, the * command might have failed */ public String runcmd(String cmd) { return runcmd(new String[]{cmd}); } /** * run a system command finally using Java::Runtime.getRuntime().exec(args) and waiting for completion * * @param args the command as it would be given on command line splitted into the space devided parts, first part is * the command, the rest are parameters and their values * @return the output produced by the command (sysout [+ "*** error ***" + syserr] if the syserr part is present, the * command might have failed */ public String runcmd(String args[]) { if (args.length == 0) { return ""; } boolean silent = false; if (args.length == 1) { String separator = "\""; ArrayList argsx = new ArrayList(); StringTokenizer toks; String tok; String cmd = args[0]; if (Settings.isWindows()) { cmd = cmd.replaceAll("\\\\ ", "%20;"); } toks = new StringTokenizer(cmd); while (toks.hasMoreTokens()) { tok = toks.nextToken(" "); if (tok.length() == 0) { continue; } if (separator.equals(tok)) { continue; } if (tok.startsWith(separator)) { if (tok.endsWith(separator)) { tok = tok.substring(1, tok.length() - 1); } else { tok = tok.substring(1); tok += toks.nextToken(separator); } } argsx.add(tok.replaceAll("%20;", " ")); } args = argsx.toArray(new String[0]); } if (args[0].startsWith("!")) { silent = true; args[0] = args[0].substring(1); } if (args[0].startsWith("#")) { String pgm = args[0].substring(1); args[0] = (new File(pgm)).getAbsolutePath(); runcmd(new String[]{"chmod", "ugo+x", args[0]}); } String result = ""; String error = runCmdError + NL; String errorOut = ""; boolean hasError = false; int retVal; try { if (!silent) { if (lvl <= Debug.getDebugLevel()) { log(lvl, Sikulix.arrayToString(args)); } else { Debug.info("runcmd: " + Sikulix.arrayToString(args)); } } Process process = Runtime.getRuntime().exec(args); BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream())); String s; while ((s = stdInput.readLine()) != null) { if (!s.isEmpty()) { result += s + NL; } } while ((s = stdError.readLine()) != null) { if (!s.isEmpty()) { errorOut += s + NL; } } if (!errorOut.isEmpty()) { error = error + errorOut; hasError = true; } process.waitFor(); retVal = process.exitValue(); process.destroy(); } catch (Exception e) { log(-1, "fatal error: " + e); result = String.format(error + "%s", e); retVal = 9999; hasError = true; } if (hasError) { result += error; } lastResult = result; return String.format("%d%s%s", retVal, NL, result); } public String getLastCommandResult() { return lastResult; } // // private String[] args = new String[0]; private String[] sargs = new String[0]; public void setArgs(String[] args, String[] sargs) { this.args = args; this.sargs = sargs; } public String[] getSikuliArgs() { return sargs; } public String[] getArgs() { return args; } public void printArgs() { if (Debug.getDebugLevel() < lvl) { return; } String[] xargs = getSikuliArgs(); if (xargs.length > 0) { Debug.log(lvl, "--- Sikuli parameters ---"); for (int i = 0; i < xargs.length; i++) { Debug.log(lvl, "%d: %s", i + 1, xargs[i]); } } xargs = getArgs(); if (xargs.length > 0) { Debug.log(lvl, "--- User parameters ---"); for (int i = 0; i < xargs.length; i++) { Debug.log(lvl, "%d: %s", i + 1, xargs[i]); } } } public static int checkArgs(String[] args, Type typ) { int debugLevel = -99; boolean runningScripts = false; List options = new ArrayList(); options.addAll(Arrays.asList(args)); for (int n = 0; n < options.size(); n++) { String opt = options.get(n); if ("nodebug".equals(opt)) { return -2; } if (Type.IDE.equals(typ) && "-s".equals(opt.toLowerCase())) { return -3; } if (!opt.startsWith("-")) { continue; } if (opt.startsWith("-d")) { debugLevel = -1; try { debugLevel = n + 1 == options.size() ? -1 : Integer.decode(options.get(n + 1)); } catch (Exception ex) { debugLevel = -1; } if (debugLevel > -1) { Debug.on(debugLevel); } } else if (opt.startsWith("-r") || opt.startsWith("-t") || opt.startsWith("-s") || opt.startsWith("-i")) { runningScripts = true; } } if (runningScripts) { return 999; } return debugLevel; } // } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Runner.java000066400000000000000000001411331315726130400237630ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.apache.commons.cli.CommandLine; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.basics.Settings; import org.sikuli.util.CommandArgs; import org.sikuli.util.CommandArgsEnum; import org.sikuli.util.JythonHelper; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import java.io.File; import java.io.FileReader; import java.lang.reflect.Method; import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * INTERNAL USE --- NOT official API
* not in version 2 */ public class Runner { static final String me = "Runner: "; static final int lvl = 3; static final RunTime runTime = RunTime.get(); public static Map endingTypes = new HashMap(); public static Map typeEndings = new HashMap(); public static Map runnerTypes = new HashMap(); public static String ERUBY = "rb"; public static String EPYTHON = "py"; public static String EJSCRIPT = "js"; public static String EASCRIPT = "script"; public static String ESSCRIPT = "ps1"; public static String EPLAIN = "txt"; public static String EDEFAULT = EPYTHON; public static String CPYTHON = "text/python"; public static String CRUBY = "text/ruby"; public static String CJSCRIPT = "text/javascript"; public static String CASCRIPT = "text/applescript"; public static String CSSCRIPT = "text/powershell"; public static String CPLAIN = "text/plain"; public static String RPYTHON = "jython"; public static String RRUBY = "jruby"; public static String RJSCRIPT = "JavaScript"; public static String RASCRIPT = "AppleScript"; public static String RSSCRIPT = "PowerShell"; public static String RRSCRIPT = "Robot"; public static String RDEFAULT = RPYTHON; private static String[] runScripts = null; private static String[] testScripts = null; private static int lastReturnCode = 0; private static String beforeJSjava8 = "load(\"nashorn:mozilla_compat.js\");"; private static String beforeJS = "importPackage(Packages.org.sikuli.script); " + "importClass(Packages.org.sikuli.basics.Debug); " + "importClass(Packages.org.sikuli.basics.Settings);"; static { endingTypes.put(EPYTHON, CPYTHON); endingTypes.put(ERUBY, CRUBY); endingTypes.put(EJSCRIPT, CJSCRIPT); endingTypes.put(EPLAIN, CPLAIN); for (String k : endingTypes.keySet()) { typeEndings.put(endingTypes.get(k), k); } runnerTypes.put(EPYTHON, RPYTHON); runnerTypes.put(ERUBY, RRUBY); runnerTypes.put(EJSCRIPT, RJSCRIPT); } static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } public static String[] evalArgs(String[] args) { CommandArgs cmdArgs = new CommandArgs("SCRIPT"); CommandLine cmdLine = cmdArgs.getCommandLine(CommandArgs.scanArgs(args)); String cmdValue; if (cmdLine == null || cmdLine.getOptions().length == 0) { log(-1, "Did not find any valid option on command line!"); cmdArgs.printHelp(); System.exit(1); } if (cmdLine.hasOption(CommandArgsEnum.HELP.shortname())) { cmdArgs.printHelp(); System.exit(1); } if (cmdLine.hasOption(CommandArgsEnum.LOGFILE.shortname())) { cmdValue = cmdLine.getOptionValue(CommandArgsEnum.LOGFILE.longname()); if (!Debug.setLogFile(cmdValue == null ? "" : cmdValue)) { System.exit(1); } } if (cmdLine.hasOption(CommandArgsEnum.USERLOGFILE.shortname())) { cmdValue = cmdLine.getOptionValue(CommandArgsEnum.USERLOGFILE.longname()); if (!Debug.setUserLogFile(cmdValue == null ? "" : cmdValue)) { System.exit(1); } } if (cmdLine.hasOption(CommandArgsEnum.DEBUG.shortname())) { cmdValue = cmdLine.getOptionValue(CommandArgsEnum.DEBUG.longname()); if (cmdValue == null) { Debug.setDebugLevel(3); Settings.LogTime = true; if (!Debug.isLogToFile()) { Debug.setLogFile(""); } } else { Debug.setDebugLevel(cmdValue); } } runTime.setArgs(cmdArgs.getUserArgs(), cmdArgs.getSikuliArgs()); log(lvl, "commandline: %s", cmdArgs.getArgsOrg()); runTime.printArgs(); // select script runner and/or start interactive session // option is overloaded - might specify runner for -r/-t if (cmdLine.hasOption(CommandArgsEnum.INTERACTIVE.shortname())) { if (!cmdLine.hasOption(CommandArgsEnum.RUN.shortname()) && !cmdLine.hasOption(CommandArgsEnum.TEST.shortname())) { runTime.interactiveRunner = cmdLine.getOptionValue(CommandArgsEnum.INTERACTIVE.longname()); runTime.runningInteractive = true; return null; } } String[] runScripts = null; runTime.runningTests = false; if (cmdLine.hasOption(CommandArgsEnum.RUN.shortname())) { runScripts = cmdLine.getOptionValues(CommandArgsEnum.RUN.longname()); } else if (cmdLine.hasOption(CommandArgsEnum.TEST.shortname())) { runScripts = cmdLine.getOptionValues(CommandArgsEnum.TEST.longname()); log(-1, "Command line option -t: not yet supported! %s", Arrays.asList(args).toString()); runTime.runningTests = true; //TODO run a script as unittest with HTMLTestRunner System.exit(1); } return runScripts; } public static int run(String givenName) { return run(givenName, new String[0]); } public static int run(String givenName, String[] args) { String savePath = ImagePath.getBundlePathSet(); int retVal = new RunBox(givenName, args, false).run(); if (savePath != null) { ImagePath.setBundlePath(savePath); } lastReturnCode = retVal; return retVal; } public static int runTest(String givenName) { return runTest(givenName, new String[0]); } public static int runTest(String givenName, String[] args) { String savePath = ImagePath.getBundlePath(); int retVal = new RunBox(givenName, args, true).run(); ImagePath.setBundlePath(savePath); return retVal; } public static int getLastReturnCode() { return lastReturnCode; } public static int runScripts(String[] args) { runScripts = Runner.evalArgs(args); String someJS = ""; int exitCode = 0; if (runScripts != null && runScripts.length > 0) { boolean runAsTest = runTime.runningTests; for (String givenScriptName : runScripts) { if (lastReturnCode == -1) { log(lvl, "Exit code -1: Terminating multi-script-run"); break; } someJS = runTime.getOption("runsetup", ""); if (!someJS.isEmpty()) { log(lvl, "Options.runsetup: %s", someJS); new RunBox().runjs(null, null, someJS, null); } RunBox rb = new RunBox(givenScriptName, runTime.getArgs(), runAsTest); exitCode = rb.run(); someJS = runTime.getOption("runteardown", ""); if (!someJS.isEmpty()) { log(lvl, "Options.runteardown: %s", someJS); new RunBox().runjs(null, null, someJS, null); } if (exitCode == -999) { exitCode = lastReturnCode; } lastReturnCode = exitCode; } } return exitCode; } public static File getScriptFile(File fScriptFolder) { if (fScriptFolder == null) { return null; } File[] content = FileManager.getScriptFile(fScriptFolder); if (null == content) { return null; } File fScript = null; for (File aFile : content) { for (String suffix : Runner.endingTypes.keySet()) { if (!aFile.getName().endsWith("." + suffix)) { continue; } fScript = aFile; break; } if (fScript != null) { break; } } return fScript; } static JythonHelper pyRunner = null; static Class cIDE; static Method mShow; static Method mHide; protected static boolean initpy() { if (pyRunner == null) { pyRunner = JythonHelper.get(); if (pyRunner == null) { return false; } pyRunner.exec("# -*- coding: utf-8 -*- "); pyRunner.exec("import org.sikuli.script.SikulixForJython"); pyRunner.exec("from sikuli import *"); } return true; } static Object rbRunner = null; static Object txtRunner = null; static ScriptEngine jsRunner = null; public static ScriptEngine initjs() { ScriptEngineManager jsFactory = new ScriptEngineManager(); ScriptEngine jsr = jsFactory.getEngineByName("JavaScript"); if (jsr != null) { log(lvl, "ScriptingEngine started: JavaScript (ending .js)"); } else { runTime.terminate(1, "ScriptingEngine for JavaScript not available"); } if (RunTime.Type.IDE.equals(runTime.runType)) { try { cIDE = Class.forName("org.sikuli.ide.SikuliIDE"); mHide = cIDE.getMethod("hideIDE", new Class[0]); mShow = cIDE.getMethod("showIDE", new Class[0]); } catch (Exception ex) { log(-1, "initjs: getIDE"); } } return jsr; } public static String prologjs(String before) { String after = before; if (after.isEmpty()) { if (runTime.isJava8()) { after += beforeJSjava8; } after += beforeJS; } else { String commands = runTime.extractResourceToString("JavaScript", "commands.js", ""); if (commands != null) { after += commands; } } return after; } public static Object[] runBoxInit(String givenName, File scriptProject, URL uScriptProject) { String gitScripts = "https://github.com/RaiMan/SikuliX-2014/tree/master/TestScripts/"; String givenScriptHost = ""; String givenScriptFolder = ""; String givenScriptName; String givenScriptScript = ""; String givenScriptType = "sikuli"; String givenScriptScriptType = RDEFAULT; Boolean givenScriptExists = true; URL uGivenScript = null; URL uGivenScriptFile = null; givenScriptName = givenName; String[] parts = null; int isNet; boolean isInline = false; givenName = givenName.trim(); if (givenName.toLowerCase().startsWith(RASCRIPT.toLowerCase())) { givenScriptScriptType = RASCRIPT; givenScriptName = null; givenScriptScript = givenName.substring(RASCRIPT.length() + 1); isInline = true; } else if (givenName.toLowerCase().startsWith(RSSCRIPT.toLowerCase())) { givenScriptScriptType = RSSCRIPT; givenScriptName = null; givenScriptScript = givenName.substring(RSSCRIPT.length() + 1); isInline = true; } else if (givenName.toLowerCase().startsWith(RRSCRIPT.toLowerCase())) { givenScriptScriptType = RRSCRIPT; givenScriptName = null; givenScriptScript = givenName.substring(RRSCRIPT.length() + 1); isInline = true; } else if (givenName.toLowerCase().startsWith("git*")) { if (givenName.length() == 4) { givenName = gitScripts + "showcase"; } else { givenName = gitScripts + givenName.substring(4); } } if (!isInline) { boolean fromNet = false; String scriptLocation = givenName; String content = ""; if (-1 < (isNet = givenName.indexOf("://"))) { givenName = givenName.substring(isNet + 3); if (givenName.indexOf(":") == -1) { givenName = givenName.replaceFirst("/", ":"); } } if (givenName.indexOf(":") > 5) { parts = givenName.split(":"); if (parts.length > 1 && !parts[1].isEmpty()) { fromNet = true; givenScriptHost = parts[0]; givenScriptName = new File(parts[1]).getName(); String fpFolder = new File(parts[1]).getParent(); if (null != fpFolder && !fpFolder.isEmpty()) { givenScriptFolder = FileManager.slashify(fpFolder, true); } } scriptLocation = givenName; givenScriptExists = false; } if (fromNet) { if (givenScriptHost.contains("github.com")) { givenScriptHost = "https://raw.githubusercontent.com"; givenScriptFolder = givenScriptFolder.replace("tree/", ""); } else { givenScriptHost = "http://" + givenScriptHost; } if (givenScriptName.endsWith(".zip")) { scriptLocation = givenScriptHost + givenScriptFolder + givenScriptName; if (0 < FileManager.isUrlUseabel(scriptLocation)) { runTime.terminate(1, ".zip from net not yet supported\n%s", scriptLocation); } } else { for (String suffix : endingTypes.keySet()) { String dlsuffix = ""; if (suffix != "js") { dlsuffix = ".txt"; } givenScriptScript = givenScriptName + "/" + givenScriptName + "." + suffix; scriptLocation = givenScriptHost + "/" + givenScriptFolder + givenScriptScript; givenScriptScriptType = runnerTypes.get(suffix); if (0 < FileManager.isUrlUseabel(scriptLocation)) { content = FileManager.downloadURLtoString(scriptLocation); break; } else if (!dlsuffix.isEmpty()) { givenScriptScript = givenScriptName + "/" + givenScriptName + "." + suffix + dlsuffix; scriptLocation = givenScriptHost + "/" + givenScriptFolder + givenScriptScript; if (0 < FileManager.isUrlUseabel(scriptLocation)) { content = FileManager.downloadURLtoString(scriptLocation); break; } } scriptLocation = givenScriptHost + "/" + givenScriptFolder + givenScriptName; } if (content != null && !content.isEmpty()) { givenScriptType = "NET"; givenScriptScript = content; givenScriptExists = true; try { uGivenScript = new URL(givenScriptHost + "/" + givenScriptFolder + givenScriptName); } catch (Exception ex) { givenScriptExists = false; } } else { givenScriptExists = false; } } if (!givenScriptExists) { log(-1, "given script location not supported or not valid:\n%s", scriptLocation); } else { String header = "# "; String trailer = "\n"; if (RJSCRIPT.equals(givenScriptScriptType)) { header = "/*\n"; trailer = "*/\n"; } header += scriptLocation + "\n"; if (Debug.is() > 2) { FileManager.writeStringToFile(header + trailer + content, new File(runTime.fSikulixStore, "LastScriptFromNet.txt")); } } } else { boolean sameFolder = givenScriptName.startsWith("./"); if (sameFolder) { givenScriptName = givenScriptName.substring(2); } if (givenScriptName.startsWith("JS*")) { givenScriptName = new File(runTime.fSxProjectTestScriptsJS, givenScriptName.substring(3)).getPath(); } if (givenScriptName.startsWith("TEST*")) { givenScriptName = new File(runTime.fSxProjectTestScripts, givenScriptName.substring(5)).getPath(); } String scriptName = new File(givenScriptName).getName(); if (scriptName.contains(".")) { parts = scriptName.split("\\."); givenScriptScript = parts[0]; givenScriptType = parts[1]; } else { givenScriptScript = scriptName; } if (sameFolder && scriptProject != null) { givenScriptName = new File(scriptProject, givenScriptName).getPath(); } else if (sameFolder && uScriptProject != null) { givenScriptHost = uScriptProject.getHost(); givenScriptFolder = uScriptProject.getPath().substring(1); } else if (scriptProject == null && givenScriptHost.isEmpty()) { String fpParent = new File(givenScriptName).getParent(); if (fpParent == null || fpParent.isEmpty()) { scriptProject = null; } else { scriptProject = new File(givenScriptName).getParentFile(); } } } } Object[] vars = new Object[]{givenScriptHost, givenScriptFolder, givenScriptName, givenScriptScript, givenScriptType, givenScriptScriptType, uGivenScript, uGivenScriptFile, givenScriptExists, scriptProject, uScriptProject}; return vars; } public static void runjsEval(String script) { new RunBox().runjsEval(script); } public static int runjs(File fScript, URL uGivenScript, String givenScriptScript, String[] args) { return new RunBox().runjs(fScript, uGivenScript, givenScriptScript, args); } public static int runas(String givenScriptScript) { return runas(givenScriptScript, false); } public static int runrobot(String code) { if (!JythonHelper.get().prepareRobot()) { return -1; } File script = new File(ImagePath.getBundlePath()); File fRobotWork = new File(script.getAbsolutePath() + ".robot"); FileManager.deleteFileOrFolder(fRobotWork); fRobotWork.mkdir(); String sName = script.getName().replace(".sikuli", ""); File fPyCode = new File(script, sName + ".py"); String pyCode = FileManager.readFileToString(fPyCode); int prefix = pyCode.indexOf("\"\"\")"); if (prefix > 0) { pyCode = pyCode.substring(prefix + 4).trim(); int refLib = code.indexOf("./inline/"); String inlineLib = ""; File fInline = null; String fpInline = ""; // Keyword implementations are inline if (!pyCode.isEmpty()) { if (refLib < 0) { log(-1, "runRobot: inline code ignored - no ./inline/"); } inlineLib = code.substring(refLib + 9); inlineLib = inlineLib.substring(0, inlineLib.indexOf("\n")).trim(); fInline = new File(fRobotWork, inlineLib + ".py"); pyCode = "from sikuli import *\n" + pyCode; FileManager.writeStringToFile(pyCode, fInline); fpInline = FileManager.slashify(fInline.getAbsolutePath(), false); code = code.replace("./inline/" + inlineLib, fpInline); } else { if (refLib > -1) { log(-1, "runRobot: having ./inline/, but no inline code found"); return -1; } } } File fRobot = new File(fRobotWork, sName + ".robot"); FileManager.writeStringToFile(code, fRobot); if (!initpy()) { log(-1, "Running Python scripts:init failed"); return -999; } pyRunner.exec("from sikuli import *; from threading import currentThread; currentThread().name = \"MainThread\""); //pyRunner.exec("import robot.run;"); String robotCmd = String.format( "ret = robot.run(\"%s\", " + "outputdir=\"%s\")", fRobot, fRobotWork); File fReport = new File(fRobotWork, "report.html"); String urlReport = fReport.getAbsolutePath(); if (RunTime.get().runningWindows) { robotCmd = robotCmd.replaceAll("\\\\", "\\\\\\\\"); urlReport = "/" + urlReport.replaceAll("\\\\", "/"); } pyRunner.exec(robotCmd + "; print \"robot.run returned:\", ret; " + String.format("print \"robot.run output is here:\\n%s\";", fRobotWork.getAbsolutePath().replaceAll("\\\\", "\\\\\\\\"))); if (new File(fRobotWork, "report.html").exists()) { App.openLink("file:" + urlReport); } return 0; } // // -N --name name Set the name of the top level test suite. Underscores // in the name are converted to spaces. Default name is // created from the name of the executed data source. // -D --doc documentation Set the documentation of the top level test suite. // Underscores in the documentation are converted to // spaces and it may also contain simple HTML formatting // (e.g. *bold* and http://url/). // -M --metadata name:value * Set metadata of the top level suite. Underscores // in the name and value are converted to spaces. Value // can contain same HTML formatting as --doc. // Example: --metadata version:1.2 // -G --settag tag * Sets given tag(s) to all executed test cases. // -t --test name * Select test cases to run by name or long name. Name // is case and space insensitive and it can also be a // simple pattern where `*` matches anything and `?` // matches any char. If using `*` and `?` in the console // is problematic see --escape and --argumentfile. // -s --suite name * Select test suites to run by name. When this option // is used with --test, --include or --exclude, only // test cases in matching suites and also matching other // filtering criteria are selected. Name can be a simple // pattern similarly as with --test and it can contain // parent name separated with a dot. For example // `-s X.Y` selects suite `Y` only if its parent is `X`. // -i --include tag * Select test cases to run by tag. Similarly as name // with --test, tag is case and space insensitive and it // is possible to use patterns with `*` and `?` as // wildcards. Tags and patterns can also be combined // together with `AND`, `OR`, and `NOT` operators. // Examples: --include foo --include bar* // --include fooANDbar* // -e --exclude tag * Select test cases not to run by tag. These tests are // not run even if included with --include. Tags are // matched using the rules explained with --include. // -R --rerunfailed output Select failed tests from an earlier output file to be // re-executed. Equivalent to selecting same tests // individually using --test option. // --runfailed output Deprecated since RF 2.8.4. Use --rerunfailed instead. // -c --critical tag * Tests having given tag are considered critical. If no // critical tags are set, all tags are critical. Tags // can be given as a pattern like with --include. // -n --noncritical tag * Tests with given tag are not critical even if they // have a tag set with --critical. Tag can be a pattern. // -v --variable name:value * Set variables in the test data. Only scalar // variables are supported and name is given without // `${}`. See --escape for how to use special characters // and --variablefile for a more powerful variable // setting mechanism that allows also list variables. // Examples: // --variable str:Hello => ${str} = `Hello` // -v str:Hi_World -E space:_ => ${str} = `Hi World` // -v x: -v y:42 => ${x} = ``, ${y} = `42` // -V --variablefile path * File to read variables from (e.g. `path/vars.py`). // Example file: // | import random // | __all__ = [`scalar`, `LIST__var`, `integer`] // | scalar = `Hello world!` // | LIST__var = [`Hello`, `list`, `world`] // | integer = random.randint(1,10) // => // ${scalar} = `Hello world!` // @{var} = [`Hello`,`list`,`world`] // ${integer} = // -d --outputdir dir Where to create output files. The default is the // directory where tests are run from and the given path // is considered relative to that unless it is absolute. // -o --output file XML output file. Given path, similarly as paths given // to --log, --report, --xunit, and --debugfile, is // relative to --outputdir unless given as an absolute // path. Other output files are created based on XML // output files after the test execution and XML outputs // can also be further processed with Rebot tool. Can be // disabled by giving a special value `NONE`. In this // case, also log and report are automatically disabled. // Default: output.xml // -l --log file HTML log file. Can be disabled by giving a special // value `NONE`. Default: log.html // Examples: `--log mylog.html`, `-l NONE` // -r --report file HTML report file. Can be disabled with `NONE` // similarly as --log. Default: report.html // -x --xunit file xUnit compatible result file. Not created unless this // option is specified. // --xunitfile file Deprecated. Use --xunit instead. // --xunitskipnoncritical Mark non-critical tests on xUnit output as skipped. // -b --debugfile file Debug file written during execution. Not created // unless this option is specified. // -T --timestampoutputs When this option is used, timestamp in a format // `YYYYMMDD-hhmmss` is added to all generated output // files between their basename and extension. For // example `-T -o output.xml -r report.html -l none` // creates files like `output-20070503-154410.xml` and // `report-20070503-154410.html`. // --splitlog Split log file into smaller pieces that open in // browser transparently. // --logtitle title Title for the generated test log. The default title // is ` Test Log`. Underscores in // the title are converted into spaces in all titles. // --reporttitle title Title for the generated test report. The default // title is ` Test Report`. // --reportbackground colors Background colors to use in the report file. // Either `all_passed:critical_passed:failed` or // `passed:failed`. Both color names and codes work. // Examples: --reportbackground green:yellow:red // --reportbackground #00E:#E00 // -L --loglevel level Threshold level for logging. Available levels: TRACE, // DEBUG, INFO (default), WARN, NONE (no logging). Use // syntax `LOGLEVEL:DEFAULT` to define the default // visible log level in log files. // Examples: --loglevel DEBUG // --loglevel DEBUG:INFO // --suitestatlevel level How many levels to show in `Statistics by Suite` // in log and report. By default all suite levels are // shown. Example: --suitestatlevel 3 // --tagstatinclude tag * Include only matching tags in `Statistics by Tag` // and `Test Details` in log and report. By default all // tags set in test cases are shown. Given `tag` can // also be a simple pattern (see e.g. --test). // --tagstatexclude tag * Exclude matching tags from `Statistics by Tag` and // `Test Details`. This option can be used with // --tagstatinclude similarly as --exclude is used with // --include. // --tagstatcombine tags:name * Create combined statistics based on tags. // These statistics are added into `Statistics by Tag` // and matching tests into `Test Details`. If optional // `name` is not given, name of the combined tag is got // from the specified tags. Tags are combined using the // rules explained in --include. // Examples: --tagstatcombine requirement-* // --tagstatcombine tag1ANDtag2:My_name // --tagdoc pattern:doc * Add documentation to tags matching given pattern. // Documentation is shown in `Test Details` and also as // a tooltip in `Statistics by Tag`. Pattern can contain // characters `*` (matches anything) and `?` (matches // any char). Documentation can contain formatting // similarly as with --doc option. // Examples: --tagdoc mytag:My_documentation // --tagdoc regression:*See*_http://info.html // --tagdoc owner-*:Original_author // --tagstatlink pattern:link:title * Add external links into `Statistics by // Tag`. Pattern can contain characters `*` (matches // anything) and `?` (matches any char). Characters // matching to wildcard expressions can be used in link // and title with syntax %N, where N is index of the // match (starting from 1). In title underscores are // automatically converted to spaces. // Examples: --tagstatlink mytag:http://my.domain:Link // --tagstatlink bug-*:http://tracker/id=%1:Bug_Tracker // --removekeywords all|passed|for|wuks|name: * Remove keyword data // from the generated log file. Keywords containing // warnings are not removed except in `all` mode. // all: remove data from all keywords // passed: remove data only from keywords in passed // test cases and suites // for: remove passed iterations from for loops // wuks: remove all but the last failing keyword // inside `BuiltIn.Wait Until Keyword Succeeds` // name:: remove data from keywords that match // the given pattern. The pattern is matched // against the full name of the keyword (e.g. // 'MyLib.Keyword', 'resource.Second Keyword'), // is case, space, and underscore insensitive, // and may contain `*` and `?` as wildcards. // Examples: --removekeywords name:Lib.HugeKw // --removekeywords name:myresource.* // --flattenkeywords for|foritem|name: * Flattens matching keywords // in the generated log file. Matching keywords get all // log messages from their child keywords and children // are discarded otherwise. // for: flatten for loops fully // foritem: flatten individual for loop iterations // name:: flatten matched keywords using same // matching rules as with // `--removekeywords name:` // --listener class * A class for monitoring test execution. Gets // notifications e.g. when a test case starts and ends. // Arguments to listener class can be given after class // name, using colon as separator. For example: // --listener MyListenerClass:arg1:arg2 // --warnonskippedfiles If this option is used, skipped test data files will // cause a warning that is visible in the console output // and the log file. By default skipped files only cause // an info level syslog message. // --nostatusrc Sets the return code to zero regardless of failures // in test cases. Error codes are returned normally. // --runemptysuite Executes tests also if the top level test suite is // empty. Useful e.g. with --include/--exclude when it // is not an error that no test matches the condition. // --dryrun Verifies test data and runs tests so that library // keywords are not executed. // --exitonfailure Stops test execution if any critical test fails. // --exitonerror Stops test execution if any error occurs when parsing // test data, importing libraries, and so on. // --skipteardownonexit Causes teardowns to be skipped if test execution is // stopped prematurely. // --randomize all|suites|tests|none Randomizes the test execution order. // all: randomizes both suites and tests // suites: randomizes suites // tests: randomizes tests // none: no randomization (default) // Use syntax `VALUE:SEED` to give a custom random seed. // The seed must be an integer. // Examples: --randomize all // --randomize tests:1234 // --runmode mode * Deprecated in version 2.8. Use individual options // --dryrun, --exitonfailure, --skipteardownonexit, or // --randomize instead. // -W --monitorwidth chars Width of the monitor output. Default is 78. // -C --monitorcolors auto|on|ansi|off Use colors on console output or not. // auto: use colors when output not redirected (default) // on: always use colors // ansi: like `on` but use ANSI colors also on Windows // off: disable colors altogether // Note that colors do not work with Jython on Windows. // -K --monitormarkers auto|on|off Show `.` (success) or `F` (failure) on // console when top level keywords in test cases end. // Values have same semantics as with --monitorcolors. // -P --pythonpath path * Additional locations (directories, ZIPs, JARs) where // to search test libraries from when they are imported. // Multiple paths can be given by separating them with a // colon (`:`) or using this option several times. Given // path can also be a glob pattern matching multiple // paths but then it normally must be escaped or quoted. // Examples: // --pythonpath libs/ // --pythonpath /opt/testlibs:mylibs.zip:yourlibs // -E star:STAR -P lib/STAR.jar -P mylib.jar // -E --escape what:with * Escape characters which are problematic in console. // `what` is the name of the character to escape and // `with` is the string to escape it with. Note that // all given arguments, incl. data sources, are escaped // so escape characters ought to be selected carefully. // <--------------------ESCAPES------------------------> // Examples: // --escape space:_ --metadata X:Value_with_spaces // -E space:SP -E quot:Q -v var:QhelloSPworldQ // -A --argumentfile path * Text file to read more arguments from. Use special // path `STDIN` to read contents from the standard input // stream. File can have both options and data sources // one per line. Contents do not need to be escaped but // spaces in the beginning and end of lines are removed. // Empty lines and lines starting with a hash character // (#) are ignored. // Example file: // | --include regression // | --name Regression Tests // | # This is a comment line // | my_tests.html // | path/to/test/directory/ // Examples: // --argumentfile argfile.txt --argumentfile STDIN // -h -? --help Print usage instructions. // --version Print version information. // //Options that are marked with an asterisk (*) can be specified multiple times. //For example, `--test first --test third` selects test cases with name `first` //and `third`. If other options are given multiple times, the last value is used. // //Long option format is case-insensitive. For example, --SuiteStatLevel is //equivalent to but easier to read than --suitestatlevel. Long options can //also be shortened as long as they are unique. For example, `--logti Title` //works while `--lo log.html` does not because the former matches only --logtitle //but the latter matches --log, --loglevel and --logtitle. // //Environment Variables //===================== // //ROBOT_OPTIONS Space separated list of default options to be placed // in front of any explicit options on the command line. //ROBOT_SYSLOG_FILE Path to a file where Robot Framework writes internal // information about parsing test case files and running // tests. Can be useful when debugging problems. If not // set, or set to special value `NONE`, writing to the // syslog file is disabled. //ROBOT_SYSLOG_LEVEL Log level to use when writing to the syslog file. // Available levels are the same as for --loglevel // command line option and the default is INFO. // public static int runas(String givenScriptScript, boolean silent) { if (!runTime.runningMac) { return -1; } String prefix = silent ? "!" : ""; String osascriptShebang = "#!/usr/bin/osascript\n"; givenScriptScript = osascriptShebang + givenScriptScript; File aFile = FileManager.createTempFile("script"); aFile.setExecutable(true); FileManager.writeStringToFile(givenScriptScript, aFile); String retVal = runTime.runcmd(new String[]{prefix + aFile.getAbsolutePath()}); String[] parts = retVal.split("\n"); int retcode = -1; try { retcode = Integer.parseInt(parts[0]); } catch (Exception ex) { } if (retcode != 0) { if (silent) { log(lvl, "AppleScript:\n%s\nreturned:\n%s", givenScriptScript, runTime.getLastCommandResult()); } else { log(-1, "AppleScript:\n%s\nreturned:\n%s", givenScriptScript, runTime.getLastCommandResult()); } } return retcode; } public static int runps(String givenScriptScript) { if (!runTime.runningWindows) { return -1; } File aFile = FileManager.createTempFile("ps1"); FileManager.writeStringToFile(givenScriptScript, aFile); String[] psDirect = new String[]{ "powershell.exe", "-ExecutionPolicy", "UnRestricted", "-NonInteractive", "-NoLogo", "-NoProfile", "-WindowStyle", "Hidden", "-File", aFile.getAbsolutePath() }; String[] psCmdType = new String[]{ "cmd.exe", "/S", "/C", "type " + aFile.getAbsolutePath() + " | powershell -noprofile -" }; String retVal = runTime.runcmd(psCmdType); String[] parts = retVal.split("\\s"); int retcode = -1; try { retcode = Integer.parseInt(parts[0]); } catch (Exception ex) { } if (retcode != 0) { log(-1, "PowerShell:\n%s\nreturned:\n%s", givenScriptScript, runTime.getLastCommandResult()); } return retcode; } public static int runpy(File fScript, URL uGivenScript, String givenScriptScript, String[] args) { return new RunBox().runpy(fScript, uGivenScript, givenScriptScript, args); } public static int runrb(File fScript, URL uGivenScript, String givenScriptScript, String[] args) { return new RunBox().runrb(fScript, uGivenScript, givenScriptScript, args); } public static int runtxt(File fScript, URL uGivenScript, String givenScriptScript, String[] args) { return new RunBox().runtxt(fScript, uGivenScript, givenScriptScript, args); } static class RunBox { String jsScript; public RunBox() { } public static int runtxt(File fScript, URL script, String scriptName, String[] args) { Runner.log(-1, "Running plain text scripts not yet supported!"); return -999; } public static int runrb(File fScript, URL script, String scriptName, String[] args) { Runner.log(-1, "Running Ruby scripts not yet supported!"); return -999; } public static int runpy(File fScript, URL script, String scriptName, String[] args) { String fpScript; if (fScript == null) { fpScript = script.toExternalForm(); } else { fpScript = fScript.getAbsolutePath(); } if (!Runner.initpy()) { Runner.log(-1, "Running Python scripts:init failed"); return -999; } if (args == null || args.length == 0) { args = RunTime.get().getArgs(); } String[] newArgs = new String[args.length + 1]; for (int i = 0; i < args.length; i++) { newArgs[i + 1] = args[i]; } Runner.pyRunner.setSysArgv(newArgs); newArgs[0] = fpScript; int retval; if (fScript == null) { ImagePath.addHTTP(fpScript); retval = (Runner.pyRunner.exec(scriptName) ? 0 : -1); ImagePath.removeHTTP(fpScript); } else { if (null == ImagePath.getBundlePathSet()) ImagePath.setBundlePath(fScript.getParent()); else { ImagePath.add(fScript.getParent()); } retval = Runner.pyRunner.execfile(fpScript); } return retval; } public void runjsEval(String script) { if (script.isEmpty()) { return; } String initSikulix = ""; if (Runner.jsRunner == null) { Runner.jsRunner = Runner.initjs(); initSikulix = Runner.prologjs(initSikulix); } try { if (!initSikulix.isEmpty()) { initSikulix = Runner.prologjs(initSikulix); Runner.jsRunner.eval(initSikulix); } Runner.log(lvl, "JavaScript: eval: %s", script); jsScript = script; Thread evalThread = new Thread() { @Override public void run() { try { Runner.mHide.invoke(null, new Class[0]); Runner.jsRunner.eval(jsScript); Runner.mShow.invoke(null, new Class[0]); } catch (Exception ex) { Runner.log(-1, "not possible:\n%s", ex); try { Runner.mShow.invoke(null, new Class[0]); } catch (Exception e) { Sikulix.terminate(901); } } } }; evalThread.start(); } catch (Exception ex) { Runner.log(-1, "init not possible:\n%s", ex); } } public int runjs(File fScript, URL script, String scriptName, String[] args) { String initSikulix = ""; if (Runner.jsRunner == null) { Runner.jsRunner = Runner.initjs(); initSikulix = Runner.prologjs(initSikulix); } try { if (null != fScript) { File innerBundle = new File(fScript.getParentFile(), scriptName + ".sikuli"); if (innerBundle.exists()) { ImagePath.setBundlePath(innerBundle.getCanonicalPath()); } else { ImagePath.setBundlePath(fScript.getParent()); } } else if (script != null) { ImagePath.addHTTP(script.toExternalForm()); String sname = new File(script.toExternalForm()).getName(); ImagePath.addHTTP(script.toExternalForm() + "/" + sname + ".sikuli"); } if (!initSikulix.isEmpty()) { initSikulix = Runner.prologjs(initSikulix); Runner.jsRunner.eval(initSikulix); initSikulix = ""; } if (null != fScript) { Runner.jsRunner.eval(new FileReader(fScript)); } else { Runner.jsRunner.eval(scriptName); } } catch (Exception ex) { Runner.log(-1, "not possible:\n%s", ex); } return 0; } // static File scriptProject = null; // static URL uScriptProject = null; RunTime runTime = RunTime.get(); boolean asTest = false; String[] args = new String[0]; String givenScriptHost = ""; String givenScriptFolder = ""; String givenScriptName = ""; String givenScriptScript = ""; String givenScriptType = "sikuli"; String givenScriptScriptType = RDEFAULT; URL uGivenScript = null; URL uGivenScriptFile = null; boolean givenScriptExists = true; RunBox(String givenName, String[] givenArgs, boolean isTest) { Object[] vars = Runner.runBoxInit(givenName, RunTime.scriptProject, RunTime.uScriptProject); givenScriptHost = (String) vars[0]; givenScriptFolder = (String) vars[1]; givenScriptName = (String) vars[2]; givenScriptScript = (String) vars[3]; givenScriptType = (String) vars[4]; givenScriptScriptType = (String) vars[5]; uGivenScript = (URL) vars[6]; uGivenScriptFile = (URL) vars[7]; givenScriptExists = (Boolean) vars[8]; RunTime.scriptProject = (File) vars[9]; RunTime.uScriptProject = (URL) vars[10]; args = givenArgs; asTest = isTest; } int run() { if (Runner.RASCRIPT.equals(givenScriptScriptType)) { return Runner.runas(givenScriptScript); } else if (Runner.RSSCRIPT.equals(givenScriptScriptType)) { return Runner.runps(givenScriptScript); } else if (Runner.RRSCRIPT.equals(givenScriptScriptType)) { return Runner.runrobot(givenScriptScript); } int exitCode = 0; log(lvl, "givenScriptName:\n%s", givenScriptName); if (-1 == FileManager.slashify(givenScriptName, false).indexOf("/") && RunTime.scriptProject != null) { givenScriptName = new File(RunTime.scriptProject, givenScriptName).getPath(); } if (givenScriptName.endsWith(".skl")) { log(-1, "RunBox.run: .skl scripts not yet supported."); return -9999; // givenScriptName = FileManager.unzipSKL(givenScriptName); // if (givenScriptName == null) { // log(-1, "not possible to make .skl runnable"); // return -9999; // } } if ("NET".equals(givenScriptType)) { if (Runner.RJSCRIPT.equals(givenScriptScriptType)) { exitCode = runjs(null, uGivenScript, givenScriptScript, args); } else if (Runner.RPYTHON.equals(givenScriptScriptType)) { exitCode = runpy(null, uGivenScript, givenScriptScript, args); } else { log(-1, "running from net not supported for %s\n%s", givenScriptScriptType, uGivenScript); } } else { File fScript = Runner.getScriptFile(new File(givenScriptName)); if (fScript == null) { return -9999; } fScript = new File(FileManager.normalizeAbsolute(fScript.getPath(), true)); if (null == RunTime.scriptProject) { RunTime.scriptProject = fScript.getParentFile().getParentFile(); } log(lvl, "Trying to run script:\n%s", fScript); if (fScript.getName().endsWith(EJSCRIPT)) { exitCode = runjs(fScript, null, givenScriptScript, args); } else if (fScript.getName().endsWith(EPYTHON)) { exitCode = runpy(fScript, null, givenScriptScript, args); } else if (fScript.getName().endsWith(ERUBY)) { exitCode = runrb(fScript, null, givenScriptScript, args); } else if (fScript.getName().endsWith(EPLAIN)) { exitCode = runtxt(fScript, null, givenScriptScript, args); } else { log(-1, "Running not supported currently for:\n%s", fScript); return -9999; } } return exitCode; } } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Screen.java000066400000000000000000000510431315726130400237310ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.*; import java.util.Date; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; import org.sikuli.util.EventObserver; import org.sikuli.util.OverlayCapturePrompt; import org.sikuli.util.ScreenHighlighter; /** * A screen represents a physical monitor with its coordinates and size according to the global * point system: the screen areas are grouped around a point (0,0) like in a cartesian system (the * top left corner and the points contained in the screen area might have negative x and/or y values) *
The screens are arranged in an array (index = id) and each screen is always the same object * (not possible to create new objects). *
A screen inherits from class Region, so it can be used as such in all aspects. If you need * the region of the screen more than once, you have to create new ones based on the screen. *
The so called primary screen is the one with top left (0,0) and has id 0. */ public class Screen extends Region implements IScreen { static RunTime runTime = RunTime.get(); private static String me = "Screen: "; private static int lvl = 3; private static Region fakeRegion; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private static IRobot globalRobot = null; protected static Screen[] screens = null; protected static int primaryScreen = -1; private static int waitForScreenshot = 300; protected IRobot robot = null; protected int curID = -1; protected int oldID = 0; protected int monitor = -1; protected boolean waitPrompt; protected OverlayCapturePrompt prompt; private final static String promptMsg = "Select a region on the screen"; public static boolean ignorePrimaryAtCapture = false; private ScreenImage lastScreenImage = null; private static boolean isActiveCapturePrompt = false; private static EventObserver captureObserver = null; private static synchronized boolean setActiveCapturePrompt() { if (isActiveCapturePrompt) { return false; } Debug.log(3, "TRACE: Screen: setActiveCapturePrompt"); isActiveCapturePrompt = true; return true; } private static synchronized void resetActiveCapturePrompt() { Debug.log(3, "TRACE: Screen: resetActiveCapturePrompt"); isActiveCapturePrompt = false; captureObserver = null; } // static { RunTime.loadLibrary("VisionProxy"); initScreens(false); } private long lastCaptureTime = -1; // private static void initScreens() { // initScreens(false); // } public int getcurrentID() { return curID; } private static void initScreens(boolean reset) { if (screens != null && !reset) { return; } log(lvl+1, "initScreens: entry"); primaryScreen = 0; setMouseRobot(); if (null == globalRobot) { screens = new Screen[1]; screens[0] = null; } else { screens = new Screen[runTime.nMonitors]; screens[0] = new Screen(0, runTime.mainMonitor); screens[0].initScreen(); int nMonitor = 0; for (int i = 1; i < screens.length; i++) { if (nMonitor == runTime.mainMonitor) { nMonitor++; } screens[i] = new Screen(i, nMonitor); screens[i].initScreen(); nMonitor++; } Mouse.init(); if (getNumberScreens() > 1) { log(lvl, "initScreens: multi monitor mouse check"); Location lnow = Mouse.at(); float mmd = Settings.MoveMouseDelay; Settings.MoveMouseDelay = 0f; Location lc = null, lcn = null; for (Screen s : screens) { lc = s.getCenter(); Mouse.move(lc); lcn = Mouse.at(); if (!lc.equals(lcn)) { log(lvl, "*** multimonitor click check: %s center: (%d, %d) --- NOT OK: (%d, %d)", s.toStringShort(), lc.x, lc.y, lcn.x, lcn.y); } else { log(lvl, "*** checking: %s center: (%d, %d) --- OK", s.toStringShort(), lc.x, lc.y); } } Mouse.move(lnow); Settings.MoveMouseDelay = mmd; } } } public static IRobot getGlobalRobot() { return globalRobot; } private static void setMouseRobot() { try { if (globalRobot == null && !GraphicsEnvironment.isHeadless()) { globalRobot = new RobotDesktop(); } } catch (AWTException e) { Debug.error("Can't initialize global Robot for Mouse: " + e.getMessage()); } } private IRobot getMouseRobot() { setMouseRobot(); if (null == globalRobot && !GraphicsEnvironment.isHeadless()) { log(-1, "problem getting a java.awt.Robot"); Sikulix.endError(999); } return globalRobot; } protected static Region getFakeRegion() { if (fakeRegion == null) { fakeRegion = new Region(0,0,5,5); } return fakeRegion; } /** * create a Screen (ScreenUnion) object as a united region of all available monitors * @return ScreenUnion */ public static ScreenUnion all() { return new ScreenUnion(); } // hack to get an additional internal constructor for the initialization private Screen(int id, boolean init) { super(); curID = id; } // hack to get an additional internal constructor for the initialization private Screen(int id, int monitor) { super(); curID = id; this.monitor = monitor; } public static Screen as(int id) { if (id < 0 || id >= runTime.nMonitors) { Debug.error("Screen(%d) not in valid range 0 to %d - using primary %d", id, runTime.nMonitors - 1, primaryScreen); return screens[0]; } else { return screens[id]; } } /** * The screen object with the given id * * @param id valid screen number */ public Screen(int id) { super(); if (id < 0 || id >= runTime.nMonitors) { Debug.error("Screen(%d) not in valid range 0 to %d - using primary %d", id, runTime.nMonitors - 1, primaryScreen); curID = primaryScreen; } else { curID = id; } monitor = screens[curID].monitor; initScreen(); } /** * INTERNAL USE * collect all physical screens to one big region
* TODO: under evaluation, wether it really makes sense * @param isScreenUnion true/false */ public Screen(boolean isScreenUnion) { super(isScreenUnion); } /** * INTERNAL USE * collect all physical screens to one big region
* This is under evaluation, wether it really makes sense */ public void setAsScreenUnion() { oldID = curID; curID = -1; } /** * INTERNAL USE * reset from being a screen union to the screen used before */ public void setAsScreen() { curID = oldID; } /** * Is the screen object having the top left corner as (0,0). If such a screen does not exist it is * the screen with id 0. */ public Screen() { super(); curID = primaryScreen; initScreen(); } //TODO: remove this method if it is not needed public void initScreen(Screen scr) { updateSelf(); } private void initScreen() { Rectangle bounds = getBounds(); x = (int) bounds.getX(); y = (int) bounds.getY(); w = (int) bounds.getWidth(); h = (int) bounds.getHeight(); // try { // robot = new RobotDesktop(this); // robot.setAutoDelay(10); // } catch (AWTException e) { // Debug.error("Can't initialize Java Robot on Screen " + curID + ": " + e.getMessage()); // robot = null; // } robot = globalRobot; } /** * {@inheritDoc} * @return Screen */ @Override public Screen getScreen() { return this; } /** * Should not be used - throws UnsupportedOperationException * @param s Screen * @return should not return */ @Override protected Region setScreen(IScreen s) { throw new UnsupportedOperationException("The setScreen() method cannot be called from a Screen object."); } /** * show the current monitor setup */ public static void showMonitors() { // initScreens(); Debug.logp("*** monitor configuration [ %s Screen(s)] ***", Screen.getNumberScreens()); Debug.logp("*** Primary is Screen %d", primaryScreen); for (int i = 0; i < runTime.nMonitors; i++) { Debug.logp("Screen %d: %s", i, Screen.getScreen(i).toStringShort()); } Debug.logp("*** end monitor configuration ***"); } /** * re-initialize the monitor setup (e.g. when it was changed while running) */ public static void resetMonitors() { Debug.error("*** BE AWARE: experimental - might not work ***"); Debug.error("Re-evaluation of the monitor setup has been requested"); Debug.error("... Current Region/Screen objects might not be valid any longer"); Debug.error("... Use existing Region/Screen objects only if you know what you are doing!"); initScreens(true); Debug.logp("*** new monitor configuration [ %s Screen(s)] ***", Screen.getNumberScreens()); Debug.logp("*** Primary is Screen %d", primaryScreen); for (int i = 0; i < runTime.nMonitors; i++) { Debug.logp("Screen %d: %s", i, Screen.getScreen(i).toStringShort()); } Debug.error("*** end new monitor configuration ***"); } //
// protected boolean useFullscreen() { return false; } private static int getValidID(int id) { if (id < 0 || id >= runTime.nMonitors) { Debug.error("Screen: invalid screen id %d - using primary screen", id); return primaryScreen; } return id; } private static int getValidMonitor(int id) { if (id < 0 || id >= runTime.nMonitors) { Debug.error("Screen: invalid screen id %d - using primary screen", id); return runTime.mainMonitor; } return screens[id].monitor; } /** * * @return number of available screens */ public static int getNumberScreens() { return runTime.nMonitors; } /** * * @return the id of the screen at (0,0), if not exists 0 */ public static int getPrimaryId() { return primaryScreen; } /** * * @return the screen at (0,0), if not exists the one with id 0 */ public static Screen getPrimaryScreen() { return screens[primaryScreen]; } /** * * @param id of the screen * @return the screen with given id, the primary screen if id is invalid */ public static Screen getScreen(int id) { return screens[getValidID(id)]; } /** * * @return the screen's rectangle */ @Override public Rectangle getBounds() { return new Rectangle(runTime.getMonitor(monitor)); } /** * * @param id of the screen * @return the physical coordinate/size
as AWT.Rectangle to avoid mix up with getROI */ public static Rectangle getBounds(int id) { return new Rectangle(runTime.getMonitor(getValidMonitor(id))); } /** * each screen has exactly one robot (internally used for screen capturing) *
available as a convenience for those who know what they are doing. Should not be needed * normally. * * @param id of the screen * @return the AWT.Robot of the given screen, if id invalid the primary screen */ public static IRobot getRobot(int id) { return getScreen(id).getRobot(); } /** * * @return the id */ @Override public int getID() { return curID; } /** * INTERNAL USE: to be compatible with ScreenUnion * @param x value * @param y value * @return id of the screen */ @Override public int getIdFromPoint(int x, int y) { return curID; } /** * Gets the Robot of this Screen. * * @return The Robot for this Screen */ @Override public IRobot getRobot() { return getMouseRobot(); } protected static IRobot getRobot(Region reg) { if (reg == null || null == reg.getScreen()) { return getPrimaryScreen().getMouseRobot(); } else { return reg.getScreen().getRobot(); } } /** * creates a region on the current screen with the given coordinate/size. The coordinate is * translated to the current screen from its relative position on the screen it would have been * created normally. * * @param loc Location * @param width value * @param height value * @return the new region */ public Region newRegion(Location loc, int width, int height) { return Region.create(loc.copyTo(this), width, height); } @Override public ScreenImage getLastScreenImageFromScreen() { return lastScreenImage; } /** * creates a location on the current screen with the given point. The coordinate is translated to * the current screen from its relative position on the screen it would have been created * normally. * * @param loc Location * @return the new location */ public Location newLocation(Location loc) { return (new Location(loc)).copyTo(this); } //
// public ScreenImage cmdCapture(Object... args) { if (args.length == 0) { return userCapture("capture an image"); } if (args.length == 1) { Object arg0 = args[0]; if (arg0 instanceof String) { return userCapture((String) arg0); } else if (arg0 instanceof Region) { return capture((Region) arg0); } else if (arg0 instanceof Rectangle) { return capture((Rectangle) arg0); } } if (args.length == 4) { Integer argInt = null; for (Object arg : args){ argInt = null; try { argInt = (Integer) arg; } catch (Exception ex) { break; } } if (argInt != null) { return capture((int) args[0], (int) args[1], (int) args[2], (int) args[3]); } } return userCapture("Invalid parameter for capture"); } /** * create a ScreenImage with the physical bounds of this screen * * @return the image */ @Override public ScreenImage capture() { return capture(getRect()); } /** * create a ScreenImage with given coordinates on this screen. * * @param x x-coordinate of the region to be captured * @param y y-coordinate of the region to be captured * @param w width of the region to be captured * @param h height of the region to be captured * @return the image of the region */ @Override public ScreenImage capture(int x, int y, int w, int h) { Rectangle rect = newRegion(new Location(x, y), w, h).getRect(); return capture(rect); } public ScreenImage captureforHighlight(int x, int y, int w, int h) { return robot.captureScreen(new Rectangle(x, y, w, h)); } /** * create a ScreenImage with given rectangle on this screen. * * @param rect The Rectangle to be captured * @return the image of the region */ @Override public ScreenImage capture(Rectangle rect) { lastCaptureTime = new Date().getTime(); ScreenImage simg = robot.captureScreen(rect); if (Settings.FindProfiling) { Debug.logp("[FindProfiling] Screen.capture [%d x %d]: %d msec", rect.width, rect.height, new Date().getTime() - lastCaptureTime); } lastScreenImage = simg; if (Debug.getDebugLevel() > lvl) { simg.saveLastScreenImage(runTime.fSikulixStore); } return simg; } /** * create a ScreenImage with given region on this screen * * @param reg The Region to be captured * @return the image of the region */ @Override public ScreenImage capture(Region reg) { return capture(reg.getRect()); } public static void doPrompt(String message, EventObserver obs) { captureObserver = obs; Screen.getPrimaryScreen().userCapture(message); } public static void closePrompt() { for (int is = 0; is < Screen.getNumberScreens(); is++) { if (!Screen.getScreen(is).hasPrompt()) { continue; } Screen.getScreen(is).prompt.close(); } } public static void closePrompt(Screen scr) { for (int is = 0; is < Screen.getNumberScreens(); is++) { if (Screen.getScreen(is).getID() == scr.getID() || !Screen.getScreen(is).hasPrompt()) { continue; } Screen.getScreen(is).prompt.close(); Screen.getScreen(is).prompt = null; } } public static void resetPrompt(OverlayCapturePrompt ocp) { int scrID = ocp.getScrID(); if (scrID > -1) { Screen.getScreen(scrID).prompt = null; } resetActiveCapturePrompt(); } public boolean hasPrompt() { return prompt != null; } /** * interactive capture with predefined message: lets the user capture a screen image using the * mouse to draw the rectangle * * @return the image */ public ScreenImage userCapture() { return userCapture(""); } /** * interactive capture with given message: lets the user capture a screen image using the mouse to * draw the rectangle * * @param message text * @return the image */ @Override public ScreenImage userCapture(final String message) { if (!setActiveCapturePrompt()) { return null; } Debug.log(3, "TRACE: Screen: userCapture"); waitPrompt = true; Thread th = new Thread() { @Override public void run() { String msg = message.isEmpty() ? promptMsg : message; for (int is = 0; is < Screen.getNumberScreens(); is++) { if (ignorePrimaryAtCapture && is == 0) { continue; } Screen.getScreen(is).prompt = new OverlayCapturePrompt(Screen.getScreen(is)); Screen.getScreen(is).prompt.addObserver(captureObserver); Screen.getScreen(is).prompt.prompt(msg); } } }; th.start(); if (captureObserver != null) { return null; } boolean isComplete = false; ScreenImage simg = null; int count = 0; while (!isComplete) { this.wait(0.1f); if (count++ > waitForScreenshot) { break; } for (int is = 0; is < Screen.getNumberScreens(); is++) { OverlayCapturePrompt ocp = Screen.getScreen(is).prompt; if (ocp == null) { continue; } if (ocp.isComplete()) { closePrompt(Screen.getScreen(is)); simg = ocp.getSelection(); if (simg != null) { Screen.getScreen(is).lastScreenImage = simg; } ocp.close(); Screen.getScreen(is).prompt = null; isComplete = true; } } } resetActiveCapturePrompt(); return simg; } public String saveCapture(String name) { return saveCapture(name, null); } public String saveCapture(String name, Region reg) { ScreenImage simg; if (reg == null) { simg = userCapture("Capture for image " + name); } else { simg = capture(reg); } if (simg == null) { return null; } else { return simg.saveInBundle(name); } } /** * interactive region create with predefined message: lets the user draw the rectangle using the * mouse * * @return the region */ public Region selectRegion() { return selectRegion("Select a region on the screen"); } /** * interactive region create with given message: lets the user draw the rectangle using the mouse * * @param message text * @return the region */ public Region selectRegion(final String message) { Debug.log(3, "TRACE: Screen: selectRegion"); ScreenImage sim = userCapture(message); if (sim == null) { return null; } Rectangle r = sim.getROI(); return Region.create((int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight()); } // // @Override public void showTarget(Location loc) { showTarget(loc, Settings.SlowMotionDelay); } protected void showTarget(Location loc, double secs) { if (Settings.isShowActions()) { ScreenHighlighter overlay = new ScreenHighlighter(this, null); overlay.showTarget(loc, (float) secs); } } // @Override public String toString() { Rectangle r = getBounds(); String scrText = curID == -1 ? "Union" : "" + curID; return String.format("S(%s)[%d,%d %dx%d] E:%s, T:%.1f", scrText, (int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight(), getThrowException() ? "Y" : "N", getAutoWaitTimeout()); } /** * only a short version of toString() * * @return like S(0) [0,0, 1440x900] */ @Override public String toStringShort() { Rectangle r = getBounds(); String scrText = curID == -1 ? "Union" : "" + curID; return String.format("S(%s)[%d,%d %dx%d]", scrText, (int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight()); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/ScreenImage.java000066400000000000000000000120121315726130400246650ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Settings; import org.sikuli.basics.FileManager; import org.sikuli.basics.Debug; import java.awt.*; import java.awt.image.*; import java.io.*; import javax.imageio.ImageIO; /** * CANDIDATE FOR DEPRECATION * * stores a BufferedImage usually ceated by screen capture, * the screen rectangle it was taken from and * the filename, where it is stored as PNG (only if requested) * * This will be replaced by Image in the long run */ public class ScreenImage { /** * x, y, w, h of the stored ROI * */ public int x, y, w, h; protected Rectangle _roi; protected BufferedImage _img; protected String _filename = null; /** * create ScreenImage with given * * @param roi the rectangle it was taken from * @param img the BufferedImage */ public ScreenImage(Rectangle roi, BufferedImage img) { _img = img; _roi = roi; x = (int) roi.getX(); y = (int) roi.getY(); w = (int) roi.getWidth(); h = (int) roi.getHeight(); } public ScreenImage getSub(Rectangle sub) { if (!_roi.contains(sub)) { return this; } BufferedImage img = _img.getSubimage(sub.x - x, sub.y - y, sub.width, sub.height); return new ScreenImage(sub, img); } /** * creates the PNG tempfile only when needed. * * @return absolute path to stored tempfile * @throws IOException if not found * @deprecated use getFile() instead */ @Deprecated public String getFilename() throws IOException { return getFile(); } /** * INTERNAL USE: use getTimedFile() instead * @return absolute path to stored file */ public String getFile() { if (_filename == null) { _filename = save(); } return _filename; } /** * stores the image as PNG file in the standard temp folder * with a created filename (sikuliximage-timestamp.png) * if not yet stored before * * @return absolute path to stored file */ public String save() { return FileManager.saveTimedImage(_img, RunTime.get().fpBaseTempPath, "sikuliximage"); } /** * stores the image as PNG file in the given path * with a created filename (sikuliximage-timestamp.png) * * @param path valid path string * @return absolute path to stored file */ public String getFile(String path) { return save(path); } /** * stores the image as PNG file in the given path * with a created filename (sikuliximage-timestamp.png) * * @param path valid path string * @return absolute path to stored file */ public String save(String path) { return FileManager.saveTimedImage(_img, path, "sikuliximage"); } /** * stores the image as PNG file in the given path * with a created filename (givenName-timestamp.png) * * @param path valid path string * @param name file name * @return absolute path to stored file */ public String save(String path, String name) { return FileManager.saveTimedImage(_img, path, name); } /** * stores the image as PNG file in the given path * with the given filename * * @param path valid path string * @param name filename (.png is added if not present) * @return absolute path to stored file */ public String getFile(String path, String name) { if (name == null) { name = Settings.getTimestamp() + ".png"; } else if (!name.endsWith(".png")) { name += ".png"; } try { File tmp = new File(path, name); createFile(tmp); Debug.log(3, "ScreenImage.getFile:\n%s", tmp); } catch (IOException iOException) { Debug.error("ScreenImage.getFile: IOException", iOException); return null; } return _filename; } public String saveInBundle(String name) { if (!name.endsWith(".png")) { name += ".png"; } if (!name.startsWith("_")) { name = "_" + name; } File fImage = new File(name); try { fImage = new File(ImagePath.getBundlePath(), name); createFile(fImage); Debug.log(3, "ScreenImage.saveInBundle:\n%s", fImage); } catch (IOException iOException) { Debug.error("ScreenImage.saveInBundle: IOException", iOException); return null; } Image.reload(fImage.getAbsolutePath()); return fImage.getAbsolutePath(); } // store image to given path if not yet stored private void createFile(File tmp) throws IOException { String filename = tmp.getAbsolutePath(); if (_filename == null || !filename.equals(_filename) || tmp.getName().startsWith("_")) { ImageIO.write(_img, "png", tmp); _filename = filename; } } /** * * @return the stored image in memory */ public BufferedImage getImage() { return _img; } /** * * @return the Region, the iamge was created from */ public Region getRegion() { return new Region(_roi); } /** * * @return the screen rectangle, the iamge was created from */ public Rectangle getROI() { return _roi; } public void saveLastScreenImage(File fPath) { try { ImageIO.write(_img, "png", new File(fPath, "LastScreenImage.png")); } catch (Exception ex) {} } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/ScreenUnion.java000066400000000000000000000040261315726130400247410ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.basics.Debug; import java.awt.Rectangle; /** * CANDIDATE FOR DEPRECATION * INTERNAL USE * An extension of DesktopScreen, that uses all active monitors as one big screen * * TO BE EVALUATED: is this really needed? */ public class ScreenUnion extends Screen { private Rectangle _bounds; public ScreenUnion() { super(true); _bounds = new Rectangle(); for (int i = 0; i < Screen.getNumberScreens(); i++) { _bounds = _bounds.union(Screen.getBounds(i)); } x = _bounds.x; y = _bounds.y; w = _bounds.width; h = _bounds.height; } public Region getRegion() { return Region.virtual(_bounds); } @Override public int getIdFromPoint(int x, int y) { Rectangle sr = getBounds(); int _x = x + getBounds().x; int _y = y + getBounds().y; for (int i = 0; i < getNumberScreens(); i++) { if (Screen.getScreen(i).contains(new Location(_x, _y))) { Debug.log(3, "ScreenUnion: getIdFromPoint: " + "(%d, %d) as (%d, %d) in (%d, %d, %d, %d) on %d", x, y, _x, _y, sr.x, sr.y, sr.width, sr.height, i); return i; } } Debug.log(3, "ScreenUnion: getIdFromPoint: " + "(%d, %d) as (%d, %d) in (%d, %d, %d, %d) on ???", x, y, _x, _y, sr.x, sr.y, sr.width, sr.height); return 0; } @Override public Rectangle getBounds() { return _bounds; } @Override public ScreenImage capture(Rectangle rect) { Debug.log(4, "ScreenUnion: capture: (%d,%d) %dx%d", rect.x, rect.y, rect.width, rect.height); Screen s = Screen.getPrimaryScreen(); // Location tl = new Location(rect.getLocation()); // for (Screen sx : Screen.screens) { // if (sx.contains(tl)) { // s = sx; // break; // } // } ScreenImage si = s.capture(rect); return si; } @Override public boolean useFullscreen() { return false; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/SikuliEvent.java000066400000000000000000000003741315726130400247550ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; /** * CANDIDATE FOR DEPRECATION - ONLY HERE TO BE BACKWARD COMPATIBLE */ public class SikuliEvent extends ObserveEvent { } sikulix-1.1.1/API/src/main/java/org/sikuli/script/SikuliEventAdapter.java000077500000000000000000000012051315726130400262530ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; /** * * @deprecated use @{link ObserverCallBack} instead */ @Deprecated public class SikuliEventAdapter implements SikuliEventObserver { @Override public void targetAppeared(ObserveEvent e) { appeared(e); } @Override public void targetVanished(ObserveEvent e) { vanished(e); } @Override public void targetChanged(ObserveEvent e) { changed(e); } public void appeared(ObserveEvent e) { } public void vanished(ObserveEvent e) { } public void changed(ObserveEvent e) { } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/SikuliEventObserver.java000077500000000000000000000006421315726130400264660ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.util.*; /** * see @{link SikuliEventAdapter} * @deprecated */ @Deprecated public interface SikuliEventObserver extends EventListener { public void targetAppeared(ObserveEvent e); public void targetVanished(ObserveEvent e); public void targetChanged(ObserveEvent e); } sikulix-1.1.1/API/src/main/java/org/sikuli/script/SikuliException.java000077500000000000000000000014051315726130400256310ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; /** * INTERNAL USE */ public class SikuliException extends Exception { protected String _name = "SikuliException"; public SikuliException(String msg) { super(msg); } @Override public String toString() { String ret = _name + ": " + getMessage() + "\n"; for (StackTraceElement elm : getStackTrace()) { ret += " Line " + elm.getLineNumber() + ", in file " + elm.getFileName() + "\n"; return ret; } ret += "Line ?, in File ?"; return ret; } public String toStringShort() { return _name + ": " + getMessage(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/Sikulix.java000066400000000000000000000726241315726130400241520ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import org.sikuli.vnc.VNCScreen; import org.sikuli.android.ADBScreen; import org.sikuli.basics.*; import org.sikuli.util.JythonHelper; import org.sikuli.util.ScreenHighlighter; import org.sikuli.util.SikulixFileChooser; import org.sikuli.util.Tests; import javax.swing.*; import java.awt.*; import java.io.File; import java.io.IOException; import java.net.URL; import java.security.CodeSource; /** * INTERNAL USE ONLY --- NOT part of official API */ public class Sikulix { private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, "Sikulix: " + message, args); } private static void p(String msg, Object... args) { System.out.println(String.format(msg, args)); } private static void terminate(int retVal, String msg, Object... args) { p(msg, args); System.exit(retVal); } private static boolean runningFromJar; private static String jarPath; private static String jarParentPath; private static final String prefNonSikuli = "nonSikuli_"; private static RunTime rt = null; public static int testNumber = -1; private static boolean shouldRunServer = false; private static Point locPopAt = null; static { String jarName = ""; CodeSource codeSrc = Sikulix.class.getProtectionDomain().getCodeSource(); if (codeSrc != null && codeSrc.getLocation() != null) { jarName = codeSrc.getLocation().getPath(); } if (jarName.contains("sikulixsetupAPI")) { JOptionPane.showMessageDialog(null, "Not useable!\nRun setup first!", "sikulixsetupAPI", JOptionPane.ERROR_MESSAGE); System.exit(0); } rt = RunTime.get(); if (Debug.getDebugLevel() == 0) { Debug.setDebugLevel(1); } if (codeSrc != null && codeSrc.getLocation() != null) { URL jarURL = codeSrc.getLocation(); jarPath = FileManager.slashify(new File(jarURL.getPath()).getAbsolutePath(), false); jarParentPath = (new File(jarPath)).getParent(); if (jarPath.endsWith(".jar")) { runningFromJar = true; } else { jarPath += "/"; } } } /** * checking parameter -d on commandline
* 0 - list all available tests
* 1 - run all available tests
* n - run the test with that number if available * * @param args currently only -d is evaluated */ public static void main(String[] args) throws FindFailed { if (args.length > 0 && args[0].toLowerCase().startsWith("-s")) { shouldRunServer = true; } else { int dl = RunTime.checkArgs(args, RunTime.Type.API); if (dl > -1 && dl < 999) { testNumber = dl; Debug.on(3); } else { testNumber = -1; } testNumber = rt.getOptionNumber("testing.test", testNumber); if (dl == 999) { int exitCode = Runner.runScripts(args); cleanUp(exitCode); System.exit(exitCode); } else if (testNumber > -1) { if (!rt.testing) { rt.show(); rt.testing = true; } Tests.runTest(testNumber); System.exit(1); } } rt = RunTime.get(); if (rt.fSxBaseJar.getName().contains("setup")) { Sikulix.popError("Not useable!\nRun setup first!"); System.exit(0); } if (shouldRunServer) { if (RunServer.run(null)) { System.exit(1); } } boolean playing = false; if (args.length > 0 && "play".equals(args[0])) { playing = true; //-------- playground // -------- playground } if (playing) { System.exit(1); } String version = String.format("(%s-%s)", rt.getVersionShort(), rt.sxBuildStamp); File lastSession = new File(rt.fSikulixStore, "LastAPIJavaScript.js"); String runSomeJS = ""; if (lastSession.exists()) { runSomeJS = FileManager.readFileToString(lastSession); } runSomeJS = inputText("enter some JavaScript (know what you do - may silently die ;-)" + "\nexample: run(\"git*\") will run the JavaScript showcase from GitHub" + "\nWhat you enter now will be shown the next time.", "API::JavaScriptRunner " + version, 10, 60, runSomeJS); if (runSomeJS == null || runSomeJS.isEmpty()) { popup("Nothing to do!", version); } else { while (!runSomeJS.isEmpty()) { FileManager.writeStringToFile(runSomeJS, lastSession); Runner.runjs(null, null, runSomeJS, null); runSomeJS = inputText("Edit the JavaScript and/or press OK to run it (again)\n" + "Press Cancel to terminate", "API::JavaScriptRunner " + version, 10, 60, runSomeJS); } } System.exit(0); } /** * add a jar to the scripting environment
* Jython: added to sys.path
* JRuby: not yet supported
* JavaScript: not yet supported
* if no scripting active (API usage), jar is added to classpath if available * * @param fpJar absolute path to a jar (relative: searched according to Extension concept, * but first on sys.path) * @return the absolute path to the jar or null, if not available */ public static String load(String fpJar) { return load(fpJar, null); } /** * add a jar to the scripting environment or to classpath
* Jython: added to sys.path
* JRuby: only added to classpath
* JavaScript: only added to classpath
* if no scripting is active (API usage), jar is added to classpath if available
* additionally: fpJar/fpJarImagePath is added to ImagePath (not checked) * * @param fpJar absolute path to a jar (relative: searched according to Extension concept, * but first on sys.path) * @param fpJarImagePath path relative to jar root inside jar * @return the absolute path to the jar or null, if not available */ public static String load(String fpJar, String fpJarImagePath) { JythonHelper jython = JythonHelper.get(); String fpJarFound = null; if (jython != null) { File aFile = jython.existsSysPathJar(fpJar); if (aFile != null) { fpJar = aFile.getAbsolutePath(); } fpJarFound = jython.load(fpJar); } else { File fJarFound = rt.asExtension(fpJar); if (fJarFound != null) { fpJarFound = fJarFound.getAbsolutePath(); rt.addToClasspath(fpJarFound); } } if (fpJarFound != null && fpJarImagePath != null) { ImagePath.addJar(fpJarFound, fpJarImagePath); } return fpJarFound; } /** * build a jar on the fly at runtime from a folder.
* special for Jython: if the folder contains a __init__.py on first level, * the folder will be copied to the jar root (hence preserving module folders) * * @param targetJar absolute path to the created jar (parent folder must exist, jar is overwritten) * @param sourceFolder absolute path to a folder, the contained folder structure * will be copied to the jar root level * @return */ public static boolean buildJarFromFolder(String targetJar, String sourceFolder) { log(lvl, "buildJarFromFolder: \nfrom Folder: %s\nto Jar: %s", sourceFolder, targetJar); File fJar = new File(targetJar); if (!fJar.getParentFile().exists()) { log(-1, "buildJarFromFolder: parent folder of Jar not available"); return false; } File fSrc = new File(sourceFolder); if (!fSrc.exists() || !fSrc.isDirectory()) { log(-1, "buildJarFromFolder: source folder not available"); return false; } String prefix = null; if (new File(fSrc, "__init__.py").exists() || new File(fSrc, "__init__$py.class").exists()) { prefix = fSrc.getName(); if (prefix.endsWith("_")) { prefix = prefix.substring(0, prefix.length() - 1); } } return FileManager.buildJar(targetJar, new String[]{null}, new String[]{sourceFolder}, new String[]{prefix}, null); } /** * the foo.py files in the given source folder are compiled to JVM-ByteCode-classfiles foo$py.class * and stored in the target folder (thus securing your code against changes).
* A folder structure is preserved. All files not ending as .py will be copied also. * The target folder might then be packed to a jar using buildJarFromFolder.
* Be aware: you will get no feedback about any compile problems, * so make sure your code compiles error free. Currently there is no support for running such a jar, * it can only be used with load()/import, but you might provide a simple script that does load()/import * and then runs something based on available functions in the jar code. * * @param fpSource absolute path to a folder/folder-tree containing the stuff to be copied/compiled * @param fpTarget the folder that will contain the copied/compiled stuff (folder is first deleted) * @return false if anything goes wrong, true means should have worked */ public static boolean compileJythonFolder(String fpSource, String fpTarget) { JythonHelper jython = JythonHelper.get(); if (jython != null) { File fTarget = new File(fpTarget); FileManager.deleteFileOrFolder(fTarget); fTarget.mkdirs(); if (!fTarget.exists()) { log(-1, "compileJythonFolder: target folder not available\n%", fTarget); return false; } File fSource = new File(fpSource); if (!fSource.exists()) { log(-1, "compileJythonFolder: source folder not available\n", fSource); return false; } if (fTarget.equals(fSource)) { log(-1, "compileJythonFolder: target folder cannot be the same as the source folder"); return false; } FileManager.xcopy(fSource, fTarget); if (!jython.exec("import compileall")) { return false; } jython = doCompileJythonFolder(jython, fTarget); FileManager.traverseFolder(fTarget, new CompileJythonFilter(jython)); } return false; } private static class CompileJythonFilter implements FileManager.FileFilter { JythonHelper jython = null; public CompileJythonFilter(JythonHelper jython) { this.jython = jython; } @Override public boolean accept(File entry) { if (jython != null && entry.isDirectory()) { jython = doCompileJythonFolder(jython, entry); } return false; } } private static JythonHelper doCompileJythonFolder(JythonHelper jython, File fSource) { String fpSource = FileManager.slashify(fSource.getAbsolutePath(), false); if (!jython.exec(String.format("compileall.compile_dir(\"%s\"," + "maxlevels = 0, quiet = 1)", fpSource))) { return null; } for (File aFile : fSource.listFiles()) { if (aFile.getName().endsWith(".py")) { aFile.delete(); } } return jython; } private static boolean addFromProject(String project, String aJar) { File aFile = null; if (rt.fSxProject == null) { return false; } else { aFile = new File(rt.fSxProject, project); } aFile = new File(aFile, "target/" + aJar); return rt.addToClasspath(aFile.getAbsolutePath()); } public static boolean isRunningFromJar() { return runningFromJar; } public static String getJarPath() { return jarPath; } public static String getJarParentPath() { return jarParentPath; } private static boolean runningSikulixapi = false; /** * Get the value of runningSikulixUtilapi * * @return the value of runningSikulixUtilapi */ public static boolean isRunningSikulixapi() { return runningSikulixapi; } /** * Set the value of runningSikulixUtilapi * * @param runningAPI new value of runningSikulixUtilapi */ public static void setRunningSikulixapi(boolean runningAPI) { runningSikulixapi = runningAPI; } /** * call this, to initialize Sikuli up to useability * * @return the primary screen object or null if headless */ public static Screen init() { if (!canRun()) { return null; } //TODO collect initializations here Mouse.init(); return new Screen(); } /** * Can SikuliX be run on this machine? * * @return true if not running headless false otherwise */ public static boolean canRun() { return !RunTime.get().isHeadless(); } /** * INTERNAL USE: convenience function: runs {@link #cleanUp(int)}, prints a message endNormal and terminates with * returncode * * @param n */ public static void endNormal(int n) { log(lvl, "endNormal: %d", n); cleanUp(n); System.exit(n); } /** * INTERNAL USE: convenience function: runs {@link #cleanUp(int)}, prints a message endWarning and terminates with * returncode * * @param n returncode */ public static void endWarning(int n) { log(lvl, "endWarning: %d", n); cleanUp(n); System.exit(n); } /** * INTERNAL USE: convenience function: runs {@link #cleanUp(int)}, prints a message endError and terminates with * returncode * * @param n */ public static void endError(int n) { log(lvl, "endError: %d", n); cleanUp(n); System.exit(n); } public static void terminate(int n) { String msg = "***** Terminating SikuliX Setup after a fatal error" + (n == 0 ? "*****\n" : " %d *****\n") + "SikuliX is not useable!\n" + "Check the error log at " + Debug.logfile; if (Settings.runningSetup) { if (Settings.noPupUps) { log(-1, msg, n); } else { popError(String.format(msg, n)); } } else { msg = "***** Terminating SikuliX after a fatal error" + (n == 0 ? "*****\n" : " %d *****\n") + "It makes no sense to continue!\n" + "If you do not have any idea about the error cause or solution, run again\n" + "with a Debug level of 3. You might paste the output to the Q&A board."; log(-1, msg, n); if (Settings.isRunningIDE) { popError(String.format(msg, n)); } cleanUp(n); } if (!Settings.isRunningIDE) { System.exit(1); } else { throw new IllegalStateException("Aborting script due to an internal error - see log"); } } /** * INTERNAL USE: resets stateful Sikuli X features:
* ScreenHighlighter, Observing, Mouse, Key, Hotkeys
* When in IDE: resets selected options to defaults (TODO) * * @param n returncode */ public static void cleanUp(int n) { log(lvl, "cleanUp: %d", n); VNCScreen.stopAll(); ADBScreen.stop(); ScreenHighlighter.closeAll(); Observing.cleanUp(); HotkeyManager.reset(); try { new RobotDesktop().keyUp(); } catch (AWTException e) { } Mouse.reset(); } /** * INTERNAL USE: used in setup: tests basic SikulixUtil features * * @return success */ public static boolean testSetup() { return doTestSetup("Java API", false); } /** * INTERNAL USE: used in setup: tests basic SikulixUtil features * * @return success */ public static boolean testSetup(String src) { return doTestSetup(src, false); } /** * INTERNAL USE: used in setup: tests basic SikulixUtil features * * @return success */ public static boolean testSetupSilent() { Settings.noPupUps = true; return doTestSetup("Java API", true); } private static boolean doTestSetup(String testSetupSource, boolean silent) { Region r = Region.create(0, 0, 100, 100); Image img = new Image(r.getScreen().capture(r).getImage()); Pattern p = new Pattern(img); Finder f = new Finder(img); boolean success = (null != f.find(p)); log(lvl, "testSetup: Finder setup with image %s", (!success ? "did not work" : "worked")); if (success &= f.hasNext()) { success = (null != f.find(img.asFile())); log(lvl, "testSetup: Finder setup with image file %s", (!success ? "did not work" : "worked")); success &= f.hasNext(); String screenFind = "Screen.find(imagefile)"; try { ((Screen) r.getScreen()).find(img.asFile()); log(lvl, "testSetup: %s worked", screenFind); screenFind = "repeated Screen.find(imagefile)"; ((Screen) r.getScreen()).find(img.asFile()); log(lvl, "testSetup: %s worked", screenFind); } catch (Exception ex) { log(lvl, "testSetup: %s did not work", screenFind); success = false; } } if (success) { if (!silent) { popup("Hallo from Sikulix.testSetup: " + testSetupSource + "\n" + "SikuliX seems to be working!\n\nHave fun!"); log(lvl, "testSetup: Finder.find: worked"); } else { System.out.println("[info] RunSetup: Sikulix.testSetup: Java Sikuli seems to be working!"); } return true; } log(lvl, "testSetup: last Screen/Finder.find: did not work"); return false; } @Deprecated public static boolean addToClasspath(String jar) { return RunTime.get().addToClasspath(jar); } @Deprecated public static boolean isOnClasspath(String artefact) { return null != RunTime.get().isOnClasspath(artefact); } public static String run(String cmdline) { return run(new String[]{cmdline}); } public static String run(String[] cmd) { return rt.runcmd(cmd); } public static void popError(String message) { popError(message, "Sikuli"); } public static void popError(String message, String title) { JFrame anchor = popLocation(); JOptionPane.showMessageDialog(anchor, message, title, JOptionPane.ERROR_MESSAGE); if (anchor != null) { anchor.dispose(); } } /** * request user's input as one line of text
* with hidden = true:
* the dialog works as password input (input text hidden as bullets)
* take care to destroy the return value as soon as possible (internally the password is deleted on return) * * @param msg * @param preset * @param title * @param hidden * @return the text entered */ public static String input(String msg, String preset, String title, boolean hidden) { JFrame anchor = popLocation(); String ret = ""; if (!hidden) { if ("".equals(title)) { title = "Sikuli input request"; } ret = (String) JOptionPane.showInputDialog(anchor, msg, title, JOptionPane.PLAIN_MESSAGE, null, null, preset); } else { preset = ""; JTextArea tm = new JTextArea(msg); tm.setColumns(20); tm.setLineWrap(true); tm.setWrapStyleWord(true); tm.setEditable(false); tm.setBackground(new JLabel().getBackground()); JPasswordField pw = new JPasswordField(preset); JPanel pnl = new JPanel(); pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS)); pnl.add(pw); pnl.add(Box.createVerticalStrut(10)); pnl.add(tm); int retval = JOptionPane.showConfirmDialog(anchor, pnl, title, JOptionPane.OK_CANCEL_OPTION); if (0 == retval) { char[] pwc = pw.getPassword(); for (int i = 0; i < pwc.length; i++) { ret = ret + pwc[i]; pwc[i] = 0; } } } if (anchor != null) { anchor.dispose(); } return ret; } public static String input(String msg, String title, boolean hidden) { return input(msg, "", title, hidden); } public static String input(String msg, boolean hidden) { return input(msg, "", "", hidden); } public static String input(String msg, String preset, String title) { return input(msg, preset, title, false); } public static String input(String msg, String preset) { return input(msg, preset, "", false); } public static String input(String msg) { return input(msg, "", "", false); } public static boolean popAsk(String msg) { return popAsk(msg, null); } public static boolean popAsk(String msg, String title) { if (title == null) { title = "... something to decide!"; } JFrame anchor = popLocation(); int ret = JOptionPane.showConfirmDialog(anchor, msg, title, JOptionPane.YES_NO_OPTION); if (anchor != null) { anchor.dispose(); } if (ret == JOptionPane.CLOSED_OPTION || ret == JOptionPane.NO_OPTION) { return false; } return true; } public static void popup(String message) { popup(message, "Sikuli"); } public static void popup(String message, String title) { JFrame anchor = popLocation(); JOptionPane.showMessageDialog(anchor, message, title, JOptionPane.PLAIN_MESSAGE); if (anchor != null) { anchor.dispose(); } } public static Location popat(Location at) { locPopAt = new Point(at.x, at.y); return new Location(locPopAt); } public static Location popat(Region at) { locPopAt = new Point(at.getCenter().x, at.getCenter().y); return new Location(locPopAt); } public static Location popat(int atx, int aty) { locPopAt = new Point(atx, aty); return new Location(locPopAt); } public static Location popat() { locPopAt = getLocPopAt(); return new Location(locPopAt); } private static Point getLocPopAt() { Rectangle screen0 = rt.getMonitor(0); if (null == screen0) { return null; } return new Point((int) screen0.getCenterX(), (int) screen0.getCenterY()); } private static JFrame popLocation() { if (null == locPopAt) { locPopAt = getLocPopAt(); if (null == locPopAt) { return null; } } return popLocation(locPopAt.x, locPopAt.y); } private static JFrame popLocation(int x, int y) { JFrame anchor = new JFrame(); anchor.setAlwaysOnTop(true); anchor.setUndecorated(true); anchor.setSize(1, 1); anchor.setLocation(x, y); anchor.setVisible(true); return anchor; } public static String popSelect(String msg, String[] options, String preset) { return popSelect(msg, null, options, preset); } public static String popSelect(String msg, String[] options) { if (options.length == 0) { return ""; } return popSelect(msg, null, options, options[0]); } public static String popSelect(String msg, String title, String[] options) { if (options.length == 0) { return ""; } return popSelect(msg, title, options, options[0]); } public static String popSelect(String msg, String title, String[] options, String preset) { if (title == null || "".equals(title)) { title = "... something to select!"; } if (options.length == 0) { return ""; } if (preset == null) { preset = options[0]; } JFrame anchor = popLocation(); String ret = (String) JOptionPane.showInputDialog(anchor, msg, title, JOptionPane.PLAIN_MESSAGE, null, options, preset); if (anchor != null) { anchor.dispose(); } return ret; } public static String popFile(String title) { popat(new Screen(0).getCenter()); JFrame anchor = popLocation(); SikulixFileChooser fc = new SikulixFileChooser(anchor); File ret = fc.show(title); fc = null; if (anchor != null) { anchor.dispose(); } if (ret == null) { return ""; } return ret.getAbsolutePath(); } public static String inputText(String msg) { return inputText(msg, "", 0, 0, ""); } public static String inputText(String msg, int lines, int width) { return inputText(msg, "", lines, width, ""); } public static String inputText(String msg, int lines, int width, String text) { return inputText(msg, "", lines, width, text); } public static String inputText(String msg, String text) { return inputText(msg, "", 0, 0, text); } /** * Shows a dialog request to enter text in a multiline text field
* it has line wrapping on word bounds and a vertical scrollbar if needed * * @param msg the message to display below the textfield * @param title the title for the dialog (default: SikuliX input request) * @param lines the maximum number of lines visible in the text field (default 9) * @param width the maximum number of characters visible in one line (default 20 letters m) * @param text a preset text to show * @return The user's input including the line breaks. */ public static String inputText(String msg, String title, int lines, int width, String text) { width = Math.max(20, width); lines = Math.max(9, lines); if ("".equals(title)) { title = "SikuliX input request"; } JTextArea ta = new JTextArea(""); String fontname = "Dialog"; int pluswidth = 1; if (Settings.InputFontMono) { fontname = "Monospaced"; pluswidth = 3; } ta.setFont(new Font(fontname, Font.PLAIN, Math.max(14, Settings.InputFontSize))); int w = (width + pluswidth) * ta.getFontMetrics(ta.getFont()).charWidth('m'); int h = (lines + 1) * ta.getFontMetrics(ta.getFont()).getHeight(); ta.setText(text); ta.setLineWrap(true); ta.setWrapStyleWord(true); JScrollPane sp = new JScrollPane(); sp.setViewportView(ta); sp.setPreferredSize(new Dimension(w, h)); JTextArea tm = new JTextArea(""); tm.setFont(new Font(fontname, Font.PLAIN, Math.max(14, Settings.InputFontSize))); tm.setColumns(width); tm.setText(msg); tm.setLineWrap(true); tm.setWrapStyleWord(true); tm.setEditable(false); tm.setBackground(new JLabel().getBackground()); JPanel pnl = new JPanel(); pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS)); pnl.add(sp); pnl.add(Box.createVerticalStrut(10)); pnl.add(tm); pnl.add(Box.createVerticalStrut(10)); JFrame anchor = popLocation(); int ret = JOptionPane.showConfirmDialog(anchor, pnl, title, JOptionPane.OK_CANCEL_OPTION); if (anchor != null) { anchor.dispose(); } if (0 == ret) { return ta.getText(); } else { return null; } } public static boolean importPrefs(String path) { return true; } public static String arrayToString(String[] args) { String ret = ""; for (String s : args) { if (s.contains(" ")) { s = "\"" + s + "\""; } ret += s + " "; } return ret; } public static boolean exportPrefs(String path) { return true; } /** * store a key-value-pair in Javas persistent preferences storage that is used by SikuliX to save settings and * information between IDE sessions
* this allows, to easily make some valuable information persistent * * @param key name of the item * @param value item content */ public static void prefStore(String key, String value) { PreferencesUser.getInstance().put(prefNonSikuli + key, value); } /** * retrieve the value of a previously stored a key-value-pair from Javas persistent preferences storage that is used * by SikuliX to save settings and information between IDE sessions
* * @param key name of the item * @return the item content or empty string if not stored yet */ public static String prefLoad(String key) { return PreferencesUser.getInstance().get(prefNonSikuli + key, ""); } /** * retrieve the value of a previously stored a key-value-pair from Javas persistent preferences storage that is used * by SikuliX to save settings and information between IDE sessions
* * @param key name of the item * @param value the item content or the given value if not stored yet (default) * @return the item content or the given default */ public static String prefLoad(String key, String value) { return PreferencesUser.getInstance().get(prefNonSikuli + key, value); } /** * permanently remove the previously stored key-value-pair having the given key from Javas persistent preferences * storage that is used by SikuliX to save settings and information between IDE sessions
* * @param key name of the item to permanently remove * @return the item content that would be returned by prefLoad(key) */ public static String prefRemove(String key) { String val = prefLoad(key); PreferencesUser.getInstance().remove(prefNonSikuli + key); return val; } /** * permanently remove all previously stored key-value-pairs (by prefsStore()) from Javas persistent preferences * storage that is used by SikuliX to save settings and information between IDE sessions
*/ public static void prefRemove() { PreferencesUser.getInstance().removeAll(prefNonSikuli); } /** * convenience for a password protected VNCScreen connection * (use theVNCScreen.stop() to stop the connection) * active screens are auto-stopped at cleanup * * @param theIP the server IP * @param thePort the port number * @param password a needed password for the server in plain text * @param cTimeout seconds to wait for a valid connection * @param timeout value in milli-seconds during normal operation * @return a VNCScreen object */ public static VNCScreen vncStart(String theIP, int thePort, String password, int cTimeout, int timeout) { try { return VNCScreen.start(theIP, thePort, password, cTimeout, timeout); } catch (IOException e) { throw new RuntimeException(e); } } /** * convenience for a VNCScreen connection (use theVNCScreen.stop() to stop the connection) * active screens are auto-stopped at cleanup * * @param theIP the server IP * @param thePort the port number * @param cTimeout seconds to wait for a valid connection * @param timeout value in milli-seconds during normal operation * @return a VNCScreen object */ public static VNCScreen vncStart(String theIP, int thePort, int cTimeout, int timeout) { try { return VNCScreen.start(theIP, thePort, cTimeout, timeout); } catch (IOException e) { throw new RuntimeException(e); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/SikulixForJython.java000066400000000000000000000037271315726130400260130ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.io.File; import java.net.URL; import org.sikuli.basics.FileManager; import org.sikuli.util.JythonHelper; /** * Can be used in pure Jython environments to add the Sikuli Python API to sys.path
* Usage: (before any Sikuli features are used)
* import org.sikuli.script.SikulixForJython
* from sikuli import * */ public class SikulixForJython { private static SikulixForJython instance = null; private static final int lvl = 3; static { JythonHelper helper = JythonHelper.get(); helper.log(lvl, "SikulixForJython: init: starting"); RunTime runTime = RunTime.get(); String sikuliStuff = "sikuli/Sikuli"; File fSikuliStuff = helper.existsSysPathModule(sikuliStuff); String libSikuli = "/Lib/" + sikuliStuff + ".py"; String fpSikuliStuff; if (null == fSikuliStuff) { URL uSikuliStuff = runTime.resourceLocation(libSikuli); if (uSikuliStuff == null) { runTime.dumpClassPath(); helper.terminate(1, "no suitable sikulix...jar on classpath"); } fpSikuliStuff = runTime.fSikulixLib.getAbsolutePath(); if (!helper.hasSysPath(fpSikuliStuff)) { helper.log(lvl, "sikuli/*.py not found on current Jython::sys.path"); helper.addSysPath(fpSikuliStuff); if (!helper.hasSysPath(fpSikuliStuff)) { helper.terminate(1, "not possible to add to Jython::sys.path:\n%s", fpSikuliStuff); } helper.log(lvl, "added as Jython::sys.path[0]:\n%s", fpSikuliStuff); } else { helper.log(lvl, "sikuli/*.py is on Jython::sys.path at:\n%s", fpSikuliStuff); } } helper.addSitePackages(); helper.log(lvl, "SikulixForJython: init: success"); } private SikulixForJython() { } public static SikulixForJython get() { if (null == instance) { instance = new SikulixForJython(); } return instance; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/TextRecognizer.java000077500000000000000000000074241315726130400254750ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.script; import java.awt.image.BufferedImage; import org.sikuli.basics.Settings; import org.sikuli.basics.FileManager; import org.sikuli.basics.Debug; import java.io.*; import java.util.LinkedList; import java.util.List; import org.sikuli.natives.Mat; import org.sikuli.natives.OCRWord; import org.sikuli.natives.OCRWords; import org.sikuli.natives.Vision; /** * INTERNAL USE --- NOT part of official API * * Will be rewritten for use of Tess4J - Java only implementation */ public class TextRecognizer { static RunTime runTime = RunTime.get(); private static TextRecognizer _instance = null; private static boolean initSuccess = false; private static int lvl = 3; static { RunTime.loadLibrary("VisionProxy"); } private TextRecognizer() { init(); } private void init() { File fTessdataPath = null; initSuccess = false; if (Settings.OcrDataPath != null) { fTessdataPath = new File(FileManager.slashify(Settings.OcrDataPath, false), "tessdata"); initSuccess = fTessdataPath.exists(); } if(!initSuccess) { fTessdataPath = new File(runTime.fSikulixAppPath, "SikulixTesseract/tessdata"); if (!(initSuccess = fTessdataPath.exists())) { if (!(initSuccess = (null != runTime.extractTessData(fTessdataPath)))) { Debug.error("TextRecognizer: init: export tessdata not possible - run setup with option 3"); } } } if (!new File(fTessdataPath, "eng.traineddata").exists()) { initSuccess = false; } if (!initSuccess) { Debug.error("TextRecognizer not working: tessdata stuff not available at:\n%s", fTessdataPath); Settings.OcrTextRead = false; Settings.OcrTextSearch = false; } else { Settings.OcrDataPath = fTessdataPath.getParent(); Vision.initOCR(FileManager.slashify(Settings.OcrDataPath, true)); Debug.log(lvl, "TextRecognizer: init OK: using as data folder:\n%s", Settings.OcrDataPath); } } public static TextRecognizer getInstance() { if (_instance == null) { _instance = new TextRecognizer(); } if (!initSuccess) { return null; } return _instance; } public static void reset() { _instance = null; Vision.setSParameter("OCRLang", Settings.OcrLanguage); } public enum ListTextMode { WORD, LINE, PARAGRAPH }; public List listText(ScreenImage simg, Region parent) { return listText(simg, parent, ListTextMode.WORD); } //TODO: support LINE and PARAGRAPH // listText only supports WORD mode now. public List listText(ScreenImage simg, Region parent, ListTextMode mode) { Mat mat = Image.convertBufferedImageToMat(simg.getImage()); OCRWords words = Vision.recognize_as_ocrtext(mat).getWords(); List ret = new LinkedList(); for (int i = 0; i < words.size(); i++) { OCRWord w = words.get(i); Match m = new Match(parent.x + w.getX(), parent.y + w.getY(), w.getWidth(), w.getHeight(), w.getScore(), parent.getScreen(), w.getString()); ret.add(m); } return ret; } public String recognize(ScreenImage simg) { BufferedImage img = simg.getImage(); return recognize(img); } public String recognize(BufferedImage img) { if (initSuccess) { Mat mat = Image.convertBufferedImageToMat(img); return Vision.recognize(mat).trim(); } else { return ""; } } public String recognizeWord(ScreenImage simg) { BufferedImage img = simg.getImage(); return recognizeWord(img); } public String recognizeWord(BufferedImage img) { if (initSuccess) { Mat mat = Image.convertBufferedImageToMat(img); return Vision.recognizeWord(mat).trim(); } else { return ""; } } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/compare/000077500000000000000000000000001315726130400232725ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/script/compare/DistanceComparator.java000077500000000000000000000040701315726130400277230ustar00rootroot00000000000000/* * Copyright 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * * added Kelthuzad 2013 */ package org.sikuli.script.compare; import java.util.Comparator; import org.sikuli.script.Location; import org.sikuli.script.Region; /** * Compares the Regions by distance to a point. */ public class DistanceComparator implements Comparator { /** X value for comparison */ private double x; /** Y value for comparison */ private double y; /** * Constructor for class DistanceComparator. * @param x X-Position for comparison * @param y Y-Position for comparison */ public DistanceComparator(int x, int y) { this.x = x; this.y = y; } /** * Constructor for class DistanceComparator. * @param location Location for comparison */ public DistanceComparator(Location location) { this.x = location.getX(); this.y = location.getY(); } /** * Constructor for class DistanceComparator. * @param region Region for comparison */ public DistanceComparator(Region region) { this.x = region.getCenter().getX(); this.y = region.getCenter().getY(); } /** * Compares the distance of two {@link Region} objects. * @param region1 The first {@link Region} object * @param region2 The second {@link Region} object * @return *
    *
  • -1 if the distance to region2 is smaller than to region1
  • *
  • 0 if the distances are equal
  • *
  • 1 if the distance to region1 is smaller than to region2
  • *
*/ @Override public int compare(Region region1, Region region2) { if (region1 == region2) { return 0; } double distance1 = Math.sqrt(Math.pow(y - region1.getY(),2) + Math.pow(x - region1.getX(),2)); double distance2 = Math.sqrt(Math.pow(y - region2.getY(),2) + Math.pow(x - region2.getX(),2)); if (distance1 == distance2) { return 0; } return distance1 < distance2 ? -1 : 1; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/compare/HorizontalComparator.java000077500000000000000000000017571315726130400303330ustar00rootroot00000000000000/* * Copyright 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * * added Kelthuzad 2013 */ package org.sikuli.script.compare; import java.util.Comparator; import org.sikuli.script.Region; /** * Compares the Regions by x-position. */ public class HorizontalComparator implements Comparator { /** * Compares the X-Position of two {@link Region} objects. * @param region1 The first {@link Region} object * @param region2 The second {@link Region} object * @return *
    *
  • -1 if the x-position of region1 is smaller
  • *
  • 0 if the x-positions are equal
  • *
  • 1 if the x-position of region2 is smaller
  • *
*/ @Override public int compare(Region region1, Region region2) { if (region1 == region2) { return 0; } if (region1.getX() == region2.getX()) { return 0; } return region1.getX() < region2.getX() ? -1 : 1; } } sikulix-1.1.1/API/src/main/java/org/sikuli/script/compare/VerticalComparator.java000077500000000000000000000017551315726130400277510ustar00rootroot00000000000000/* * Copyright 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * * added Kelthuzad 2013 */ package org.sikuli.script.compare; import java.util.Comparator; import org.sikuli.script.Region; /** * Compares the Regions by y-position. */ public class VerticalComparator implements Comparator { /** * Compares the Y-Position of two {@link Region} objects. * @param region1 The first {@link Region} object * @param region2 The second {@link Region} object * @return *
    *
  • -1 if the y-position of region1 is smaller
  • *
  • 0 if the y-positions are equal
  • *
  • 1 if the y-position of region2 is smaller
  • *
*/ @Override public int compare(Region region1, Region region2) { if (region1 == region2) { return 0; } if (region1.getY() == region2.getY()) { return 0; } return region1.getY() < region2.getY() ? -1 : 1; } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/000077500000000000000000000000001315726130400213155ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/util/CommandArgs.java000066400000000000000000000170261315726130400243610ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.sikuli.basics.Debug; public class CommandArgs { private static String _callerType = ""; Options _options; ArrayList userArgs = new ArrayList(); ArrayList sikuliArgs = new ArrayList(); static String argsOrg = ""; private static boolean isIDE(String callerType) { return ("IDE".equals(callerType)); } public static boolean isIDE() { return ("IDE".equals(_callerType)); } private static boolean isScript(String callerType) { return ("SCRIPT".equals(callerType)); } public static boolean isScript() { return ("SCRIPT".equals(_callerType)); } private static boolean isOther(String callerType) { return (!isIDE(callerType) && !isScript(callerType)); } public CommandArgs(String type) { if (!isIDE(type) && !isScript(type)) { Debug.error("Commandline Parser not configured for " + type); _callerType = "OTHER"; } else { _callerType = type; } init(); } public CommandLine getCommandLine(String[] args) { CommandLineParser parser = new PosixParser(); CommandLine cmd = null; boolean isUserArg = false; for (int i=0; i < args.length; i++) { if (!isUserArg && args[i].startsWith("--")) { isUserArg = true; continue; } if (isUserArg) { userArgs.add(args[i]); } else { sikuliArgs.add(args[i]); } } try { cmd = parser.parse(_options, sikuliArgs.toArray(new String[]{}), true); } catch (ParseException exp) { Debug.error(exp.getMessage()); } return cmd; } public String[] getUserArgs() { return userArgs.toArray(new String[0]); } public String[] getSikuliArgs() { return sikuliArgs.toArray(new String[0]); } /** * Adds all options to the Options object */ @SuppressWarnings("static-access") private void init() { _options = new Options(); _options.addOption(CommandArgsEnum.HELP.shortname(), CommandArgsEnum.HELP.longname(), false, CommandArgsEnum.HELP.description()); _options.addOption( OptionBuilder.withLongOpt(CommandArgsEnum.DEBUG.longname()) .hasOptionalArg() .withArgName(CommandArgsEnum.DEBUG.argname()) .withDescription(CommandArgsEnum.DEBUG.description()) .create(CommandArgsEnum.DEBUG.shortname().charAt(0))); _options.addOption( OptionBuilder.withLongOpt(CommandArgsEnum.LOGFILE.longname()) .hasOptionalArg() .withArgName(CommandArgsEnum.LOGFILE.argname()) .withDescription(CommandArgsEnum.LOGFILE.description()) .create(CommandArgsEnum.LOGFILE.shortname().charAt(0))); _options.addOption( OptionBuilder.withLongOpt(CommandArgsEnum.USERLOGFILE.longname()) .hasOptionalArg() .withArgName(CommandArgsEnum.USERLOGFILE.argname()) .withDescription(CommandArgsEnum.USERLOGFILE.description()) .create(CommandArgsEnum.USERLOGFILE.shortname().charAt(0))); _options.addOption(CommandArgsEnum.CONSOLE.shortname(), CommandArgsEnum.CONSOLE.longname(), false, CommandArgsEnum.CONSOLE.description()); _options.addOption(CommandArgsEnum.SPLASH.shortname(), CommandArgsEnum.SPLASH.longname(), false, CommandArgsEnum.SPLASH.description()); _options.addOption( OptionBuilder.withLongOpt(CommandArgsEnum.INTERACTIVE.longname()) .hasOptionalArg() .withArgName(CommandArgsEnum.INTERACTIVE.argname()) .withDescription(CommandArgsEnum.INTERACTIVE.description()) .create(CommandArgsEnum.INTERACTIVE.shortname().charAt(0))); _options.addOption( OptionBuilder.withLongOpt(CommandArgsEnum.SERVER.longname()) .hasOptionalArg() .withArgName(CommandArgsEnum.SERVER.argname()) .withDescription(CommandArgsEnum.SERVER.description()) .create(CommandArgsEnum.SERVER.shortname().charAt(0))); _options.addOption( OptionBuilder.withLongOpt(CommandArgsEnum.LOAD.longname()) .withDescription(CommandArgsEnum.LOAD.description()) .hasOptionalArgs() .withArgName(CommandArgsEnum.LOAD.argname()) .create(CommandArgsEnum.LOAD.shortname().charAt(0))); _options.addOption( OptionBuilder.withLongOpt(CommandArgsEnum.TEST.longname()) .withDescription(CommandArgsEnum.TEST.description()) .hasOptionalArgs() .withArgName(CommandArgsEnum.TEST.argname()) .create(CommandArgsEnum.TEST.shortname().charAt(0))); _options.addOption( OptionBuilder.withLongOpt(CommandArgsEnum.RUN.longname()) .withDescription(CommandArgsEnum.RUN.description()) .hasOptionalArgs() .withArgName(CommandArgsEnum.RUN.argname()) .create(CommandArgsEnum.RUN.shortname().charAt(0))); } /** * Prints the help */ public void printHelp() { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp(80, "\n", "----- Running SikuliX " + "-------------", _options, "-----\n (.sikuli might be omitted, is assumed)\n" + "path relative to current working directory or absolute path\n" + "though deprecated: so called executables .skl can be used too\n" + "------\nanything after --\nor after something beginning with --\n" + "go to script as user parameters (respecting enclosing \")\n" + "------\n-d use this option if you encounter any weird problems\n" + "DebugLevel=3 and all output goes to /SikuliLog.text\n" + "----------------------------------------------------------------", true); } public static String[] scanArgs(String[] args) { //TODO detect leading and/or trailing blanks argsOrg = System.getenv("SIKULI_COMMAND"); if (argsOrg == null) { argsOrg = System.getProperty("sikuli.SIKULI_COMMAND"); } if (argsOrg == null) { argsOrg = ""; } String sep = "\""; String temp = null; Pattern pat; Matcher m; List nargs = new ArrayList(); for (String arg : args) { if (arg.startsWith("asApp")) { continue; } if (arg.startsWith(sep)) { if (!arg.endsWith(sep)) { temp = arg.substring(1); continue; } } else if (arg.endsWith(sep)) { if (temp != null) { arg = temp + " " + arg.substring(0, arg.length() - 1); if (argsOrg != null && !argsOrg.contains(arg)) { arg = arg.replace(" ", " *?"); pat = Pattern.compile("(" + arg + ")"); m = pat.matcher(argsOrg); if (m.find()) { arg = m.group(); } else { arg = "?" + arg + "?"; } } temp = null; } } else if (temp != null) { temp += " " + arg; continue; } nargs.add(arg); } return nargs.toArray(new String[0]); } public String getArgsOrg() { return argsOrg; } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/CommandArgsEnum.java000066400000000000000000000052351315726130400252050ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; /** * Enum that stores the info about the commandline args */ public enum CommandArgsEnum { /** * Shows the help */ HELP("help", "h", null, "print this help message"), /** * set debug level */ DEBUG("debug", "d", "debug level", "positive integer (1)"), /** * outputfile for Sikuli logging messages */ LOGFILE("logfile", "f", "Sikuli logfile", "a valid filename (WorkingDir/SikuliLog.txt)"), /** * outputfile for user logging messages */ USERLOGFILE("userlog", "u", "User logfile", "a valid filename (WorkingDir/UserLog.txt)"), /** * Starts an interactive session */ INTERACTIVE("interactive", "i", "[runner (jython)]", "start interactive session and/or select ScriptRunner"), /** * Runs the script */ RUN("run", "r", "foobar.sikuli", "run script"), /** * Runs the script as testcase */ TEST("test", "t", "foobar.sikuli", "runs script as unittest"), /** * Prints all errormessages to stdout */ CONSOLE("console", "c", null, "print all output to commandline (IDE message area)"), /** * Prints all errormessages to stdout */ SPLASH("splash", "x", null, "show a splash screen to enter options"), /** * Preloads script in IDE */ LOAD("load", "l", "one or more foobar.sikuli", "preload scripts in IDE"), /** * run as server */ SERVER("server", "s", "[port (50001)]", "run as server on optional port"); /** * Longname of the parameter */ private String longname; /** * Shortname of the parameter */ private String shortname; /** * The param name */ private String argname; /** * The description */ private String description; /** * Returns the long name * * @return Longname of the parameter */ public String longname() { return longname; } /** * Returns the short name * * @return Shortname of the parameter */ public String shortname() { return shortname; } /** * Returns the argname * * @return The argname */ public String argname() { return argname; } /** * Description for the param * * @return the description */ public String description() { return description; } /** * Private constructor for class CommandArgsEnum. * * @param longname The long name for the param * @param shortname The short name for the param * @param argname The argname * @param description The description for the Command Args */ private CommandArgsEnum(String longname, String shortname, String argname, String description) { this.longname = longname; this.shortname = shortname; this.argname = argname; this.description = description; } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/EventObserver.java000077500000000000000000000003351315726130400247550ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; /** * INTRENAL USE */ public interface EventObserver { public void update(EventSubject s); } sikulix-1.1.1/API/src/main/java/org/sikuli/util/EventSubject.java000077500000000000000000000004031315726130400245610ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; /** * INTERNAL USE */ public interface EventSubject { public void addObserver(EventObserver o); public void notifyObserver(); } sikulix-1.1.1/API/src/main/java/org/sikuli/util/FakeRobot.java000066400000000000000000000050001315726130400240270ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import org.sikuli.basics.Debug; import org.sikuli.script.*; import java.awt.*; /** * for testing non local IScreen implementations (e.g. VNCScreen) */ public class FakeRobot implements IRobot{ public static IRobot getDesktopRobot() { try { return new RobotDesktop(); } catch (AWTException e) { Debug.error("FakeRobot: getDesktopRobot: not possible: returning null"); return null; } } @Override public void keyDown(String keys) { } @Override public void keyUp(String keys) { } @Override public void keyDown(int code) { } @Override public void keyUp(int code) { } @Override public void keyUp() { } @Override public void pressModifiers(int modifiers) { } @Override public void releaseModifiers(int modifiers) { } @Override public void typeChar(char character, KeyMode mode) { } @Override public void typeKey(int key) { } @Override public void typeStarts() { } @Override public void typeEnds() { } @Override public void mouseMove(int x, int y) { Debug.error("FakeRobot: mouseMove: (%d, %d)", x, y); } @Override public void mouseDown(int buttons) { } @Override public int mouseUp(int buttons) { Debug.error("FakeRobot: mouseUp: most probably a click"); return 0; } @Override public void mouseReset() { } @Override public void clickStarts() { } @Override public void clickEnds() { } @Override public void smoothMove(Location dest) { Debug.error("FakeRobot: mouseMove: (%d, %d)", dest.x, dest.y); } @Override public void smoothMove(Location src, Location dest, long ms) { Debug.error("FakeRobot: mouseMove: (%d, %d)", dest.x, dest.y); } @Override public void mouseWheel(int wheelAmt) { } @Override public ScreenImage captureScreen(Rectangle screenRect) { Debug.log(3, "FakeRobot: captureScreen: should not be used: returning null"); return null; } @Override public void waitForIdle() { } @Override public void delay(int ms) { } @Override public void setAutoDelay(int ms) { } @Override public Color getColorAt(int x, int y) { return null; } @Override public void cleanup() { } @Override public boolean isRemote() { return true; } /** * Return the underlying device object (if any). */ @Override public IScreen getScreen() { return null; } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/JLangHelperInterface.java000066400000000000000000000013171315726130400261360ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; /** * This interface provides script language specific methods which are non a part * of ScriptRunner classes. * * These methods may be used both in case of a script running from IDE or from * sikulixapi library without IDE. */ public interface JLangHelperInterface { /** * Run callback for observers. * * @param args * is array for two elements. First is a callback object. Second * is an event. * @return true if the callback was ran correctly. False in other cases. */ public boolean runObserveCallback(Object[] args); } sikulix-1.1.1/API/src/main/java/org/sikuli/util/JRubyHelper.java000066400000000000000000000041761315726130400243630ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import java.lang.reflect.Array; import java.lang.reflect.Method; import org.sikuli.basics.Debug; /** * This class implements JRuby specific parts */ public class JRubyHelper implements JLangHelperInterface { private static final String me = "JRubyHelper: "; /** * Mandatory method which returns an instance of the helper * * @return */ public static JRubyHelper get() { return new JRubyHelper(); } @Override public boolean runObserveCallback(Object[] args) { boolean result = false; Object callback = args[0]; Object e = args[1]; try { Class rubyProcClass = callback.getClass(); Method getRuntime = rubyProcClass.getMethod("getRuntime", new Class[0]); Object runtime = getRuntime.invoke(callback, new Object[0]); Class runtimeClass = getRuntime.getReturnType(); Method getCurrentContext = runtimeClass.getMethod("getCurrentContext", new Class[0]); Object context = getCurrentContext.invoke(runtime, new Object[0]); Class jrubyUtil = Class.forName("org.jruby.javasupport.JavaUtil"); Method convertJavaToRuby = jrubyUtil.getMethod("convertJavaToRuby", new Class[] { runtimeClass, Object.class }); Object paramForRuby = convertJavaToRuby.invoke(null, new Object[] { runtime, e }); Object iRubyObject = Array.newInstance(Class.forName("org.jruby.runtime.builtin.IRubyObject"), 1); Array.set(iRubyObject, 0, paramForRuby); Method call = rubyProcClass.getMethod("call", new Class[] { context.getClass(), iRubyObject.getClass() }); call.invoke(callback, new Object[] { context, iRubyObject }); result = true; } catch (Exception ex) { String msg = ex.getMessage(); Debug.error("ObserverCallBack: problem with scripting handler: %s\n%s\n%s", me, callback, msg); } return result; } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/JythonHelper.java000066400000000000000000000567201315726130400246050ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.basics.Settings; import org.sikuli.script.ImagePath; import org.sikuli.script.RunTime; import org.sikuli.script.Sikulix; public class JythonHelper implements JLangHelperInterface { static RunTime runTime = RunTime.get(); // private static final String me = "JythonSupport: "; private static int lvl = 3; public void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private void logp(String message, Object... args) { Debug.logx(-3, message, args); } private void logp(int level, String message, Object... args) { if (level <= Debug.getDebugLevel()) { logp(message, args); } } public void terminate(int retVal, String msg, Object... args) { runTime.terminate(retVal, me + msg, args); } // static JythonHelper instance = null; static Object interpreter = null; List sysPath = new ArrayList(); List sysArgv = new ArrayList(); int nPathAdded = 0; int nPathSaved = -1; static Class[] nc = new Class[0]; static Class[] nc1 = new Class[1]; static Class cInterpreter = null; static Class cPyException = null; static Class cList = null; static Class cPy = null; static Class cPyFunction = null; static Class cPyMethod = null; static Class cPyInstance = null; static Class cPyObject = null; static Class cPyString = null; static Method mLen, mGet, mSet, mAdd, mRemove, mClear; static Method mGetSystemState, mExec, mExecfile; static Field PI_path; private JythonHelper() { } public static JythonHelper get() { if (instance == null) { instance = new JythonHelper(); instance.log(lvl, "init: starting"); try { cInterpreter = Class.forName("org.python.util.PythonInterpreter"); } catch (Exception ex) { String sJython = new File(runTime.SikuliJython).getName(); File fJython = new File(runTime.fSikulixDownloadsGeneric, sJython); instance.log(lvl, "trying to use setup downloaded Jython:\n%s", fJython.getAbsolutePath()); if (fJython.exists()) { runTime.addToClasspath(fJython.getAbsolutePath()); } else { instance.log(-1, "Not possible to get a Jython on to the classpath!"); cInterpreter = null; } } try { cInterpreter = Class.forName("org.python.util.PythonInterpreter"); mGetSystemState = cInterpreter.getMethod("getSystemState", nc); mExec = cInterpreter.getMethod("exec", new Class[]{String.class}); mExecfile = cInterpreter.getMethod("execfile", new Class[]{String.class}); Constructor PI_new = cInterpreter.getConstructor(nc); interpreter = PI_new.newInstance(null); cPyException = Class.forName("org.python.core.PyException"); cList = Class.forName("org.python.core.PyList"); cPy = Class.forName("org.python.core.Py"); cPyFunction = Class.forName("org.python.core.PyFunction"); cPyMethod = Class.forName("org.python.core.PyMethod"); cPyInstance = Class.forName("org.python.core.PyInstance"); cPyObject = Class.forName("org.python.core.PyObject"); cPyString = Class.forName("org.python.core.PyString"); mLen = cList.getMethod("__len__", nc); mClear = cList.getMethod("clear", nc); mGet = cList.getMethod("get", new Class[]{int.class}); mSet = cList.getMethod("set", new Class[]{int.class, Object.class}); mAdd = cList.getMethod("add", new Class[]{Object.class}); mRemove = cList.getMethod("remove", new Class[]{int.class}); } catch (Exception ex) { cInterpreter = null; } instance.log(lvl, "init: success"); } if (cInterpreter == null) { instance.runTime.terminate(1, "JythonHelper: no Jython on classpath"); } runTime.isJythonReady = true; return instance; } private void noOp() { } // for debugging as breakpoint class PyException { Object inst = null; Field fType = null; Field fValue = null; Field fTrBack = null; public PyException(Object i) { inst = i; cPyException.cast(inst); try { fType = cPyException.getField("type"); fValue = cPyException.getField("value"); fTrBack = cPyException.getField("traceback"); } catch (Exception ex) { noOp(); } } public int isTypeExit() { try { if (fType.get(inst).toString().contains("SystemExit")) { return Integer.parseInt(fValue.get(inst).toString()); } } catch (Exception ex) { return -999; } return -1; } } class PyInstance { Object inst = null; Method mGetAttr = null; Method mInvoke = null; public PyInstance(Object i) { inst = i; cPyInstance.cast(inst); try { mGetAttr = cPyInstance.getMethod("__getattr__", String.class); mInvoke = cPyInstance.getMethod("invoke", String.class, cPyObject); } catch (Exception ex) { noOp(); } } public Object get() { return inst; } Object __getattr__(String mName) { if (mGetAttr == null) { return null; } Object method = null; try { method = mGetAttr.invoke(inst, mName); } catch (Exception ex) { } return method; } public void invoke(String mName, Object arg) { if (mInvoke != null) { try { mInvoke.invoke(inst, mName, arg); } catch (Exception ex) { noOp(); } } } } class PyFunction { public String __name__; Object func = null; Method mCall = null; Method mCall1 = null; public PyFunction(Object f) { func = f; try { cPyFunction.cast(func); mCall = cPyFunction.getMethod("__call__"); mCall1 = cPyFunction.getMethod("__call__", cPyObject); } catch (Exception ex) { func = null; } if (func == null) { try { func = f; cPyMethod.cast(func); mCall = cPyMethod.getMethod("__call__"); mCall1 = cPyMethod.getMethod("__call__", cPyObject); } catch (Exception ex) { func = null; } } } void __call__(Object arg) { if (mCall1 != null) { try { mCall1.invoke(func, arg); } catch (Exception ex) { } } } void __call__() { if (mCall != null) { try { mCall.invoke(func); } catch (Exception ex) { } } } } class Py { Method mJava2py = null; public Py() { try { mJava2py = cPy.getMethod("java2py", Object.class); } catch (Exception ex) { noOp(); } } Object java2py(Object arg) { if (mJava2py == null) { return null; } Object pyObject = null; try { pyObject = mJava2py.invoke(null, arg); } catch (Exception ex) { noOp(); } return pyObject; } } class PyString { String aString = ""; Object pyString = null; public PyString(String s) { aString = s; try { pyString = cPyString.getConstructor(String.class).newInstance(aString); } catch (Exception ex) { } } public Object get() { return pyString; } } public boolean exec(String code) { try { mExec.invoke(interpreter, code); } catch (Exception ex) { PyException pex = new PyException(ex.getCause()); if (pex.isTypeExit() < 0) { log(-1, "exec: returns:\n%s", ex.getCause()); } return false; } return true; } public int execfile(String fpScript) { int retval = -999; try { mExecfile.invoke(interpreter, fpScript); } catch (Exception ex) { PyException pex = new PyException(ex.getCause()); if ((retval = pex.isTypeExit()) < 0) { log(-1, "execFile: returns:\n%s", ex.getCause()); } } return retval; } //TODO check signature (instance method) public boolean checkCallback(Object[] args) { PyInstance inst = new PyInstance(args[0]); String mName = (String) args[1]; Object method = inst.__getattr__(mName); if (method == null || !method.getClass().getName().contains("PyMethod")) { log(-100, "checkCallback: Object: %s, Method not found: %s", inst, mName); return false; } return true; } public boolean runLoggerCallback(Object[] args) { PyInstance inst = new PyInstance(args[0]); String mName = (String) args[1]; String msg = (String) args[2]; Object method = inst.__getattr__(mName); if (method == null || !method.getClass().getName().contains("PyMethod")) { log(-100, "runLoggerCallback: Object: %s, Method not found: %s", inst, mName); return false; } try { PyString pmsg = new PyString(msg); inst.invoke(mName, pmsg.get()); } catch (Exception ex) { log(-100, "runLoggerCallback: invoke: %s", ex.getMessage()); return false; } return true; } @Override public boolean runObserveCallback(Object[] args) { PyFunction func = new PyFunction(args[0]); boolean success = true; try { func.__call__(new Py().java2py(args[1])); } catch (Exception ex) { // if (!"".equals(func.__name__)) { if (!func.toString().contains("")) { log(-1, "runObserveCallback: jython invoke: %s", ex.getMessage()); return false; } success = false; } if (success) { return true; } try { func.__call__(); } catch (Exception ex) { log(-1, "runObserveCallback: jython invoke : %s", ex.getMessage()); return false; } return true; } //TODO implement generalized callback public boolean runCallback(Object[] args) { PyInstance inst = (PyInstance) args[0]; String mName = (String) args[1]; Object method = inst.__getattr__(mName); if (method == null || !method.getClass().getName().contains("PyMethod")) { log(-1, "runCallback: Object: %s, Method not found: %s", inst, mName); return false; } try { PyString pmsg = new PyString("not yet supported"); inst.invoke(mName, pmsg.get()); } catch (Exception ex) { log(-1, "runCallback: invoke: %s", ex.getMessage()); return false; } return true; } public static JythonHelper set(Object ip) { JythonHelper.get(); interpreter = ip; return instance; } public boolean prepareRobot() { if (runTime.isRunningFromJar()) { File fLibRobot = new File(runTime.fSikulixLib, "robot"); if (!fLibRobot.exists()) { log(-1, "prepareRobot: not available: %s", fLibRobot); Sikulix.terminate(1); } if (!hasSysPath(runTime.fSikulixLib.getAbsolutePath())) { insertSysPath(runTime.fSikulixLib); } } if (!hasSysPath(new File(Settings.BundlePath).getParent())) { appendSysPath(new File(Settings.BundlePath).getParent()); } exec("import robot"); return true; } public String load(String fpJarOrFolder) { //## //# loads a Sikuli extension (.jar) from //# 1. user's sikuli data path //# 2. bundle path //# //def load(jar): // def _load(abspath): // if os.path.exists(abspath): // if not abspath in sys.path: // sys.path.append(abspath) // return True // return False // // if JythonHelper.load(jar): // return True // // if _load(jar): // return True // path = getBundlePath() // if path: // jarInBundle = os.path.join(path, jar) // if _load(jarInBundle): // return True // path = ExtensionManager.getInstance().getLoadPath(jar) // if path and _load(path): // return True // return False log(lvl, "load: to be loaded:\n%s", fpJarOrFolder); if (!fpJarOrFolder.endsWith(".jar")) { fpJarOrFolder += ".jar"; } String fpBundle = ImagePath.getBundlePath(); File fJar = new File(FileManager.normalizeAbsolute(fpJarOrFolder, false)); if (!fJar.exists()) { fJar = new File(fpBundle, fpJarOrFolder); fJar = new File(FileManager.normalizeAbsolute(fJar.getPath(), false)); if (!fJar.exists()) { fJar = new File(runTime.fSikulixExtensions, fpJarOrFolder); if (!fJar.exists()) { fJar = new File(runTime.fSikulixLib, fpJarOrFolder); if (!fJar.exists()) { fJar = null; } } } } if (fJar != null) { if (runTime.addToClasspath(fJar.getPath())) { if (!hasSysPath(fJar.getPath())) { insertSysPath(fJar); } } else { log(-1, "load: not possible"); } } else { log(-1, "load: could not be found - even not in bundle nor in Lib nor in Extensions"); } if (fJar == null) { return null; } return fJar.getAbsolutePath(); } private long lastRun = 0; private List importedScripts = new ArrayList(); String name = ""; public void reloadImported() { if (lastRun > 0) { for (File fMod : importedScripts) { name = getPyName(fMod); if (new File(fMod, name + ".py").lastModified() > lastRun) { log(lvl, "reload: %s", fMod); get().exec("reload(" + name + ")"); }; } } lastRun = new Date().getTime(); } private String getPyName(File fMod) { String ending = ".sikuli"; String name = fMod.getName(); if (name.endsWith(ending)) { name = name.substring(0, name.length() - ending.length()); } return name; } public String findModule(String modName, Object packPath, Object sysPath) { if (modName.endsWith(".*")) { log(lvl + 1, "findModule: %s", modName); return null; } if (packPath != null) { log(lvl + 1, "findModule: in pack: %s (%s)", modName, packPath); return null; } int nDot = modName.lastIndexOf("."); String modNameFull = modName; if (nDot > -1) { modName = modName.substring(nDot + 1); } String fpBundle = ImagePath.getBundlePath(); File fParentBundle = null; File fModule = null; if (fpBundle != null) { fParentBundle = new File(fpBundle).getParentFile(); fModule = existsModule(modName, fParentBundle); } if (fModule == null) { fModule = existsSysPathModule(modName); if (fModule == null) { return null; } } log(lvl + 1, "findModule: final: %s [%s]", fModule.getName(), fModule.getParent()); if (fModule.getName().endsWith(".sikuli")) { importedScripts.add(fModule); return fModule.getAbsolutePath(); } return null; } public String loadModulePrepare(String modName, String modPath) { log(lvl + 1, "loadModulePrepare: %s in %s", modName, modPath); int nDot = modName.lastIndexOf("."); if (nDot > -1) { modName = modName.substring(nDot + 1); } addSysPath(modPath); if (modPath.endsWith(".sikuli")) { ImagePath.add(modPath); } return modName; } private File existsModule(String mName, File fFolder) { if (mName.endsWith(".sikuli") || mName.endsWith(".py")) { return null; } File fSikuli = new File(fFolder, mName + ".sikuli"); if (fSikuli.exists()) { return fSikuli; } File fPython = new File(fFolder, mName + ".py"); if (fPython.exists()) { return fPython; } return null; } public void getSysArgv() { sysArgv = new ArrayList(); if (null == cInterpreter) { sysArgv = null; return; } try { Object aState = mGetSystemState.invoke(interpreter, (Object[]) null); Field fArgv = aState.getClass().getField("argv"); Object pyArgv = fArgv.get(aState); Integer argvLen = (Integer) mLen.invoke(pyArgv, (Object[]) null); for (int i = 0; i < argvLen; i++) { String entry = (String) mGet.invoke(pyArgv, i); log(lvl + 1, "sys.path[%2d] = %s", i, entry); sysArgv.add(entry); } } catch (Exception ex) { sysArgv = null; } } public void setSysArgv(String[] args) { if (null == cInterpreter || null == sysArgv) { return; } try { Object aState = mGetSystemState.invoke(interpreter, (Object[]) null); Field fArgv = aState.getClass().getField("argv"); Object pyArgv = fArgv.get(aState); mClear.invoke(pyArgv, null); for (String arg : args) { mAdd.invoke(pyArgv, arg); } } catch (Exception ex) { sysArgv = null; } } public void getSysPath() { sysPath = new ArrayList(); if (null == cInterpreter) { sysPath = null; return; } try { Object aState = mGetSystemState.invoke(interpreter, (Object[]) null); Field fPath = aState.getClass().getField("path"); Object pyPath = fPath.get(aState); Integer pathLen = (Integer) mLen.invoke(pyPath, (Object[]) null); for (int i = 0; i < pathLen; i++) { String entry = (String) mGet.invoke(pyPath, i); log(lvl + 1, "sys.path[%2d] = %s", i, entry); sysPath.add(entry); } } catch (Exception ex) { sysPath = null; } } public void setSysPath() { if (null == cInterpreter || null == sysPath) { return; } try { Object aState = mGetSystemState.invoke(interpreter, (Object[]) null); Field fPath = aState.getClass().getField("path"); Object pyPath = fPath.get(aState); Integer pathLen = (Integer) mLen.invoke(pyPath, (Object[]) null); for (int i = 0; i < pathLen && i < sysPath.size(); i++) { String entry = sysPath.get(i); log(lvl + 1, "sys.path.set[%2d] = %s", i, entry); mSet.invoke(pyPath, i, entry); } if (pathLen < sysPath.size()) { for (int i = pathLen; i < sysPath.size(); i++) { String entry = sysPath.get(i); log(lvl + 1, "sys.path.add[%2d] = %s", i, entry); mAdd.invoke(pyPath, entry); } } if (pathLen > sysPath.size()) { for (int i = sysPath.size(); i < pathLen; i++) { String entry = (String) mGet.invoke(pyPath, i); log(lvl + 1, "sys.path.rem[%2d] = %s", i, entry); mRemove.invoke(pyPath, i); } } } catch (Exception ex) { sysPath = null; } } public void addSitePackages() { File fLibFolder = runTime.fSikulixLib; File fSitePackages = new File(fLibFolder, "site-packages"); if (fSitePackages.exists()) { addSysPath(fSitePackages); if (hasSysPath(fSitePackages.getAbsolutePath())) { log(lvl, "added as Jython::sys.path[0]:\n%s", fSitePackages); } File fSites = new File(fSitePackages, "sites.txt"); String sSites = ""; if (fSites.exists()) { sSites = FileManager.readFileToString(fSites); if (!sSites.isEmpty()) { log(lvl, "found Lib/site-packages/sites.txt"); String[] listSites = sSites.split("\n"); for (String site : listSites) { String path = site.trim(); if (!path.isEmpty()) { appendSysPath(path); log(lvl, "adding from Lib/site-packages/sites.txt:\n%s", path); } } } } } String fpBundle = ImagePath.getPath(0); if (fpBundle != null) { addSysPath(fpBundle); } } public void addSysPath(String fpFolder) { if (!hasSysPath(fpFolder)) { sysPath.add(0, fpFolder); setSysPath(); nPathAdded++; } } public void appendSysPath(String fpFolder) { if (!hasSysPath(fpFolder)) { sysPath.add(fpFolder); setSysPath(); nPathAdded++; } } public void putSysPath(String fpFolder, int n) { if (n < 1 || n > sysPath.size()) { addSysPath(fpFolder); } else { sysPath.add(n, fpFolder); setSysPath(); nPathAdded++; } } public void addSysPath(File fFolder) { addSysPath(fFolder.getAbsolutePath()); } public void insertSysPath(File fFolder) { getSysPath(); sysPath.add((nPathSaved > -1 ? nPathSaved : 0), fFolder.getAbsolutePath()); setSysPath(); nPathSaved = -1; } public void removeSysPath(File fFolder) { int n; if (-1 < (n = getSysPathEntry(fFolder))) { sysPath.remove(n); nPathSaved = n; setSysPath(); nPathAdded = nPathAdded == 0 ? 0 : nPathAdded--; } } public boolean hasSysPath(String fpFolder) { getSysPath(); for (String fpPath : sysPath) { if (FileManager.pathEquals(fpPath, fpFolder)) { return true; } } return false; } public int getSysPathEntry(File fFolder) { getSysPath(); int n = 0; for (String fpPath : sysPath) { if (FileManager.pathEquals(fpPath, fFolder.getAbsolutePath())) { return n; } n++; } return -1; } public File existsSysPathModule(String modname) { getSysPath(); File fModule = null; for (String fpPath : sysPath) { fModule = existsModule(modname, new File(fpPath)); if (null != fModule) { break; } } return fModule; } public File existsSysPathJar(String fpJar) { getSysPath(); File fJar = null; for (String fpPath : sysPath) { fJar = new File(fpPath, fpJar); if (fJar.exists()) { break; } fJar = null; } return fJar; } public void showSysPath() { if (Debug.is(lvl)) { getSysPath(); log(lvl, "***** Jython sys.path"); for (int i = 0; i < sysPath.size(); i++) { logp(lvl, "%2d: %s", i, sysPath.get(i)); } log(lvl, "***** Jython sys.path end"); } } public String getCurrentLine() { String trace = ""; Object frame = null; Object back = null; try { Method mGetFrame = cPy.getMethod("getFrame", nc); Class cPyFrame = Class.forName("org.python.core.PyFrame"); Field fLineno = cPyFrame.getField("f_lineno"); Field fCode = cPyFrame.getField("f_code"); Field fBack = cPyFrame.getField("f_back"); Class cPyBaseCode = Class.forName("org.python.core.PyBaseCode"); Field fFilename = cPyBaseCode.getField("co_filename"); frame = mGetFrame.invoke(cPy, (Object[]) null); back = fBack.get(frame); if (null == back) { trace = "Jython: at " + getCurrentLineTraceElement(fLineno, fCode, fFilename, frame); } else { trace = "Jython traceback - current first:\n" + getCurrentLineTraceElement(fLineno, fCode, fFilename, frame); while (null != back) { String line = getCurrentLineTraceElement(fLineno, fCode, fFilename, back); if (! line.startsWith("Region (")) { trace += "\n" + line; } back = fBack.get(back); } } } catch (Exception ex) { trace = String.format("Jython: getCurrentLine: INSPECT EXCEPTION: %s", ex); } return trace; } private String getCurrentLineTraceElement(Field fLineno, Field fCode, Field fFilename, Object frame) { String trace = ""; try { int lineno = fLineno.getInt(frame); Object code = fCode.get(frame); Object filename = fFilename.get(code); String fname = FileManager.getName((String) filename); fname = fname.replace(".py", ""); trace = String.format("%s (%d)", fname, lineno); } catch (Exception ex) { } return trace; } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/LinuxSupport.java000066400000000000000000000360041315726130400246570ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.PrintStream; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.script.RunTime; /** * INTERNAL USE: all things needed with Linux at setup or runtime */ public class LinuxSupport { static final RunTime runTime = RunTime.get(); // private static final String me = "LinuxSupport: "; private static int lvl = 3; private static boolean isCopiedProvided = false; private static boolean haveBuilt = false; public static boolean shouldUseProvided = false; // private static String osArch; public static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private static void logp(String message, Object... args) { Debug.logx(-3, message, args); } // static File fWorkDir = null; private static final String buildFolderSrc = "Build/Source"; private static final String buildFolderInclude = "Build/Include"; private static final String buildFolderTarget = "Build/Target"; static File fLibs = runTime.fLibsFolder; public static final String slibVision = "VisionProxy"; public static final String libVision = "lib" + slibVision + ".so"; public static final String libGrabKey = "libJXGrabKey.so"; static boolean libSearched = false; private static String libOpenCVcore = ""; private static String libOpenCVimgproc = ""; private static String libOpenCVhighgui = ""; private static String libTesseract = ""; private static boolean opencvAvail = true; private static boolean tessAvail = true; public static String getLinuxDistro() { if (!runTime.runningLinux) { return ""; } String result = runTime.runcmd("lsb_release -i -r -s"); String linuxDistro = result.replaceAll("\n", " ").trim(); if (linuxDistro.contains("*** error ***")) { log(lvl, "command returns error: lsb_release -i -r -s\n%s", result); linuxDistro = "???DISTRO???"; } return linuxDistro; } public static boolean existsLibs() { return new File(runTime.fLibsProvided, libVision).exists() || new File(runTime.fLibsProvided, libGrabKey).exists(); } public static boolean copyProvidedLibs(File fLibsFolder) { String[] toCopy = runTime.fLibsProvided.list(new FilenameFilter() { @Override public boolean accept(File folder, String name) { if (name.endsWith(".so")) { return true; } return false; } }); boolean success = false; if (toCopy != null) { for (String aLib : toCopy) { success |= FileManager.xcopy(new File(runTime.fLibsProvided, aLib), new File(fLibsFolder, aLib)); } } return success; } public static boolean checkAllLibs() { boolean success = false; if (!isCopiedProvided && !runTime.useLibsProvided) { success = true; String[] allLibs = runTime.fLibsProvided.list(new FilenameFilter() { @Override public boolean accept(File folder, String name) { if (name.toLowerCase().endsWith(".so")) { return true; } return false; } }); if (allLibs != null) { for (String sLib : allLibs) { File fSrc = new File(runTime.fLibsProvided, sLib); File fTgt = new File(runTime.fLibsFolder, sLib); success &= FileManager.xcopy(fSrc, fTgt); log(3, "Copy provided lib: %s (%s)", sLib, (success ? "ok" : "did not work")); } } isCopiedProvided = true; } else if (!haveBuilt) { haveBuilt = true; success = haveToBuild(); } shouldUseProvided = success; return success; } public static boolean haveToBuild() { boolean success = true; log(3, "we have to build libVisionProxy.so"); success &= checkNeeded(); if (success) { success &= buildVision(); } return success; } private static boolean checkNeeded() { String cmdRet; boolean checkSuccess = true; log(lvl, "checking: availability of OpenCV and Tesseract"); log(lvl, "checking: scanning loader cache (ldconfig -p)"); cmdRet = runTime.runcmd("ldconfig -p"); if (cmdRet.contains(runTime.runCmdError)) { log(-1, "checking: ldconfig returns error:\ns", cmdRet); checkSuccess = false; } else { String[] libs = cmdRet.split("\n"); for (String libx : libs) { libx = libx.trim(); if (!libx.startsWith("lib")) { continue; } if (libx.startsWith("libopencv_core.so.")) { libOpenCVcore = libx.split("=>")[1].trim(); } else if (libx.startsWith("libopencv_highgui.so.")) { libOpenCVhighgui = libx.split("=>")[1].trim(); } else if (libx.startsWith("libopencv_imgproc.so.")) { libOpenCVimgproc = libx.split("=>")[1].trim(); } else if (libx.startsWith("libtesseract.so.")) { libTesseract = libx.split("=>")[1].trim(); } } if (libOpenCVcore.isEmpty() || libOpenCVhighgui.isEmpty() || libOpenCVimgproc.isEmpty()) { log(-1, "checking: OpenCV not in loader cache (see doc-note on OpenCV)"); opencvAvail = checkSuccess = false; } else { log(lvl, "checking: found OpenCV libs:\n%s\n%s\n%s", libOpenCVcore, libOpenCVhighgui, libOpenCVimgproc); } if (libTesseract.isEmpty()) { log(-1, "checking: Tesseract not in loader cache (see doc-note on Tesseract)"); tessAvail = checkSuccess = false; } else { log(lvl, "checking: found Tesseract lib:\n%s", libTesseract); } } // checking wmctrl, xdotool // cmdRet = runTime.runcmd("wmctrl -m"); // if (cmdRet.contains(runTime.runCmdError)) { // log(-1, "checking: wmctrl not available or not working"); // } else { // log(lvl, "checking: wmctrl seems to be available"); // } // cmdRet = runTime.runcmd("xdotool version"); // if (cmdRet.contains(runTime.runCmdError)) { // log(-1, "checking: xdotool not available or not working"); // } else { // log(lvl, "checking: xdotool seems to be available"); // } return checkSuccess; } private static boolean runLdd(File lib) { // ldd -r lib // undefined symbol: _ZN2cv3MatC1ERKS0_RKNS_5Rect_IiEE (./libVisionProxy.so) String cmdRet = runTime.runcmd("ldd -r " + lib); String[] retLines; boolean success = true; retLines = cmdRet.split("continued: build on the fly on Linux at runtime: if bundled do not work, looking for provided - if these do not work, try to build. setup not ready yet. \n"); String libName = lib.getName(); String libsMissing = ""; for (String line : retLines) { if (line.contains("undefined symbol:") && line.contains(libName)) { line = line.split("symbol:")[1].trim().split("\\s")[0]; libsMissing += line + ":"; } } if (libsMissing.isEmpty()) { log(lvl, "checking: should work: %s", libName); } else { log(-1, "checking: might not work, has undefined symbols: %s", libName); log(lvl, "%s", libsMissing); success = false; } return success; } public static boolean buildVision() { File fLibsSaveDir = runTime.fLibsProvided; fWorkDir = fLibsSaveDir.getParentFile(); fWorkDir.mkdirs(); fLibsSaveDir.mkdir(); File fTarget = new File(fWorkDir, buildFolderTarget); File fSource = new File(fWorkDir, buildFolderSrc); File fInclude = new File(fWorkDir, buildFolderInclude); fInclude.mkdirs(); File[] javas = new File[]{null, null}; javas[0] = new File(System.getProperty("java.home")); String jhome = System.getenv("JAVA_HOME"); if (jhome != null) { javas[1] = new File(jhome); } log(lvl, "buildVision: starting inline build: libVisionProxy.so"); log(lvl, "buildVision: java.home from java props: %s", javas[0]); log(lvl, "buildVision: JAVA_HOME from environment: %s", javas[1]); File javaHome = null; for (File jh : javas) { if (jh == null) { continue; } if (!new File(jh, "bin/javac").exists()) { jh = jh.getParentFile(); } if (!new File(jh, "bin/javac").exists()) { jh = null; } if (jh != null) { if (new File(jh, "include/jni.h").exists()) { javaHome = jh; break; } } } if (javaHome == null) { log(-1, "buildVision: no valid Java JDK available nor found"); return false; } log(lvl, "buildVision: JDK: found at: %s", javaHome); File cmdFile = new File(fWorkDir, "runBuild"); String libVisionPath = new File(fTarget, libVision).getAbsolutePath(); String sRunBuild = runTime.extractResourceToString("/Support/Linux", "runBuild", ""); sRunBuild = sRunBuild.replace("#jdkdir#", "jdkdir=" + javaHome.getAbsolutePath()); String inclUsr = "/usr/include"; String inclUsrLocal = "/usr/local/include"; boolean exportIncludeOpenCV = false; boolean exportIncludeTesseract = false; String inclLib = "opencv2"; if (!new File(inclUsr, inclLib).exists() && !new File(inclUsrLocal, inclLib).exists()) { log(lvl, "buildVision: opencv-include: not found - using the bundled include files"); exportIncludeOpenCV = true; } inclLib = "tesseract"; if (!new File(inclUsr, inclLib).exists() && !new File(inclUsrLocal, inclLib).exists()) { log(lvl, "buildVision: tesseract-include: not found - using the bundled include files"); exportIncludeTesseract = true; } boolean success = (null != runTime.extractResourcesToFolder("/srcnativelibs/Vision", fSource, null)); if (!success) { log(-1, "buildVision: cannot export bundled sources"); } if (exportIncludeOpenCV) { if (null == runTime.extractResourcesToFolder("/srcnativelibs/Include/OpenCV", fInclude, null)) { log(-1, "buildVision: cannot export opencv includes"); success = false; } } if (exportIncludeTesseract) { if (null == runTime.extractResourcesToFolder("/srcnativelibs/Include/Tesseract", fInclude, null)) { log(-1, "buildVision: cannot export tesseract includes"); success = false; } } if (success && (exportIncludeOpenCV || exportIncludeTesseract)) { sRunBuild = sRunBuild.replace("#extrainclude#", "extrainclude=$work/Include"); } if (success) { sRunBuild = sRunBuild.replace("#work#", "work=" + fTarget.getParentFile().getAbsolutePath()); sRunBuild = sRunBuild.replace("#opencvcore#", "opencvcore=" + libOpenCVcore); sRunBuild = sRunBuild.replace("#opencvimgproc#", "opencvimgproc=" + libOpenCVimgproc); sRunBuild = sRunBuild.replace("#opencvhighgui#", "opencvhighgui=" + libOpenCVhighgui); sRunBuild = sRunBuild.replace("#tesseractlib#", "tesseractlib=" + libTesseract); } log(lvl, "**** content of build script:\n(stored at: %s)\n%s\n**** content end", cmdFile.getAbsolutePath(), sRunBuild); try { PrintStream psCmdFile = new PrintStream(cmdFile); psCmdFile.print(sRunBuild); psCmdFile.close(); } catch (Exception ex) { log(-1, "buildVision: problem writing command file:\n%s", cmdFile); return false; } cmdFile.setExecutable(true); if (success && opencvAvail && tessAvail) { log(lvl, "buildVision: running build script"); String cmdRet = runTime.runcmd(cmdFile.getAbsolutePath()); if (cmdRet.contains(runTime.runCmdError)) { log(-1, "buildVision: build script returns error:\n%s", cmdRet); return false; } else { log(lvl, "buildVision: checking created libVisionProxy.so"); if (!runLdd(new File(libVisionPath))) { log(-1, "------- output of the build run\n%s", cmdRet); return false; } } } else { log(-1, "buildVision: corrrect the reported problems and try again"); return false; } File providedLib = new File(fLibsSaveDir, libVision); if (!FileManager.xcopy(new File(libVisionPath), providedLib)) { log(-1, "buildVision: could not save:\n%s", providedLib); return false; } if (runTime.fLibsFolder.exists()) { copyProvidedLibs(runTime.fLibsFolder); } log(lvl, "buildVision: ending inline build: success:\n%s", providedLib); return true; } //TODO needed??? processLibs // private static final String[] libsExport = new String[]{null, null}; private static final String[] libsCheck = new String[]{null, null}; private static boolean processLibs(String fpLibsJar) { // boolean shouldBuildVisionNow = false; // if (!fLibs.exists()) { // fLibs.mkdirs(); // } // if (fLibs.exists()) { // if (fpLibsJar != null) { // runTime.extractResourcesToFolderFromJar(fpLibsJar, runTime.fpJarLibs, fLibs, null); // } else { // runTime.extractResourcesToFolder(runTime.fpJarLibs, fWorkDir, null); // } // libsCheck[0] = new File(fLibs, libVision).getAbsolutePath(); // libsCheck[1] = new File(fLibs, libGrabKey).getAbsolutePath(); // File fLibCheck; // for (int i = 0; i < libsCheck.length; i++) { // fLibCheck = new File(libsCheck[i]); // if (fLibCheck.exists()) { // if (!checklibs(fLibCheck)) { ////TODO why? JXGrabKey unresolved: pthread // if (i == 0) { // if (libsExport[i] == null) { // log(-1, "provided %s might not be useable on this Linux - see log", fLibCheck.getName()); // } else { // log(-1, "bundled %s might not be useable on this Linux - see log", fLibCheck.getName()); // } // shouldBuildVisionNow = true; // } // } // } else { // log(-1, "check not possible for\n%s", fLibCheck); // } // } // } else { // log(-1, "check useability of libs: problems with libs folder\n%s", fLibs); // } // return shouldBuildVisionNow; return true; } private static boolean checklibs(File lib) { String cmdRet; String[] retLines; boolean checkSuccess = true; if (!libSearched) { checkSuccess = checkNeeded(); libSearched = true; } log(lvl, "checking\n%s", lib); // readelf -d lib // 0x0000000000000001 (NEEDED) Shared library: [libtesseract.so.3] cmdRet = runTime.runcmd("readelf -d " + lib); if (cmdRet.contains(runTime.runCmdError)) { log(-1, "checking: readelf returns error:\ns", cmdRet); checkSuccess = false; } else { retLines = cmdRet.split("\n"); String libsNeeded = ""; for (String line : retLines) { if (line.contains("(NEEDED)")) { line = line.split("\\[")[1].replace("]", ""); libsNeeded += line + ":"; } } log(lvl, libsNeeded); } if (!runLdd(lib)) { checkSuccess = false; } // return false; // for testing return checkSuccess; } // } sikulix-1.1.1/API/src/main/java/org/sikuli/util/OverlayCapturePrompt.java000066400000000000000000000275271315726130400263440ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseMotionAdapter; import java.awt.image.BufferedImage; import java.awt.image.RasterFormatException; import java.awt.image.RescaleOp; import javax.swing.JFrame; import org.sikuli.basics.Debug; import org.sikuli.script.IScreen; import org.sikuli.script.Location; import org.sikuli.script.Screen; import org.sikuli.script.ScreenImage; /** * INTERNAL USE implements the screen overlay used with the capture feature */ public class OverlayCapturePrompt extends JFrame implements EventSubject { final static float MIN_DARKER_FACTOR = 0.6f; final static long MSG_DISPLAY_TIME = 2000; final static long WIN_FADE_IN_TIME = 200; static final Font fontMsg = new Font("Arial", Font.PLAIN, 60); static final Color selFrameColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); static final Color selCrossColor = new Color(1.0f, 0.0f, 0.0f, 0.6f); static final Color screenFrameColor = new Color(1.0f, 0.0f, 0.0f, 0.6f); private Rectangle screenFrame = null; static final BasicStroke strokeScreenFrame = new BasicStroke(5); static final BasicStroke _StrokeCross = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1, new float[]{2f}, 0); static final BasicStroke bs = new BasicStroke(1); private EventObserver captureObserver = null; private IScreen scrOCP; private BufferedImage scr_img = null; private BufferedImage scr_img_darker = null; private BufferedImage bi = null; private float darker_factor; private Rectangle rectSelection; private int srcScreenId = -1; private Location srcScreenLocation = null; private Location destScreenLocation = null; private int srcx, srcy, destx, desty; private boolean canceled = false; private String promptMsg = ""; private boolean dragging = false; private boolean hasFinished = false; private boolean hasStarted = false; private boolean mouseMoves = false; private int scr_img_type = BufferedImage.TYPE_INT_RGB; private double scr_img_scale = 1; private Rectangle scr_img_rect = null; private ScreenImage scr_img_original = null; private boolean isLocalScreen = true; // private JPanel _panel = null; // private Graphics2D _currG2D = null; public OverlayCapturePrompt(IScreen scr) { // super(); Debug.log(3, "TRACE: OverlayCapturePrompt: init: S(%d)", scr.getID()); scrOCP = scr; canceled = false; setUndecorated(true); setAlwaysOnTop(true); setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); rectSelection = new Rectangle(); if (scr.isOtherScreen()) { isLocalScreen = false; } // _panel = new javax.swing.JPanel() { // @Override // protected void paintComponent(Graphics g) { // if (g instanceof Graphics2D) { // Graphics2D g2d = (Graphics2D) g; // _currG2D = g2d; // } else { // super.paintComponent(g); // } // } // }; // _panel.setLayout(null); // add(_panel); addMouseListener(new MouseAdapter() { @Override public void mousePressed(java.awt.event.MouseEvent e) { if (scr_img == null) { return; } if (e.getButton() != java.awt.event.MouseEvent.BUTTON1) { return; } hasStarted = true; destx = srcx = e.getX(); desty = srcy = e.getY(); if (isLocalScreen) { srcScreenId = scrOCP.getIdFromPoint(srcx, srcy); srcScreenLocation = new Location(srcx + scrOCP.getX(), srcy + scrOCP.getY()); Debug.log(3, "CapturePrompt: started at (%d,%d) as %s on %d", srcx, srcy, srcScreenLocation.toStringShort(), srcScreenId); } promptMsg = null; repaint(); } @Override public void mouseReleased(java.awt.event.MouseEvent e) { if (scr_img == null) { return; } if (e.getButton() != java.awt.event.MouseEvent.BUTTON1) { canceled = true; Debug.log(3, "CapturePrompt: aborted: not using left mouse button"); } else { if (isLocalScreen) { destScreenLocation = new Location(destx + scrOCP.getX(), desty + scrOCP.getY()); Debug.log(3, "CapturePrompt: finished at (%d,%d) as %s on %d", destx, desty, destScreenLocation.toStringShort(), srcScreenId); } } hasFinished = true; setVisible(false); notifyObserver(); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(java.awt.event.MouseEvent e) { if (promptMsg == null) { return; } if (!mouseMoves) { mouseMoves = true; return; } promptMsg = null; repaint(); } @Override public void mouseDragged(java.awt.event.MouseEvent e) { if (!hasStarted || scr_img == null) { return; } if (!dragging) { if (promptMsg != null) { Screen.closePrompt((Screen) scrOCP); } dragging = true; } destx = e.getX(); desty = e.getY(); repaint(); } }); addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { hasFinished = canceled = true; Debug.log(3, "CapturePrompt: aborted using key ESC"); setVisible(false); notifyObserver(); } } }); } public int getScrID() { return srcScreenId; } public void close() { Debug.log(4, "CapturePrompt.close: S(%d) freeing resources", scrOCP.getID()); setVisible(false); dispose(); scr_img = null; scr_img_darker = null; bi = null; } public void prompt(String msg, int delayMS) { try { Thread.sleep(delayMS); } catch (InterruptedException ie) { } prompt(msg); } public void prompt(int delayMS) { prompt(null, delayMS); } public void prompt() { prompt(null); } public void prompt(String msg) { scr_img_original = scrOCP.capture(); scr_img = scr_img_original.getImage(); scr_img_darker = scr_img; scr_img_type = scr_img.getType(); scr_img_rect = new Rectangle(scrOCP.getBounds()); promptMsg = msg; if (isLocalScreen) { darker_factor = 0.6f; RescaleOp op = new RescaleOp(darker_factor, 0, null); scr_img_darker = op.filter(scr_img, null); } else { promptMsg = null; if (scr_img_rect.height > Screen.getPrimaryScreen().getBounds().getHeight()) { scr_img_scale = Screen.getPrimaryScreen().getBounds().getHeight() / scr_img_rect.height; } if (scr_img_rect.width > Screen.getPrimaryScreen().getBounds().getWidth()) { scr_img_scale = Math.min(Screen.getPrimaryScreen().getBounds().getWidth() / scr_img_rect.width, scr_img_scale); } if (1 != scr_img_scale) { scr_img_rect.width = (int) (scr_img_rect.width * scr_img_scale); scr_img_rect.height = (int) (scr_img_rect.height * scr_img_scale); Image tmp = scr_img.getScaledInstance(scr_img_rect.width, scr_img_rect.height, Image.SCALE_SMOOTH); scr_img = new BufferedImage(scr_img_rect.width, scr_img_rect.height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = scr_img.createGraphics(); g2d.drawImage(tmp, 0, 0, null); g2d.dispose(); scr_img_darker = scr_img; } } this.setBounds(scr_img_rect); this.setVisible(true); } public boolean isComplete() { return hasFinished; } @Override public void addObserver(EventObserver obs) { Debug.log(3, "TRACE: OverlayCapturePrompt: addObserver: %s", obs != null); captureObserver = obs; } @Override public void notifyObserver() { Debug.log(3, "TRACE: OverlayCapturePrompt: notifyObserver: %s", captureObserver != null); if (null != captureObserver) { captureObserver.update(this); } } public ScreenImage getSelection() { if (canceled) { return null; } BufferedImage cropImg = cropSelection(); if (cropImg == null) { return null; } rectSelection.x += scrOCP.getX(); rectSelection.y += scrOCP.getY(); ScreenImage ret = new ScreenImage(rectSelection, cropImg); return ret; } private BufferedImage cropSelection() { int w = rectSelection.width, h = rectSelection.height; if (w <= 0 || h <= 0) { return null; } int x = rectSelection.x; int y = rectSelection.y; if (!isLocalScreen && scr_img_scale != 1) { x = (int) (x / scr_img_scale); y = (int) (y / scr_img_scale); w = (int) (w / scr_img_scale); h = (int) (h / scr_img_scale); } BufferedImage crop = new BufferedImage(w, h, scr_img_type); Graphics2D crop_g2d = crop.createGraphics(); try { crop_g2d.drawImage(scr_img_original.getImage().getSubimage(x, y, w, h), null, 0, 0); } catch (RasterFormatException e) { Debug.error("OverlayCapturePrompt: cropSelection: RasterFormatException", e.getMessage()); } crop_g2d.dispose(); return crop; } void drawMessage(Graphics2D g2d) { if (promptMsg == null) { return; } g2d.setFont(fontMsg); g2d.setColor(new Color(1f, 1f, 1f, 1)); int sw = g2d.getFontMetrics().stringWidth(promptMsg); int sh = g2d.getFontMetrics().getMaxAscent(); Rectangle ubound = scrOCP.getBounds(); for (int i = 0; i < Screen.getNumberScreens(); i++) { if (!Screen.getScreen(i).hasPrompt()) { continue; } Rectangle bound = Screen.getBounds(i); int cx = bound.x + (bound.width - sw) / 2 - ubound.x; int cy = bound.y + (bound.height - sh) / 2 - ubound.y; g2d.drawString(promptMsg, cx, cy); } } private void drawSelection(Graphics2D g2d) { if (srcx != destx || srcy != desty) { int x1 = (srcx < destx) ? srcx : destx; int y1 = (srcy < desty) ? srcy : desty; int x2 = (srcx > destx) ? srcx : destx; int y2 = (srcy > desty) ? srcy : desty; rectSelection.x = x1; rectSelection.y = y1; rectSelection.width = (x2 - x1) + 1; rectSelection.height = (y2 - y1) + 1; if (rectSelection.width > 0 && rectSelection.height > 0) { g2d.drawImage(scr_img.getSubimage(x1, y1, x2 - x1 + 1, y2 - y1 + 1), null, x1, y1); } g2d.setColor(selFrameColor); g2d.setStroke(bs); g2d.draw(rectSelection); int cx = (x1 + x2) / 2; int cy = (y1 + y2) / 2; g2d.setColor(selCrossColor); g2d.setStroke(_StrokeCross); g2d.drawLine(cx, y1, cx, y2); g2d.drawLine(x1, cy, x2, cy); if (isLocalScreen && Screen.getNumberScreens() > 1) { drawScreenFrame(g2d, srcScreenId); } } } private void drawScreenFrame(Graphics2D g2d, int scrId) { if (!isLocalScreen) { return; } g2d.setColor(screenFrameColor); g2d.setStroke(strokeScreenFrame); if (screenFrame == null) { screenFrame = Screen.getBounds(scrId); Rectangle ubound = scrOCP.getBounds(); screenFrame.x -= ubound.x; screenFrame.y -= ubound.y; int sw = (int) (strokeScreenFrame.getLineWidth() / 2); screenFrame.x += sw; screenFrame.y += sw; screenFrame.width -= sw * 2; screenFrame.height -= sw * 2; } g2d.draw(screenFrame); } @Override public void paint(Graphics g) { if (scr_img != null) { Graphics2D g2dWin = (Graphics2D) g; if (bi == null) { bi = new BufferedImage(scr_img_rect.width, scr_img_rect.height, scr_img_type); } Graphics2D bfG2 = bi.createGraphics(); bfG2.drawImage(scr_img_darker, 0, 0, this); drawMessage(bfG2); drawSelection(bfG2); g2dWin.drawImage(bi, 0, 0, this); setVisible(true); } else { setVisible(false); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/OverlayTransparentWindow.java000077500000000000000000000045711315726130400272250ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import org.sikuli.basics.Settings; import org.sikuli.basics.Debug; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.lang.reflect.Method; import javax.swing.JFrame; import javax.swing.JPanel; /** * INTERNAL USE * implements a transparent screen overlay for various purposes */ public class OverlayTransparentWindow extends JFrame implements EventSubject { private JPanel _panel = null; private Color _col = null; private OverlayTransparentWindow _win = null; private Graphics2D _currG2D = null; private EventObserver _obs; public OverlayTransparentWindow() { init(null, null); } public OverlayTransparentWindow(Color col, EventObserver o) { init(col, o); } private void init(Color col, EventObserver o) { setUndecorated(true); setAlwaysOnTop(true); if (col != null) { _obs = o; _win = this; try { setBackground(col); } catch (Exception e) { Debug.error("OverlayTransparentWindow.setBackground: did not work"); } _panel = new javax.swing.JPanel() { @Override protected void paintComponent(Graphics g) { if (g instanceof Graphics2D) { Graphics2D g2d = (Graphics2D) g; _currG2D = g2d; if (Settings.JavaVersion < 7) { g2d.setColor(_col); g2d.fillRect(0, 0, getWidth(), getHeight()); } if (_obs != null) { _obs.update(_win); } } else { super.paintComponent(g); } } }; _panel.setLayout(null); add(_panel); } } @Override public void setOpacity(float alpha) { try { Class c = Class.forName("javax.swing.JFrame"); Method m = c.getMethod("setOpacity", float.class); m.invoke(this, alpha); } catch (Exception e) { Debug.error("OverlayTransparentWindow.setOpacity: did not work"); } } public JPanel getJPanel() { return _panel; } public Graphics2D getJPanelGraphics() { return _currG2D; } @Override public void addObserver(EventObserver o) { _obs = o; } @Override public void notifyObserver() { _obs.update(this); } public void close() { setVisible(false); dispose(); } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/ScreenHighlighter.java000066400000000000000000000232161315726130400255620ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.RenderingHints; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.image.BufferedImage; import java.awt.image.RescaleOp; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; import javax.swing.JPanel; import org.sikuli.basics.Animator; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; import org.sikuli.script.IScreen; import org.sikuli.script.Location; import org.sikuli.script.Region; import org.sikuli.script.Screen; import org.sikuli.script.ScreenImage; /** * INTERNAL USE produces and manages the red framed rectangles from Region.highlight() */ public class ScreenHighlighter extends OverlayTransparentWindow implements MouseListener { static Color _transparentColor = new Color(0F, 0F, 0F, 0.2F); Color _targetColor = Color.RED; final static int TARGET_SIZE = 50; final static int DRAGGING_TIME = 200; static int MARGIN = 20; static Set _opened = new HashSet(); IScreen _scr; BufferedImage _screen = null; BufferedImage _darker_screen = null; BufferedImage bi = null; int srcx, srcy, destx, desty; Location _lastTarget; boolean _borderOnly = false; Animator _anim; BasicStroke _StrokeCross = new BasicStroke(1); BasicStroke _StrokeCircle = new BasicStroke(2); BasicStroke _StrokeBorder = new BasicStroke(3); Animator _aniX, _aniY; boolean noWaitAfter = false; boolean _native_transparent = false; boolean _double_buffered = false; boolean _isTransparentSupported = false; public ScreenHighlighter(IScreen scr, String color) { _scr = scr; init(); setVisible(false); setAlwaysOnTop(true); if (color != null) { // a frame color is specified // if not decodable, then predefined Color.RED is used if (color.startsWith("#")) { if (color.length() > 7) { // might be the version #nnnnnnnnn if (color.length() == 10) { int cR = 255, cG = 0, cB = 0; try { cR = Integer.decode(color.substring(1, 4)); cG = Integer.decode(color.substring(4, 7)); cB = Integer.decode(color.substring(7, 10)); } catch (NumberFormatException ex) { } try { _targetColor = new Color(cR, cG, cB); } catch (IllegalArgumentException ex) { } } } else { // supposing it is a hex value try { _targetColor = new Color(Integer.decode(color)); } catch (NumberFormatException nex) { } } } else { // supposing color contains one of the defined color names if (!color.endsWith("Gray") || "Gray".equals(color)) { // the name might be given with any mix of lower/upper-case // only lightGray, LIGHT_GRAY, darkGray and DARK_GRAY must be given exactly color = color.toUpperCase(); } try { Field field = Class.forName("java.awt.Color").getField(color); _targetColor = (Color) field.get(null); } catch (Exception e) { } } } } private void init() { _opened.add(this); if (Settings.isLinux()) { _double_buffered = true; } else if (Settings.isMac()) { _native_transparent = true; } GraphicsDevice screenDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); _isTransparentSupported = screenDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT) && screenDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT) && screenDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT); // ((JPanel) getContentPane()).setDoubleBuffered(_double_buffered); addMouseListener(this); } public void setWaitAfter(boolean state) { noWaitAfter = state; } @Override public void close() { setVisible(false); _opened.remove(this); clean(); if (!noWaitAfter) { try { Thread.sleep((int) (Settings.WaitAfterHighlight > 0.3f ? Settings.WaitAfterHighlight * 1000 - 300 : 300)); } catch (InterruptedException e) { } } } private void closeAfter(float secs) { try { Thread.sleep((int) secs * 1000 - 300); } catch (InterruptedException e) { } close(); } public static void closeAll() { if (_opened.size() > 0) { Debug.log(3, "ScreenHighlighter: Removing all highlights"); for (ScreenHighlighter s : _opened) { if (s.isVisible()) { s.setVisible(false); s.clean(); } } _opened.clear(); } } private void clean() { dispose(); _screen = null; _darker_screen = null; bi = null; } // @Override public void mousePressed(MouseEvent e) { } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } // @Override public void mouseClicked(MouseEvent e) { setVisible(false); } public void highlight(Region r_) { //change due to oracle blog: https://blogs.oracle.com/thejavatutorials/entry/translucent_and_shaped_windows_in if (!_isTransparentSupported) { Debug.error("highlight transparent is not support on " + System.getProperty("os.name")+ "!"); //use at least an not transparent color _transparentColor = Color.pink; } _borderOnly = true; Region r; r = r_.grow(3); if (!_native_transparent) { captureScreen(r.x, r.y, r.w, r.h); } setLocation(r.x, r.y); setSize(r.w, r.h); this.setBackground(_transparentColor); setVisible(true); requestFocus(); } protected boolean isWindowTranslucencySupported() { if(Settings.isLinux()){ GraphicsDevice screenDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); return screenDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT) && screenDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT) && screenDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT); } return true; } public void highlight(Region r_, float secs) { highlight(r_); closeAfter(secs); } public void showTarget(Location loc, float secs) { final int w = TARGET_SIZE, h = TARGET_SIZE; int x = loc.x - w / 2, y = loc.y - w / 2; _lastTarget = loc; Debug.log(2, "showTarget " + x + " " + y + " " + w + " " + h); showWindow(x, y, w, h, secs); } private void captureScreen(int x, int y, int w, int h) { ScreenImage img = ((Screen)_scr).captureforHighlight(x, y, w, h); _screen = img.getImage(); float scaleFactor = .6f; RescaleOp op = new RescaleOp(scaleFactor, 0, null); _darker_screen = op.filter(_screen, null); } @Override public void paint(Graphics g) { super.paint(g); Graphics2D g2d; if (_native_transparent || _screen != null) { if (_double_buffered) { if (bi == null || bi.getWidth(this) != getWidth() || bi.getHeight(this) != getHeight()) { bi = new BufferedImage( getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB); } Graphics2D bfG2 = bi.createGraphics(); g2d = bfG2; } else { g2d = (Graphics2D) g; } g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR)); g2d.fillRect(0, 0, getWidth(), getHeight()); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if (_borderOnly) { if (!_native_transparent) { g2d.drawImage(_screen, 0, 0, this); } drawBorder(g2d); } else { if (!_native_transparent) { g2d.drawImage(_screen, 0, 0, this); } drawTarget(g2d); } if (_double_buffered) { ((Graphics2D) g).drawImage(bi, 0, 0, this); } if (!isVisible()) { setVisible(true); } } else { if (isVisible()) { setVisible(false); } } } private void drawBorder(Graphics2D g2d) { g2d.setColor(_targetColor); g2d.setStroke(_StrokeBorder); int w = (int) _StrokeBorder.getLineWidth(); g2d.drawRect(w / 2, w / 2, getWidth() - w, getHeight() - w); } private void drawTarget(Graphics2D g2d) { int r = TARGET_SIZE / 2; g2d.setColor(Color.black); g2d.setStroke(_StrokeCross); g2d.drawLine(0, r, r * 2, r); g2d.drawLine(r, 0, r, r * 2); g2d.setColor(_targetColor); g2d.setStroke(_StrokeCircle); drawCircle(r, r, r - 4, g2d); drawCircle(r, r, r - 10, g2d); } private void drawCircle(int x, int y, int radius, Graphics g) { g.drawOval(x - radius, y - radius, radius * 2, radius * 2); } private void showWindow(int x, int y, int w, int h, float secs) { if (!_native_transparent) { captureScreen(x, y, w, h); } setLocation(x, y); setSize(w, h); this.setBackground(_targetColor); this.repaint(); setVisible(true); requestFocus(); closeAfter(secs); } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/SikulixFileChooser.java000066400000000000000000000142011315726130400257310ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import java.awt.FileDialog; import java.awt.Frame; import java.io.File; //import java.io.FilenameFilter; import javax.swing.JFileChooser; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; import org.sikuli.basics.Debug; import org.sikuli.basics.PreferencesUser; import org.sikuli.basics.Settings; import org.sikuli.script.Sikulix; public class SikulixFileChooser { static final int FILES = JFileChooser.FILES_ONLY; static final int DIRS = JFileChooser.DIRECTORIES_ONLY; static final int DIRSANDFILES = JFileChooser.FILES_AND_DIRECTORIES; static final int SAVE = FileDialog.SAVE; static final int LOAD = FileDialog.LOAD; Frame _parent; boolean accessingAsFile = false; boolean loadingImage = false; public SikulixFileChooser(Frame parent) { _parent = parent; } public SikulixFileChooser(Frame parent, boolean accessingAsFile) { _parent = parent; this.accessingAsFile = accessingAsFile; } private boolean isGeneric() { return (_parent.getWidth() == 1 && _parent.getHeight() == 1); } public File show(String title) { File ret = showFileChooser(title, LOAD, DIRSANDFILES); return ret; } public File load() { String type = "Sikuli Script (*.sikuli, *.skl)"; String title = "Open a Sikuli Script"; File ret = showFileChooser(title, LOAD, DIRSANDFILES, new SikulixFileFilter(type, "o")); return ret; } public File save() { String type = "Sikuli Script (*.sikuli)"; String title = "Save a Sikuli Script"; File ret = showFileChooser(title, SAVE, DIRS, new SikulixFileFilter(type, "s")); return ret; } public File export() { String type = "Sikuli packed Script (*.skl)"; String title = "Export as Sikuli packed Script"; File ret = showFileChooser(title, SAVE, FILES, new SikulixFileFilter(type, "e")); return ret; } public File loadImage() { loadingImage = true; File ret = showFileChooser("Load Image File", LOAD, FILES, new FileNameExtensionFilter("Image files (jpg, png)", "jpg", "jpeg", "png")); return ret; } private File showFileChooser(String title, int mode, int selectionMode, Object... filters) { String last_dir = PreferencesUser.getInstance().get("LAST_OPEN_DIR", ""); Debug.log(3,"showFileChooser: %s at %s", title.split(" ")[0], last_dir); JFileChooser fchooser = null; File fileChoosen = null; while (true) { fchooser = new JFileChooser(); if (!last_dir.isEmpty()) { fchooser.setCurrentDirectory(new File(last_dir)); } fchooser.setSelectedFile(null); fchooser.setDialogTitle(title); boolean shouldTraverse = false; String btnApprove = "Select"; if (isGeneric()) { fchooser.setFileSelectionMode(DIRSANDFILES); fchooser.setAcceptAllFileFilterUsed(true); shouldTraverse = true; } else { if (Settings.isMac() && Settings.isJava7() && selectionMode == DIRS) { selectionMode = DIRSANDFILES; } fchooser.setFileSelectionMode(selectionMode); if (mode == FileDialog.SAVE) { fchooser.setDialogType(JFileChooser.SAVE_DIALOG); btnApprove = "Save"; } if (filters.length == 0) { fchooser.setAcceptAllFileFilterUsed(true); shouldTraverse = true; } else { fchooser.setAcceptAllFileFilterUsed(false); for (Object filter : filters) { if (filter instanceof SikulixFileFilter) { fchooser.addChoosableFileFilter((SikulixFileFilter) filter); } else { fchooser.setFileFilter((FileNameExtensionFilter) filter); shouldTraverse = true; } } } } if (shouldTraverse && Settings.isMac()) { fchooser.putClientProperty("JFileChooser.packageIsTraversable", "always"); } if (fchooser.showDialog(_parent, btnApprove) != JFileChooser.APPROVE_OPTION) { return null; } fileChoosen = fchooser.getSelectedFile(); // folders must contain a valid scriptfile if (!isGeneric() && mode == FileDialog.LOAD && !isValidScript(fileChoosen)) { Sikulix.popError("Folder not a valid SikuliX script\nTry again."); last_dir = fileChoosen.getParentFile().getAbsolutePath(); continue; } break; } String lastDir = fileChoosen.getParent(); if (null == lastDir) { lastDir = fileChoosen.getAbsolutePath(); } PreferencesUser.getInstance().put("LAST_OPEN_DIR", lastDir); return fileChoosen; } private boolean isValidScript(File f) { String[] endings = new String[]{".py", ".rb", ".js"}; String fName = f.getName(); if (loadingImage || fName.endsWith(".skl")) { return true; } if (fName.endsWith(".sikuli")) { fName = fName.substring(0, fName.length() - 7); } boolean valid = false; for (String ending : endings) { if (new File(f, fName + ending).exists()) { return true; } } return false; } private static boolean isExt(String fName, String givenExt) { int i = fName.lastIndexOf('.'); if (i > 0) { if (fName.substring(i + 1).toLowerCase().equals(givenExt)) { return true; } } return false; } class SikulixFileFilter extends FileFilter { private String _type, _desc; public SikulixFileFilter(String desc, String type) { _type = type; _desc = desc; } @Override public boolean accept(File f) { if ("o".equals(_type) && (isExt(f.getName(), "sikuli") || isExt(f.getName(), "skl"))) { return true; } if ("s".equals(_type) && isExt(f.getName(), "sikuli")) { return true; } if ("e".equals(_type)) { if (isExt(f.getName(), "skl")) { return true; } if (Settings.isMac() && isExt(f.getName(), "sikuli")) { return false; } } if (f.isDirectory()) { return true; } return false; } @Override public String getDescription() { return _desc; } } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/SysJNA.java000066400000000000000000000066041315726130400232750ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import org.bridj.BridJ; import org.bridj.Pointer; import org.bridj.ann.Library; import org.sikuli.basics.Debug; /** * Direct access to system functions via JNI, JNA, BridJ, ... */ public class SysJNA { /** * Direct access to Windows API kernel32.dll via BridJ */ @Library("kernel32") public static class WinKernel32 { static { BridJ.register(); } // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx //DWORD WINAPI GetEnvironmentVariable( // _In_opt_ LPCTSTR lpName, // _Out_opt_ LPTSTR lpBuffer, // _In_ DWORD nSize //); private static native int GetEnvironmentVariableW( Pointer lpName, Pointer lpBuffer, int nSize ); // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686206(v=vs.85).aspx //BOOL WINAPI SetEnvironmentVariable( // _In_ LPCTSTR lpName, // _In_opt_ LPCTSTR lpValue //); private static native boolean SetEnvironmentVariableW( Pointer lpName, Pointer lpValue ); /** * get the current value of a variable from real Windows environment * * @param name of the environment variable * @return current content */ public static String getEnvironmentVariable(String name) { final int BUFFER_SIZE = 32767; Pointer buffer = Pointer.allocateArray(Character.class, BUFFER_SIZE); int result = GetEnvironmentVariableW(Pointer.pointerToWideCString(name), buffer, BUFFER_SIZE); if (result == 0) { Debug.error("WinKernel32: getEnvironmentVariable: does not work for: %s", name); return null; } return buffer.getWideCString(); } /** * set the value of a variable in real Windows environment * * @param name of the environment variable * @param value of the environment variable * @return success */ public static boolean setEnvironmentVariable(String name, String value) { if (!SetEnvironmentVariableW(Pointer.pointerToWideCString(name), Pointer.pointerToWideCString(value))) { Debug.error("WinKernel32: setEnvironmentVariable: does not work for: %s = %s", name, value); return false; } return true; } } /** * Direct access to Windows API user32.dll via BridJ */ @Library("user32") public static class WinUser32 { /* https://msdn.microsoft.com/pt-br/library/windows/desktop/dd375731 VK_NUM_LOCK 0x90 VK_SCROLL 0x91 VK_CAPITAL 0x14 */ private static int WinNumLock = 0x90; private static int WinScrollLock = 0x91; private static int WinCapsLock = 0x14; static { BridJ.register(); } public static boolean isNumLockOn() { int state = GetKeyState(WinNumLock); return state > 0; } public static boolean isScrollLockOn() { int state = GetKeyState(WinScrollLock); return state > 0; } public static boolean isCapsLockOn() { int state = GetKeyState(WinCapsLock); return state > 0; } /* https://msdn.microsoft.com/en-us/library/ms646301(VS.85).aspx SHORT WINAPI GetKeyState( _In_ int nVirtKey ); */ private static native int GetKeyState(int aVK); } } sikulix-1.1.1/API/src/main/java/org/sikuli/util/Tests.java000066400000000000000000000213041315726130400232620ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import java.awt.Desktop; import java.io.File; import java.io.FilenameFilter; import java.lang.reflect.Method; import java.net.URI; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.basics.Settings; import org.sikuli.script.App; import org.sikuli.script.ImagePath; import org.sikuli.script.Match; import org.sikuli.script.Region; import org.sikuli.script.RunTime; import org.sikuli.script.Screen; import org.sikuli.script.Sikulix; public class Tests { private static RunTime rt; private static int lvl = 3; private static final String prefNonSikuli = "nonSikuli_"; private static void log(int level, String message, Object... args) { Debug.logx(level, "API-Tests: " + message, args); } private static void logp(String message, Object... args) { if (rt.runningWinApp) { log(0, message, args); } else { System.out.println(String.format(message, args)); } } private static void p(String msg, Object... args) { System.out.println(String.format(msg, args)); } private static void terminate(int retVal, String msg, Object... args) { p(msg, args); System.exit(retVal); } public static void runTest(int testNumber) { rt = RunTime.get(); Method[] tests = Tests.class.getDeclaredMethods(); if (testNumber == 0) { logp("***** available tests"); } for (Method test : tests) { if (!test.getName().startsWith("test")) { continue; } String tName = test.getName().substring(4); int tNum = -1; try { tNum = Integer.decode(tName.substring(0, 2)); } catch (Exception ex) { continue; } if (testNumber == 0) { logp(tName); continue; } if (testNumber == 1 || tNum == testNumber) { logp("\n========== running test: %s ==========", test.getName()); try { test.invoke(null, new Object[0]); } catch (Exception ex) { log(-1, "not possible:\n%s", ex); } } } } public static void test03_ImageFromJar() { rt = RunTime.get(); File fImageJar = null; String imagePath = "org.sikuli.script.Image/ImagesAPI"; String sJar = "sikulixsetup"; if (!rt.runningWinApp) { if (rt.fSxProject == null) { if (null == rt.isOnClasspath("/Setup/target/Setup")) { fImageJar = new File(rt.fSxBase, "sikulixsetup-1.1.0.jar"); } } else { fImageJar = new File(rt.fSxProject, "Setup/target/sikulixsetup-1.1.0-plain.jar"); } if (fImageJar == null || !fImageJar.exists()) { fImageJar = rt.fSxBaseJar; String sJarApi = rt.isOnClasspath("sikulixapi"); if (sJarApi == null) { sJar = "sikulixapi"; } else { sJar = null; } if (sJar != null) { logp("terminating: cannot run - missing: the jar with images: %s", sJar); System.exit(1); } } else { imagePath = "org.sikuli.setup.RunSetup/Images"; rt.addToClasspath(fImageJar.getPath()); } } logp("******** starting test with imagePath: %s", imagePath); rt.dumpClassPath(); Screen s = new Screen(); ImagePath.add(imagePath); String browser = "Google Chrome"; if (Desktop.isDesktopSupported()) { String lp = "https://launchpad.net/sikuli"; Desktop dt = Desktop.getDesktop(); if (dt.isSupported(Desktop.Action.BROWSE)) { try{ dt.browse(new URI(lp)); } catch (Exception ex) { rt.terminate(1, "Desktop.browse: %s", lp); } App appBrowser = new App(browser); while (null == appBrowser.window()) { s.wait(1.0); } } } App.focus(browser); s.wait(1.0); Region win = App.focusedWindow(); win.highlight(2); if (null != s.exists("SikuliLogo")) { s.exists("SikuliLogo", 0); s.highlight(-2); if (rt.runningWindows) { s.write("#A.#F4."); } else if (rt.runningMac) { s.write("#M.q"); } else { s.write("#C.q"); } s.wait(1.0); } logp("******** ending test"); } public static void test02_ExtractResourcesFromClasspath() { rt = RunTime.get(); if (rt.testSwitch()) { addToCP("Libswin"); } else { addJarToCP("Libswin", null); } rt.dumpClassPath(); File testFolder = new File(rt.fUserDir, "SikulixTest"); FileManager.deleteFileOrFolder(testFolder.getAbsolutePath()); rt.extractResourcesToFolder("/META-INF/libs", testFolder, new FilenameFilter() { @Override public boolean accept(File dir, String name) { if (name.endsWith(".txt")) { return false; } if (name.contains("opencv")) { return false; } if (dir.getName().endsWith("libs32")) { return false; } return true; } }); } public static void test04_ResourceListToFile() { rt = RunTime.get(); String msg = "worked"; if (null != rt.resourceListAsFile("Lib", rt.fTestFile, null)) { msg = "did not work"; } log(lvl, "*** %s *** test03_ResourceListToFile: Lib to:\n%s", msg, rt.fTestFile); } public static void test05_MakeLibswinContentFile() { rt = RunTime.get(); String msg = "worked"; addJarToCP("Libswin", null); File content = null; String fsContent = "Libswin/src/main/resources/META-INF/libs/windows/" + rt.fpContent; if (rt.fSxProject != null) { content = new File(rt.fSxProject, fsContent); } else { content = new File(rt.fTestFolder, rt.fpContent); } if (null != rt.resourceListAsFile("META-INF/libs/windows", content, new FilenameFilter() { @Override public boolean accept(File dir, String name) { if (name.contains(rt.fpContent)) { return false; } return true; } })) { msg = "did not work"; } log(lvl, "*** %s *** test05_MakeLibswinContentFile: META-INF/libs/windows to:\n%s", msg, content); } private static boolean addToCP(String folder) { if (rt.fSxProject != null) { rt.addToClasspath(new File(rt.fSxProject, folder + "/target/classes").getAbsolutePath()); return true; } return false; } private static boolean addJarToCP(String folder, String filter) { if (rt.fSxProject != null) { File aFolder = new File(rt.fSxProject, folder + "/target"); if (!aFolder.exists()) { return false; } for (String sFile : aFolder.list()) { if (sFile.endsWith(".jar")) { if (filter != null && !filter.isEmpty() && !sFile.contains(filter)) { continue; } rt.addToClasspath(new File(aFolder, sFile.toString()).getAbsolutePath()); return true; } } return false; } return false; } private static void lastScreenImageTest() { Screen s = new Screen(); Debug.on(3); ImagePath.add(Sikulix.class.getCanonicalName() + "/ImagesAPI.sikuli"); File fResults = new File(System.getProperty("user.home"), "SikulixScreenImages"); FileManager.resetFolder(fResults); String fpResults = fResults.getPath(); if (Settings.isMac()) { App.focus("Safari"); } else { App.focus("Google Chrome"); } String raimanlogo = "raimanlogo"; Match mFound = null; try { if (null == s.exists(raimanlogo, 0)) { Desktop.getDesktop().browse(new URI("http://sikulix.com")); s.wait(raimanlogo, 10); } s.hover(); Region winBrowser = App.focusedWindow(); String image = "btnnightly"; mFound = winBrowser.exists(image); if (null != mFound) { p("mFound: %s", mFound); p("mFound.Image: %s", mFound.getImage()); p("mFound.ImageFile: %s", mFound.getImageFilename()); winBrowser.highlight(-1); winBrowser.click(); winBrowser.getLastScreenImageFile(fpResults, image + "screen.png"); } else { terminate(1, "missing: %s", image); System.exit(1); } image = "nightly"; mFound = winBrowser.exists(image, 10); if (null != mFound) { p("mFound: %s", mFound); p("mFound.Image: %s", mFound.getImage()); p("mFound.ImageFile: %s", mFound.getImageFilename()); winBrowser.highlight(-1); winBrowser.getLastScreenImageFile(fpResults, image + "screen.png"); } else { terminate(1, "missing: %s", image); } } catch (Exception ex) { terminate(1, "some problems"); } s.write("#C.w"); s.wait(2f); App.focus("NetBeans"); System.exit(1); } } sikulix-1.1.1/API/src/main/java/org/sikuli/vnc/000077500000000000000000000000001315726130400211265ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/java/org/sikuli/vnc/BasicUserPasswdGetter.java000066400000000000000000000026701315726130400262130ustar00rootroot00000000000000/* Copyright (c) 2017, Sikuli.org, sikulix.com * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package org.sikuli.vnc; import com.tigervnc.rfb.UserPasswdGetter; /** * Simple implementation of UserPasswdGetter that returns a fixed password. */ class BasicUserPasswdGetter implements UserPasswdGetter { private final String password; public BasicUserPasswdGetter(String password) { this.password = password; } @Override public boolean getUserPasswd(StringBuffer user, StringBuffer passwd) { if (user != null) { user.setLength(0); } if (password != null) { passwd.setLength(0); passwd.append(password); return true; } else { return false; } } } sikulix-1.1.1/API/src/main/java/org/sikuli/vnc/ThreadLocalSecurityClient.java000066400000000000000000000072101315726130400270420ustar00rootroot00000000000000/* Copyright (c) 2017, Sikuli.org, sikulix.com * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package org.sikuli.vnc; import com.tigervnc.rfb.CConnection; import com.tigervnc.rfb.CSecurity; import com.tigervnc.rfb.SecurityClient; import com.tigervnc.rfb.UserPasswdGetter; import com.tigervnc.vncviewer.CConn; import java.lang.reflect.Field; /** * Workaround for static state in TigerVNC. * * The UserPasswdGetter used by TigerVNC is a static field (CConn.upg). * In SikuliX need to be able to specify a password per connection without requesting user input. * To do that we set the global UserPasswdGetter to one that retrieves the actual UserPasswdGetter * instance from a thread-local variable. * This extension of SecurityClient sets the thread-local variable to the correct value just before * it is going to be requested by the TigerVNC code. */ class ThreadLocalSecurityClient extends SecurityClient { private static final ThreadLocal UPG = new ThreadLocal<>(); static { CConn.upg = new UserPasswdGetter() { @Override public boolean getUserPasswd(StringBuffer user, StringBuffer pass) { UserPasswdGetter upg = UPG.get(); if (upg != null) { return upg.getUserPasswd(user, pass); } else { if (user != null) { user.setLength(0); } if (pass != null) { pass.setLength(0); } return false; } } }; } private final UserPasswdGetter upg; ThreadLocalSecurityClient(UserPasswdGetter userPasswdGetter) { upg = userPasswdGetter; try { // An assertion fails is the SecurityClient#msg is null // I couldn't find any other way to set it than via reflection. Field field = SecurityClient.class.getDeclaredField("msg"); field.setAccessible(true); field.set(this, ""); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } @Override public CSecurity GetCSecurity(int securityType) { final CSecurity security = super.GetCSecurity(securityType); if (security != null) { return new CSecurity() { @Override public boolean processMsg(CConnection cConnection) { UPG.set(upg); return security.processMsg(cConnection); } @Override public int getType() { return security.getType(); } @Override public String description() { return security.description(); } }; } else { return null; } } } sikulix-1.1.1/API/src/main/java/org/sikuli/vnc/VNCClient.java000066400000000000000000000162031315726130400235600ustar00rootroot00000000000000/* Copyright (c) 2017, Sikuli.org, sikulix.com * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package org.sikuli.vnc; import com.tigervnc.network.TcpSocket; import com.tigervnc.rdr.FdInStreamBlockCallback; import com.tigervnc.rfb.*; import com.tigervnc.rfb.Exception; import com.tigervnc.rfb.Point; import com.tigervnc.vncviewer.CConn; import java.awt.*; import java.awt.image.BufferedImage; import java.io.Closeable; import java.io.IOException; class VNCClient extends CConnection implements FdInStreamBlockCallback, Closeable { static ThreadLocal UPG = new ThreadLocal<>(); static { CConn.upg = new UserPasswdGetter() { @Override public boolean getUserPasswd(StringBuffer stringBuffer, StringBuffer stringBuffer1) { UserPasswdGetter upg = UPG.get(); return false; } }; } private final TcpSocket sock; private boolean shuttingDown = false; private PixelFormat serverPF; private int currentEncoding; private VNCFrameBuffer frameBuffer; public static VNCClient connect(String address, int port, String password, boolean shareConnection) throws IOException { VNCClient client = new VNCClient(address, port, password, shareConnection); while (client.state() != VNCClient.RFBSTATE_NORMAL) { client.processMsg(); } return client; } private VNCClient(String address, int port, final String password, boolean shareConnection) throws IOException { this.security = new ThreadLocalSecurityClient(new BasicUserPasswdGetter(password)); this.currentEncoding = Encodings.encodingTight; this.setShared(shareConnection); setServerName(address); setServerPort(port); this.sock = new TcpSocket(this.getServerName(), this.getServerPort()); this.sock.inStream().setBlockCallback(this); this.setStreams(this.sock.inStream(), this.sock.outStream()); this.initialiseProtocol(); } @Override public PixelFormat getPreferredPF() { return new PixelFormat(); } @Override public void serverInit() { super.serverInit(); this.serverPF = this.cp.pf(); this.frameBuffer = new VNCFrameBuffer(this.cp.width, this.cp.height, this.serverPF); this.writer().writeSetEncodings(this.currentEncoding, true); } public void setDesktopSize(int var1, int var2) { super.setDesktopSize(var1, var2); this.resizeFramebuffer(); } public void setColourMapEntries(int offset, int nbColors, int[] rgb) { frameBuffer.setColourMapEntries(offset, nbColors, rgb); } private void resizeFramebuffer() { if (this.frameBuffer != null) { if (this.cp.width != 0 || this.cp.height != 0) { if (this.frameBuffer.width() != this.cp.width || this.frameBuffer.height() != this.cp.height) { this.frameBuffer.resize(cp.width, cp.height); } } } } public void refreshFramebuffer() { refreshFramebuffer(0, 0, cp.width, cp.height, false); } /** * Sends FramebufferUpdateRequest message to server. * * @param x X coordinate of desired region * @param y Y coordinate of desired region * @param w Width of desired region * @param h Height of desired region * @param incremental Zero sends entire desktop, One sends changes only. * @throws IOException If there is a socket error */ public void refreshFramebuffer(int x, int y, int w, int h, boolean incremental) { writer().writeFramebufferUpdateRequest(new Rect(x, y, w, h), incremental); } @Override public void framebufferUpdateStart() { refreshFramebuffer(0, 0, cp.width, cp.height, true); } @Override public void framebufferUpdateEnd() { } public void fillRect(Rect r, int p) { this.frameBuffer.fillRect(r.tl.x, r.tl.y, r.width(), r.height(), p); } public void imageRect(Rect r, Object p) { this.frameBuffer.imageRect(r.tl.x, r.tl.y, r.width(), r.height(), p); } public void copyRect(Rect r, int sx, int sy) { this.frameBuffer.copyRect(r.tl.x, r.tl.y, r.width(), r.height(), sx, sy); } /** * Tells VNC server to depress key. * * @param key X Window System Keysym for key. * @throws IOException If there is a socket error. */ protected void keyDown(int key) throws IOException { writer().writeKeyEvent(key, true); } /** * Tells VNC server to release key. * * @param key X Window System Keysym for key. * @throws IOException If there is a socket error. */ protected void keyUp(int key) throws IOException { writer().writeKeyEvent(key, false); } /** * Tells VNC server to perform a mouse event. bOne through bEight are mouse * buttons one through eight respectively. A zero means release that * button, and a one means depress that button. * * @param buttonState logical or of BUTTON_N_DOWN * @param x X coordinate of action * @param y Y coordinate of action * @throws IOException If there is a socket error. */ protected void mouseEvent(int buttonState, int x, int y) throws IOException { writer().writePointerEvent(new Point(x, y), buttonState); } /** * Closes the connection * * @throws IOException */ public void close() throws IOException { this.shuttingDown = true; if (this.sock != null) { this.sock.shutdown(); } } /** * Returns the VNCClient Object as a string */ public String toString() { return "VNCClient: " + getServerName() + ":" + getServerPort(); } public Rectangle getBounds() { return new Rectangle(0, 0, this.cp.width, this.cp.height); } public BufferedImage getFrameBuffer(int x, int y, int w, int h) { return frameBuffer.getImage(x, y, w, h); } @Override public void blockCallback() { try { synchronized (this) { this.wait(1L); } } catch (InterruptedException var4) { throw new Exception(var4.getMessage()); } } public void processMessages() { while (!shuttingDown) { processMsg(); } } } sikulix-1.1.1/API/src/main/java/org/sikuli/vnc/VNCFrameBuffer.java000066400000000000000000000116471315726130400245350ustar00rootroot00000000000000/* Copyright (c) 2017, Sikuli.org, sikulix.com * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package org.sikuli.vnc; import com.tigervnc.rfb.PixelBuffer; import com.tigervnc.rfb.PixelFormat; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.nio.ByteOrder; /** * An off-screen frame buffer that can be used to capture screen contents. */ class VNCFrameBuffer extends PixelBuffer { private final Object imageLock = new Object(); private BufferedImage image; private DataBuffer db; public VNCFrameBuffer(int width, int height, PixelFormat serverPF) { PixelFormat nativePF = this.getNativePF(); if (nativePF.depth > serverPF.depth) { this.setPF(serverPF); } else { this.setPF(nativePF); } this.resize(width, height); } public void resize(int width, int height) { if (width != this.width() || height != this.height()) { this.width_ = width; this.height_ = height; this.createImage(width, height); } } private PixelFormat getNativePF() { return new PixelFormat( 32, 24, ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN, true, 255, 255, 255, 16, 8, 0 ); } public void setColourMapEntries(int offset, int nbColors, int[] rgb) { throw new RuntimeException("Not supported yet"); } private void createImage(int width, int height) { synchronized (imageLock) { if (width != 0 && height != 0) { WritableRaster raster = this.cm.createCompatibleWritableRaster(width, height); this.image = new BufferedImage(this.cm, raster, false, null); this.db = raster.getDataBuffer(); } } } public void fillRect(int x, int y, int w, int h, int pixelValue) { synchronized (imageLock) { Graphics2D g2d = this.image.createGraphics(); switch (this.format.depth) { case 24: g2d.setColor(new Color(pixelValue)); g2d.fillRect(x, y, w, h); break; default: g2d.setColor(new Color(0xff000000 | this.cm.getRed(pixelValue) << 16 | this.cm.getGreen(pixelValue) << 8 | this.cm.getBlue(pixelValue))); g2d.fillRect(x, y, w, h); } g2d.dispose(); } } public void imageRect(int x, int y, int w, int h, Object p) { if (p instanceof Image) { Image img = (Image) p; synchronized (imageLock) { Graphics2D g2d = this.image.createGraphics(); g2d.drawImage(img, x, y, w, h, null); g2d.dispose(); } img.flush(); } else { synchronized (imageLock) { SampleModel sampleModel = this.image.getSampleModel(); if (sampleModel.getTransferType() == DataBuffer.TYPE_BYTE) { byte[] byteData = new byte[((int[]) p).length]; for (int i = 0; i < byteData.length; ++i) { byteData[i] = (byte) ((int[]) p)[i]; } p = byteData; } sampleModel.setDataElements(x, y, w, h, p, this.db); } } } public void copyRect(int dx, int dy, int w, int h, int sx, int sy) { synchronized (imageLock) { Graphics2D g2d = this.image.createGraphics(); g2d.copyArea(sx, sy, w, h, dx - sx, dy - sy); g2d.dispose(); } } public BufferedImage getImage(int x, int y, int w, int h) { BufferedImage i; synchronized (imageLock) { i = new BufferedImage(image.getColorModel(), image.getColorModel().createCompatibleWritableRaster(w, h), false, null); Graphics2D g2d = i.createGraphics(); g2d.drawImage(image, 0, 0, w, h, x, y, w, h, null); g2d.dispose(); } return i; } } sikulix-1.1.1/API/src/main/java/org/sikuli/vnc/VNCRobot.java000066400000000000000000000476221315726130400234400ustar00rootroot00000000000000/* Copyright (c) 2017, Sikuli.org, sikulix.com * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package org.sikuli.vnc; import org.sikuli.basics.Settings; import org.sikuli.script.*; import java.awt.*; import java.awt.event.KeyEvent; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; import static java.awt.event.KeyEvent.*; class VNCRobot implements IRobot { public static final int VNC_POINTER_EVENT_BUTTON_1 = 1 << 0; public static final int VNC_POINTER_EVENT_BUTTON_2 = 1 << 1; public static final int VNC_POINTER_EVENT_BUTTON_3 = 1 << 2; public static final int VNC_POINTER_EVENT_BUTTON_4 = 1 << 3; public static final int VNC_POINTER_EVENT_BUTTON_5 = 1 << 4; public static final int VNC_POINTER_EVENT_BUTTON_6 = 1 << 5; public static final int VNC_POINTER_EVENT_BUTTON_7 = 1 << 6; public static final int VNC_POINTER_EVENT_BUTTON_8 = 1 << 7; private final VNCScreen screen; private int mouseX; private int mouseY; private int mouseButtons; private int autoDelay; private Set pressedKeys; private boolean shiftPressed; public VNCRobot(VNCScreen screen) { this.screen = screen; this.autoDelay = 100; this.pressedKeys = new TreeSet<>(); } @Override public ScreenImage captureScreen(Rectangle screenRect) { return screen.capture(screenRect); } @Override public boolean isRemote() { return true; } @Override public IScreen getScreen() { return screen; } @Override public void keyDown(String keys) { for (int i = 0; i < keys.length(); i++) { typeChar(keys.charAt(i), KeyMode.PRESS_ONLY); } } @Override public void keyUp(String keys) { for (int i = 0; i < keys.length(); i++) { typeChar(keys.charAt(i), KeyMode.RELEASE_ONLY); } } @Override public void keyDown(int code) { typeKey(code, KeyMode.PRESS_ONLY); } @Override public void keyUp(int code) { typeCode(keyToXlib(code), KeyMode.RELEASE_ONLY); } @Override public void keyUp() { for (Integer key : new ArrayList<>(pressedKeys)) { typeCode(key, KeyMode.RELEASE_ONLY); } } @Override public void pressModifiers(int modifiers) { typeModifiers(modifiers, KeyMode.PRESS_ONLY); } @Override public void releaseModifiers(int modifiers) { typeModifiers(modifiers, KeyMode.RELEASE_ONLY); } private void typeModifiers(int modifiers, KeyMode keyMode) { if ((modifiers & KeyModifier.CTRL) != 0) typeKey(KeyEvent.VK_CONTROL, keyMode); if ((modifiers & KeyModifier.SHIFT) != 0) typeCode(KeyEvent.VK_SHIFT, keyMode); if ((modifiers & KeyModifier.ALT) != 0) typeCode(KeyEvent.VK_ALT, keyMode); if ((modifiers & KeyModifier.ALTGR) != 0) typeCode(KeyEvent.VK_ALT_GRAPH, keyMode); if ((modifiers & KeyModifier.META) != 0) typeCode(KeyEvent.VK_META, keyMode); } @Override public void typeStarts() { // Nothing to do } @Override public void typeEnds() { // Nothing to do } @Override public void typeKey(int key) { typeKey(key, KeyMode.PRESS_RELEASE); } @Override public void typeChar(char character, KeyMode mode) { if (character > '\ue000' && character < '\ue050') { typeKey(Key.toJavaKeyCode(character)[0], mode); } else { typeCode(charToXlib(character), mode); } } public void typeKey(int key, KeyMode mode) { typeCode(keyToXlib(key), mode); } private void typeCode(int xlibCode, KeyMode mode) { boolean addShift = requiresShift(xlibCode) && !shiftPressed; try { if (mode == KeyMode.PRESS_RELEASE || mode == KeyMode.PRESS_ONLY) { if (addShift) { pressKey(XKeySym.XK_Shift_L); } pressKey(xlibCode); if (xlibCode == XKeySym.XK_Shift_L || xlibCode == XKeySym.XK_Shift_R || xlibCode == XKeySym.XK_Shift_Lock) { shiftPressed = true; } } if (mode == KeyMode.PRESS_RELEASE || mode == KeyMode.RELEASE_ONLY) { releaseKey(xlibCode); if (addShift) { releaseKey(XKeySym.XK_Shift_L); } if (xlibCode == XKeySym.XK_Shift_L || xlibCode == XKeySym.XK_Shift_R || xlibCode == XKeySym.XK_Shift_Lock) { shiftPressed = false; } } } catch (IOException e) { throw new RuntimeException(e); } } private void pressKey(int key) throws IOException { screen.getClient().keyDown(key); pressedKeys.add(key); } private void releaseKey(int key) throws IOException { screen.getClient().keyUp(key); pressedKeys.remove(key); } private int charToXlib(char c) { if (c >= 0x0020 && c <= 0x00FF) { return c; } switch (c) { case '\u0008': return XKeySym.XK_BackSpace; case '\u0009': return XKeySym.XK_Tab; case '\n': return XKeySym.XK_Linefeed; case '\u000b': return XKeySym.XK_Clear; case '\r': return XKeySym.XK_Return; case '\u0013': return XKeySym.XK_Pause; case '\u0014': return XKeySym.XK_Scroll_Lock; case '\u0015': return XKeySym.XK_Sys_Req; case '\u001b': return XKeySym.XK_Escape; case '\u007f': return XKeySym.XK_Delete; default: throw new IllegalArgumentException("Cannot type character " + c); } } private int keyToXlib(int code) { switch (code) { case VK_ENTER: return XKeySym.XK_Return; case VK_BACK_SPACE: return XKeySym.XK_BackSpace; case VK_TAB: return XKeySym.XK_Tab; case VK_CANCEL: return XKeySym.XK_Cancel; case VK_CLEAR: return XKeySym.XK_Clear; case VK_SHIFT: return XKeySym.XK_Shift_L; case VK_CONTROL: return XKeySym.XK_Control_L; case VK_ALT: return XKeySym.XK_Alt_L; case VK_PAUSE: return XKeySym.XK_Pause; case VK_CAPS_LOCK: return XKeySym.XK_Caps_Lock; case VK_ESCAPE: return XKeySym.XK_Escape; case VK_SPACE: return XKeySym.XK_space; case VK_PAGE_UP: return XKeySym.XK_Page_Up; case VK_PAGE_DOWN: return XKeySym.XK_Page_Down; case VK_END: return XKeySym.XK_End; case VK_HOME: return XKeySym.XK_Home; case VK_LEFT: return XKeySym.XK_Left; case VK_UP: return XKeySym.XK_Up; case VK_RIGHT: return XKeySym.XK_Right; case VK_DOWN: return XKeySym.XK_Down; case VK_COMMA: return XKeySym.XK_comma; case VK_MINUS: return XKeySym.XK_minus; case VK_PERIOD: return XKeySym.XK_period; case VK_SLASH: return XKeySym.XK_slash; case VK_0: return XKeySym.XK_0; case VK_1: return XKeySym.XK_1; case VK_2: return XKeySym.XK_2; case VK_3: return XKeySym.XK_3; case VK_4: return XKeySym.XK_4; case VK_5: return XKeySym.XK_5; case VK_6: return XKeySym.XK_6; case VK_7: return XKeySym.XK_7; case VK_8: return XKeySym.XK_8; case VK_9: return XKeySym.XK_9; case VK_SEMICOLON: return XKeySym.XK_semicolon; case VK_EQUALS: return XKeySym.XK_equal; case VK_A: return shiftPressed ? XKeySym.XK_A : XKeySym.XK_a; case VK_B: return shiftPressed ? XKeySym.XK_B : XKeySym.XK_b; case VK_C: return shiftPressed ? XKeySym.XK_C : XKeySym.XK_c; case VK_D: return shiftPressed ? XKeySym.XK_D : XKeySym.XK_d; case VK_E: return shiftPressed ? XKeySym.XK_E : XKeySym.XK_e; case VK_F: return shiftPressed ? XKeySym.XK_F : XKeySym.XK_f; case VK_G: return shiftPressed ? XKeySym.XK_G : XKeySym.XK_g; case VK_H: return shiftPressed ? XKeySym.XK_H : XKeySym.XK_h; case VK_I: return shiftPressed ? XKeySym.XK_I : XKeySym.XK_i; case VK_J: return shiftPressed ? XKeySym.XK_J : XKeySym.XK_j; case VK_K: return shiftPressed ? XKeySym.XK_K : XKeySym.XK_k; case VK_L: return shiftPressed ? XKeySym.XK_L : XKeySym.XK_l; case VK_M: return shiftPressed ? XKeySym.XK_M : XKeySym.XK_m; case VK_N: return shiftPressed ? XKeySym.XK_N : XKeySym.XK_n; case VK_O: return shiftPressed ? XKeySym.XK_O : XKeySym.XK_o; case VK_P: return shiftPressed ? XKeySym.XK_P : XKeySym.XK_p; case VK_Q: return shiftPressed ? XKeySym.XK_Q : XKeySym.XK_q; case VK_R: return shiftPressed ? XKeySym.XK_R : XKeySym.XK_r; case VK_S: return shiftPressed ? XKeySym.XK_S : XKeySym.XK_s; case VK_T: return shiftPressed ? XKeySym.XK_T : XKeySym.XK_t; case VK_U: return shiftPressed ? XKeySym.XK_U : XKeySym.XK_u; case VK_V: return shiftPressed ? XKeySym.XK_V : XKeySym.XK_v; case VK_W: return shiftPressed ? XKeySym.XK_W : XKeySym.XK_w; case VK_X: return shiftPressed ? XKeySym.XK_X : XKeySym.XK_x; case VK_Y: return shiftPressed ? XKeySym.XK_Y : XKeySym.XK_y; case VK_Z: return shiftPressed ? XKeySym.XK_Z : XKeySym.XK_z; case VK_OPEN_BRACKET: return XKeySym.XK_bracketleft; case VK_BACK_SLASH: return XKeySym.XK_backslash; case VK_CLOSE_BRACKET: return XKeySym.XK_bracketright; case VK_NUMPAD0: return XKeySym.XK_KP_0; case VK_NUMPAD1: return XKeySym.XK_KP_1; case VK_NUMPAD2: return XKeySym.XK_KP_2; case VK_NUMPAD3: return XKeySym.XK_KP_3; case VK_NUMPAD4: return XKeySym.XK_KP_4; case VK_NUMPAD5: return XKeySym.XK_KP_5; case VK_NUMPAD6: return XKeySym.XK_KP_6; case VK_NUMPAD7: return XKeySym.XK_KP_7; case VK_NUMPAD8: return XKeySym.XK_KP_8; case VK_NUMPAD9: return XKeySym.XK_KP_9; case VK_MULTIPLY: return XKeySym.XK_KP_Multiply; case VK_ADD: return XKeySym.XK_KP_Add; case VK_SEPARATOR: return XKeySym.XK_KP_Separator; case VK_SUBTRACT: return XKeySym.XK_KP_Subtract; case VK_DECIMAL: return XKeySym.XK_KP_Decimal; case VK_DIVIDE: return XKeySym.XK_KP_Divide; case VK_DELETE: return XKeySym.XK_KP_Delete; case VK_NUM_LOCK: return XKeySym.XK_Num_Lock; case VK_SCROLL_LOCK: return XKeySym.XK_Scroll_Lock; case VK_F1: return XKeySym.XK_F1; case VK_F2: return XKeySym.XK_F2; case VK_F3: return XKeySym.XK_F3; case VK_F4: return XKeySym.XK_F4; case VK_F5: return XKeySym.XK_F5; case VK_F6: return XKeySym.XK_F6; case VK_F7: return XKeySym.XK_F7; case VK_F8: return XKeySym.XK_F8; case VK_F9: return XKeySym.XK_F9; case VK_F10: return XKeySym.XK_F10; case VK_F11: return XKeySym.XK_F11; case VK_F12: return XKeySym.XK_F12; case VK_F13: return XKeySym.XK_F13; case VK_F14: return XKeySym.XK_F14; case VK_F15: return XKeySym.XK_F15; case VK_F16: return XKeySym.XK_F16; case VK_F17: return XKeySym.XK_F17; case VK_F18: return XKeySym.XK_F18; case VK_F19: return XKeySym.XK_F19; case VK_F20: return XKeySym.XK_F20; case VK_F21: return XKeySym.XK_F21; case VK_F22: return XKeySym.XK_F22; case VK_F23: return XKeySym.XK_F23; case VK_F24: return XKeySym.XK_F24; case VK_PRINTSCREEN: return XKeySym.XK_Print; case VK_INSERT: return XKeySym.XK_Insert; case VK_HELP: return XKeySym.XK_Help; case VK_META: return XKeySym.XK_Meta_L; case VK_KP_UP: return XKeySym.XK_KP_Up; case VK_KP_DOWN: return XKeySym.XK_KP_Down; case VK_KP_LEFT: return XKeySym.XK_KP_Left; case VK_KP_RIGHT: return XKeySym.XK_KP_Right; case VK_DEAD_GRAVE: return XKeySym.XK_dead_grave; case VK_DEAD_ACUTE: return XKeySym.XK_dead_acute; case VK_DEAD_CIRCUMFLEX: return XKeySym.XK_dead_circumflex; case VK_DEAD_TILDE: return XKeySym.XK_dead_tilde; case VK_DEAD_MACRON: return XKeySym.XK_dead_macron; case VK_DEAD_BREVE: return XKeySym.XK_dead_breve; case VK_DEAD_ABOVEDOT: return XKeySym.XK_dead_abovedot; case VK_DEAD_DIAERESIS: return XKeySym.XK_dead_diaeresis; case VK_DEAD_ABOVERING: return XKeySym.XK_dead_abovering; case VK_DEAD_DOUBLEACUTE: return XKeySym.XK_dead_doubleacute; case VK_DEAD_CARON: return XKeySym.XK_dead_caron; case VK_DEAD_CEDILLA: return XKeySym.XK_dead_cedilla; case VK_DEAD_OGONEK: return XKeySym.XK_dead_ogonek; case VK_DEAD_IOTA: return XKeySym.XK_dead_iota; case VK_DEAD_VOICED_SOUND: return XKeySym.XK_dead_voiced_sound; case VK_DEAD_SEMIVOICED_SOUND: return XKeySym.XK_dead_semivoiced_sound; case VK_AMPERSAND: return XKeySym.XK_ampersand; case VK_ASTERISK: return XKeySym.XK_asterisk; case VK_QUOTEDBL: return XKeySym.XK_quotedbl; case VK_LESS: return XKeySym.XK_less; case VK_GREATER: return XKeySym.XK_greater; case VK_BRACELEFT: return XKeySym.XK_bracketleft; case VK_BRACERIGHT: return XKeySym.XK_bracketright; case VK_AT: return XKeySym.XK_at; case VK_COLON: return XKeySym.XK_colon; case VK_CIRCUMFLEX: return XKeySym.XK_acircumflex; case VK_DOLLAR: return XKeySym.XK_dollar; case VK_EURO_SIGN: return XKeySym.XK_EuroSign; case VK_EXCLAMATION_MARK: return XKeySym.XK_exclam; case VK_INVERTED_EXCLAMATION_MARK: return XKeySym.XK_exclamdown; case VK_LEFT_PARENTHESIS: return XKeySym.XK_parenleft; case VK_NUMBER_SIGN: return XKeySym.XK_numbersign; case VK_PLUS: return XKeySym.XK_plus; case VK_RIGHT_PARENTHESIS: return XKeySym.XK_parenright; case VK_UNDERSCORE: return XKeySym.XK_underscore; case VK_WINDOWS: return XKeySym.XK_Super_L; case VK_COMPOSE: return XKeySym.XK_Multi_key; case VK_ALT_GRAPH: return XKeySym.XK_ISO_Level3_Shift; case VK_BEGIN: return XKeySym.XK_Begin; } throw new IllegalArgumentException("Cannot type keycode " + code); } private boolean requiresShift(int xlibKeySym) { // This is keyboard layout dependent. // What's encoded here is for a basic US layout switch (xlibKeySym) { case XKeySym.XK_A: case XKeySym.XK_B: case XKeySym.XK_C: case XKeySym.XK_D: case XKeySym.XK_E: case XKeySym.XK_F: case XKeySym.XK_G: case XKeySym.XK_H: case XKeySym.XK_I: case XKeySym.XK_J: case XKeySym.XK_K: case XKeySym.XK_L: case XKeySym.XK_M: case XKeySym.XK_N: case XKeySym.XK_O: case XKeySym.XK_P: case XKeySym.XK_Q: case XKeySym.XK_R: case XKeySym.XK_S: case XKeySym.XK_T: case XKeySym.XK_U: case XKeySym.XK_V: case XKeySym.XK_W: case XKeySym.XK_X: case XKeySym.XK_Y: case XKeySym.XK_Z: case XKeySym.XK_exclam: case XKeySym.XK_at: case XKeySym.XK_numbersign: case XKeySym.XK_dollar: case XKeySym.XK_percent: case XKeySym.XK_asciicircum: case XKeySym.XK_ampersand: case XKeySym.XK_asterisk: case XKeySym.XK_parenleft: case XKeySym.XK_parenright: case XKeySym.XK_underscore: case XKeySym.XK_plus: case XKeySym.XK_braceleft: case XKeySym.XK_braceright: case XKeySym.XK_colon: case XKeySym.XK_quotedbl: case XKeySym.XK_bar: case XKeySym.XK_less: case XKeySym.XK_greater: case XKeySym.XK_question: case XKeySym.XK_asciitilde: case XKeySym.XK_plusminus: return true; default: return false; } } @Override public void mouseMove(int x, int y) { try { screen.getClient().mouseEvent(mouseButtons, x, y); mouseX = x; mouseY = y; } catch (IOException e) { throw new RuntimeException(e); } } @Override public void mouseDown(int buttons) { if ((buttons & Mouse.LEFT) != 0) mouseButtons |= VNC_POINTER_EVENT_BUTTON_1; if ((buttons & Mouse.MIDDLE) != 0) mouseButtons |= VNC_POINTER_EVENT_BUTTON_2; if ((buttons & Mouse.RIGHT) != 0) mouseButtons |= VNC_POINTER_EVENT_BUTTON_3; mouseMove(mouseX, mouseY); } @Override public int mouseUp(int buttons) { if ((buttons & Mouse.LEFT) != 0) mouseButtons &= ~VNC_POINTER_EVENT_BUTTON_1; if ((buttons & Mouse.MIDDLE) != 0) mouseButtons &= ~VNC_POINTER_EVENT_BUTTON_2; if ((buttons & Mouse.RIGHT) != 0) mouseButtons &= ~VNC_POINTER_EVENT_BUTTON_3; mouseMove(mouseX, mouseY); int remainingButtons = 0; if ((mouseButtons & VNC_POINTER_EVENT_BUTTON_1) != 0) remainingButtons |= Mouse.LEFT; if ((mouseButtons & VNC_POINTER_EVENT_BUTTON_2) != 0) remainingButtons |= Mouse.MIDDLE; if ((mouseButtons & VNC_POINTER_EVENT_BUTTON_3) != 0) remainingButtons |= Mouse.RIGHT; return remainingButtons; } @Override public void mouseReset() { mouseButtons = 0; mouseMove(mouseX, mouseY); } @Override public void clickStarts() { // Nothing to do } @Override public void clickEnds() { // Nothing to do } @Override public void smoothMove(Location dest) { smoothMove(new Location(mouseX, mouseY), dest, (long) (Settings.MoveMouseDelay * 1000L)); } @Override public void smoothMove(Location src, Location dest, long duration) { if (duration <= 0) { mouseMove(dest.getX(), dest.getY()); return; } float x = src.getX(); float y = src.getY(); float dx = dest.getX() - src.getX(); float dy = dest.getY() - src.getY(); long start = System.currentTimeMillis(); long elapsed = 0; do { float fraction = (float) elapsed / (float) duration; mouseMove((int) (x + fraction * dx), (int) (y + fraction * dy)); delay(autoDelay); elapsed = System.currentTimeMillis() - start; } while (elapsed < duration); mouseMove(dest.x, dest.y); } @Override public void mouseWheel(int wheelAmt) { if (wheelAmt == Mouse.WHEEL_DOWN) { mouseButtons |= VNC_POINTER_EVENT_BUTTON_5; mouseMove(mouseX, mouseY); mouseButtons &= ~VNC_POINTER_EVENT_BUTTON_5; mouseMove(mouseX, mouseY); } else if (wheelAmt == Mouse.WHEEL_UP) { mouseButtons |= VNC_POINTER_EVENT_BUTTON_4; mouseMove(mouseX, mouseY); mouseButtons &= ~VNC_POINTER_EVENT_BUTTON_4; mouseMove(mouseX, mouseY); } } @Override public void waitForIdle() { // Nothing to do } @Override public void delay(int ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { // ignored } } @Override public void setAutoDelay(int ms) { autoDelay = ms; } @Override public Color getColorAt(int x, int y) { ScreenImage image = captureScreen(new Rectangle(x, y, 1, 1)); return new Color(image.getImage().getRGB(0, 0)); } @Override public void cleanup() { // Nothing to do } } sikulix-1.1.1/API/src/main/java/org/sikuli/vnc/VNCScreen.java000066400000000000000000000123131315726130400235570ustar00rootroot00000000000000/* Copyright (c) 2017, Sikuli.org, sikulix.com * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package org.sikuli.vnc; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; import org.sikuli.script.*; import org.sikuli.util.OverlayCapturePrompt; import org.sikuli.util.ScreenHighlighter; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class VNCScreen extends Region implements IScreen, Closeable { private final VNCClient client; private volatile boolean closed; private final IRobot robot; private ScreenImage lastScreenImage; private static List screens = new ArrayList<>(); public static VNCScreen start(String theIP, int thePort, String password, int cTimeout, int timeout) throws IOException { VNCScreen scr = new VNCScreen(VNCClient.connect(theIP, thePort, password, true)); screens.add(scr); return scr; } public static VNCScreen start(String theIP, int thePort, int cTimeout, int timeout) throws IOException { VNCScreen scr = new VNCScreen(VNCClient.connect(theIP, thePort, null, true)); screens.add(scr); return scr; } public void stop() { try { close(); } catch (IOException e) { Debug.error("VNCScreen: stop: %s", e.getMessage()); } screens.remove(this); } public static void stopAll() { for (VNCScreen scr : screens) { try { scr.close(); } catch (IOException e) { Debug.error("VNCScreen: stopAll: %s", e.getMessage()); } } } private VNCScreen(final VNCClient client) { this.client = client; this.robot = new VNCRobot(this); setOtherScreen(this); setRect(getBounds()); initScreen(this); new Thread(new Runnable() { @Override public void run() { try { client.processMessages(); } catch (RuntimeException e) { if (!closed) { throw e; } } } }).start(); client.refreshFramebuffer(); //RunTime.get().pause(5); } @Override public void close() throws IOException { closed = true; client.close(); screens.clear(); } @Override public IRobot getRobot() { return robot; } @Override public Rectangle getBounds() { return client.getBounds(); } @Override public ScreenImage capture() { return capture(getBounds()); } @Override public ScreenImage capture(Region reg) { return capture(reg.x, reg.y, reg.w, reg.h); } @Override public ScreenImage capture(Rectangle rect) { return capture(rect.x, rect.y, rect.width, rect.height); } @Override public ScreenImage capture(int x, int y, int w, int h) { BufferedImage image = client.getFrameBuffer(x, y, w, h); ScreenImage img = new ScreenImage( new Rectangle(x, y, w, h), image ); lastScreenImage = img; return img; } public void showTarget(Location loc) { showTarget(loc, Settings.SlowMotionDelay); } protected void showTarget(Location loc, double secs) { if (Settings.isShowActions()) { ScreenHighlighter overlay = new ScreenHighlighter(this, null); overlay.showTarget(loc, (float) secs); } } @Override public int getID() { return 0; } @Override public int getIdFromPoint(int srcx, int srcy) { return 0; } @Override protected Location getLocationFromTarget(PSIMRL target) throws FindFailed { Location location = super.getLocationFromTarget(target); if (location != null) { location.setOtherScreen(this); } return location; } @Override public ScreenImage getLastScreenImageFromScreen() { return lastScreenImage; } @Override public ScreenImage userCapture(final String msg) { if (robot == null) { return null; } final OverlayCapturePrompt prompt = new OverlayCapturePrompt(this); Thread th = new Thread() { @Override public void run() { prompt.prompt(msg); } }; th.start(); boolean hasShot = false; ScreenImage simg = null; int count = 0; while (!hasShot) { this.wait(0.1f); if (count++ > 300) { break; } if (prompt == null) { continue; } if (prompt.isComplete()) { simg = prompt.getSelection(); if (simg != null) { lastScreenImage = simg; hasShot = true; } prompt.close(); } } prompt.close(); return simg; } public VNCClient getClient() { return client; } } sikulix-1.1.1/API/src/main/java/org/sikuli/vnc/XKeySym.java000066400000000000000000005334551315726130400233610ustar00rootroot00000000000000package org.sikuli.vnc; /*********************************************************** Copyright 1987, 1994, 1998 The Open Group Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Digital not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ class XKeySym { /* $XFree86: $ */ /* * The "X11 Window System Protocol" standard defines in Appendix A the * keysym codes. These 29-bit integer values identify characters or * functions associated with each key (e.g., via the visible * engraving) of a keyboard layout. This file assigns mnemonic macro * names for these keysyms. * * This file is also compiled (by xc/lib/X11/util/makekeys.c) into * hash tables that can be accessed with X11 library functions such as * XStringToKeysym() and XKeysymToString(). * * Where a keysym corresponds one-to-one to an ISO 10646 / Unicode * character, this is noted in a comment that provides both the U+xxxx * Unicode position, as well as the official Unicode name of the * character. * * Where the correspondence is either not one-to-one or semantically * unclear, the Unicode position and name are enclosed in * parentheses. Such legacy keysyms should be considered deprecated * and are not recommended for use in future keyboard mappings. * * For any future extension of the keysyms with characters already * found in ISO 10646 / Unicode, the following algorithm shall be * used. The new keysym code position will simply be the character's * Unicode number plus 0x01000000. The keysym values in the range * 0x01000100 to 0x0110ffff are reserved to represent Unicode * characters in the range U+0100 to U+10FFFF. * * While most newer Unicode-based X11 clients do already accept * Unicode-mapped keysyms in the range 0x01000100 to 0x0110ffff, it * will remain necessary for clients -- in the interest of * compatibility with existing servers -- to also understand the * existing legacy keysym values in the range 0x0100 to 0x20ff. * * Where several mnemonic names are defined for the same keysym in this * file, all but the first one listed should be considered deprecated. * * Mnemonic names for keysyms are defined in this file with lines * that match one of these Perl regular expressions: * * /^\#define XK_([a-zA-Z_0-9]+)\s+0x([0-9a-f]+)\s*\/\* U+([0-9A-F]{4,6}) (.*) \*\/\s*$/ * /^\#define XK_([a-zA-Z_0-9]+)\s+0x([0-9a-f]+)\s*\/\*\(U+([0-9A-F]{4,6}) (.*)\)\*\/\s*$/ * /^\#define XK_([a-zA-Z_0-9]+)\s+0x([0-9a-f]+)\s*(\/\*\s*(.*)\s*\*\/)?\s*$/ * * When adding new keysyms to this file, do not forget to also update the * mappings in xc/lib/X11/KeyBind.c and the protocol specification in * xc/doc/specs/XProtocol/X11.keysyms. */ /* * Now that the Xorg code base is managed in Git repositories, the KeyBind.c * and X11.keysyms files mentioned in the last comment block are located at: * * src/KeyBind.c in the repo git://anongit.freedesktop.org/xorg/lib/libX11 * specs/XProtocol/X11.keysyms in the repo git://anongit.freedesktop.org/xorg/doc/xorg-docs */ public static final int XK_VoidSymbol = 0xffffff; /* Void symbol */ /* * TTY function keys, cleverly chosen to map to ASCII, for convenience of * programming, but could have been arbitrary (at the cost of lookup * tables in client code). */ public static final int XK_BackSpace = 0xff08; /* Back space, back char */ public static final int XK_Tab = 0xff09; public static final int XK_Linefeed = 0xff0a; /* Linefeed, LF */ public static final int XK_Clear = 0xff0b; public static final int XK_Return = 0xff0d; /* Return, enter */ public static final int XK_Pause = 0xff13; /* Pause, hold */ public static final int XK_Scroll_Lock = 0xff14; public static final int XK_Sys_Req = 0xff15; public static final int XK_Escape = 0xff1b; public static final int XK_Delete = 0xffff; /* Delete, rubout */ /* International & multi-key character composition */ public static final int XK_Multi_key = 0xff20; /* Multi-key character compose */ public static final int XK_Codeinput = 0xff37; public static final int XK_SingleCandidate = 0xff3c; public static final int XK_MultipleCandidate = 0xff3d; public static final int XK_PreviousCandidate = 0xff3e; /* Japanese keyboard support */ public static final int XK_Kanji = 0xff21; /* Kanji, Kanji convert */ public static final int XK_Muhenkan = 0xff22; /* Cancel Conversion */ public static final int XK_Henkan_Mode = 0xff23; /* Start/Stop Conversion */ public static final int XK_Henkan = 0xff23; /* Alias for Henkan_Mode */ public static final int XK_Romaji = 0xff24; /* to Romaji */ public static final int XK_Hiragana = 0xff25; /* to Hiragana */ public static final int XK_Katakana = 0xff26; /* to Katakana */ public static final int XK_Hiragana_Katakana = 0xff27; /* Hiragana/Katakana toggle */ public static final int XK_Zenkaku = 0xff28; /* to Zenkaku */ public static final int XK_Hankaku = 0xff29; /* to Hankaku */ public static final int XK_Zenkaku_Hankaku = 0xff2a; /* Zenkaku/Hankaku toggle */ public static final int XK_Touroku = 0xff2b; /* Add to Dictionary */ public static final int XK_Massyo = 0xff2c; /* Delete from Dictionary */ public static final int XK_Kana_Lock = 0xff2d; /* Kana Lock */ public static final int XK_Kana_Shift = 0xff2e; /* Kana Shift */ public static final int XK_Eisu_Shift = 0xff2f; /* Alphanumeric Shift */ public static final int XK_Eisu_toggle = 0xff30; /* Alphanumeric toggle */ public static final int XK_Kanji_Bangou = 0xff37; /* Codeinput */ public static final int XK_Zen_Koho = 0xff3d; /* Multiple/All Candidate(s) */ public static final int XK_Mae_Koho = 0xff3e; /* Previous Candidate */ /* 0xff31 thru 0xff3f are under XK_KOREAN */ /* Cursor control & motion */ public static final int XK_Home = 0xff50; public static final int XK_Left = 0xff51; /* Move left, left arrow */ public static final int XK_Up = 0xff52; /* Move up, up arrow */ public static final int XK_Right = 0xff53; /* Move right, right arrow */ public static final int XK_Down = 0xff54; /* Move down, down arrow */ public static final int XK_Prior = 0xff55; /* Prior, previous */ public static final int XK_Page_Up = 0xff55; public static final int XK_Next = 0xff56; /* Next */ public static final int XK_Page_Down = 0xff56; public static final int XK_End = 0xff57; /* EOL */ public static final int XK_Begin = 0xff58; /* BOL */ /* Misc functions */ public static final int XK_Select = 0xff60; /* Select, mark */ public static final int XK_Print = 0xff61; public static final int XK_Execute = 0xff62; /* Execute, run, do */ public static final int XK_Insert = 0xff63; /* Insert, insert here */ public static final int XK_Undo = 0xff65; public static final int XK_Redo = 0xff66; /* Redo, again */ public static final int XK_Menu = 0xff67; public static final int XK_Find = 0xff68; /* Find, search */ public static final int XK_Cancel = 0xff69; /* Cancel, stop, abort, exit */ public static final int XK_Help = 0xff6a; /* Help */ public static final int XK_Break = 0xff6b; public static final int XK_Mode_switch = 0xff7e; /* Character set switch */ public static final int XK_script_switch = 0xff7e; /* Alias for mode_switch */ public static final int XK_Num_Lock = 0xff7f; /* Keypad functions, keypad numbers cleverly chosen to map to ASCII */ public static final int XK_KP_Space = 0xff80; /* Space */ public static final int XK_KP_Tab = 0xff89; public static final int XK_KP_Enter = 0xff8d; /* Enter */ public static final int XK_KP_F1 = 0xff91; /* PF1, KP_A, ... */ public static final int XK_KP_F2 = 0xff92; public static final int XK_KP_F3 = 0xff93; public static final int XK_KP_F4 = 0xff94; public static final int XK_KP_Home = 0xff95; public static final int XK_KP_Left = 0xff96; public static final int XK_KP_Up = 0xff97; public static final int XK_KP_Right = 0xff98; public static final int XK_KP_Down = 0xff99; public static final int XK_KP_Prior = 0xff9a; public static final int XK_KP_Page_Up = 0xff9a; public static final int XK_KP_Next = 0xff9b; public static final int XK_KP_Page_Down = 0xff9b; public static final int XK_KP_End = 0xff9c; public static final int XK_KP_Begin = 0xff9d; public static final int XK_KP_Insert = 0xff9e; public static final int XK_KP_Delete = 0xff9f; public static final int XK_KP_Equal = 0xffbd; /* Equals */ public static final int XK_KP_Multiply = 0xffaa; public static final int XK_KP_Add = 0xffab; public static final int XK_KP_Separator = 0xffac; /* Separator, often comma */ public static final int XK_KP_Subtract = 0xffad; public static final int XK_KP_Decimal = 0xffae; public static final int XK_KP_Divide = 0xffaf; public static final int XK_KP_0 = 0xffb0; public static final int XK_KP_1 = 0xffb1; public static final int XK_KP_2 = 0xffb2; public static final int XK_KP_3 = 0xffb3; public static final int XK_KP_4 = 0xffb4; public static final int XK_KP_5 = 0xffb5; public static final int XK_KP_6 = 0xffb6; public static final int XK_KP_7 = 0xffb7; public static final int XK_KP_8 = 0xffb8; public static final int XK_KP_9 = 0xffb9; /* * Auxiliary functions; note the duplicate definitions for left and right * function keys; Sun keyboards and a few other manufacturers have such * function key groups on the left and/or right sides of the keyboard. * We've not found a keyboard with more than 35 function keys total. */ public static final int XK_F1 = 0xffbe; public static final int XK_F2 = 0xffbf; public static final int XK_F3 = 0xffc0; public static final int XK_F4 = 0xffc1; public static final int XK_F5 = 0xffc2; public static final int XK_F6 = 0xffc3; public static final int XK_F7 = 0xffc4; public static final int XK_F8 = 0xffc5; public static final int XK_F9 = 0xffc6; public static final int XK_F10 = 0xffc7; public static final int XK_F11 = 0xffc8; public static final int XK_L1 = 0xffc8; public static final int XK_F12 = 0xffc9; public static final int XK_L2 = 0xffc9; public static final int XK_F13 = 0xffca; public static final int XK_L3 = 0xffca; public static final int XK_F14 = 0xffcb; public static final int XK_L4 = 0xffcb; public static final int XK_F15 = 0xffcc; public static final int XK_L5 = 0xffcc; public static final int XK_F16 = 0xffcd; public static final int XK_L6 = 0xffcd; public static final int XK_F17 = 0xffce; public static final int XK_L7 = 0xffce; public static final int XK_F18 = 0xffcf; public static final int XK_L8 = 0xffcf; public static final int XK_F19 = 0xffd0; public static final int XK_L9 = 0xffd0; public static final int XK_F20 = 0xffd1; public static final int XK_L10 = 0xffd1; public static final int XK_F21 = 0xffd2; public static final int XK_R1 = 0xffd2; public static final int XK_F22 = 0xffd3; public static final int XK_R2 = 0xffd3; public static final int XK_F23 = 0xffd4; public static final int XK_R3 = 0xffd4; public static final int XK_F24 = 0xffd5; public static final int XK_R4 = 0xffd5; public static final int XK_F25 = 0xffd6; public static final int XK_R5 = 0xffd6; public static final int XK_F26 = 0xffd7; public static final int XK_R6 = 0xffd7; public static final int XK_F27 = 0xffd8; public static final int XK_R7 = 0xffd8; public static final int XK_F28 = 0xffd9; public static final int XK_R8 = 0xffd9; public static final int XK_F29 = 0xffda; public static final int XK_R9 = 0xffda; public static final int XK_F30 = 0xffdb; public static final int XK_R10 = 0xffdb; public static final int XK_F31 = 0xffdc; public static final int XK_R11 = 0xffdc; public static final int XK_F32 = 0xffdd; public static final int XK_R12 = 0xffdd; public static final int XK_F33 = 0xffde; public static final int XK_R13 = 0xffde; public static final int XK_F34 = 0xffdf; public static final int XK_R14 = 0xffdf; public static final int XK_F35 = 0xffe0; public static final int XK_R15 = 0xffe0; /* Modifiers */ public static final int XK_Shift_L = 0xffe1; /* Left shift */ public static final int XK_Shift_R = 0xffe2; /* Right shift */ public static final int XK_Control_L = 0xffe3; /* Left control */ public static final int XK_Control_R = 0xffe4; /* Right control */ public static final int XK_Caps_Lock = 0xffe5; /* Caps lock */ public static final int XK_Shift_Lock = 0xffe6; /* Shift lock */ public static final int XK_Meta_L = 0xffe7; /* Left meta */ public static final int XK_Meta_R = 0xffe8; /* Right meta */ public static final int XK_Alt_L = 0xffe9; /* Left alt */ public static final int XK_Alt_R = 0xffea; /* Right alt */ public static final int XK_Super_L = 0xffeb; /* Left super */ public static final int XK_Super_R = 0xffec; /* Right super */ public static final int XK_Hyper_L = 0xffed; /* Left hyper */ public static final int XK_Hyper_R = 0xffee; /* Right hyper */ /* * Keyboard (XKB) Extension function and modifier keys * (from Appendix C of "The X Keyboard Extension: Protocol Specification") * Byte 3 = 0xfe */ public static final int XK_ISO_Lock = 0xfe01; public static final int XK_ISO_Level2_Latch = 0xfe02; public static final int XK_ISO_Level3_Shift = 0xfe03; public static final int XK_ISO_Level3_Latch = 0xfe04; public static final int XK_ISO_Level3_Lock = 0xfe05; public static final int XK_ISO_Level5_Shift = 0xfe11; public static final int XK_ISO_Level5_Latch = 0xfe12; public static final int XK_ISO_Level5_Lock = 0xfe13; public static final int XK_ISO_Group_Shift = 0xff7e; /* Alias for mode_switch */ public static final int XK_ISO_Group_Latch = 0xfe06; public static final int XK_ISO_Group_Lock = 0xfe07; public static final int XK_ISO_Next_Group = 0xfe08; public static final int XK_ISO_Next_Group_Lock = 0xfe09; public static final int XK_ISO_Prev_Group = 0xfe0a; public static final int XK_ISO_Prev_Group_Lock = 0xfe0b; public static final int XK_ISO_First_Group = 0xfe0c; public static final int XK_ISO_First_Group_Lock = 0xfe0d; public static final int XK_ISO_Last_Group = 0xfe0e; public static final int XK_ISO_Last_Group_Lock = 0xfe0f; public static final int XK_ISO_Left_Tab = 0xfe20; public static final int XK_ISO_Move_Line_Up = 0xfe21; public static final int XK_ISO_Move_Line_Down = 0xfe22; public static final int XK_ISO_Partial_Line_Up = 0xfe23; public static final int XK_ISO_Partial_Line_Down = 0xfe24; public static final int XK_ISO_Partial_Space_Left = 0xfe25; public static final int XK_ISO_Partial_Space_Right = 0xfe26; public static final int XK_ISO_Set_Margin_Left = 0xfe27; public static final int XK_ISO_Set_Margin_Right = 0xfe28; public static final int XK_ISO_Release_Margin_Left = 0xfe29; public static final int XK_ISO_Release_Margin_Right = 0xfe2a; public static final int XK_ISO_Release_Both_Margins = 0xfe2b; public static final int XK_ISO_Fast_Cursor_Left = 0xfe2c; public static final int XK_ISO_Fast_Cursor_Right = 0xfe2d; public static final int XK_ISO_Fast_Cursor_Up = 0xfe2e; public static final int XK_ISO_Fast_Cursor_Down = 0xfe2f; public static final int XK_ISO_Continuous_Underline = 0xfe30; public static final int XK_ISO_Discontinuous_Underline = 0xfe31; public static final int XK_ISO_Emphasize = 0xfe32; public static final int XK_ISO_Center_Object = 0xfe33; public static final int XK_ISO_Enter = 0xfe34; public static final int XK_dead_grave = 0xfe50; public static final int XK_dead_acute = 0xfe51; public static final int XK_dead_circumflex = 0xfe52; public static final int XK_dead_tilde = 0xfe53; public static final int XK_dead_macron = 0xfe54; public static final int XK_dead_breve = 0xfe55; public static final int XK_dead_abovedot = 0xfe56; public static final int XK_dead_diaeresis = 0xfe57; public static final int XK_dead_abovering = 0xfe58; public static final int XK_dead_doubleacute = 0xfe59; public static final int XK_dead_caron = 0xfe5a; public static final int XK_dead_cedilla = 0xfe5b; public static final int XK_dead_ogonek = 0xfe5c; public static final int XK_dead_iota = 0xfe5d; public static final int XK_dead_voiced_sound = 0xfe5e; public static final int XK_dead_semivoiced_sound = 0xfe5f; public static final int XK_dead_belowdot = 0xfe60; public static final int XK_dead_hook = 0xfe61; public static final int XK_dead_horn = 0xfe62; public static final int XK_dead_stroke = 0xfe63; public static final int XK_dead_abovecomma = 0xfe64; public static final int XK_dead_psili = 0xfe64; /* alias for dead_abovecomma */ public static final int XK_dead_abovereversedcomma = 0xfe65; public static final int XK_dead_dasia = 0xfe66; /* alias for dead_abovereversedcomma */ public static final int XK_First_Virtual_Screen = 0xfed0; public static final int XK_Prev_Virtual_Screen = 0xfed1; public static final int XK_Next_Virtual_Screen = 0xfed2; public static final int XK_Last_Virtual_Screen = 0xfed4; public static final int XK_Terminate_Server = 0xfed5; public static final int XK_AccessX_Enable = 0xfe70; public static final int XK_AccessX_Feedback_Enable = 0xfe71; public static final int XK_RepeatKeys_Enable = 0xfe72; public static final int XK_SlowKeys_Enable = 0xfe73; public static final int XK_BounceKeys_Enable = 0xfe74; public static final int XK_StickyKeys_Enable = 0xfe75; public static final int XK_MouseKeys_Enable = 0xfe76; public static final int XK_MouseKeys_Accel_Enable = 0xfe77; public static final int XK_Overlay1_Enable = 0xfe78; public static final int XK_Overlay2_Enable = 0xfe79; public static final int XK_AudibleBell_Enable = 0xfe7a; public static final int XK_Pointer_Left = 0xfee0; public static final int XK_Pointer_Right = 0xfee1; public static final int XK_Pointer_Up = 0xfee2; public static final int XK_Pointer_Down = 0xfee3; public static final int XK_Pointer_UpLeft = 0xfee4; public static final int XK_Pointer_UpRight = 0xfee5; public static final int XK_Pointer_DownLeft = 0xfee6; public static final int XK_Pointer_DownRight = 0xfee7; public static final int XK_Pointer_Button_Dflt = 0xfee8; public static final int XK_Pointer_Button1 = 0xfee9; public static final int XK_Pointer_Button2 = 0xfeea; public static final int XK_Pointer_Button3 = 0xfeeb; public static final int XK_Pointer_Button4 = 0xfeec; public static final int XK_Pointer_Button5 = 0xfeed; public static final int XK_Pointer_DblClick_Dflt = 0xfeee; public static final int XK_Pointer_DblClick1 = 0xfeef; public static final int XK_Pointer_DblClick2 = 0xfef0; public static final int XK_Pointer_DblClick3 = 0xfef1; public static final int XK_Pointer_DblClick4 = 0xfef2; public static final int XK_Pointer_DblClick5 = 0xfef3; public static final int XK_Pointer_Drag_Dflt = 0xfef4; public static final int XK_Pointer_Drag1 = 0xfef5; public static final int XK_Pointer_Drag2 = 0xfef6; public static final int XK_Pointer_Drag3 = 0xfef7; public static final int XK_Pointer_Drag4 = 0xfef8; public static final int XK_Pointer_Drag5 = 0xfefd; public static final int XK_Pointer_EnableKeys = 0xfef9; public static final int XK_Pointer_Accelerate = 0xfefa; public static final int XK_Pointer_DfltBtnNext = 0xfefb; public static final int XK_Pointer_DfltBtnPrev = 0xfefc; /* * 3270 Terminal Keys * Byte 3 = 0xfd */ public static final int XK_3270_Duplicate = 0xfd01; public static final int XK_3270_FieldMark = 0xfd02; public static final int XK_3270_Right2 = 0xfd03; public static final int XK_3270_Left2 = 0xfd04; public static final int XK_3270_BackTab = 0xfd05; public static final int XK_3270_EraseEOF = 0xfd06; public static final int XK_3270_EraseInput = 0xfd07; public static final int XK_3270_Reset = 0xfd08; public static final int XK_3270_Quit = 0xfd09; public static final int XK_3270_PA1 = 0xfd0a; public static final int XK_3270_PA2 = 0xfd0b; public static final int XK_3270_PA3 = 0xfd0c; public static final int XK_3270_Test = 0xfd0d; public static final int XK_3270_Attn = 0xfd0e; public static final int XK_3270_CursorBlink = 0xfd0f; public static final int XK_3270_AltCursor = 0xfd10; public static final int XK_3270_KeyClick = 0xfd11; public static final int XK_3270_Jump = 0xfd12; public static final int XK_3270_Ident = 0xfd13; public static final int XK_3270_Rule = 0xfd14; public static final int XK_3270_Copy = 0xfd15; public static final int XK_3270_Play = 0xfd16; public static final int XK_3270_Setup = 0xfd17; public static final int XK_3270_Record = 0xfd18; public static final int XK_3270_ChangeScreen = 0xfd19; public static final int XK_3270_DeleteWord = 0xfd1a; public static final int XK_3270_ExSelect = 0xfd1b; public static final int XK_3270_CursorSelect = 0xfd1c; public static final int XK_3270_PrintScreen = 0xfd1d; public static final int XK_3270_Enter = 0xfd1e; /* * Latin 1 * (ISO/IEC 8859-1 = Unicode U+0020..U+00FF) * Byte 3 = 0 */ public static final int XK_space = 0x0020; /* U+0020 SPACE */ public static final int XK_exclam = 0x0021; /* U+0021 EXCLAMATION MARK */ public static final int XK_quotedbl = 0x0022; /* U+0022 QUOTATION MARK */ public static final int XK_numbersign = 0x0023; /* U+0023 NUMBER SIGN */ public static final int XK_dollar = 0x0024; /* U+0024 DOLLAR SIGN */ public static final int XK_percent = 0x0025; /* U+0025 PERCENT SIGN */ public static final int XK_ampersand = 0x0026; /* U+0026 AMPERSAND */ public static final int XK_apostrophe = 0x0027; /* U+0027 APOSTROPHE */ public static final int XK_quoteright = 0x0027; /* deprecated */ public static final int XK_parenleft = 0x0028; /* U+0028 LEFT PARENTHESIS */ public static final int XK_parenright = 0x0029; /* U+0029 RIGHT PARENTHESIS */ public static final int XK_asterisk = 0x002a; /* U+002A ASTERISK */ public static final int XK_plus = 0x002b; /* U+002B PLUS SIGN */ public static final int XK_comma = 0x002c; /* U+002C COMMA */ public static final int XK_minus = 0x002d; /* U+002D HYPHEN-MINUS */ public static final int XK_period = 0x002e; /* U+002E FULL STOP */ public static final int XK_slash = 0x002f; /* U+002F SOLIDUS */ public static final int XK_0 = 0x0030; /* U+0030 DIGIT ZERO */ public static final int XK_1 = 0x0031; /* U+0031 DIGIT ONE */ public static final int XK_2 = 0x0032; /* U+0032 DIGIT TWO */ public static final int XK_3 = 0x0033; /* U+0033 DIGIT THREE */ public static final int XK_4 = 0x0034; /* U+0034 DIGIT FOUR */ public static final int XK_5 = 0x0035; /* U+0035 DIGIT FIVE */ public static final int XK_6 = 0x0036; /* U+0036 DIGIT SIX */ public static final int XK_7 = 0x0037; /* U+0037 DIGIT SEVEN */ public static final int XK_8 = 0x0038; /* U+0038 DIGIT EIGHT */ public static final int XK_9 = 0x0039; /* U+0039 DIGIT NINE */ public static final int XK_colon = 0x003a; /* U+003A COLON */ public static final int XK_semicolon = 0x003b; /* U+003B SEMICOLON */ public static final int XK_less = 0x003c; /* U+003C LESS-THAN SIGN */ public static final int XK_equal = 0x003d; /* U+003D EQUALS SIGN */ public static final int XK_greater = 0x003e; /* U+003E GREATER-THAN SIGN */ public static final int XK_question = 0x003f; /* U+003F QUESTION MARK */ public static final int XK_at = 0x0040; /* U+0040 COMMERCIAL AT */ public static final int XK_A = 0x0041; /* U+0041 LATIN CAPITAL LETTER A */ public static final int XK_B = 0x0042; /* U+0042 LATIN CAPITAL LETTER B */ public static final int XK_C = 0x0043; /* U+0043 LATIN CAPITAL LETTER C */ public static final int XK_D = 0x0044; /* U+0044 LATIN CAPITAL LETTER D */ public static final int XK_E = 0x0045; /* U+0045 LATIN CAPITAL LETTER E */ public static final int XK_F = 0x0046; /* U+0046 LATIN CAPITAL LETTER F */ public static final int XK_G = 0x0047; /* U+0047 LATIN CAPITAL LETTER G */ public static final int XK_H = 0x0048; /* U+0048 LATIN CAPITAL LETTER H */ public static final int XK_I = 0x0049; /* U+0049 LATIN CAPITAL LETTER I */ public static final int XK_J = 0x004a; /* U+004A LATIN CAPITAL LETTER J */ public static final int XK_K = 0x004b; /* U+004B LATIN CAPITAL LETTER K */ public static final int XK_L = 0x004c; /* U+004C LATIN CAPITAL LETTER L */ public static final int XK_M = 0x004d; /* U+004D LATIN CAPITAL LETTER M */ public static final int XK_N = 0x004e; /* U+004E LATIN CAPITAL LETTER N */ public static final int XK_O = 0x004f; /* U+004F LATIN CAPITAL LETTER O */ public static final int XK_P = 0x0050; /* U+0050 LATIN CAPITAL LETTER P */ public static final int XK_Q = 0x0051; /* U+0051 LATIN CAPITAL LETTER Q */ public static final int XK_R = 0x0052; /* U+0052 LATIN CAPITAL LETTER R */ public static final int XK_S = 0x0053; /* U+0053 LATIN CAPITAL LETTER S */ public static final int XK_T = 0x0054; /* U+0054 LATIN CAPITAL LETTER T */ public static final int XK_U = 0x0055; /* U+0055 LATIN CAPITAL LETTER U */ public static final int XK_V = 0x0056; /* U+0056 LATIN CAPITAL LETTER V */ public static final int XK_W = 0x0057; /* U+0057 LATIN CAPITAL LETTER W */ public static final int XK_X = 0x0058; /* U+0058 LATIN CAPITAL LETTER X */ public static final int XK_Y = 0x0059; /* U+0059 LATIN CAPITAL LETTER Y */ public static final int XK_Z = 0x005a; /* U+005A LATIN CAPITAL LETTER Z */ public static final int XK_bracketleft = 0x005b; /* U+005B LEFT SQUARE BRACKET */ public static final int XK_backslash = 0x005c; /* U+005C REVERSE SOLIDUS */ public static final int XK_bracketright = 0x005d; /* U+005D RIGHT SQUARE BRACKET */ public static final int XK_asciicircum = 0x005e; /* U+005E CIRCUMFLEX ACCENT */ public static final int XK_underscore = 0x005f; /* U+005F LOW LINE */ public static final int XK_grave = 0x0060; /* U+0060 GRAVE ACCENT */ public static final int XK_quoteleft = 0x0060; /* deprecated */ public static final int XK_a = 0x0061; /* U+0061 LATIN SMALL LETTER A */ public static final int XK_b = 0x0062; /* U+0062 LATIN SMALL LETTER B */ public static final int XK_c = 0x0063; /* U+0063 LATIN SMALL LETTER C */ public static final int XK_d = 0x0064; /* U+0064 LATIN SMALL LETTER D */ public static final int XK_e = 0x0065; /* U+0065 LATIN SMALL LETTER E */ public static final int XK_f = 0x0066; /* U+0066 LATIN SMALL LETTER F */ public static final int XK_g = 0x0067; /* U+0067 LATIN SMALL LETTER G */ public static final int XK_h = 0x0068; /* U+0068 LATIN SMALL LETTER H */ public static final int XK_i = 0x0069; /* U+0069 LATIN SMALL LETTER I */ public static final int XK_j = 0x006a; /* U+006A LATIN SMALL LETTER J */ public static final int XK_k = 0x006b; /* U+006B LATIN SMALL LETTER K */ public static final int XK_l = 0x006c; /* U+006C LATIN SMALL LETTER L */ public static final int XK_m = 0x006d; /* U+006D LATIN SMALL LETTER M */ public static final int XK_n = 0x006e; /* U+006E LATIN SMALL LETTER N */ public static final int XK_o = 0x006f; /* U+006F LATIN SMALL LETTER O */ public static final int XK_p = 0x0070; /* U+0070 LATIN SMALL LETTER P */ public static final int XK_q = 0x0071; /* U+0071 LATIN SMALL LETTER Q */ public static final int XK_r = 0x0072; /* U+0072 LATIN SMALL LETTER R */ public static final int XK_s = 0x0073; /* U+0073 LATIN SMALL LETTER S */ public static final int XK_t = 0x0074; /* U+0074 LATIN SMALL LETTER T */ public static final int XK_u = 0x0075; /* U+0075 LATIN SMALL LETTER U */ public static final int XK_v = 0x0076; /* U+0076 LATIN SMALL LETTER V */ public static final int XK_w = 0x0077; /* U+0077 LATIN SMALL LETTER W */ public static final int XK_x = 0x0078; /* U+0078 LATIN SMALL LETTER X */ public static final int XK_y = 0x0079; /* U+0079 LATIN SMALL LETTER Y */ public static final int XK_z = 0x007a; /* U+007A LATIN SMALL LETTER Z */ public static final int XK_braceleft = 0x007b; /* U+007B LEFT CURLY BRACKET */ public static final int XK_bar = 0x007c; /* U+007C VERTICAL LINE */ public static final int XK_braceright = 0x007d; /* U+007D RIGHT CURLY BRACKET */ public static final int XK_asciitilde = 0x007e; /* U+007E TILDE */ public static final int XK_nobreakspace = 0x00a0; /* U+00A0 NO-BREAK SPACE */ public static final int XK_exclamdown = 0x00a1; /* U+00A1 INVERTED EXCLAMATION MARK */ public static final int XK_cent = 0x00a2; /* U+00A2 CENT SIGN */ public static final int XK_sterling = 0x00a3; /* U+00A3 POUND SIGN */ public static final int XK_currency = 0x00a4; /* U+00A4 CURRENCY SIGN */ public static final int XK_yen = 0x00a5; /* U+00A5 YEN SIGN */ public static final int XK_brokenbar = 0x00a6; /* U+00A6 BROKEN BAR */ public static final int XK_section = 0x00a7; /* U+00A7 SECTION SIGN */ public static final int XK_diaeresis = 0x00a8; /* U+00A8 DIAERESIS */ public static final int XK_copyright = 0x00a9; /* U+00A9 COPYRIGHT SIGN */ public static final int XK_ordfeminine = 0x00aa; /* U+00AA FEMININE ORDINAL INDICATOR */ public static final int XK_guillemotleft = 0x00ab; /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ public static final int XK_notsign = 0x00ac; /* U+00AC NOT SIGN */ public static final int XK_hyphen = 0x00ad; /* U+00AD SOFT HYPHEN */ public static final int XK_registered = 0x00ae; /* U+00AE REGISTERED SIGN */ public static final int XK_macron = 0x00af; /* U+00AF MACRON */ public static final int XK_degree = 0x00b0; /* U+00B0 DEGREE SIGN */ public static final int XK_plusminus = 0x00b1; /* U+00B1 PLUS-MINUS SIGN */ public static final int XK_twosuperior = 0x00b2; /* U+00B2 SUPERSCRIPT TWO */ public static final int XK_threesuperior = 0x00b3; /* U+00B3 SUPERSCRIPT THREE */ public static final int XK_acute = 0x00b4; /* U+00B4 ACUTE ACCENT */ public static final int XK_mu = 0x00b5; /* U+00B5 MICRO SIGN */ public static final int XK_paragraph = 0x00b6; /* U+00B6 PILCROW SIGN */ public static final int XK_periodcentered = 0x00b7; /* U+00B7 MIDDLE DOT */ public static final int XK_cedilla = 0x00b8; /* U+00B8 CEDILLA */ public static final int XK_onesuperior = 0x00b9; /* U+00B9 SUPERSCRIPT ONE */ public static final int XK_masculine = 0x00ba; /* U+00BA MASCULINE ORDINAL INDICATOR */ public static final int XK_guillemotright = 0x00bb; /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ public static final int XK_onequarter = 0x00bc; /* U+00BC VULGAR FRACTION ONE QUARTER */ public static final int XK_onehalf = 0x00bd; /* U+00BD VULGAR FRACTION ONE HALF */ public static final int XK_threequarters = 0x00be; /* U+00BE VULGAR FRACTION THREE QUARTERS */ public static final int XK_questiondown = 0x00bf; /* U+00BF INVERTED QUESTION MARK */ public static final int XK_Agrave = 0x00c0; /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */ public static final int XK_Aacute = 0x00c1; /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */ public static final int XK_Acircumflex = 0x00c2; /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ public static final int XK_Atilde = 0x00c3; /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */ public static final int XK_Adiaeresis = 0x00c4; /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */ public static final int XK_Aring = 0x00c5; /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */ public static final int XK_AE = 0x00c6; /* U+00C6 LATIN CAPITAL LETTER AE */ public static final int XK_Ccedilla = 0x00c7; /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */ public static final int XK_Egrave = 0x00c8; /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */ public static final int XK_Eacute = 0x00c9; /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */ public static final int XK_Ecircumflex = 0x00ca; /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ public static final int XK_Ediaeresis = 0x00cb; /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */ public static final int XK_Igrave = 0x00cc; /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */ public static final int XK_Iacute = 0x00cd; /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */ public static final int XK_Icircumflex = 0x00ce; /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ public static final int XK_Idiaeresis = 0x00cf; /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */ public static final int XK_ETH = 0x00d0; /* U+00D0 LATIN CAPITAL LETTER ETH */ public static final int XK_Eth = 0x00d0; /* deprecated */ public static final int XK_Ntilde = 0x00d1; /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */ public static final int XK_Ograve = 0x00d2; /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */ public static final int XK_Oacute = 0x00d3; /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */ public static final int XK_Ocircumflex = 0x00d4; /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ public static final int XK_Otilde = 0x00d5; /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */ public static final int XK_Odiaeresis = 0x00d6; /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */ public static final int XK_multiply = 0x00d7; /* U+00D7 MULTIPLICATION SIGN */ public static final int XK_Oslash = 0x00d8; /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */ public static final int XK_Ooblique = 0x00d8; /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */ public static final int XK_Ugrave = 0x00d9; /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */ public static final int XK_Uacute = 0x00da; /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */ public static final int XK_Ucircumflex = 0x00db; /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ public static final int XK_Udiaeresis = 0x00dc; /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */ public static final int XK_Yacute = 0x00dd; /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */ public static final int XK_THORN = 0x00de; /* U+00DE LATIN CAPITAL LETTER THORN */ public static final int XK_Thorn = 0x00de; /* deprecated */ public static final int XK_ssharp = 0x00df; /* U+00DF LATIN SMALL LETTER SHARP S */ public static final int XK_agrave = 0x00e0; /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */ public static final int XK_aacute = 0x00e1; /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */ public static final int XK_acircumflex = 0x00e2; /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */ public static final int XK_atilde = 0x00e3; /* U+00E3 LATIN SMALL LETTER A WITH TILDE */ public static final int XK_adiaeresis = 0x00e4; /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */ public static final int XK_aring = 0x00e5; /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */ public static final int XK_ae = 0x00e6; /* U+00E6 LATIN SMALL LETTER AE */ public static final int XK_ccedilla = 0x00e7; /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */ public static final int XK_egrave = 0x00e8; /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */ public static final int XK_eacute = 0x00e9; /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */ public static final int XK_ecircumflex = 0x00ea; /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */ public static final int XK_ediaeresis = 0x00eb; /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */ public static final int XK_igrave = 0x00ec; /* U+00EC LATIN SMALL LETTER I WITH GRAVE */ public static final int XK_iacute = 0x00ed; /* U+00ED LATIN SMALL LETTER I WITH ACUTE */ public static final int XK_icircumflex = 0x00ee; /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */ public static final int XK_idiaeresis = 0x00ef; /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */ public static final int XK_eth = 0x00f0; /* U+00F0 LATIN SMALL LETTER ETH */ public static final int XK_ntilde = 0x00f1; /* U+00F1 LATIN SMALL LETTER N WITH TILDE */ public static final int XK_ograve = 0x00f2; /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */ public static final int XK_oacute = 0x00f3; /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */ public static final int XK_ocircumflex = 0x00f4; /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */ public static final int XK_otilde = 0x00f5; /* U+00F5 LATIN SMALL LETTER O WITH TILDE */ public static final int XK_odiaeresis = 0x00f6; /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */ public static final int XK_division = 0x00f7; /* U+00F7 DIVISION SIGN */ public static final int XK_oslash = 0x00f8; /* U+00F8 LATIN SMALL LETTER O WITH STROKE */ public static final int XK_ooblique = 0x00f8; /* U+00F8 LATIN SMALL LETTER O WITH STROKE */ public static final int XK_ugrave = 0x00f9; /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */ public static final int XK_uacute = 0x00fa; /* U+00FA LATIN SMALL LETTER U WITH ACUTE */ public static final int XK_ucircumflex = 0x00fb; /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */ public static final int XK_udiaeresis = 0x00fc; /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */ public static final int XK_yacute = 0x00fd; /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */ public static final int XK_thorn = 0x00fe; /* U+00FE LATIN SMALL LETTER THORN */ public static final int XK_ydiaeresis = 0x00ff; /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */ /* * Latin 2 * Byte 3 = 1 */ public static final int XK_Aogonek = 0x01a1; /* U+0104 LATIN CAPITAL LETTER A WITH OGONEK */ public static final int XK_breve = 0x01a2; /* U+02D8 BREVE */ public static final int XK_Lstroke = 0x01a3; /* U+0141 LATIN CAPITAL LETTER L WITH STROKE */ public static final int XK_Lcaron = 0x01a5; /* U+013D LATIN CAPITAL LETTER L WITH CARON */ public static final int XK_Sacute = 0x01a6; /* U+015A LATIN CAPITAL LETTER S WITH ACUTE */ public static final int XK_Scaron = 0x01a9; /* U+0160 LATIN CAPITAL LETTER S WITH CARON */ public static final int XK_Scedilla = 0x01aa; /* U+015E LATIN CAPITAL LETTER S WITH CEDILLA */ public static final int XK_Tcaron = 0x01ab; /* U+0164 LATIN CAPITAL LETTER T WITH CARON */ public static final int XK_Zacute = 0x01ac; /* U+0179 LATIN CAPITAL LETTER Z WITH ACUTE */ public static final int XK_Zcaron = 0x01ae; /* U+017D LATIN CAPITAL LETTER Z WITH CARON */ public static final int XK_Zabovedot = 0x01af; /* U+017B LATIN CAPITAL LETTER Z WITH DOT ABOVE */ public static final int XK_aogonek = 0x01b1; /* U+0105 LATIN SMALL LETTER A WITH OGONEK */ public static final int XK_ogonek = 0x01b2; /* U+02DB OGONEK */ public static final int XK_lstroke = 0x01b3; /* U+0142 LATIN SMALL LETTER L WITH STROKE */ public static final int XK_lcaron = 0x01b5; /* U+013E LATIN SMALL LETTER L WITH CARON */ public static final int XK_sacute = 0x01b6; /* U+015B LATIN SMALL LETTER S WITH ACUTE */ public static final int XK_caron = 0x01b7; /* U+02C7 CARON */ public static final int XK_scaron = 0x01b9; /* U+0161 LATIN SMALL LETTER S WITH CARON */ public static final int XK_scedilla = 0x01ba; /* U+015F LATIN SMALL LETTER S WITH CEDILLA */ public static final int XK_tcaron = 0x01bb; /* U+0165 LATIN SMALL LETTER T WITH CARON */ public static final int XK_zacute = 0x01bc; /* U+017A LATIN SMALL LETTER Z WITH ACUTE */ public static final int XK_doubleacute = 0x01bd; /* U+02DD DOUBLE ACUTE ACCENT */ public static final int XK_zcaron = 0x01be; /* U+017E LATIN SMALL LETTER Z WITH CARON */ public static final int XK_zabovedot = 0x01bf; /* U+017C LATIN SMALL LETTER Z WITH DOT ABOVE */ public static final int XK_Racute = 0x01c0; /* U+0154 LATIN CAPITAL LETTER R WITH ACUTE */ public static final int XK_Abreve = 0x01c3; /* U+0102 LATIN CAPITAL LETTER A WITH BREVE */ public static final int XK_Lacute = 0x01c5; /* U+0139 LATIN CAPITAL LETTER L WITH ACUTE */ public static final int XK_Cacute = 0x01c6; /* U+0106 LATIN CAPITAL LETTER C WITH ACUTE */ public static final int XK_Ccaron = 0x01c8; /* U+010C LATIN CAPITAL LETTER C WITH CARON */ public static final int XK_Eogonek = 0x01ca; /* U+0118 LATIN CAPITAL LETTER E WITH OGONEK */ public static final int XK_Ecaron = 0x01cc; /* U+011A LATIN CAPITAL LETTER E WITH CARON */ public static final int XK_Dcaron = 0x01cf; /* U+010E LATIN CAPITAL LETTER D WITH CARON */ public static final int XK_Dstroke = 0x01d0; /* U+0110 LATIN CAPITAL LETTER D WITH STROKE */ public static final int XK_Nacute = 0x01d1; /* U+0143 LATIN CAPITAL LETTER N WITH ACUTE */ public static final int XK_Ncaron = 0x01d2; /* U+0147 LATIN CAPITAL LETTER N WITH CARON */ public static final int XK_Odoubleacute = 0x01d5; /* U+0150 LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ public static final int XK_Rcaron = 0x01d8; /* U+0158 LATIN CAPITAL LETTER R WITH CARON */ public static final int XK_Uring = 0x01d9; /* U+016E LATIN CAPITAL LETTER U WITH RING ABOVE */ public static final int XK_Udoubleacute = 0x01db; /* U+0170 LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ public static final int XK_Tcedilla = 0x01de; /* U+0162 LATIN CAPITAL LETTER T WITH CEDILLA */ public static final int XK_racute = 0x01e0; /* U+0155 LATIN SMALL LETTER R WITH ACUTE */ public static final int XK_abreve = 0x01e3; /* U+0103 LATIN SMALL LETTER A WITH BREVE */ public static final int XK_lacute = 0x01e5; /* U+013A LATIN SMALL LETTER L WITH ACUTE */ public static final int XK_cacute = 0x01e6; /* U+0107 LATIN SMALL LETTER C WITH ACUTE */ public static final int XK_ccaron = 0x01e8; /* U+010D LATIN SMALL LETTER C WITH CARON */ public static final int XK_eogonek = 0x01ea; /* U+0119 LATIN SMALL LETTER E WITH OGONEK */ public static final int XK_ecaron = 0x01ec; /* U+011B LATIN SMALL LETTER E WITH CARON */ public static final int XK_dcaron = 0x01ef; /* U+010F LATIN SMALL LETTER D WITH CARON */ public static final int XK_dstroke = 0x01f0; /* U+0111 LATIN SMALL LETTER D WITH STROKE */ public static final int XK_nacute = 0x01f1; /* U+0144 LATIN SMALL LETTER N WITH ACUTE */ public static final int XK_ncaron = 0x01f2; /* U+0148 LATIN SMALL LETTER N WITH CARON */ public static final int XK_odoubleacute = 0x01f5; /* U+0151 LATIN SMALL LETTER O WITH DOUBLE ACUTE */ public static final int XK_udoubleacute = 0x01fb; /* U+0171 LATIN SMALL LETTER U WITH DOUBLE ACUTE */ public static final int XK_rcaron = 0x01f8; /* U+0159 LATIN SMALL LETTER R WITH CARON */ public static final int XK_uring = 0x01f9; /* U+016F LATIN SMALL LETTER U WITH RING ABOVE */ public static final int XK_tcedilla = 0x01fe; /* U+0163 LATIN SMALL LETTER T WITH CEDILLA */ public static final int XK_abovedot = 0x01ff; /* U+02D9 DOT ABOVE */ /* * Latin 3 * Byte 3 = 2 */ public static final int XK_Hstroke = 0x02a1; /* U+0126 LATIN CAPITAL LETTER H WITH STROKE */ public static final int XK_Hcircumflex = 0x02a6; /* U+0124 LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ public static final int XK_Iabovedot = 0x02a9; /* U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE */ public static final int XK_Gbreve = 0x02ab; /* U+011E LATIN CAPITAL LETTER G WITH BREVE */ public static final int XK_Jcircumflex = 0x02ac; /* U+0134 LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ public static final int XK_hstroke = 0x02b1; /* U+0127 LATIN SMALL LETTER H WITH STROKE */ public static final int XK_hcircumflex = 0x02b6; /* U+0125 LATIN SMALL LETTER H WITH CIRCUMFLEX */ public static final int XK_idotless = 0x02b9; /* U+0131 LATIN SMALL LETTER DOTLESS I */ public static final int XK_gbreve = 0x02bb; /* U+011F LATIN SMALL LETTER G WITH BREVE */ public static final int XK_jcircumflex = 0x02bc; /* U+0135 LATIN SMALL LETTER J WITH CIRCUMFLEX */ public static final int XK_Cabovedot = 0x02c5; /* U+010A LATIN CAPITAL LETTER C WITH DOT ABOVE */ public static final int XK_Ccircumflex = 0x02c6; /* U+0108 LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ public static final int XK_Gabovedot = 0x02d5; /* U+0120 LATIN CAPITAL LETTER G WITH DOT ABOVE */ public static final int XK_Gcircumflex = 0x02d8; /* U+011C LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ public static final int XK_Ubreve = 0x02dd; /* U+016C LATIN CAPITAL LETTER U WITH BREVE */ public static final int XK_Scircumflex = 0x02de; /* U+015C LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ public static final int XK_cabovedot = 0x02e5; /* U+010B LATIN SMALL LETTER C WITH DOT ABOVE */ public static final int XK_ccircumflex = 0x02e6; /* U+0109 LATIN SMALL LETTER C WITH CIRCUMFLEX */ public static final int XK_gabovedot = 0x02f5; /* U+0121 LATIN SMALL LETTER G WITH DOT ABOVE */ public static final int XK_gcircumflex = 0x02f8; /* U+011D LATIN SMALL LETTER G WITH CIRCUMFLEX */ public static final int XK_ubreve = 0x02fd; /* U+016D LATIN SMALL LETTER U WITH BREVE */ public static final int XK_scircumflex = 0x02fe; /* U+015D LATIN SMALL LETTER S WITH CIRCUMFLEX */ /* * Latin 4 * Byte 3 = 3 */ public static final int XK_kra = 0x03a2; /* U+0138 LATIN SMALL LETTER KRA */ public static final int XK_kappa = 0x03a2; /* deprecated */ public static final int XK_Rcedilla = 0x03a3; /* U+0156 LATIN CAPITAL LETTER R WITH CEDILLA */ public static final int XK_Itilde = 0x03a5; /* U+0128 LATIN CAPITAL LETTER I WITH TILDE */ public static final int XK_Lcedilla = 0x03a6; /* U+013B LATIN CAPITAL LETTER L WITH CEDILLA */ public static final int XK_Emacron = 0x03aa; /* U+0112 LATIN CAPITAL LETTER E WITH MACRON */ public static final int XK_Gcedilla = 0x03ab; /* U+0122 LATIN CAPITAL LETTER G WITH CEDILLA */ public static final int XK_Tslash = 0x03ac; /* U+0166 LATIN CAPITAL LETTER T WITH STROKE */ public static final int XK_rcedilla = 0x03b3; /* U+0157 LATIN SMALL LETTER R WITH CEDILLA */ public static final int XK_itilde = 0x03b5; /* U+0129 LATIN SMALL LETTER I WITH TILDE */ public static final int XK_lcedilla = 0x03b6; /* U+013C LATIN SMALL LETTER L WITH CEDILLA */ public static final int XK_emacron = 0x03ba; /* U+0113 LATIN SMALL LETTER E WITH MACRON */ public static final int XK_gcedilla = 0x03bb; /* U+0123 LATIN SMALL LETTER G WITH CEDILLA */ public static final int XK_tslash = 0x03bc; /* U+0167 LATIN SMALL LETTER T WITH STROKE */ public static final int XK_ENG = 0x03bd; /* U+014A LATIN CAPITAL LETTER ENG */ public static final int XK_eng = 0x03bf; /* U+014B LATIN SMALL LETTER ENG */ public static final int XK_Amacron = 0x03c0; /* U+0100 LATIN CAPITAL LETTER A WITH MACRON */ public static final int XK_Iogonek = 0x03c7; /* U+012E LATIN CAPITAL LETTER I WITH OGONEK */ public static final int XK_Eabovedot = 0x03cc; /* U+0116 LATIN CAPITAL LETTER E WITH DOT ABOVE */ public static final int XK_Imacron = 0x03cf; /* U+012A LATIN CAPITAL LETTER I WITH MACRON */ public static final int XK_Ncedilla = 0x03d1; /* U+0145 LATIN CAPITAL LETTER N WITH CEDILLA */ public static final int XK_Omacron = 0x03d2; /* U+014C LATIN CAPITAL LETTER O WITH MACRON */ public static final int XK_Kcedilla = 0x03d3; /* U+0136 LATIN CAPITAL LETTER K WITH CEDILLA */ public static final int XK_Uogonek = 0x03d9; /* U+0172 LATIN CAPITAL LETTER U WITH OGONEK */ public static final int XK_Utilde = 0x03dd; /* U+0168 LATIN CAPITAL LETTER U WITH TILDE */ public static final int XK_Umacron = 0x03de; /* U+016A LATIN CAPITAL LETTER U WITH MACRON */ public static final int XK_amacron = 0x03e0; /* U+0101 LATIN SMALL LETTER A WITH MACRON */ public static final int XK_iogonek = 0x03e7; /* U+012F LATIN SMALL LETTER I WITH OGONEK */ public static final int XK_eabovedot = 0x03ec; /* U+0117 LATIN SMALL LETTER E WITH DOT ABOVE */ public static final int XK_imacron = 0x03ef; /* U+012B LATIN SMALL LETTER I WITH MACRON */ public static final int XK_ncedilla = 0x03f1; /* U+0146 LATIN SMALL LETTER N WITH CEDILLA */ public static final int XK_omacron = 0x03f2; /* U+014D LATIN SMALL LETTER O WITH MACRON */ public static final int XK_kcedilla = 0x03f3; /* U+0137 LATIN SMALL LETTER K WITH CEDILLA */ public static final int XK_uogonek = 0x03f9; /* U+0173 LATIN SMALL LETTER U WITH OGONEK */ public static final int XK_utilde = 0x03fd; /* U+0169 LATIN SMALL LETTER U WITH TILDE */ public static final int XK_umacron = 0x03fe; /* U+016B LATIN SMALL LETTER U WITH MACRON */ /* * Latin 8 */ public static final int XK_Babovedot = 0x1001e02; /* U+1E02 LATIN CAPITAL LETTER B WITH DOT ABOVE */ public static final int XK_babovedot = 0x1001e03; /* U+1E03 LATIN SMALL LETTER B WITH DOT ABOVE */ public static final int XK_Dabovedot = 0x1001e0a; /* U+1E0A LATIN CAPITAL LETTER D WITH DOT ABOVE */ public static final int XK_Wgrave = 0x1001e80; /* U+1E80 LATIN CAPITAL LETTER W WITH GRAVE */ public static final int XK_Wacute = 0x1001e82; /* U+1E82 LATIN CAPITAL LETTER W WITH ACUTE */ public static final int XK_dabovedot = 0x1001e0b; /* U+1E0B LATIN SMALL LETTER D WITH DOT ABOVE */ public static final int XK_Ygrave = 0x1001ef2; /* U+1EF2 LATIN CAPITAL LETTER Y WITH GRAVE */ public static final int XK_Fabovedot = 0x1001e1e; /* U+1E1E LATIN CAPITAL LETTER F WITH DOT ABOVE */ public static final int XK_fabovedot = 0x1001e1f; /* U+1E1F LATIN SMALL LETTER F WITH DOT ABOVE */ public static final int XK_Mabovedot = 0x1001e40; /* U+1E40 LATIN CAPITAL LETTER M WITH DOT ABOVE */ public static final int XK_mabovedot = 0x1001e41; /* U+1E41 LATIN SMALL LETTER M WITH DOT ABOVE */ public static final int XK_Pabovedot = 0x1001e56; /* U+1E56 LATIN CAPITAL LETTER P WITH DOT ABOVE */ public static final int XK_wgrave = 0x1001e81; /* U+1E81 LATIN SMALL LETTER W WITH GRAVE */ public static final int XK_pabovedot = 0x1001e57; /* U+1E57 LATIN SMALL LETTER P WITH DOT ABOVE */ public static final int XK_wacute = 0x1001e83; /* U+1E83 LATIN SMALL LETTER W WITH ACUTE */ public static final int XK_Sabovedot = 0x1001e60; /* U+1E60 LATIN CAPITAL LETTER S WITH DOT ABOVE */ public static final int XK_ygrave = 0x1001ef3; /* U+1EF3 LATIN SMALL LETTER Y WITH GRAVE */ public static final int XK_Wdiaeresis = 0x1001e84; /* U+1E84 LATIN CAPITAL LETTER W WITH DIAERESIS */ public static final int XK_wdiaeresis = 0x1001e85; /* U+1E85 LATIN SMALL LETTER W WITH DIAERESIS */ public static final int XK_sabovedot = 0x1001e61; /* U+1E61 LATIN SMALL LETTER S WITH DOT ABOVE */ public static final int XK_Wcircumflex = 0x1000174; /* U+0174 LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ public static final int XK_Tabovedot = 0x1001e6a; /* U+1E6A LATIN CAPITAL LETTER T WITH DOT ABOVE */ public static final int XK_Ycircumflex = 0x1000176; /* U+0176 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ public static final int XK_wcircumflex = 0x1000175; /* U+0175 LATIN SMALL LETTER W WITH CIRCUMFLEX */ public static final int XK_tabovedot = 0x1001e6b; /* U+1E6B LATIN SMALL LETTER T WITH DOT ABOVE */ public static final int XK_ycircumflex = 0x1000177; /* U+0177 LATIN SMALL LETTER Y WITH CIRCUMFLEX */ /* * Latin 9 * Byte 3 = 0x13 */ public static final int XK_OE = 0x13bc; /* U+0152 LATIN CAPITAL LIGATURE OE */ public static final int XK_oe = 0x13bd; /* U+0153 LATIN SMALL LIGATURE OE */ public static final int XK_Ydiaeresis = 0x13be; /* U+0178 LATIN CAPITAL LETTER Y WITH DIAERESIS */ /* * Katakana * Byte 3 = 4 */ public static final int XK_overline = 0x047e; /* U+203E OVERLINE */ public static final int XK_kana_fullstop = 0x04a1; /* U+3002 IDEOGRAPHIC FULL STOP */ public static final int XK_kana_openingbracket = 0x04a2; /* U+300C LEFT CORNER BRACKET */ public static final int XK_kana_closingbracket = 0x04a3; /* U+300D RIGHT CORNER BRACKET */ public static final int XK_kana_comma = 0x04a4; /* U+3001 IDEOGRAPHIC COMMA */ public static final int XK_kana_conjunctive = 0x04a5; /* U+30FB KATAKANA MIDDLE DOT */ public static final int XK_kana_middledot = 0x04a5; /* deprecated */ public static final int XK_kana_WO = 0x04a6; /* U+30F2 KATAKANA LETTER WO */ public static final int XK_kana_a = 0x04a7; /* U+30A1 KATAKANA LETTER SMALL A */ public static final int XK_kana_i = 0x04a8; /* U+30A3 KATAKANA LETTER SMALL I */ public static final int XK_kana_u = 0x04a9; /* U+30A5 KATAKANA LETTER SMALL U */ public static final int XK_kana_e = 0x04aa; /* U+30A7 KATAKANA LETTER SMALL E */ public static final int XK_kana_o = 0x04ab; /* U+30A9 KATAKANA LETTER SMALL O */ public static final int XK_kana_ya = 0x04ac; /* U+30E3 KATAKANA LETTER SMALL YA */ public static final int XK_kana_yu = 0x04ad; /* U+30E5 KATAKANA LETTER SMALL YU */ public static final int XK_kana_yo = 0x04ae; /* U+30E7 KATAKANA LETTER SMALL YO */ public static final int XK_kana_tsu = 0x04af; /* U+30C3 KATAKANA LETTER SMALL TU */ public static final int XK_kana_tu = 0x04af; /* deprecated */ public static final int XK_prolongedsound = 0x04b0; /* U+30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK */ public static final int XK_kana_A = 0x04b1; /* U+30A2 KATAKANA LETTER A */ public static final int XK_kana_I = 0x04b2; /* U+30A4 KATAKANA LETTER I */ public static final int XK_kana_U = 0x04b3; /* U+30A6 KATAKANA LETTER U */ public static final int XK_kana_E = 0x04b4; /* U+30A8 KATAKANA LETTER E */ public static final int XK_kana_O = 0x04b5; /* U+30AA KATAKANA LETTER O */ public static final int XK_kana_KA = 0x04b6; /* U+30AB KATAKANA LETTER KA */ public static final int XK_kana_KI = 0x04b7; /* U+30AD KATAKANA LETTER KI */ public static final int XK_kana_KU = 0x04b8; /* U+30AF KATAKANA LETTER KU */ public static final int XK_kana_KE = 0x04b9; /* U+30B1 KATAKANA LETTER KE */ public static final int XK_kana_KO = 0x04ba; /* U+30B3 KATAKANA LETTER KO */ public static final int XK_kana_SA = 0x04bb; /* U+30B5 KATAKANA LETTER SA */ public static final int XK_kana_SHI = 0x04bc; /* U+30B7 KATAKANA LETTER SI */ public static final int XK_kana_SU = 0x04bd; /* U+30B9 KATAKANA LETTER SU */ public static final int XK_kana_SE = 0x04be; /* U+30BB KATAKANA LETTER SE */ public static final int XK_kana_SO = 0x04bf; /* U+30BD KATAKANA LETTER SO */ public static final int XK_kana_TA = 0x04c0; /* U+30BF KATAKANA LETTER TA */ public static final int XK_kana_CHI = 0x04c1; /* U+30C1 KATAKANA LETTER TI */ public static final int XK_kana_TI = 0x04c1; /* deprecated */ public static final int XK_kana_TSU = 0x04c2; /* U+30C4 KATAKANA LETTER TU */ public static final int XK_kana_TU = 0x04c2; /* deprecated */ public static final int XK_kana_TE = 0x04c3; /* U+30C6 KATAKANA LETTER TE */ public static final int XK_kana_TO = 0x04c4; /* U+30C8 KATAKANA LETTER TO */ public static final int XK_kana_NA = 0x04c5; /* U+30CA KATAKANA LETTER NA */ public static final int XK_kana_NI = 0x04c6; /* U+30CB KATAKANA LETTER NI */ public static final int XK_kana_NU = 0x04c7; /* U+30CC KATAKANA LETTER NU */ public static final int XK_kana_NE = 0x04c8; /* U+30CD KATAKANA LETTER NE */ public static final int XK_kana_NO = 0x04c9; /* U+30CE KATAKANA LETTER NO */ public static final int XK_kana_HA = 0x04ca; /* U+30CF KATAKANA LETTER HA */ public static final int XK_kana_HI = 0x04cb; /* U+30D2 KATAKANA LETTER HI */ public static final int XK_kana_FU = 0x04cc; /* U+30D5 KATAKANA LETTER HU */ public static final int XK_kana_HU = 0x04cc; /* deprecated */ public static final int XK_kana_HE = 0x04cd; /* U+30D8 KATAKANA LETTER HE */ public static final int XK_kana_HO = 0x04ce; /* U+30DB KATAKANA LETTER HO */ public static final int XK_kana_MA = 0x04cf; /* U+30DE KATAKANA LETTER MA */ public static final int XK_kana_MI = 0x04d0; /* U+30DF KATAKANA LETTER MI */ public static final int XK_kana_MU = 0x04d1; /* U+30E0 KATAKANA LETTER MU */ public static final int XK_kana_ME = 0x04d2; /* U+30E1 KATAKANA LETTER ME */ public static final int XK_kana_MO = 0x04d3; /* U+30E2 KATAKANA LETTER MO */ public static final int XK_kana_YA = 0x04d4; /* U+30E4 KATAKANA LETTER YA */ public static final int XK_kana_YU = 0x04d5; /* U+30E6 KATAKANA LETTER YU */ public static final int XK_kana_YO = 0x04d6; /* U+30E8 KATAKANA LETTER YO */ public static final int XK_kana_RA = 0x04d7; /* U+30E9 KATAKANA LETTER RA */ public static final int XK_kana_RI = 0x04d8; /* U+30EA KATAKANA LETTER RI */ public static final int XK_kana_RU = 0x04d9; /* U+30EB KATAKANA LETTER RU */ public static final int XK_kana_RE = 0x04da; /* U+30EC KATAKANA LETTER RE */ public static final int XK_kana_RO = 0x04db; /* U+30ED KATAKANA LETTER RO */ public static final int XK_kana_WA = 0x04dc; /* U+30EF KATAKANA LETTER WA */ public static final int XK_kana_N = 0x04dd; /* U+30F3 KATAKANA LETTER N */ public static final int XK_voicedsound = 0x04de; /* U+309B KATAKANA-HIRAGANA VOICED SOUND MARK */ public static final int XK_semivoicedsound = 0x04df; /* U+309C KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ public static final int XK_kana_switch = 0xff7e; /* Alias for mode_switch */ /* * Arabic * Byte 3 = 5 */ public static final int XK_Farsi_0 = 0x10006f0; /* U+06F0 EXTENDED ARABIC-INDIC DIGIT ZERO */ public static final int XK_Farsi_1 = 0x10006f1; /* U+06F1 EXTENDED ARABIC-INDIC DIGIT ONE */ public static final int XK_Farsi_2 = 0x10006f2; /* U+06F2 EXTENDED ARABIC-INDIC DIGIT TWO */ public static final int XK_Farsi_3 = 0x10006f3; /* U+06F3 EXTENDED ARABIC-INDIC DIGIT THREE */ public static final int XK_Farsi_4 = 0x10006f4; /* U+06F4 EXTENDED ARABIC-INDIC DIGIT FOUR */ public static final int XK_Farsi_5 = 0x10006f5; /* U+06F5 EXTENDED ARABIC-INDIC DIGIT FIVE */ public static final int XK_Farsi_6 = 0x10006f6; /* U+06F6 EXTENDED ARABIC-INDIC DIGIT SIX */ public static final int XK_Farsi_7 = 0x10006f7; /* U+06F7 EXTENDED ARABIC-INDIC DIGIT SEVEN */ public static final int XK_Farsi_8 = 0x10006f8; /* U+06F8 EXTENDED ARABIC-INDIC DIGIT EIGHT */ public static final int XK_Farsi_9 = 0x10006f9; /* U+06F9 EXTENDED ARABIC-INDIC DIGIT NINE */ public static final int XK_Arabic_percent = 0x100066a; /* U+066A ARABIC PERCENT SIGN */ public static final int XK_Arabic_superscript_alef = 0x1000670; /* U+0670 ARABIC LETTER SUPERSCRIPT ALEF */ public static final int XK_Arabic_tteh = 0x1000679; /* U+0679 ARABIC LETTER TTEH */ public static final int XK_Arabic_peh = 0x100067e; /* U+067E ARABIC LETTER PEH */ public static final int XK_Arabic_tcheh = 0x1000686; /* U+0686 ARABIC LETTER TCHEH */ public static final int XK_Arabic_ddal = 0x1000688; /* U+0688 ARABIC LETTER DDAL */ public static final int XK_Arabic_rreh = 0x1000691; /* U+0691 ARABIC LETTER RREH */ public static final int XK_Arabic_comma = 0x05ac; /* U+060C ARABIC COMMA */ public static final int XK_Arabic_fullstop = 0x10006d4; /* U+06D4 ARABIC FULL STOP */ public static final int XK_Arabic_0 = 0x1000660; /* U+0660 ARABIC-INDIC DIGIT ZERO */ public static final int XK_Arabic_1 = 0x1000661; /* U+0661 ARABIC-INDIC DIGIT ONE */ public static final int XK_Arabic_2 = 0x1000662; /* U+0662 ARABIC-INDIC DIGIT TWO */ public static final int XK_Arabic_3 = 0x1000663; /* U+0663 ARABIC-INDIC DIGIT THREE */ public static final int XK_Arabic_4 = 0x1000664; /* U+0664 ARABIC-INDIC DIGIT FOUR */ public static final int XK_Arabic_5 = 0x1000665; /* U+0665 ARABIC-INDIC DIGIT FIVE */ public static final int XK_Arabic_6 = 0x1000666; /* U+0666 ARABIC-INDIC DIGIT SIX */ public static final int XK_Arabic_7 = 0x1000667; /* U+0667 ARABIC-INDIC DIGIT SEVEN */ public static final int XK_Arabic_8 = 0x1000668; /* U+0668 ARABIC-INDIC DIGIT EIGHT */ public static final int XK_Arabic_9 = 0x1000669; /* U+0669 ARABIC-INDIC DIGIT NINE */ public static final int XK_Arabic_semicolon = 0x05bb; /* U+061B ARABIC SEMICOLON */ public static final int XK_Arabic_question_mark = 0x05bf; /* U+061F ARABIC QUESTION MARK */ public static final int XK_Arabic_hamza = 0x05c1; /* U+0621 ARABIC LETTER HAMZA */ public static final int XK_Arabic_maddaonalef = 0x05c2; /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */ public static final int XK_Arabic_hamzaonalef = 0x05c3; /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */ public static final int XK_Arabic_hamzaonwaw = 0x05c4; /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */ public static final int XK_Arabic_hamzaunderalef = 0x05c5; /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */ public static final int XK_Arabic_hamzaonyeh = 0x05c6; /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */ public static final int XK_Arabic_alef = 0x05c7; /* U+0627 ARABIC LETTER ALEF */ public static final int XK_Arabic_beh = 0x05c8; /* U+0628 ARABIC LETTER BEH */ public static final int XK_Arabic_tehmarbuta = 0x05c9; /* U+0629 ARABIC LETTER TEH MARBUTA */ public static final int XK_Arabic_teh = 0x05ca; /* U+062A ARABIC LETTER TEH */ public static final int XK_Arabic_theh = 0x05cb; /* U+062B ARABIC LETTER THEH */ public static final int XK_Arabic_jeem = 0x05cc; /* U+062C ARABIC LETTER JEEM */ public static final int XK_Arabic_hah = 0x05cd; /* U+062D ARABIC LETTER HAH */ public static final int XK_Arabic_khah = 0x05ce; /* U+062E ARABIC LETTER KHAH */ public static final int XK_Arabic_dal = 0x05cf; /* U+062F ARABIC LETTER DAL */ public static final int XK_Arabic_thal = 0x05d0; /* U+0630 ARABIC LETTER THAL */ public static final int XK_Arabic_ra = 0x05d1; /* U+0631 ARABIC LETTER REH */ public static final int XK_Arabic_zain = 0x05d2; /* U+0632 ARABIC LETTER ZAIN */ public static final int XK_Arabic_seen = 0x05d3; /* U+0633 ARABIC LETTER SEEN */ public static final int XK_Arabic_sheen = 0x05d4; /* U+0634 ARABIC LETTER SHEEN */ public static final int XK_Arabic_sad = 0x05d5; /* U+0635 ARABIC LETTER SAD */ public static final int XK_Arabic_dad = 0x05d6; /* U+0636 ARABIC LETTER DAD */ public static final int XK_Arabic_tah = 0x05d7; /* U+0637 ARABIC LETTER TAH */ public static final int XK_Arabic_zah = 0x05d8; /* U+0638 ARABIC LETTER ZAH */ public static final int XK_Arabic_ain = 0x05d9; /* U+0639 ARABIC LETTER AIN */ public static final int XK_Arabic_ghain = 0x05da; /* U+063A ARABIC LETTER GHAIN */ public static final int XK_Arabic_tatweel = 0x05e0; /* U+0640 ARABIC TATWEEL */ public static final int XK_Arabic_feh = 0x05e1; /* U+0641 ARABIC LETTER FEH */ public static final int XK_Arabic_qaf = 0x05e2; /* U+0642 ARABIC LETTER QAF */ public static final int XK_Arabic_kaf = 0x05e3; /* U+0643 ARABIC LETTER KAF */ public static final int XK_Arabic_lam = 0x05e4; /* U+0644 ARABIC LETTER LAM */ public static final int XK_Arabic_meem = 0x05e5; /* U+0645 ARABIC LETTER MEEM */ public static final int XK_Arabic_noon = 0x05e6; /* U+0646 ARABIC LETTER NOON */ public static final int XK_Arabic_ha = 0x05e7; /* U+0647 ARABIC LETTER HEH */ public static final int XK_Arabic_heh = 0x05e7; /* deprecated */ public static final int XK_Arabic_waw = 0x05e8; /* U+0648 ARABIC LETTER WAW */ public static final int XK_Arabic_alefmaksura = 0x05e9; /* U+0649 ARABIC LETTER ALEF MAKSURA */ public static final int XK_Arabic_yeh = 0x05ea; /* U+064A ARABIC LETTER YEH */ public static final int XK_Arabic_fathatan = 0x05eb; /* U+064B ARABIC FATHATAN */ public static final int XK_Arabic_dammatan = 0x05ec; /* U+064C ARABIC DAMMATAN */ public static final int XK_Arabic_kasratan = 0x05ed; /* U+064D ARABIC KASRATAN */ public static final int XK_Arabic_fatha = 0x05ee; /* U+064E ARABIC FATHA */ public static final int XK_Arabic_damma = 0x05ef; /* U+064F ARABIC DAMMA */ public static final int XK_Arabic_kasra = 0x05f0; /* U+0650 ARABIC KASRA */ public static final int XK_Arabic_shadda = 0x05f1; /* U+0651 ARABIC SHADDA */ public static final int XK_Arabic_sukun = 0x05f2; /* U+0652 ARABIC SUKUN */ public static final int XK_Arabic_madda_above = 0x1000653; /* U+0653 ARABIC MADDAH ABOVE */ public static final int XK_Arabic_hamza_above = 0x1000654; /* U+0654 ARABIC HAMZA ABOVE */ public static final int XK_Arabic_hamza_below = 0x1000655; /* U+0655 ARABIC HAMZA BELOW */ public static final int XK_Arabic_jeh = 0x1000698; /* U+0698 ARABIC LETTER JEH */ public static final int XK_Arabic_veh = 0x10006a4; /* U+06A4 ARABIC LETTER VEH */ public static final int XK_Arabic_keheh = 0x10006a9; /* U+06A9 ARABIC LETTER KEHEH */ public static final int XK_Arabic_gaf = 0x10006af; /* U+06AF ARABIC LETTER GAF */ public static final int XK_Arabic_noon_ghunna = 0x10006ba; /* U+06BA ARABIC LETTER NOON GHUNNA */ public static final int XK_Arabic_heh_doachashmee = 0x10006be; /* U+06BE ARABIC LETTER HEH DOACHASHMEE */ public static final int XK_Farsi_yeh = 0x10006cc; /* U+06CC ARABIC LETTER FARSI YEH */ public static final int XK_Arabic_farsi_yeh = 0x10006cc; /* U+06CC ARABIC LETTER FARSI YEH */ public static final int XK_Arabic_yeh_baree = 0x10006d2; /* U+06D2 ARABIC LETTER YEH BARREE */ public static final int XK_Arabic_heh_goal = 0x10006c1; /* U+06C1 ARABIC LETTER HEH GOAL */ public static final int XK_Arabic_switch = 0xff7e; /* Alias for mode_switch */ /* * Cyrillic * Byte 3 = 6 */ public static final int XK_Cyrillic_GHE_bar = 0x1000492; /* U+0492 CYRILLIC CAPITAL LETTER GHE WITH STROKE */ public static final int XK_Cyrillic_ghe_bar = 0x1000493; /* U+0493 CYRILLIC SMALL LETTER GHE WITH STROKE */ public static final int XK_Cyrillic_ZHE_descender = 0x1000496; /* U+0496 CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER */ public static final int XK_Cyrillic_zhe_descender = 0x1000497; /* U+0497 CYRILLIC SMALL LETTER ZHE WITH DESCENDER */ public static final int XK_Cyrillic_KA_descender = 0x100049a; /* U+049A CYRILLIC CAPITAL LETTER KA WITH DESCENDER */ public static final int XK_Cyrillic_ka_descender = 0x100049b; /* U+049B CYRILLIC SMALL LETTER KA WITH DESCENDER */ public static final int XK_Cyrillic_KA_vertstroke = 0x100049c; /* U+049C CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE */ public static final int XK_Cyrillic_ka_vertstroke = 0x100049d; /* U+049D CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE */ public static final int XK_Cyrillic_EN_descender = 0x10004a2; /* U+04A2 CYRILLIC CAPITAL LETTER EN WITH DESCENDER */ public static final int XK_Cyrillic_en_descender = 0x10004a3; /* U+04A3 CYRILLIC SMALL LETTER EN WITH DESCENDER */ public static final int XK_Cyrillic_U_straight = 0x10004ae; /* U+04AE CYRILLIC CAPITAL LETTER STRAIGHT U */ public static final int XK_Cyrillic_u_straight = 0x10004af; /* U+04AF CYRILLIC SMALL LETTER STRAIGHT U */ public static final int XK_Cyrillic_U_straight_bar = 0x10004b0; /* U+04B0 CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE */ public static final int XK_Cyrillic_u_straight_bar = 0x10004b1; /* U+04B1 CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE */ public static final int XK_Cyrillic_HA_descender = 0x10004b2; /* U+04B2 CYRILLIC CAPITAL LETTER HA WITH DESCENDER */ public static final int XK_Cyrillic_ha_descender = 0x10004b3; /* U+04B3 CYRILLIC SMALL LETTER HA WITH DESCENDER */ public static final int XK_Cyrillic_CHE_descender = 0x10004b6; /* U+04B6 CYRILLIC CAPITAL LETTER CHE WITH DESCENDER */ public static final int XK_Cyrillic_che_descender = 0x10004b7; /* U+04B7 CYRILLIC SMALL LETTER CHE WITH DESCENDER */ public static final int XK_Cyrillic_CHE_vertstroke = 0x10004b8; /* U+04B8 CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE */ public static final int XK_Cyrillic_che_vertstroke = 0x10004b9; /* U+04B9 CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE */ public static final int XK_Cyrillic_SHHA = 0x10004ba; /* U+04BA CYRILLIC CAPITAL LETTER SHHA */ public static final int XK_Cyrillic_shha = 0x10004bb; /* U+04BB CYRILLIC SMALL LETTER SHHA */ public static final int XK_Cyrillic_SCHWA = 0x10004d8; /* U+04D8 CYRILLIC CAPITAL LETTER SCHWA */ public static final int XK_Cyrillic_schwa = 0x10004d9; /* U+04D9 CYRILLIC SMALL LETTER SCHWA */ public static final int XK_Cyrillic_I_macron = 0x10004e2; /* U+04E2 CYRILLIC CAPITAL LETTER I WITH MACRON */ public static final int XK_Cyrillic_i_macron = 0x10004e3; /* U+04E3 CYRILLIC SMALL LETTER I WITH MACRON */ public static final int XK_Cyrillic_O_bar = 0x10004e8; /* U+04E8 CYRILLIC CAPITAL LETTER BARRED O */ public static final int XK_Cyrillic_o_bar = 0x10004e9; /* U+04E9 CYRILLIC SMALL LETTER BARRED O */ public static final int XK_Cyrillic_U_macron = 0x10004ee; /* U+04EE CYRILLIC CAPITAL LETTER U WITH MACRON */ public static final int XK_Cyrillic_u_macron = 0x10004ef; /* U+04EF CYRILLIC SMALL LETTER U WITH MACRON */ public static final int XK_Serbian_dje = 0x06a1; /* U+0452 CYRILLIC SMALL LETTER DJE */ public static final int XK_Macedonia_gje = 0x06a2; /* U+0453 CYRILLIC SMALL LETTER GJE */ public static final int XK_Cyrillic_io = 0x06a3; /* U+0451 CYRILLIC SMALL LETTER IO */ public static final int XK_Ukrainian_ie = 0x06a4; /* U+0454 CYRILLIC SMALL LETTER UKRAINIAN IE */ public static final int XK_Ukranian_je = 0x06a4; /* deprecated */ public static final int XK_Macedonia_dse = 0x06a5; /* U+0455 CYRILLIC SMALL LETTER DZE */ public static final int XK_Ukrainian_i = 0x06a6; /* U+0456 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ public static final int XK_Ukranian_i = 0x06a6; /* deprecated */ public static final int XK_Ukrainian_yi = 0x06a7; /* U+0457 CYRILLIC SMALL LETTER YI */ public static final int XK_Ukranian_yi = 0x06a7; /* deprecated */ public static final int XK_Cyrillic_je = 0x06a8; /* U+0458 CYRILLIC SMALL LETTER JE */ public static final int XK_Serbian_je = 0x06a8; /* deprecated */ public static final int XK_Cyrillic_lje = 0x06a9; /* U+0459 CYRILLIC SMALL LETTER LJE */ public static final int XK_Serbian_lje = 0x06a9; /* deprecated */ public static final int XK_Cyrillic_nje = 0x06aa; /* U+045A CYRILLIC SMALL LETTER NJE */ public static final int XK_Serbian_nje = 0x06aa; /* deprecated */ public static final int XK_Serbian_tshe = 0x06ab; /* U+045B CYRILLIC SMALL LETTER TSHE */ public static final int XK_Macedonia_kje = 0x06ac; /* U+045C CYRILLIC SMALL LETTER KJE */ public static final int XK_Ukrainian_ghe_with_upturn = 0x06ad; /* U+0491 CYRILLIC SMALL LETTER GHE WITH UPTURN */ public static final int XK_Byelorussian_shortu = 0x06ae; /* U+045E CYRILLIC SMALL LETTER SHORT U */ public static final int XK_Cyrillic_dzhe = 0x06af; /* U+045F CYRILLIC SMALL LETTER DZHE */ public static final int XK_Serbian_dze = 0x06af; /* deprecated */ public static final int XK_numerosign = 0x06b0; /* U+2116 NUMERO SIGN */ public static final int XK_Serbian_DJE = 0x06b1; /* U+0402 CYRILLIC CAPITAL LETTER DJE */ public static final int XK_Macedonia_GJE = 0x06b2; /* U+0403 CYRILLIC CAPITAL LETTER GJE */ public static final int XK_Cyrillic_IO = 0x06b3; /* U+0401 CYRILLIC CAPITAL LETTER IO */ public static final int XK_Ukrainian_IE = 0x06b4; /* U+0404 CYRILLIC CAPITAL LETTER UKRAINIAN IE */ public static final int XK_Ukranian_JE = 0x06b4; /* deprecated */ public static final int XK_Macedonia_DSE = 0x06b5; /* U+0405 CYRILLIC CAPITAL LETTER DZE */ public static final int XK_Ukrainian_I = 0x06b6; /* U+0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ public static final int XK_Ukranian_I = 0x06b6; /* deprecated */ public static final int XK_Ukrainian_YI = 0x06b7; /* U+0407 CYRILLIC CAPITAL LETTER YI */ public static final int XK_Ukranian_YI = 0x06b7; /* deprecated */ public static final int XK_Cyrillic_JE = 0x06b8; /* U+0408 CYRILLIC CAPITAL LETTER JE */ public static final int XK_Serbian_JE = 0x06b8; /* deprecated */ public static final int XK_Cyrillic_LJE = 0x06b9; /* U+0409 CYRILLIC CAPITAL LETTER LJE */ public static final int XK_Serbian_LJE = 0x06b9; /* deprecated */ public static final int XK_Cyrillic_NJE = 0x06ba; /* U+040A CYRILLIC CAPITAL LETTER NJE */ public static final int XK_Serbian_NJE = 0x06ba; /* deprecated */ public static final int XK_Serbian_TSHE = 0x06bb; /* U+040B CYRILLIC CAPITAL LETTER TSHE */ public static final int XK_Macedonia_KJE = 0x06bc; /* U+040C CYRILLIC CAPITAL LETTER KJE */ public static final int XK_Ukrainian_GHE_WITH_UPTURN = 0x06bd; /* U+0490 CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ public static final int XK_Byelorussian_SHORTU = 0x06be; /* U+040E CYRILLIC CAPITAL LETTER SHORT U */ public static final int XK_Cyrillic_DZHE = 0x06bf; /* U+040F CYRILLIC CAPITAL LETTER DZHE */ public static final int XK_Serbian_DZE = 0x06bf; /* deprecated */ public static final int XK_Cyrillic_yu = 0x06c0; /* U+044E CYRILLIC SMALL LETTER YU */ public static final int XK_Cyrillic_a = 0x06c1; /* U+0430 CYRILLIC SMALL LETTER A */ public static final int XK_Cyrillic_be = 0x06c2; /* U+0431 CYRILLIC SMALL LETTER BE */ public static final int XK_Cyrillic_tse = 0x06c3; /* U+0446 CYRILLIC SMALL LETTER TSE */ public static final int XK_Cyrillic_de = 0x06c4; /* U+0434 CYRILLIC SMALL LETTER DE */ public static final int XK_Cyrillic_ie = 0x06c5; /* U+0435 CYRILLIC SMALL LETTER IE */ public static final int XK_Cyrillic_ef = 0x06c6; /* U+0444 CYRILLIC SMALL LETTER EF */ public static final int XK_Cyrillic_ghe = 0x06c7; /* U+0433 CYRILLIC SMALL LETTER GHE */ public static final int XK_Cyrillic_ha = 0x06c8; /* U+0445 CYRILLIC SMALL LETTER HA */ public static final int XK_Cyrillic_i = 0x06c9; /* U+0438 CYRILLIC SMALL LETTER I */ public static final int XK_Cyrillic_shorti = 0x06ca; /* U+0439 CYRILLIC SMALL LETTER SHORT I */ public static final int XK_Cyrillic_ka = 0x06cb; /* U+043A CYRILLIC SMALL LETTER KA */ public static final int XK_Cyrillic_el = 0x06cc; /* U+043B CYRILLIC SMALL LETTER EL */ public static final int XK_Cyrillic_em = 0x06cd; /* U+043C CYRILLIC SMALL LETTER EM */ public static final int XK_Cyrillic_en = 0x06ce; /* U+043D CYRILLIC SMALL LETTER EN */ public static final int XK_Cyrillic_o = 0x06cf; /* U+043E CYRILLIC SMALL LETTER O */ public static final int XK_Cyrillic_pe = 0x06d0; /* U+043F CYRILLIC SMALL LETTER PE */ public static final int XK_Cyrillic_ya = 0x06d1; /* U+044F CYRILLIC SMALL LETTER YA */ public static final int XK_Cyrillic_er = 0x06d2; /* U+0440 CYRILLIC SMALL LETTER ER */ public static final int XK_Cyrillic_es = 0x06d3; /* U+0441 CYRILLIC SMALL LETTER ES */ public static final int XK_Cyrillic_te = 0x06d4; /* U+0442 CYRILLIC SMALL LETTER TE */ public static final int XK_Cyrillic_u = 0x06d5; /* U+0443 CYRILLIC SMALL LETTER U */ public static final int XK_Cyrillic_zhe = 0x06d6; /* U+0436 CYRILLIC SMALL LETTER ZHE */ public static final int XK_Cyrillic_ve = 0x06d7; /* U+0432 CYRILLIC SMALL LETTER VE */ public static final int XK_Cyrillic_softsign = 0x06d8; /* U+044C CYRILLIC SMALL LETTER SOFT SIGN */ public static final int XK_Cyrillic_yeru = 0x06d9; /* U+044B CYRILLIC SMALL LETTER YERU */ public static final int XK_Cyrillic_ze = 0x06da; /* U+0437 CYRILLIC SMALL LETTER ZE */ public static final int XK_Cyrillic_sha = 0x06db; /* U+0448 CYRILLIC SMALL LETTER SHA */ public static final int XK_Cyrillic_e = 0x06dc; /* U+044D CYRILLIC SMALL LETTER E */ public static final int XK_Cyrillic_shcha = 0x06dd; /* U+0449 CYRILLIC SMALL LETTER SHCHA */ public static final int XK_Cyrillic_che = 0x06de; /* U+0447 CYRILLIC SMALL LETTER CHE */ public static final int XK_Cyrillic_hardsign = 0x06df; /* U+044A CYRILLIC SMALL LETTER HARD SIGN */ public static final int XK_Cyrillic_YU = 0x06e0; /* U+042E CYRILLIC CAPITAL LETTER YU */ public static final int XK_Cyrillic_A = 0x06e1; /* U+0410 CYRILLIC CAPITAL LETTER A */ public static final int XK_Cyrillic_BE = 0x06e2; /* U+0411 CYRILLIC CAPITAL LETTER BE */ public static final int XK_Cyrillic_TSE = 0x06e3; /* U+0426 CYRILLIC CAPITAL LETTER TSE */ public static final int XK_Cyrillic_DE = 0x06e4; /* U+0414 CYRILLIC CAPITAL LETTER DE */ public static final int XK_Cyrillic_IE = 0x06e5; /* U+0415 CYRILLIC CAPITAL LETTER IE */ public static final int XK_Cyrillic_EF = 0x06e6; /* U+0424 CYRILLIC CAPITAL LETTER EF */ public static final int XK_Cyrillic_GHE = 0x06e7; /* U+0413 CYRILLIC CAPITAL LETTER GHE */ public static final int XK_Cyrillic_HA = 0x06e8; /* U+0425 CYRILLIC CAPITAL LETTER HA */ public static final int XK_Cyrillic_I = 0x06e9; /* U+0418 CYRILLIC CAPITAL LETTER I */ public static final int XK_Cyrillic_SHORTI = 0x06ea; /* U+0419 CYRILLIC CAPITAL LETTER SHORT I */ public static final int XK_Cyrillic_KA = 0x06eb; /* U+041A CYRILLIC CAPITAL LETTER KA */ public static final int XK_Cyrillic_EL = 0x06ec; /* U+041B CYRILLIC CAPITAL LETTER EL */ public static final int XK_Cyrillic_EM = 0x06ed; /* U+041C CYRILLIC CAPITAL LETTER EM */ public static final int XK_Cyrillic_EN = 0x06ee; /* U+041D CYRILLIC CAPITAL LETTER EN */ public static final int XK_Cyrillic_O = 0x06ef; /* U+041E CYRILLIC CAPITAL LETTER O */ public static final int XK_Cyrillic_PE = 0x06f0; /* U+041F CYRILLIC CAPITAL LETTER PE */ public static final int XK_Cyrillic_YA = 0x06f1; /* U+042F CYRILLIC CAPITAL LETTER YA */ public static final int XK_Cyrillic_ER = 0x06f2; /* U+0420 CYRILLIC CAPITAL LETTER ER */ public static final int XK_Cyrillic_ES = 0x06f3; /* U+0421 CYRILLIC CAPITAL LETTER ES */ public static final int XK_Cyrillic_TE = 0x06f4; /* U+0422 CYRILLIC CAPITAL LETTER TE */ public static final int XK_Cyrillic_U = 0x06f5; /* U+0423 CYRILLIC CAPITAL LETTER U */ public static final int XK_Cyrillic_ZHE = 0x06f6; /* U+0416 CYRILLIC CAPITAL LETTER ZHE */ public static final int XK_Cyrillic_VE = 0x06f7; /* U+0412 CYRILLIC CAPITAL LETTER VE */ public static final int XK_Cyrillic_SOFTSIGN = 0x06f8; /* U+042C CYRILLIC CAPITAL LETTER SOFT SIGN */ public static final int XK_Cyrillic_YERU = 0x06f9; /* U+042B CYRILLIC CAPITAL LETTER YERU */ public static final int XK_Cyrillic_ZE = 0x06fa; /* U+0417 CYRILLIC CAPITAL LETTER ZE */ public static final int XK_Cyrillic_SHA = 0x06fb; /* U+0428 CYRILLIC CAPITAL LETTER SHA */ public static final int XK_Cyrillic_E = 0x06fc; /* U+042D CYRILLIC CAPITAL LETTER E */ public static final int XK_Cyrillic_SHCHA = 0x06fd; /* U+0429 CYRILLIC CAPITAL LETTER SHCHA */ public static final int XK_Cyrillic_CHE = 0x06fe; /* U+0427 CYRILLIC CAPITAL LETTER CHE */ public static final int XK_Cyrillic_HARDSIGN = 0x06ff; /* U+042A CYRILLIC CAPITAL LETTER HARD SIGN */ /* * Greek * (based on an early draft of, and not quite identical to, ISO/IEC 8859-7) * Byte 3 = 7 */ public static final int XK_Greek_ALPHAaccent = 0x07a1; /* U+0386 GREEK CAPITAL LETTER ALPHA WITH TONOS */ public static final int XK_Greek_EPSILONaccent = 0x07a2; /* U+0388 GREEK CAPITAL LETTER EPSILON WITH TONOS */ public static final int XK_Greek_ETAaccent = 0x07a3; /* U+0389 GREEK CAPITAL LETTER ETA WITH TONOS */ public static final int XK_Greek_IOTAaccent = 0x07a4; /* U+038A GREEK CAPITAL LETTER IOTA WITH TONOS */ public static final int XK_Greek_IOTAdieresis = 0x07a5; /* U+03AA GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ public static final int XK_Greek_IOTAdiaeresis = 0x07a5; /* old typo */ public static final int XK_Greek_OMICRONaccent = 0x07a7; /* U+038C GREEK CAPITAL LETTER OMICRON WITH TONOS */ public static final int XK_Greek_UPSILONaccent = 0x07a8; /* U+038E GREEK CAPITAL LETTER UPSILON WITH TONOS */ public static final int XK_Greek_UPSILONdieresis = 0x07a9; /* U+03AB GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ public static final int XK_Greek_OMEGAaccent = 0x07ab; /* U+038F GREEK CAPITAL LETTER OMEGA WITH TONOS */ public static final int XK_Greek_accentdieresis = 0x07ae; /* U+0385 GREEK DIALYTIKA TONOS */ public static final int XK_Greek_horizbar = 0x07af; /* U+2015 HORIZONTAL BAR */ public static final int XK_Greek_alphaaccent = 0x07b1; /* U+03AC GREEK SMALL LETTER ALPHA WITH TONOS */ public static final int XK_Greek_epsilonaccent = 0x07b2; /* U+03AD GREEK SMALL LETTER EPSILON WITH TONOS */ public static final int XK_Greek_etaaccent = 0x07b3; /* U+03AE GREEK SMALL LETTER ETA WITH TONOS */ public static final int XK_Greek_iotaaccent = 0x07b4; /* U+03AF GREEK SMALL LETTER IOTA WITH TONOS */ public static final int XK_Greek_iotadieresis = 0x07b5; /* U+03CA GREEK SMALL LETTER IOTA WITH DIALYTIKA */ public static final int XK_Greek_iotaaccentdieresis = 0x07b6; /* U+0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ public static final int XK_Greek_omicronaccent = 0x07b7; /* U+03CC GREEK SMALL LETTER OMICRON WITH TONOS */ public static final int XK_Greek_upsilonaccent = 0x07b8; /* U+03CD GREEK SMALL LETTER UPSILON WITH TONOS */ public static final int XK_Greek_upsilondieresis = 0x07b9; /* U+03CB GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ public static final int XK_Greek_upsilonaccentdieresis = 0x07ba; /* U+03B0 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ public static final int XK_Greek_omegaaccent = 0x07bb; /* U+03CE GREEK SMALL LETTER OMEGA WITH TONOS */ public static final int XK_Greek_ALPHA = 0x07c1; /* U+0391 GREEK CAPITAL LETTER ALPHA */ public static final int XK_Greek_BETA = 0x07c2; /* U+0392 GREEK CAPITAL LETTER BETA */ public static final int XK_Greek_GAMMA = 0x07c3; /* U+0393 GREEK CAPITAL LETTER GAMMA */ public static final int XK_Greek_DELTA = 0x07c4; /* U+0394 GREEK CAPITAL LETTER DELTA */ public static final int XK_Greek_EPSILON = 0x07c5; /* U+0395 GREEK CAPITAL LETTER EPSILON */ public static final int XK_Greek_ZETA = 0x07c6; /* U+0396 GREEK CAPITAL LETTER ZETA */ public static final int XK_Greek_ETA = 0x07c7; /* U+0397 GREEK CAPITAL LETTER ETA */ public static final int XK_Greek_THETA = 0x07c8; /* U+0398 GREEK CAPITAL LETTER THETA */ public static final int XK_Greek_IOTA = 0x07c9; /* U+0399 GREEK CAPITAL LETTER IOTA */ public static final int XK_Greek_KAPPA = 0x07ca; /* U+039A GREEK CAPITAL LETTER KAPPA */ public static final int XK_Greek_LAMDA = 0x07cb; /* U+039B GREEK CAPITAL LETTER LAMDA */ public static final int XK_Greek_LAMBDA = 0x07cb; /* U+039B GREEK CAPITAL LETTER LAMDA */ public static final int XK_Greek_MU = 0x07cc; /* U+039C GREEK CAPITAL LETTER MU */ public static final int XK_Greek_NU = 0x07cd; /* U+039D GREEK CAPITAL LETTER NU */ public static final int XK_Greek_XI = 0x07ce; /* U+039E GREEK CAPITAL LETTER XI */ public static final int XK_Greek_OMICRON = 0x07cf; /* U+039F GREEK CAPITAL LETTER OMICRON */ public static final int XK_Greek_PI = 0x07d0; /* U+03A0 GREEK CAPITAL LETTER PI */ public static final int XK_Greek_RHO = 0x07d1; /* U+03A1 GREEK CAPITAL LETTER RHO */ public static final int XK_Greek_SIGMA = 0x07d2; /* U+03A3 GREEK CAPITAL LETTER SIGMA */ public static final int XK_Greek_TAU = 0x07d4; /* U+03A4 GREEK CAPITAL LETTER TAU */ public static final int XK_Greek_UPSILON = 0x07d5; /* U+03A5 GREEK CAPITAL LETTER UPSILON */ public static final int XK_Greek_PHI = 0x07d6; /* U+03A6 GREEK CAPITAL LETTER PHI */ public static final int XK_Greek_CHI = 0x07d7; /* U+03A7 GREEK CAPITAL LETTER CHI */ public static final int XK_Greek_PSI = 0x07d8; /* U+03A8 GREEK CAPITAL LETTER PSI */ public static final int XK_Greek_OMEGA = 0x07d9; /* U+03A9 GREEK CAPITAL LETTER OMEGA */ public static final int XK_Greek_alpha = 0x07e1; /* U+03B1 GREEK SMALL LETTER ALPHA */ public static final int XK_Greek_beta = 0x07e2; /* U+03B2 GREEK SMALL LETTER BETA */ public static final int XK_Greek_gamma = 0x07e3; /* U+03B3 GREEK SMALL LETTER GAMMA */ public static final int XK_Greek_delta = 0x07e4; /* U+03B4 GREEK SMALL LETTER DELTA */ public static final int XK_Greek_epsilon = 0x07e5; /* U+03B5 GREEK SMALL LETTER EPSILON */ public static final int XK_Greek_zeta = 0x07e6; /* U+03B6 GREEK SMALL LETTER ZETA */ public static final int XK_Greek_eta = 0x07e7; /* U+03B7 GREEK SMALL LETTER ETA */ public static final int XK_Greek_theta = 0x07e8; /* U+03B8 GREEK SMALL LETTER THETA */ public static final int XK_Greek_iota = 0x07e9; /* U+03B9 GREEK SMALL LETTER IOTA */ public static final int XK_Greek_kappa = 0x07ea; /* U+03BA GREEK SMALL LETTER KAPPA */ public static final int XK_Greek_lamda = 0x07eb; /* U+03BB GREEK SMALL LETTER LAMDA */ public static final int XK_Greek_lambda = 0x07eb; /* U+03BB GREEK SMALL LETTER LAMDA */ public static final int XK_Greek_mu = 0x07ec; /* U+03BC GREEK SMALL LETTER MU */ public static final int XK_Greek_nu = 0x07ed; /* U+03BD GREEK SMALL LETTER NU */ public static final int XK_Greek_xi = 0x07ee; /* U+03BE GREEK SMALL LETTER XI */ public static final int XK_Greek_omicron = 0x07ef; /* U+03BF GREEK SMALL LETTER OMICRON */ public static final int XK_Greek_pi = 0x07f0; /* U+03C0 GREEK SMALL LETTER PI */ public static final int XK_Greek_rho = 0x07f1; /* U+03C1 GREEK SMALL LETTER RHO */ public static final int XK_Greek_sigma = 0x07f2; /* U+03C3 GREEK SMALL LETTER SIGMA */ public static final int XK_Greek_finalsmallsigma = 0x07f3; /* U+03C2 GREEK SMALL LETTER FINAL SIGMA */ public static final int XK_Greek_tau = 0x07f4; /* U+03C4 GREEK SMALL LETTER TAU */ public static final int XK_Greek_upsilon = 0x07f5; /* U+03C5 GREEK SMALL LETTER UPSILON */ public static final int XK_Greek_phi = 0x07f6; /* U+03C6 GREEK SMALL LETTER PHI */ public static final int XK_Greek_chi = 0x07f7; /* U+03C7 GREEK SMALL LETTER CHI */ public static final int XK_Greek_psi = 0x07f8; /* U+03C8 GREEK SMALL LETTER PSI */ public static final int XK_Greek_omega = 0x07f9; /* U+03C9 GREEK SMALL LETTER OMEGA */ public static final int XK_Greek_switch = 0xff7e; /* Alias for mode_switch */ /* * Technical * (from the DEC VT330/VT420 Technical Character Set, http://vt100.net/charsets/technical.html) * Byte 3 = 8 */ public static final int XK_leftradical = 0x08a1; /* U+23B7 RADICAL SYMBOL BOTTOM */ public static final int XK_topleftradical = 0x08a2; /*(U+250C BOX DRAWINGS LIGHT DOWN AND RIGHT)*/ public static final int XK_horizconnector = 0x08a3; /*(U+2500 BOX DRAWINGS LIGHT HORIZONTAL)*/ public static final int XK_topintegral = 0x08a4; /* U+2320 TOP HALF INTEGRAL */ public static final int XK_botintegral = 0x08a5; /* U+2321 BOTTOM HALF INTEGRAL */ public static final int XK_vertconnector = 0x08a6; /*(U+2502 BOX DRAWINGS LIGHT VERTICAL)*/ public static final int XK_topleftsqbracket = 0x08a7; /* U+23A1 LEFT SQUARE BRACKET UPPER CORNER */ public static final int XK_botleftsqbracket = 0x08a8; /* U+23A3 LEFT SQUARE BRACKET LOWER CORNER */ public static final int XK_toprightsqbracket = 0x08a9; /* U+23A4 RIGHT SQUARE BRACKET UPPER CORNER */ public static final int XK_botrightsqbracket = 0x08aa; /* U+23A6 RIGHT SQUARE BRACKET LOWER CORNER */ public static final int XK_topleftparens = 0x08ab; /* U+239B LEFT PARENTHESIS UPPER HOOK */ public static final int XK_botleftparens = 0x08ac; /* U+239D LEFT PARENTHESIS LOWER HOOK */ public static final int XK_toprightparens = 0x08ad; /* U+239E RIGHT PARENTHESIS UPPER HOOK */ public static final int XK_botrightparens = 0x08ae; /* U+23A0 RIGHT PARENTHESIS LOWER HOOK */ public static final int XK_leftmiddlecurlybrace = 0x08af; /* U+23A8 LEFT CURLY BRACKET MIDDLE PIECE */ public static final int XK_rightmiddlecurlybrace = 0x08b0; /* U+23AC RIGHT CURLY BRACKET MIDDLE PIECE */ public static final int XK_topleftsummation = 0x08b1; public static final int XK_botleftsummation = 0x08b2; public static final int XK_topvertsummationconnector = 0x08b3; public static final int XK_botvertsummationconnector = 0x08b4; public static final int XK_toprightsummation = 0x08b5; public static final int XK_botrightsummation = 0x08b6; public static final int XK_rightmiddlesummation = 0x08b7; public static final int XK_lessthanequal = 0x08bc; /* U+2264 LESS-THAN OR EQUAL TO */ public static final int XK_notequal = 0x08bd; /* U+2260 NOT EQUAL TO */ public static final int XK_greaterthanequal = 0x08be; /* U+2265 GREATER-THAN OR EQUAL TO */ public static final int XK_integral = 0x08bf; /* U+222B INTEGRAL */ public static final int XK_therefore = 0x08c0; /* U+2234 THEREFORE */ public static final int XK_variation = 0x08c1; /* U+221D PROPORTIONAL TO */ public static final int XK_infinity = 0x08c2; /* U+221E INFINITY */ public static final int XK_nabla = 0x08c5; /* U+2207 NABLA */ public static final int XK_approximate = 0x08c8; /* U+223C TILDE OPERATOR */ public static final int XK_similarequal = 0x08c9; /* U+2243 ASYMPTOTICALLY EQUAL TO */ public static final int XK_ifonlyif = 0x08cd; /* U+21D4 LEFT RIGHT DOUBLE ARROW */ public static final int XK_implies = 0x08ce; /* U+21D2 RIGHTWARDS DOUBLE ARROW */ public static final int XK_identical = 0x08cf; /* U+2261 IDENTICAL TO */ public static final int XK_radical = 0x08d6; /* U+221A SQUARE ROOT */ public static final int XK_includedin = 0x08da; /* U+2282 SUBSET OF */ public static final int XK_includes = 0x08db; /* U+2283 SUPERSET OF */ public static final int XK_intersection = 0x08dc; /* U+2229 INTERSECTION */ public static final int XK_union = 0x08dd; /* U+222A UNION */ public static final int XK_logicaland = 0x08de; /* U+2227 LOGICAL AND */ public static final int XK_logicalor = 0x08df; /* U+2228 LOGICAL OR */ public static final int XK_partialderivative = 0x08ef; /* U+2202 PARTIAL DIFFERENTIAL */ public static final int XK_function = 0x08f6; /* U+0192 LATIN SMALL LETTER F WITH HOOK */ public static final int XK_leftarrow = 0x08fb; /* U+2190 LEFTWARDS ARROW */ public static final int XK_uparrow = 0x08fc; /* U+2191 UPWARDS ARROW */ public static final int XK_rightarrow = 0x08fd; /* U+2192 RIGHTWARDS ARROW */ public static final int XK_downarrow = 0x08fe; /* U+2193 DOWNWARDS ARROW */ /* * Special * (from the DEC VT100 Special Graphics Character Set) * Byte 3 = 9 */ public static final int XK_blank = 0x09df; public static final int XK_soliddiamond = 0x09e0; /* U+25C6 BLACK DIAMOND */ public static final int XK_checkerboard = 0x09e1; /* U+2592 MEDIUM SHADE */ public static final int XK_ht = 0x09e2; /* U+2409 SYMBOL FOR HORIZONTAL TABULATION */ public static final int XK_ff = 0x09e3; /* U+240C SYMBOL FOR FORM FEED */ public static final int XK_cr = 0x09e4; /* U+240D SYMBOL FOR CARRIAGE RETURN */ public static final int XK_lf = 0x09e5; /* U+240A SYMBOL FOR LINE FEED */ public static final int XK_nl = 0x09e8; /* U+2424 SYMBOL FOR NEWLINE */ public static final int XK_vt = 0x09e9; /* U+240B SYMBOL FOR VERTICAL TABULATION */ public static final int XK_lowrightcorner = 0x09ea; /* U+2518 BOX DRAWINGS LIGHT UP AND LEFT */ public static final int XK_uprightcorner = 0x09eb; /* U+2510 BOX DRAWINGS LIGHT DOWN AND LEFT */ public static final int XK_upleftcorner = 0x09ec; /* U+250C BOX DRAWINGS LIGHT DOWN AND RIGHT */ public static final int XK_lowleftcorner = 0x09ed; /* U+2514 BOX DRAWINGS LIGHT UP AND RIGHT */ public static final int XK_crossinglines = 0x09ee; /* U+253C BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ public static final int XK_horizlinescan1 = 0x09ef; /* U+23BA HORIZONTAL SCAN LINE-1 */ public static final int XK_horizlinescan3 = 0x09f0; /* U+23BB HORIZONTAL SCAN LINE-3 */ public static final int XK_horizlinescan5 = 0x09f1; /* U+2500 BOX DRAWINGS LIGHT HORIZONTAL */ public static final int XK_horizlinescan7 = 0x09f2; /* U+23BC HORIZONTAL SCAN LINE-7 */ public static final int XK_horizlinescan9 = 0x09f3; /* U+23BD HORIZONTAL SCAN LINE-9 */ public static final int XK_leftt = 0x09f4; /* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ public static final int XK_rightt = 0x09f5; /* U+2524 BOX DRAWINGS LIGHT VERTICAL AND LEFT */ public static final int XK_bott = 0x09f6; /* U+2534 BOX DRAWINGS LIGHT UP AND HORIZONTAL */ public static final int XK_topt = 0x09f7; /* U+252C BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ public static final int XK_vertbar = 0x09f8; /* U+2502 BOX DRAWINGS LIGHT VERTICAL */ /* * Publishing * (these are probably from a long forgotten DEC Publishing * font that once shipped with DECwrite) * Byte 3 = 0x0a */ public static final int XK_emspace = 0x0aa1; /* U+2003 EM SPACE */ public static final int XK_enspace = 0x0aa2; /* U+2002 EN SPACE */ public static final int XK_em3space = 0x0aa3; /* U+2004 THREE-PER-EM SPACE */ public static final int XK_em4space = 0x0aa4; /* U+2005 FOUR-PER-EM SPACE */ public static final int XK_digitspace = 0x0aa5; /* U+2007 FIGURE SPACE */ public static final int XK_punctspace = 0x0aa6; /* U+2008 PUNCTUATION SPACE */ public static final int XK_thinspace = 0x0aa7; /* U+2009 THIN SPACE */ public static final int XK_hairspace = 0x0aa8; /* U+200A HAIR SPACE */ public static final int XK_emdash = 0x0aa9; /* U+2014 EM DASH */ public static final int XK_endash = 0x0aaa; /* U+2013 EN DASH */ public static final int XK_signifblank = 0x0aac; /*(U+2423 OPEN BOX)*/ public static final int XK_ellipsis = 0x0aae; /* U+2026 HORIZONTAL ELLIPSIS */ public static final int XK_doubbaselinedot = 0x0aaf; /* U+2025 TWO DOT LEADER */ public static final int XK_onethird = 0x0ab0; /* U+2153 VULGAR FRACTION ONE THIRD */ public static final int XK_twothirds = 0x0ab1; /* U+2154 VULGAR FRACTION TWO THIRDS */ public static final int XK_onefifth = 0x0ab2; /* U+2155 VULGAR FRACTION ONE FIFTH */ public static final int XK_twofifths = 0x0ab3; /* U+2156 VULGAR FRACTION TWO FIFTHS */ public static final int XK_threefifths = 0x0ab4; /* U+2157 VULGAR FRACTION THREE FIFTHS */ public static final int XK_fourfifths = 0x0ab5; /* U+2158 VULGAR FRACTION FOUR FIFTHS */ public static final int XK_onesixth = 0x0ab6; /* U+2159 VULGAR FRACTION ONE SIXTH */ public static final int XK_fivesixths = 0x0ab7; /* U+215A VULGAR FRACTION FIVE SIXTHS */ public static final int XK_careof = 0x0ab8; /* U+2105 CARE OF */ public static final int XK_figdash = 0x0abb; /* U+2012 FIGURE DASH */ public static final int XK_leftanglebracket = 0x0abc; /*(U+27E8 MATHEMATICAL LEFT ANGLE BRACKET)*/ public static final int XK_decimalpoint = 0x0abd; /*(U+002E FULL STOP)*/ public static final int XK_rightanglebracket = 0x0abe; /*(U+27E9 MATHEMATICAL RIGHT ANGLE BRACKET)*/ public static final int XK_marker = 0x0abf; public static final int XK_oneeighth = 0x0ac3; /* U+215B VULGAR FRACTION ONE EIGHTH */ public static final int XK_threeeighths = 0x0ac4; /* U+215C VULGAR FRACTION THREE EIGHTHS */ public static final int XK_fiveeighths = 0x0ac5; /* U+215D VULGAR FRACTION FIVE EIGHTHS */ public static final int XK_seveneighths = 0x0ac6; /* U+215E VULGAR FRACTION SEVEN EIGHTHS */ public static final int XK_trademark = 0x0ac9; /* U+2122 TRADE MARK SIGN */ public static final int XK_signaturemark = 0x0aca; /*(U+2613 SALTIRE)*/ public static final int XK_trademarkincircle = 0x0acb; public static final int XK_leftopentriangle = 0x0acc; /*(U+25C1 WHITE LEFT-POINTING TRIANGLE)*/ public static final int XK_rightopentriangle = 0x0acd; /*(U+25B7 WHITE RIGHT-POINTING TRIANGLE)*/ public static final int XK_emopencircle = 0x0ace; /*(U+25CB WHITE CIRCLE)*/ public static final int XK_emopenrectangle = 0x0acf; /*(U+25AF WHITE VERTICAL RECTANGLE)*/ public static final int XK_leftsinglequotemark = 0x0ad0; /* U+2018 LEFT SINGLE QUOTATION MARK */ public static final int XK_rightsinglequotemark = 0x0ad1; /* U+2019 RIGHT SINGLE QUOTATION MARK */ public static final int XK_leftdoublequotemark = 0x0ad2; /* U+201C LEFT DOUBLE QUOTATION MARK */ public static final int XK_rightdoublequotemark = 0x0ad3; /* U+201D RIGHT DOUBLE QUOTATION MARK */ public static final int XK_prescription = 0x0ad4; /* U+211E PRESCRIPTION TAKE */ public static final int XK_minutes = 0x0ad6; /* U+2032 PRIME */ public static final int XK_seconds = 0x0ad7; /* U+2033 DOUBLE PRIME */ public static final int XK_latincross = 0x0ad9; /* U+271D LATIN CROSS */ public static final int XK_hexagram = 0x0ada; public static final int XK_filledrectbullet = 0x0adb; /*(U+25AC BLACK RECTANGLE)*/ public static final int XK_filledlefttribullet = 0x0adc; /*(U+25C0 BLACK LEFT-POINTING TRIANGLE)*/ public static final int XK_filledrighttribullet = 0x0add; /*(U+25B6 BLACK RIGHT-POINTING TRIANGLE)*/ public static final int XK_emfilledcircle = 0x0ade; /*(U+25CF BLACK CIRCLE)*/ public static final int XK_emfilledrect = 0x0adf; /*(U+25AE BLACK VERTICAL RECTANGLE)*/ public static final int XK_enopencircbullet = 0x0ae0; /*(U+25E6 WHITE BULLET)*/ public static final int XK_enopensquarebullet = 0x0ae1; /*(U+25AB WHITE SMALL SQUARE)*/ public static final int XK_openrectbullet = 0x0ae2; /*(U+25AD WHITE RECTANGLE)*/ public static final int XK_opentribulletup = 0x0ae3; /*(U+25B3 WHITE UP-POINTING TRIANGLE)*/ public static final int XK_opentribulletdown = 0x0ae4; /*(U+25BD WHITE DOWN-POINTING TRIANGLE)*/ public static final int XK_openstar = 0x0ae5; /*(U+2606 WHITE STAR)*/ public static final int XK_enfilledcircbullet = 0x0ae6; /*(U+2022 BULLET)*/ public static final int XK_enfilledsqbullet = 0x0ae7; /*(U+25AA BLACK SMALL SQUARE)*/ public static final int XK_filledtribulletup = 0x0ae8; /*(U+25B2 BLACK UP-POINTING TRIANGLE)*/ public static final int XK_filledtribulletdown = 0x0ae9; /*(U+25BC BLACK DOWN-POINTING TRIANGLE)*/ public static final int XK_leftpointer = 0x0aea; /*(U+261C WHITE LEFT POINTING INDEX)*/ public static final int XK_rightpointer = 0x0aeb; /*(U+261E WHITE RIGHT POINTING INDEX)*/ public static final int XK_club = 0x0aec; /* U+2663 BLACK CLUB SUIT */ public static final int XK_diamond = 0x0aed; /* U+2666 BLACK DIAMOND SUIT */ public static final int XK_heart = 0x0aee; /* U+2665 BLACK HEART SUIT */ public static final int XK_maltesecross = 0x0af0; /* U+2720 MALTESE CROSS */ public static final int XK_dagger = 0x0af1; /* U+2020 DAGGER */ public static final int XK_doubledagger = 0x0af2; /* U+2021 DOUBLE DAGGER */ public static final int XK_checkmark = 0x0af3; /* U+2713 CHECK MARK */ public static final int XK_ballotcross = 0x0af4; /* U+2717 BALLOT X */ public static final int XK_musicalsharp = 0x0af5; /* U+266F MUSIC SHARP SIGN */ public static final int XK_musicalflat = 0x0af6; /* U+266D MUSIC FLAT SIGN */ public static final int XK_malesymbol = 0x0af7; /* U+2642 MALE SIGN */ public static final int XK_femalesymbol = 0x0af8; /* U+2640 FEMALE SIGN */ public static final int XK_telephone = 0x0af9; /* U+260E BLACK TELEPHONE */ public static final int XK_telephonerecorder = 0x0afa; /* U+2315 TELEPHONE RECORDER */ public static final int XK_phonographcopyright = 0x0afb; /* U+2117 SOUND RECORDING COPYRIGHT */ public static final int XK_caret = 0x0afc; /* U+2038 CARET */ public static final int XK_singlelowquotemark = 0x0afd; /* U+201A SINGLE LOW-9 QUOTATION MARK */ public static final int XK_doublelowquotemark = 0x0afe; /* U+201E DOUBLE LOW-9 QUOTATION MARK */ public static final int XK_cursor = 0x0aff; /* * APL * Byte 3 = 0x0b */ public static final int XK_leftcaret = 0x0ba3; /*(U+003C LESS-THAN SIGN)*/ public static final int XK_rightcaret = 0x0ba6; /*(U+003E GREATER-THAN SIGN)*/ public static final int XK_downcaret = 0x0ba8; /*(U+2228 LOGICAL OR)*/ public static final int XK_upcaret = 0x0ba9; /*(U+2227 LOGICAL AND)*/ public static final int XK_overbar = 0x0bc0; /*(U+00AF MACRON)*/ public static final int XK_downtack = 0x0bc2; /* U+22A5 UP TACK */ public static final int XK_upshoe = 0x0bc3; /*(U+2229 INTERSECTION)*/ public static final int XK_downstile = 0x0bc4; /* U+230A LEFT FLOOR */ public static final int XK_underbar = 0x0bc6; /*(U+005F LOW LINE)*/ public static final int XK_jot = 0x0bca; /* U+2218 RING OPERATOR */ public static final int XK_quad = 0x0bcc; /* U+2395 APL FUNCTIONAL SYMBOL QUAD */ public static final int XK_uptack = 0x0bce; /* U+22A4 DOWN TACK */ public static final int XK_circle = 0x0bcf; /* U+25CB WHITE CIRCLE */ public static final int XK_upstile = 0x0bd3; /* U+2308 LEFT CEILING */ public static final int XK_downshoe = 0x0bd6; /*(U+222A UNION)*/ public static final int XK_rightshoe = 0x0bd8; /*(U+2283 SUPERSET OF)*/ public static final int XK_leftshoe = 0x0bda; /*(U+2282 SUBSET OF)*/ public static final int XK_lefttack = 0x0bdc; /* U+22A2 RIGHT TACK */ public static final int XK_righttack = 0x0bfc; /* U+22A3 LEFT TACK */ /* * Hebrew * Byte 3 = 0x0c */ public static final int XK_hebrew_doublelowline = 0x0cdf; /* U+2017 DOUBLE LOW LINE */ public static final int XK_hebrew_aleph = 0x0ce0; /* U+05D0 HEBREW LETTER ALEF */ public static final int XK_hebrew_bet = 0x0ce1; /* U+05D1 HEBREW LETTER BET */ public static final int XK_hebrew_beth = 0x0ce1; /* deprecated */ public static final int XK_hebrew_gimel = 0x0ce2; /* U+05D2 HEBREW LETTER GIMEL */ public static final int XK_hebrew_gimmel = 0x0ce2; /* deprecated */ public static final int XK_hebrew_dalet = 0x0ce3; /* U+05D3 HEBREW LETTER DALET */ public static final int XK_hebrew_daleth = 0x0ce3; /* deprecated */ public static final int XK_hebrew_he = 0x0ce4; /* U+05D4 HEBREW LETTER HE */ public static final int XK_hebrew_waw = 0x0ce5; /* U+05D5 HEBREW LETTER VAV */ public static final int XK_hebrew_zain = 0x0ce6; /* U+05D6 HEBREW LETTER ZAYIN */ public static final int XK_hebrew_zayin = 0x0ce6; /* deprecated */ public static final int XK_hebrew_chet = 0x0ce7; /* U+05D7 HEBREW LETTER HET */ public static final int XK_hebrew_het = 0x0ce7; /* deprecated */ public static final int XK_hebrew_tet = 0x0ce8; /* U+05D8 HEBREW LETTER TET */ public static final int XK_hebrew_teth = 0x0ce8; /* deprecated */ public static final int XK_hebrew_yod = 0x0ce9; /* U+05D9 HEBREW LETTER YOD */ public static final int XK_hebrew_finalkaph = 0x0cea; /* U+05DA HEBREW LETTER FINAL KAF */ public static final int XK_hebrew_kaph = 0x0ceb; /* U+05DB HEBREW LETTER KAF */ public static final int XK_hebrew_lamed = 0x0cec; /* U+05DC HEBREW LETTER LAMED */ public static final int XK_hebrew_finalmem = 0x0ced; /* U+05DD HEBREW LETTER FINAL MEM */ public static final int XK_hebrew_mem = 0x0cee; /* U+05DE HEBREW LETTER MEM */ public static final int XK_hebrew_finalnun = 0x0cef; /* U+05DF HEBREW LETTER FINAL NUN */ public static final int XK_hebrew_nun = 0x0cf0; /* U+05E0 HEBREW LETTER NUN */ public static final int XK_hebrew_samech = 0x0cf1; /* U+05E1 HEBREW LETTER SAMEKH */ public static final int XK_hebrew_samekh = 0x0cf1; /* deprecated */ public static final int XK_hebrew_ayin = 0x0cf2; /* U+05E2 HEBREW LETTER AYIN */ public static final int XK_hebrew_finalpe = 0x0cf3; /* U+05E3 HEBREW LETTER FINAL PE */ public static final int XK_hebrew_pe = 0x0cf4; /* U+05E4 HEBREW LETTER PE */ public static final int XK_hebrew_finalzade = 0x0cf5; /* U+05E5 HEBREW LETTER FINAL TSADI */ public static final int XK_hebrew_finalzadi = 0x0cf5; /* deprecated */ public static final int XK_hebrew_zade = 0x0cf6; /* U+05E6 HEBREW LETTER TSADI */ public static final int XK_hebrew_zadi = 0x0cf6; /* deprecated */ public static final int XK_hebrew_qoph = 0x0cf7; /* U+05E7 HEBREW LETTER QOF */ public static final int XK_hebrew_kuf = 0x0cf7; /* deprecated */ public static final int XK_hebrew_resh = 0x0cf8; /* U+05E8 HEBREW LETTER RESH */ public static final int XK_hebrew_shin = 0x0cf9; /* U+05E9 HEBREW LETTER SHIN */ public static final int XK_hebrew_taw = 0x0cfa; /* U+05EA HEBREW LETTER TAV */ public static final int XK_hebrew_taf = 0x0cfa; /* deprecated */ public static final int XK_Hebrew_switch = 0xff7e; /* Alias for mode_switch */ /* * Thai * Byte 3 = 0x0d */ public static final int XK_Thai_kokai = 0x0da1; /* U+0E01 THAI CHARACTER KO KAI */ public static final int XK_Thai_khokhai = 0x0da2; /* U+0E02 THAI CHARACTER KHO KHAI */ public static final int XK_Thai_khokhuat = 0x0da3; /* U+0E03 THAI CHARACTER KHO KHUAT */ public static final int XK_Thai_khokhwai = 0x0da4; /* U+0E04 THAI CHARACTER KHO KHWAI */ public static final int XK_Thai_khokhon = 0x0da5; /* U+0E05 THAI CHARACTER KHO KHON */ public static final int XK_Thai_khorakhang = 0x0da6; /* U+0E06 THAI CHARACTER KHO RAKHANG */ public static final int XK_Thai_ngongu = 0x0da7; /* U+0E07 THAI CHARACTER NGO NGU */ public static final int XK_Thai_chochan = 0x0da8; /* U+0E08 THAI CHARACTER CHO CHAN */ public static final int XK_Thai_choching = 0x0da9; /* U+0E09 THAI CHARACTER CHO CHING */ public static final int XK_Thai_chochang = 0x0daa; /* U+0E0A THAI CHARACTER CHO CHANG */ public static final int XK_Thai_soso = 0x0dab; /* U+0E0B THAI CHARACTER SO SO */ public static final int XK_Thai_chochoe = 0x0dac; /* U+0E0C THAI CHARACTER CHO CHOE */ public static final int XK_Thai_yoying = 0x0dad; /* U+0E0D THAI CHARACTER YO YING */ public static final int XK_Thai_dochada = 0x0dae; /* U+0E0E THAI CHARACTER DO CHADA */ public static final int XK_Thai_topatak = 0x0daf; /* U+0E0F THAI CHARACTER TO PATAK */ public static final int XK_Thai_thothan = 0x0db0; /* U+0E10 THAI CHARACTER THO THAN */ public static final int XK_Thai_thonangmontho = 0x0db1; /* U+0E11 THAI CHARACTER THO NANGMONTHO */ public static final int XK_Thai_thophuthao = 0x0db2; /* U+0E12 THAI CHARACTER THO PHUTHAO */ public static final int XK_Thai_nonen = 0x0db3; /* U+0E13 THAI CHARACTER NO NEN */ public static final int XK_Thai_dodek = 0x0db4; /* U+0E14 THAI CHARACTER DO DEK */ public static final int XK_Thai_totao = 0x0db5; /* U+0E15 THAI CHARACTER TO TAO */ public static final int XK_Thai_thothung = 0x0db6; /* U+0E16 THAI CHARACTER THO THUNG */ public static final int XK_Thai_thothahan = 0x0db7; /* U+0E17 THAI CHARACTER THO THAHAN */ public static final int XK_Thai_thothong = 0x0db8; /* U+0E18 THAI CHARACTER THO THONG */ public static final int XK_Thai_nonu = 0x0db9; /* U+0E19 THAI CHARACTER NO NU */ public static final int XK_Thai_bobaimai = 0x0dba; /* U+0E1A THAI CHARACTER BO BAIMAI */ public static final int XK_Thai_popla = 0x0dbb; /* U+0E1B THAI CHARACTER PO PLA */ public static final int XK_Thai_phophung = 0x0dbc; /* U+0E1C THAI CHARACTER PHO PHUNG */ public static final int XK_Thai_fofa = 0x0dbd; /* U+0E1D THAI CHARACTER FO FA */ public static final int XK_Thai_phophan = 0x0dbe; /* U+0E1E THAI CHARACTER PHO PHAN */ public static final int XK_Thai_fofan = 0x0dbf; /* U+0E1F THAI CHARACTER FO FAN */ public static final int XK_Thai_phosamphao = 0x0dc0; /* U+0E20 THAI CHARACTER PHO SAMPHAO */ public static final int XK_Thai_moma = 0x0dc1; /* U+0E21 THAI CHARACTER MO MA */ public static final int XK_Thai_yoyak = 0x0dc2; /* U+0E22 THAI CHARACTER YO YAK */ public static final int XK_Thai_rorua = 0x0dc3; /* U+0E23 THAI CHARACTER RO RUA */ public static final int XK_Thai_ru = 0x0dc4; /* U+0E24 THAI CHARACTER RU */ public static final int XK_Thai_loling = 0x0dc5; /* U+0E25 THAI CHARACTER LO LING */ public static final int XK_Thai_lu = 0x0dc6; /* U+0E26 THAI CHARACTER LU */ public static final int XK_Thai_wowaen = 0x0dc7; /* U+0E27 THAI CHARACTER WO WAEN */ public static final int XK_Thai_sosala = 0x0dc8; /* U+0E28 THAI CHARACTER SO SALA */ public static final int XK_Thai_sorusi = 0x0dc9; /* U+0E29 THAI CHARACTER SO RUSI */ public static final int XK_Thai_sosua = 0x0dca; /* U+0E2A THAI CHARACTER SO SUA */ public static final int XK_Thai_hohip = 0x0dcb; /* U+0E2B THAI CHARACTER HO HIP */ public static final int XK_Thai_lochula = 0x0dcc; /* U+0E2C THAI CHARACTER LO CHULA */ public static final int XK_Thai_oang = 0x0dcd; /* U+0E2D THAI CHARACTER O ANG */ public static final int XK_Thai_honokhuk = 0x0dce; /* U+0E2E THAI CHARACTER HO NOKHUK */ public static final int XK_Thai_paiyannoi = 0x0dcf; /* U+0E2F THAI CHARACTER PAIYANNOI */ public static final int XK_Thai_saraa = 0x0dd0; /* U+0E30 THAI CHARACTER SARA A */ public static final int XK_Thai_maihanakat = 0x0dd1; /* U+0E31 THAI CHARACTER MAI HAN-AKAT */ public static final int XK_Thai_saraaa = 0x0dd2; /* U+0E32 THAI CHARACTER SARA AA */ public static final int XK_Thai_saraam = 0x0dd3; /* U+0E33 THAI CHARACTER SARA AM */ public static final int XK_Thai_sarai = 0x0dd4; /* U+0E34 THAI CHARACTER SARA I */ public static final int XK_Thai_saraii = 0x0dd5; /* U+0E35 THAI CHARACTER SARA II */ public static final int XK_Thai_saraue = 0x0dd6; /* U+0E36 THAI CHARACTER SARA UE */ public static final int XK_Thai_sarauee = 0x0dd7; /* U+0E37 THAI CHARACTER SARA UEE */ public static final int XK_Thai_sarau = 0x0dd8; /* U+0E38 THAI CHARACTER SARA U */ public static final int XK_Thai_sarauu = 0x0dd9; /* U+0E39 THAI CHARACTER SARA UU */ public static final int XK_Thai_phinthu = 0x0dda; /* U+0E3A THAI CHARACTER PHINTHU */ public static final int XK_Thai_maihanakat_maitho = 0x0dde; public static final int XK_Thai_baht = 0x0ddf; /* U+0E3F THAI CURRENCY SYMBOL BAHT */ public static final int XK_Thai_sarae = 0x0de0; /* U+0E40 THAI CHARACTER SARA E */ public static final int XK_Thai_saraae = 0x0de1; /* U+0E41 THAI CHARACTER SARA AE */ public static final int XK_Thai_sarao = 0x0de2; /* U+0E42 THAI CHARACTER SARA O */ public static final int XK_Thai_saraaimaimuan = 0x0de3; /* U+0E43 THAI CHARACTER SARA AI MAIMUAN */ public static final int XK_Thai_saraaimaimalai = 0x0de4; /* U+0E44 THAI CHARACTER SARA AI MAIMALAI */ public static final int XK_Thai_lakkhangyao = 0x0de5; /* U+0E45 THAI CHARACTER LAKKHANGYAO */ public static final int XK_Thai_maiyamok = 0x0de6; /* U+0E46 THAI CHARACTER MAIYAMOK */ public static final int XK_Thai_maitaikhu = 0x0de7; /* U+0E47 THAI CHARACTER MAITAIKHU */ public static final int XK_Thai_maiek = 0x0de8; /* U+0E48 THAI CHARACTER MAI EK */ public static final int XK_Thai_maitho = 0x0de9; /* U+0E49 THAI CHARACTER MAI THO */ public static final int XK_Thai_maitri = 0x0dea; /* U+0E4A THAI CHARACTER MAI TRI */ public static final int XK_Thai_maichattawa = 0x0deb; /* U+0E4B THAI CHARACTER MAI CHATTAWA */ public static final int XK_Thai_thanthakhat = 0x0dec; /* U+0E4C THAI CHARACTER THANTHAKHAT */ public static final int XK_Thai_nikhahit = 0x0ded; /* U+0E4D THAI CHARACTER NIKHAHIT */ public static final int XK_Thai_leksun = 0x0df0; /* U+0E50 THAI DIGIT ZERO */ public static final int XK_Thai_leknung = 0x0df1; /* U+0E51 THAI DIGIT ONE */ public static final int XK_Thai_leksong = 0x0df2; /* U+0E52 THAI DIGIT TWO */ public static final int XK_Thai_leksam = 0x0df3; /* U+0E53 THAI DIGIT THREE */ public static final int XK_Thai_leksi = 0x0df4; /* U+0E54 THAI DIGIT FOUR */ public static final int XK_Thai_lekha = 0x0df5; /* U+0E55 THAI DIGIT FIVE */ public static final int XK_Thai_lekhok = 0x0df6; /* U+0E56 THAI DIGIT SIX */ public static final int XK_Thai_lekchet = 0x0df7; /* U+0E57 THAI DIGIT SEVEN */ public static final int XK_Thai_lekpaet = 0x0df8; /* U+0E58 THAI DIGIT EIGHT */ public static final int XK_Thai_lekkao = 0x0df9; /* U+0E59 THAI DIGIT NINE */ /* * Korean * Byte 3 = 0x0e */ public static final int XK_Hangul = 0xff31; /* Hangul start/stop(toggle) */ public static final int XK_Hangul_Start = 0xff32; /* Hangul start */ public static final int XK_Hangul_End = 0xff33; /* Hangul end, English start */ public static final int XK_Hangul_Hanja = 0xff34; /* Start Hangul->Hanja Conversion */ public static final int XK_Hangul_Jamo = 0xff35; /* Hangul Jamo mode */ public static final int XK_Hangul_Romaja = 0xff36; /* Hangul Romaja mode */ public static final int XK_Hangul_Codeinput = 0xff37; /* Hangul code input mode */ public static final int XK_Hangul_Jeonja = 0xff38; /* Jeonja mode */ public static final int XK_Hangul_Banja = 0xff39; /* Banja mode */ public static final int XK_Hangul_PreHanja = 0xff3a; /* Pre Hanja conversion */ public static final int XK_Hangul_PostHanja = 0xff3b; /* Post Hanja conversion */ public static final int XK_Hangul_SingleCandidate = 0xff3c; /* Single candidate */ public static final int XK_Hangul_MultipleCandidate = 0xff3d; /* Multiple candidate */ public static final int XK_Hangul_PreviousCandidate = 0xff3e; /* Previous candidate */ public static final int XK_Hangul_Special = 0xff3f; /* Special symbols */ public static final int XK_Hangul_switch = 0xff7e; /* Alias for mode_switch */ /* Hangul Consonant Characters */ public static final int XK_Hangul_Kiyeog = 0x0ea1; public static final int XK_Hangul_SsangKiyeog = 0x0ea2; public static final int XK_Hangul_KiyeogSios = 0x0ea3; public static final int XK_Hangul_Nieun = 0x0ea4; public static final int XK_Hangul_NieunJieuj = 0x0ea5; public static final int XK_Hangul_NieunHieuh = 0x0ea6; public static final int XK_Hangul_Dikeud = 0x0ea7; public static final int XK_Hangul_SsangDikeud = 0x0ea8; public static final int XK_Hangul_Rieul = 0x0ea9; public static final int XK_Hangul_RieulKiyeog = 0x0eaa; public static final int XK_Hangul_RieulMieum = 0x0eab; public static final int XK_Hangul_RieulPieub = 0x0eac; public static final int XK_Hangul_RieulSios = 0x0ead; public static final int XK_Hangul_RieulTieut = 0x0eae; public static final int XK_Hangul_RieulPhieuf = 0x0eaf; public static final int XK_Hangul_RieulHieuh = 0x0eb0; public static final int XK_Hangul_Mieum = 0x0eb1; public static final int XK_Hangul_Pieub = 0x0eb2; public static final int XK_Hangul_SsangPieub = 0x0eb3; public static final int XK_Hangul_PieubSios = 0x0eb4; public static final int XK_Hangul_Sios = 0x0eb5; public static final int XK_Hangul_SsangSios = 0x0eb6; public static final int XK_Hangul_Ieung = 0x0eb7; public static final int XK_Hangul_Jieuj = 0x0eb8; public static final int XK_Hangul_SsangJieuj = 0x0eb9; public static final int XK_Hangul_Cieuc = 0x0eba; public static final int XK_Hangul_Khieuq = 0x0ebb; public static final int XK_Hangul_Tieut = 0x0ebc; public static final int XK_Hangul_Phieuf = 0x0ebd; public static final int XK_Hangul_Hieuh = 0x0ebe; /* Hangul Vowel Characters */ public static final int XK_Hangul_A = 0x0ebf; public static final int XK_Hangul_AE = 0x0ec0; public static final int XK_Hangul_YA = 0x0ec1; public static final int XK_Hangul_YAE = 0x0ec2; public static final int XK_Hangul_EO = 0x0ec3; public static final int XK_Hangul_E = 0x0ec4; public static final int XK_Hangul_YEO = 0x0ec5; public static final int XK_Hangul_YE = 0x0ec6; public static final int XK_Hangul_O = 0x0ec7; public static final int XK_Hangul_WA = 0x0ec8; public static final int XK_Hangul_WAE = 0x0ec9; public static final int XK_Hangul_OE = 0x0eca; public static final int XK_Hangul_YO = 0x0ecb; public static final int XK_Hangul_U = 0x0ecc; public static final int XK_Hangul_WEO = 0x0ecd; public static final int XK_Hangul_WE = 0x0ece; public static final int XK_Hangul_WI = 0x0ecf; public static final int XK_Hangul_YU = 0x0ed0; public static final int XK_Hangul_EU = 0x0ed1; public static final int XK_Hangul_YI = 0x0ed2; public static final int XK_Hangul_I = 0x0ed3; /* Hangul syllable-final (JongSeong) Characters */ public static final int XK_Hangul_J_Kiyeog = 0x0ed4; public static final int XK_Hangul_J_SsangKiyeog = 0x0ed5; public static final int XK_Hangul_J_KiyeogSios = 0x0ed6; public static final int XK_Hangul_J_Nieun = 0x0ed7; public static final int XK_Hangul_J_NieunJieuj = 0x0ed8; public static final int XK_Hangul_J_NieunHieuh = 0x0ed9; public static final int XK_Hangul_J_Dikeud = 0x0eda; public static final int XK_Hangul_J_Rieul = 0x0edb; public static final int XK_Hangul_J_RieulKiyeog = 0x0edc; public static final int XK_Hangul_J_RieulMieum = 0x0edd; public static final int XK_Hangul_J_RieulPieub = 0x0ede; public static final int XK_Hangul_J_RieulSios = 0x0edf; public static final int XK_Hangul_J_RieulTieut = 0x0ee0; public static final int XK_Hangul_J_RieulPhieuf = 0x0ee1; public static final int XK_Hangul_J_RieulHieuh = 0x0ee2; public static final int XK_Hangul_J_Mieum = 0x0ee3; public static final int XK_Hangul_J_Pieub = 0x0ee4; public static final int XK_Hangul_J_PieubSios = 0x0ee5; public static final int XK_Hangul_J_Sios = 0x0ee6; public static final int XK_Hangul_J_SsangSios = 0x0ee7; public static final int XK_Hangul_J_Ieung = 0x0ee8; public static final int XK_Hangul_J_Jieuj = 0x0ee9; public static final int XK_Hangul_J_Cieuc = 0x0eea; public static final int XK_Hangul_J_Khieuq = 0x0eeb; public static final int XK_Hangul_J_Tieut = 0x0eec; public static final int XK_Hangul_J_Phieuf = 0x0eed; public static final int XK_Hangul_J_Hieuh = 0x0eee; /* Ancient Hangul Consonant Characters */ public static final int XK_Hangul_RieulYeorinHieuh = 0x0eef; public static final int XK_Hangul_SunkyeongeumMieum = 0x0ef0; public static final int XK_Hangul_SunkyeongeumPieub = 0x0ef1; public static final int XK_Hangul_PanSios = 0x0ef2; public static final int XK_Hangul_KkogjiDalrinIeung = 0x0ef3; public static final int XK_Hangul_SunkyeongeumPhieuf = 0x0ef4; public static final int XK_Hangul_YeorinHieuh = 0x0ef5; /* Ancient Hangul Vowel Characters */ public static final int XK_Hangul_AraeA = 0x0ef6; public static final int XK_Hangul_AraeAE = 0x0ef7; /* Ancient Hangul syllable-final (JongSeong) Characters */ public static final int XK_Hangul_J_PanSios = 0x0ef8; public static final int XK_Hangul_J_KkogjiDalrinIeung = 0x0ef9; public static final int XK_Hangul_J_YeorinHieuh = 0x0efa; /* Korean currency symbol */ public static final int XK_Korean_Won = 0x0eff; /*(U+20A9 WON SIGN)*/ /* * Armenian */ public static final int XK_Armenian_ligature_ew = 0x1000587; /* U+0587 ARMENIAN SMALL LIGATURE ECH YIWN */ public static final int XK_Armenian_full_stop = 0x1000589; /* U+0589 ARMENIAN FULL STOP */ public static final int XK_Armenian_verjaket = 0x1000589; /* U+0589 ARMENIAN FULL STOP */ public static final int XK_Armenian_separation_mark = 0x100055d; /* U+055D ARMENIAN COMMA */ public static final int XK_Armenian_but = 0x100055d; /* U+055D ARMENIAN COMMA */ public static final int XK_Armenian_hyphen = 0x100058a; /* U+058A ARMENIAN HYPHEN */ public static final int XK_Armenian_yentamna = 0x100058a; /* U+058A ARMENIAN HYPHEN */ public static final int XK_Armenian_exclam = 0x100055c; /* U+055C ARMENIAN EXCLAMATION MARK */ public static final int XK_Armenian_amanak = 0x100055c; /* U+055C ARMENIAN EXCLAMATION MARK */ public static final int XK_Armenian_accent = 0x100055b; /* U+055B ARMENIAN EMPHASIS MARK */ public static final int XK_Armenian_shesht = 0x100055b; /* U+055B ARMENIAN EMPHASIS MARK */ public static final int XK_Armenian_question = 0x100055e; /* U+055E ARMENIAN QUESTION MARK */ public static final int XK_Armenian_paruyk = 0x100055e; /* U+055E ARMENIAN QUESTION MARK */ public static final int XK_Armenian_AYB = 0x1000531; /* U+0531 ARMENIAN CAPITAL LETTER AYB */ public static final int XK_Armenian_ayb = 0x1000561; /* U+0561 ARMENIAN SMALL LETTER AYB */ public static final int XK_Armenian_BEN = 0x1000532; /* U+0532 ARMENIAN CAPITAL LETTER BEN */ public static final int XK_Armenian_ben = 0x1000562; /* U+0562 ARMENIAN SMALL LETTER BEN */ public static final int XK_Armenian_GIM = 0x1000533; /* U+0533 ARMENIAN CAPITAL LETTER GIM */ public static final int XK_Armenian_gim = 0x1000563; /* U+0563 ARMENIAN SMALL LETTER GIM */ public static final int XK_Armenian_DA = 0x1000534; /* U+0534 ARMENIAN CAPITAL LETTER DA */ public static final int XK_Armenian_da = 0x1000564; /* U+0564 ARMENIAN SMALL LETTER DA */ public static final int XK_Armenian_YECH = 0x1000535; /* U+0535 ARMENIAN CAPITAL LETTER ECH */ public static final int XK_Armenian_yech = 0x1000565; /* U+0565 ARMENIAN SMALL LETTER ECH */ public static final int XK_Armenian_ZA = 0x1000536; /* U+0536 ARMENIAN CAPITAL LETTER ZA */ public static final int XK_Armenian_za = 0x1000566; /* U+0566 ARMENIAN SMALL LETTER ZA */ public static final int XK_Armenian_E = 0x1000537; /* U+0537 ARMENIAN CAPITAL LETTER EH */ public static final int XK_Armenian_e = 0x1000567; /* U+0567 ARMENIAN SMALL LETTER EH */ public static final int XK_Armenian_AT = 0x1000538; /* U+0538 ARMENIAN CAPITAL LETTER ET */ public static final int XK_Armenian_at = 0x1000568; /* U+0568 ARMENIAN SMALL LETTER ET */ public static final int XK_Armenian_TO = 0x1000539; /* U+0539 ARMENIAN CAPITAL LETTER TO */ public static final int XK_Armenian_to = 0x1000569; /* U+0569 ARMENIAN SMALL LETTER TO */ public static final int XK_Armenian_ZHE = 0x100053a; /* U+053A ARMENIAN CAPITAL LETTER ZHE */ public static final int XK_Armenian_zhe = 0x100056a; /* U+056A ARMENIAN SMALL LETTER ZHE */ public static final int XK_Armenian_INI = 0x100053b; /* U+053B ARMENIAN CAPITAL LETTER INI */ public static final int XK_Armenian_ini = 0x100056b; /* U+056B ARMENIAN SMALL LETTER INI */ public static final int XK_Armenian_LYUN = 0x100053c; /* U+053C ARMENIAN CAPITAL LETTER LIWN */ public static final int XK_Armenian_lyun = 0x100056c; /* U+056C ARMENIAN SMALL LETTER LIWN */ public static final int XK_Armenian_KHE = 0x100053d; /* U+053D ARMENIAN CAPITAL LETTER XEH */ public static final int XK_Armenian_khe = 0x100056d; /* U+056D ARMENIAN SMALL LETTER XEH */ public static final int XK_Armenian_TSA = 0x100053e; /* U+053E ARMENIAN CAPITAL LETTER CA */ public static final int XK_Armenian_tsa = 0x100056e; /* U+056E ARMENIAN SMALL LETTER CA */ public static final int XK_Armenian_KEN = 0x100053f; /* U+053F ARMENIAN CAPITAL LETTER KEN */ public static final int XK_Armenian_ken = 0x100056f; /* U+056F ARMENIAN SMALL LETTER KEN */ public static final int XK_Armenian_HO = 0x1000540; /* U+0540 ARMENIAN CAPITAL LETTER HO */ public static final int XK_Armenian_ho = 0x1000570; /* U+0570 ARMENIAN SMALL LETTER HO */ public static final int XK_Armenian_DZA = 0x1000541; /* U+0541 ARMENIAN CAPITAL LETTER JA */ public static final int XK_Armenian_dza = 0x1000571; /* U+0571 ARMENIAN SMALL LETTER JA */ public static final int XK_Armenian_GHAT = 0x1000542; /* U+0542 ARMENIAN CAPITAL LETTER GHAD */ public static final int XK_Armenian_ghat = 0x1000572; /* U+0572 ARMENIAN SMALL LETTER GHAD */ public static final int XK_Armenian_TCHE = 0x1000543; /* U+0543 ARMENIAN CAPITAL LETTER CHEH */ public static final int XK_Armenian_tche = 0x1000573; /* U+0573 ARMENIAN SMALL LETTER CHEH */ public static final int XK_Armenian_MEN = 0x1000544; /* U+0544 ARMENIAN CAPITAL LETTER MEN */ public static final int XK_Armenian_men = 0x1000574; /* U+0574 ARMENIAN SMALL LETTER MEN */ public static final int XK_Armenian_HI = 0x1000545; /* U+0545 ARMENIAN CAPITAL LETTER YI */ public static final int XK_Armenian_hi = 0x1000575; /* U+0575 ARMENIAN SMALL LETTER YI */ public static final int XK_Armenian_NU = 0x1000546; /* U+0546 ARMENIAN CAPITAL LETTER NOW */ public static final int XK_Armenian_nu = 0x1000576; /* U+0576 ARMENIAN SMALL LETTER NOW */ public static final int XK_Armenian_SHA = 0x1000547; /* U+0547 ARMENIAN CAPITAL LETTER SHA */ public static final int XK_Armenian_sha = 0x1000577; /* U+0577 ARMENIAN SMALL LETTER SHA */ public static final int XK_Armenian_VO = 0x1000548; /* U+0548 ARMENIAN CAPITAL LETTER VO */ public static final int XK_Armenian_vo = 0x1000578; /* U+0578 ARMENIAN SMALL LETTER VO */ public static final int XK_Armenian_CHA = 0x1000549; /* U+0549 ARMENIAN CAPITAL LETTER CHA */ public static final int XK_Armenian_cha = 0x1000579; /* U+0579 ARMENIAN SMALL LETTER CHA */ public static final int XK_Armenian_PE = 0x100054a; /* U+054A ARMENIAN CAPITAL LETTER PEH */ public static final int XK_Armenian_pe = 0x100057a; /* U+057A ARMENIAN SMALL LETTER PEH */ public static final int XK_Armenian_JE = 0x100054b; /* U+054B ARMENIAN CAPITAL LETTER JHEH */ public static final int XK_Armenian_je = 0x100057b; /* U+057B ARMENIAN SMALL LETTER JHEH */ public static final int XK_Armenian_RA = 0x100054c; /* U+054C ARMENIAN CAPITAL LETTER RA */ public static final int XK_Armenian_ra = 0x100057c; /* U+057C ARMENIAN SMALL LETTER RA */ public static final int XK_Armenian_SE = 0x100054d; /* U+054D ARMENIAN CAPITAL LETTER SEH */ public static final int XK_Armenian_se = 0x100057d; /* U+057D ARMENIAN SMALL LETTER SEH */ public static final int XK_Armenian_VEV = 0x100054e; /* U+054E ARMENIAN CAPITAL LETTER VEW */ public static final int XK_Armenian_vev = 0x100057e; /* U+057E ARMENIAN SMALL LETTER VEW */ public static final int XK_Armenian_TYUN = 0x100054f; /* U+054F ARMENIAN CAPITAL LETTER TIWN */ public static final int XK_Armenian_tyun = 0x100057f; /* U+057F ARMENIAN SMALL LETTER TIWN */ public static final int XK_Armenian_RE = 0x1000550; /* U+0550 ARMENIAN CAPITAL LETTER REH */ public static final int XK_Armenian_re = 0x1000580; /* U+0580 ARMENIAN SMALL LETTER REH */ public static final int XK_Armenian_TSO = 0x1000551; /* U+0551 ARMENIAN CAPITAL LETTER CO */ public static final int XK_Armenian_tso = 0x1000581; /* U+0581 ARMENIAN SMALL LETTER CO */ public static final int XK_Armenian_VYUN = 0x1000552; /* U+0552 ARMENIAN CAPITAL LETTER YIWN */ public static final int XK_Armenian_vyun = 0x1000582; /* U+0582 ARMENIAN SMALL LETTER YIWN */ public static final int XK_Armenian_PYUR = 0x1000553; /* U+0553 ARMENIAN CAPITAL LETTER PIWR */ public static final int XK_Armenian_pyur = 0x1000583; /* U+0583 ARMENIAN SMALL LETTER PIWR */ public static final int XK_Armenian_KE = 0x1000554; /* U+0554 ARMENIAN CAPITAL LETTER KEH */ public static final int XK_Armenian_ke = 0x1000584; /* U+0584 ARMENIAN SMALL LETTER KEH */ public static final int XK_Armenian_O = 0x1000555; /* U+0555 ARMENIAN CAPITAL LETTER OH */ public static final int XK_Armenian_o = 0x1000585; /* U+0585 ARMENIAN SMALL LETTER OH */ public static final int XK_Armenian_FE = 0x1000556; /* U+0556 ARMENIAN CAPITAL LETTER FEH */ public static final int XK_Armenian_fe = 0x1000586; /* U+0586 ARMENIAN SMALL LETTER FEH */ public static final int XK_Armenian_apostrophe = 0x100055a; /* U+055A ARMENIAN APOSTROPHE */ /* * Georgian */ public static final int XK_Georgian_an = 0x10010d0; /* U+10D0 GEORGIAN LETTER AN */ public static final int XK_Georgian_ban = 0x10010d1; /* U+10D1 GEORGIAN LETTER BAN */ public static final int XK_Georgian_gan = 0x10010d2; /* U+10D2 GEORGIAN LETTER GAN */ public static final int XK_Georgian_don = 0x10010d3; /* U+10D3 GEORGIAN LETTER DON */ public static final int XK_Georgian_en = 0x10010d4; /* U+10D4 GEORGIAN LETTER EN */ public static final int XK_Georgian_vin = 0x10010d5; /* U+10D5 GEORGIAN LETTER VIN */ public static final int XK_Georgian_zen = 0x10010d6; /* U+10D6 GEORGIAN LETTER ZEN */ public static final int XK_Georgian_tan = 0x10010d7; /* U+10D7 GEORGIAN LETTER TAN */ public static final int XK_Georgian_in = 0x10010d8; /* U+10D8 GEORGIAN LETTER IN */ public static final int XK_Georgian_kan = 0x10010d9; /* U+10D9 GEORGIAN LETTER KAN */ public static final int XK_Georgian_las = 0x10010da; /* U+10DA GEORGIAN LETTER LAS */ public static final int XK_Georgian_man = 0x10010db; /* U+10DB GEORGIAN LETTER MAN */ public static final int XK_Georgian_nar = 0x10010dc; /* U+10DC GEORGIAN LETTER NAR */ public static final int XK_Georgian_on = 0x10010dd; /* U+10DD GEORGIAN LETTER ON */ public static final int XK_Georgian_par = 0x10010de; /* U+10DE GEORGIAN LETTER PAR */ public static final int XK_Georgian_zhar = 0x10010df; /* U+10DF GEORGIAN LETTER ZHAR */ public static final int XK_Georgian_rae = 0x10010e0; /* U+10E0 GEORGIAN LETTER RAE */ public static final int XK_Georgian_san = 0x10010e1; /* U+10E1 GEORGIAN LETTER SAN */ public static final int XK_Georgian_tar = 0x10010e2; /* U+10E2 GEORGIAN LETTER TAR */ public static final int XK_Georgian_un = 0x10010e3; /* U+10E3 GEORGIAN LETTER UN */ public static final int XK_Georgian_phar = 0x10010e4; /* U+10E4 GEORGIAN LETTER PHAR */ public static final int XK_Georgian_khar = 0x10010e5; /* U+10E5 GEORGIAN LETTER KHAR */ public static final int XK_Georgian_ghan = 0x10010e6; /* U+10E6 GEORGIAN LETTER GHAN */ public static final int XK_Georgian_qar = 0x10010e7; /* U+10E7 GEORGIAN LETTER QAR */ public static final int XK_Georgian_shin = 0x10010e8; /* U+10E8 GEORGIAN LETTER SHIN */ public static final int XK_Georgian_chin = 0x10010e9; /* U+10E9 GEORGIAN LETTER CHIN */ public static final int XK_Georgian_can = 0x10010ea; /* U+10EA GEORGIAN LETTER CAN */ public static final int XK_Georgian_jil = 0x10010eb; /* U+10EB GEORGIAN LETTER JIL */ public static final int XK_Georgian_cil = 0x10010ec; /* U+10EC GEORGIAN LETTER CIL */ public static final int XK_Georgian_char = 0x10010ed; /* U+10ED GEORGIAN LETTER CHAR */ public static final int XK_Georgian_xan = 0x10010ee; /* U+10EE GEORGIAN LETTER XAN */ public static final int XK_Georgian_jhan = 0x10010ef; /* U+10EF GEORGIAN LETTER JHAN */ public static final int XK_Georgian_hae = 0x10010f0; /* U+10F0 GEORGIAN LETTER HAE */ public static final int XK_Georgian_he = 0x10010f1; /* U+10F1 GEORGIAN LETTER HE */ public static final int XK_Georgian_hie = 0x10010f2; /* U+10F2 GEORGIAN LETTER HIE */ public static final int XK_Georgian_we = 0x10010f3; /* U+10F3 GEORGIAN LETTER WE */ public static final int XK_Georgian_har = 0x10010f4; /* U+10F4 GEORGIAN LETTER HAR */ public static final int XK_Georgian_hoe = 0x10010f5; /* U+10F5 GEORGIAN LETTER HOE */ public static final int XK_Georgian_fi = 0x10010f6; /* U+10F6 GEORGIAN LETTER FI */ /* * Azeri (and other Turkic or Caucasian languages) */ /* latin */ public static final int XK_Xabovedot = 0x1001e8a; /* U+1E8A LATIN CAPITAL LETTER X WITH DOT ABOVE */ public static final int XK_Ibreve = 0x100012c; /* U+012C LATIN CAPITAL LETTER I WITH BREVE */ public static final int XK_Zstroke = 0x10001b5; /* U+01B5 LATIN CAPITAL LETTER Z WITH STROKE */ public static final int XK_Gcaron = 0x10001e6; /* U+01E6 LATIN CAPITAL LETTER G WITH CARON */ public static final int XK_Ocaron = 0x10001d1; /* U+01D2 LATIN CAPITAL LETTER O WITH CARON */ public static final int XK_Obarred = 0x100019f; /* U+019F LATIN CAPITAL LETTER O WITH MIDDLE TILDE */ public static final int XK_xabovedot = 0x1001e8b; /* U+1E8B LATIN SMALL LETTER X WITH DOT ABOVE */ public static final int XK_ibreve = 0x100012d; /* U+012D LATIN SMALL LETTER I WITH BREVE */ public static final int XK_zstroke = 0x10001b6; /* U+01B6 LATIN SMALL LETTER Z WITH STROKE */ public static final int XK_gcaron = 0x10001e7; /* U+01E7 LATIN SMALL LETTER G WITH CARON */ public static final int XK_ocaron = 0x10001d2; /* U+01D2 LATIN SMALL LETTER O WITH CARON */ public static final int XK_obarred = 0x1000275; /* U+0275 LATIN SMALL LETTER BARRED O */ public static final int XK_SCHWA = 0x100018f; /* U+018F LATIN CAPITAL LETTER SCHWA */ public static final int XK_schwa = 0x1000259; /* U+0259 LATIN SMALL LETTER SCHWA */ /* those are not really Caucasus */ /* For Inupiak */ public static final int XK_Lbelowdot = 0x1001e36; /* U+1E36 LATIN CAPITAL LETTER L WITH DOT BELOW */ public static final int XK_lbelowdot = 0x1001e37; /* U+1E37 LATIN SMALL LETTER L WITH DOT BELOW */ /* * Vietnamese */ public static final int XK_Abelowdot = 0x1001ea0; /* U+1EA0 LATIN CAPITAL LETTER A WITH DOT BELOW */ public static final int XK_abelowdot = 0x1001ea1; /* U+1EA1 LATIN SMALL LETTER A WITH DOT BELOW */ public static final int XK_Ahook = 0x1001ea2; /* U+1EA2 LATIN CAPITAL LETTER A WITH HOOK ABOVE */ public static final int XK_ahook = 0x1001ea3; /* U+1EA3 LATIN SMALL LETTER A WITH HOOK ABOVE */ public static final int XK_Acircumflexacute = 0x1001ea4; /* U+1EA4 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE */ public static final int XK_acircumflexacute = 0x1001ea5; /* U+1EA5 LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE */ public static final int XK_Acircumflexgrave = 0x1001ea6; /* U+1EA6 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE */ public static final int XK_acircumflexgrave = 0x1001ea7; /* U+1EA7 LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE */ public static final int XK_Acircumflexhook = 0x1001ea8; /* U+1EA8 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */ public static final int XK_acircumflexhook = 0x1001ea9; /* U+1EA9 LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */ public static final int XK_Acircumflextilde = 0x1001eaa; /* U+1EAA LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE */ public static final int XK_acircumflextilde = 0x1001eab; /* U+1EAB LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE */ public static final int XK_Acircumflexbelowdot = 0x1001eac; /* U+1EAC LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW */ public static final int XK_acircumflexbelowdot = 0x1001ead; /* U+1EAD LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW */ public static final int XK_Abreveacute = 0x1001eae; /* U+1EAE LATIN CAPITAL LETTER A WITH BREVE AND ACUTE */ public static final int XK_abreveacute = 0x1001eaf; /* U+1EAF LATIN SMALL LETTER A WITH BREVE AND ACUTE */ public static final int XK_Abrevegrave = 0x1001eb0; /* U+1EB0 LATIN CAPITAL LETTER A WITH BREVE AND GRAVE */ public static final int XK_abrevegrave = 0x1001eb1; /* U+1EB1 LATIN SMALL LETTER A WITH BREVE AND GRAVE */ public static final int XK_Abrevehook = 0x1001eb2; /* U+1EB2 LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE */ public static final int XK_abrevehook = 0x1001eb3; /* U+1EB3 LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE */ public static final int XK_Abrevetilde = 0x1001eb4; /* U+1EB4 LATIN CAPITAL LETTER A WITH BREVE AND TILDE */ public static final int XK_abrevetilde = 0x1001eb5; /* U+1EB5 LATIN SMALL LETTER A WITH BREVE AND TILDE */ public static final int XK_Abrevebelowdot = 0x1001eb6; /* U+1EB6 LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW */ public static final int XK_abrevebelowdot = 0x1001eb7; /* U+1EB7 LATIN SMALL LETTER A WITH BREVE AND DOT BELOW */ public static final int XK_Ebelowdot = 0x1001eb8; /* U+1EB8 LATIN CAPITAL LETTER E WITH DOT BELOW */ public static final int XK_ebelowdot = 0x1001eb9; /* U+1EB9 LATIN SMALL LETTER E WITH DOT BELOW */ public static final int XK_Ehook = 0x1001eba; /* U+1EBA LATIN CAPITAL LETTER E WITH HOOK ABOVE */ public static final int XK_ehook = 0x1001ebb; /* U+1EBB LATIN SMALL LETTER E WITH HOOK ABOVE */ public static final int XK_Etilde = 0x1001ebc; /* U+1EBC LATIN CAPITAL LETTER E WITH TILDE */ public static final int XK_etilde = 0x1001ebd; /* U+1EBD LATIN SMALL LETTER E WITH TILDE */ public static final int XK_Ecircumflexacute = 0x1001ebe; /* U+1EBE LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE */ public static final int XK_ecircumflexacute = 0x1001ebf; /* U+1EBF LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE */ public static final int XK_Ecircumflexgrave = 0x1001ec0; /* U+1EC0 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE */ public static final int XK_ecircumflexgrave = 0x1001ec1; /* U+1EC1 LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE */ public static final int XK_Ecircumflexhook = 0x1001ec2; /* U+1EC2 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */ public static final int XK_ecircumflexhook = 0x1001ec3; /* U+1EC3 LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */ public static final int XK_Ecircumflextilde = 0x1001ec4; /* U+1EC4 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE */ public static final int XK_ecircumflextilde = 0x1001ec5; /* U+1EC5 LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE */ public static final int XK_Ecircumflexbelowdot = 0x1001ec6; /* U+1EC6 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW */ public static final int XK_ecircumflexbelowdot = 0x1001ec7; /* U+1EC7 LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW */ public static final int XK_Ihook = 0x1001ec8; /* U+1EC8 LATIN CAPITAL LETTER I WITH HOOK ABOVE */ public static final int XK_ihook = 0x1001ec9; /* U+1EC9 LATIN SMALL LETTER I WITH HOOK ABOVE */ public static final int XK_Ibelowdot = 0x1001eca; /* U+1ECA LATIN CAPITAL LETTER I WITH DOT BELOW */ public static final int XK_ibelowdot = 0x1001ecb; /* U+1ECB LATIN SMALL LETTER I WITH DOT BELOW */ public static final int XK_Obelowdot = 0x1001ecc; /* U+1ECC LATIN CAPITAL LETTER O WITH DOT BELOW */ public static final int XK_obelowdot = 0x1001ecd; /* U+1ECD LATIN SMALL LETTER O WITH DOT BELOW */ public static final int XK_Ohook = 0x1001ece; /* U+1ECE LATIN CAPITAL LETTER O WITH HOOK ABOVE */ public static final int XK_ohook = 0x1001ecf; /* U+1ECF LATIN SMALL LETTER O WITH HOOK ABOVE */ public static final int XK_Ocircumflexacute = 0x1001ed0; /* U+1ED0 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE */ public static final int XK_ocircumflexacute = 0x1001ed1; /* U+1ED1 LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE */ public static final int XK_Ocircumflexgrave = 0x1001ed2; /* U+1ED2 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE */ public static final int XK_ocircumflexgrave = 0x1001ed3; /* U+1ED3 LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE */ public static final int XK_Ocircumflexhook = 0x1001ed4; /* U+1ED4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */ public static final int XK_ocircumflexhook = 0x1001ed5; /* U+1ED5 LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */ public static final int XK_Ocircumflextilde = 0x1001ed6; /* U+1ED6 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE */ public static final int XK_ocircumflextilde = 0x1001ed7; /* U+1ED7 LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE */ public static final int XK_Ocircumflexbelowdot = 0x1001ed8; /* U+1ED8 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW */ public static final int XK_ocircumflexbelowdot = 0x1001ed9; /* U+1ED9 LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW */ public static final int XK_Ohornacute = 0x1001eda; /* U+1EDA LATIN CAPITAL LETTER O WITH HORN AND ACUTE */ public static final int XK_ohornacute = 0x1001edb; /* U+1EDB LATIN SMALL LETTER O WITH HORN AND ACUTE */ public static final int XK_Ohorngrave = 0x1001edc; /* U+1EDC LATIN CAPITAL LETTER O WITH HORN AND GRAVE */ public static final int XK_ohorngrave = 0x1001edd; /* U+1EDD LATIN SMALL LETTER O WITH HORN AND GRAVE */ public static final int XK_Ohornhook = 0x1001ede; /* U+1EDE LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE */ public static final int XK_ohornhook = 0x1001edf; /* U+1EDF LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE */ public static final int XK_Ohorntilde = 0x1001ee0; /* U+1EE0 LATIN CAPITAL LETTER O WITH HORN AND TILDE */ public static final int XK_ohorntilde = 0x1001ee1; /* U+1EE1 LATIN SMALL LETTER O WITH HORN AND TILDE */ public static final int XK_Ohornbelowdot = 0x1001ee2; /* U+1EE2 LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW */ public static final int XK_ohornbelowdot = 0x1001ee3; /* U+1EE3 LATIN SMALL LETTER O WITH HORN AND DOT BELOW */ public static final int XK_Ubelowdot = 0x1001ee4; /* U+1EE4 LATIN CAPITAL LETTER U WITH DOT BELOW */ public static final int XK_ubelowdot = 0x1001ee5; /* U+1EE5 LATIN SMALL LETTER U WITH DOT BELOW */ public static final int XK_Uhook = 0x1001ee6; /* U+1EE6 LATIN CAPITAL LETTER U WITH HOOK ABOVE */ public static final int XK_uhook = 0x1001ee7; /* U+1EE7 LATIN SMALL LETTER U WITH HOOK ABOVE */ public static final int XK_Uhornacute = 0x1001ee8; /* U+1EE8 LATIN CAPITAL LETTER U WITH HORN AND ACUTE */ public static final int XK_uhornacute = 0x1001ee9; /* U+1EE9 LATIN SMALL LETTER U WITH HORN AND ACUTE */ public static final int XK_Uhorngrave = 0x1001eea; /* U+1EEA LATIN CAPITAL LETTER U WITH HORN AND GRAVE */ public static final int XK_uhorngrave = 0x1001eeb; /* U+1EEB LATIN SMALL LETTER U WITH HORN AND GRAVE */ public static final int XK_Uhornhook = 0x1001eec; /* U+1EEC LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE */ public static final int XK_uhornhook = 0x1001eed; /* U+1EED LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE */ public static final int XK_Uhorntilde = 0x1001eee; /* U+1EEE LATIN CAPITAL LETTER U WITH HORN AND TILDE */ public static final int XK_uhorntilde = 0x1001eef; /* U+1EEF LATIN SMALL LETTER U WITH HORN AND TILDE */ public static final int XK_Uhornbelowdot = 0x1001ef0; /* U+1EF0 LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW */ public static final int XK_uhornbelowdot = 0x1001ef1; /* U+1EF1 LATIN SMALL LETTER U WITH HORN AND DOT BELOW */ public static final int XK_Ybelowdot = 0x1001ef4; /* U+1EF4 LATIN CAPITAL LETTER Y WITH DOT BELOW */ public static final int XK_ybelowdot = 0x1001ef5; /* U+1EF5 LATIN SMALL LETTER Y WITH DOT BELOW */ public static final int XK_Yhook = 0x1001ef6; /* U+1EF6 LATIN CAPITAL LETTER Y WITH HOOK ABOVE */ public static final int XK_yhook = 0x1001ef7; /* U+1EF7 LATIN SMALL LETTER Y WITH HOOK ABOVE */ public static final int XK_Ytilde = 0x1001ef8; /* U+1EF8 LATIN CAPITAL LETTER Y WITH TILDE */ public static final int XK_ytilde = 0x1001ef9; /* U+1EF9 LATIN SMALL LETTER Y WITH TILDE */ public static final int XK_Ohorn = 0x10001a0; /* U+01A0 LATIN CAPITAL LETTER O WITH HORN */ public static final int XK_ohorn = 0x10001a1; /* U+01A1 LATIN SMALL LETTER O WITH HORN */ public static final int XK_Uhorn = 0x10001af; /* U+01AF LATIN CAPITAL LETTER U WITH HORN */ public static final int XK_uhorn = 0x10001b0; /* U+01B0 LATIN SMALL LETTER U WITH HORN */ public static final int XK_EcuSign = 0x10020a0; /* U+20A0 EURO-CURRENCY SIGN */ public static final int XK_ColonSign = 0x10020a1; /* U+20A1 COLON SIGN */ public static final int XK_CruzeiroSign = 0x10020a2; /* U+20A2 CRUZEIRO SIGN */ public static final int XK_FFrancSign = 0x10020a3; /* U+20A3 FRENCH FRANC SIGN */ public static final int XK_LiraSign = 0x10020a4; /* U+20A4 LIRA SIGN */ public static final int XK_MillSign = 0x10020a5; /* U+20A5 MILL SIGN */ public static final int XK_NairaSign = 0x10020a6; /* U+20A6 NAIRA SIGN */ public static final int XK_PesetaSign = 0x10020a7; /* U+20A7 PESETA SIGN */ public static final int XK_RupeeSign = 0x10020a8; /* U+20A8 RUPEE SIGN */ public static final int XK_WonSign = 0x10020a9; /* U+20A9 WON SIGN */ public static final int XK_NewSheqelSign = 0x10020aa; /* U+20AA NEW SHEQEL SIGN */ public static final int XK_DongSign = 0x10020ab; /* U+20AB DONG SIGN */ public static final int XK_EuroSign = 0x20ac; /* U+20AC EURO SIGN */ /* one, two and three are defined above. */ public static final int XK_zerosuperior = 0x1002070; /* U+2070 SUPERSCRIPT ZERO */ public static final int XK_foursuperior = 0x1002074; /* U+2074 SUPERSCRIPT FOUR */ public static final int XK_fivesuperior = 0x1002075; /* U+2075 SUPERSCRIPT FIVE */ public static final int XK_sixsuperior = 0x1002076; /* U+2076 SUPERSCRIPT SIX */ public static final int XK_sevensuperior = 0x1002077; /* U+2077 SUPERSCRIPT SEVEN */ public static final int XK_eightsuperior = 0x1002078; /* U+2078 SUPERSCRIPT EIGHT */ public static final int XK_ninesuperior = 0x1002079; /* U+2079 SUPERSCRIPT NINE */ public static final int XK_zerosubscript = 0x1002080; /* U+2080 SUBSCRIPT ZERO */ public static final int XK_onesubscript = 0x1002081; /* U+2081 SUBSCRIPT ONE */ public static final int XK_twosubscript = 0x1002082; /* U+2082 SUBSCRIPT TWO */ public static final int XK_threesubscript = 0x1002083; /* U+2083 SUBSCRIPT THREE */ public static final int XK_foursubscript = 0x1002084; /* U+2084 SUBSCRIPT FOUR */ public static final int XK_fivesubscript = 0x1002085; /* U+2085 SUBSCRIPT FIVE */ public static final int XK_sixsubscript = 0x1002086; /* U+2086 SUBSCRIPT SIX */ public static final int XK_sevensubscript = 0x1002087; /* U+2087 SUBSCRIPT SEVEN */ public static final int XK_eightsubscript = 0x1002088; /* U+2088 SUBSCRIPT EIGHT */ public static final int XK_ninesubscript = 0x1002089; /* U+2089 SUBSCRIPT NINE */ public static final int XK_partdifferential = 0x1002202; /* U+2202 PARTIAL DIFFERENTIAL */ public static final int XK_emptyset = 0x1002205; /* U+2205 NULL SET */ public static final int XK_elementof = 0x1002208; /* U+2208 ELEMENT OF */ public static final int XK_notelementof = 0x1002209; /* U+2209 NOT AN ELEMENT OF */ public static final int XK_containsas = 0x100220B; /* U+220B CONTAINS AS MEMBER */ public static final int XK_squareroot = 0x100221A; /* U+221A SQUARE ROOT */ public static final int XK_cuberoot = 0x100221B; /* U+221B CUBE ROOT */ public static final int XK_fourthroot = 0x100221C; /* U+221C FOURTH ROOT */ public static final int XK_dintegral = 0x100222C; /* U+222C DOUBLE INTEGRAL */ public static final int XK_tintegral = 0x100222D; /* U+222D TRIPLE INTEGRAL */ public static final int XK_because = 0x1002235; /* U+2235 BECAUSE */ public static final int XK_approxeq = 0x1002248; /* U+2245 ALMOST EQUAL TO */ public static final int XK_notapproxeq = 0x1002247; /* U+2247 NOT ALMOST EQUAL TO */ public static final int XK_notidentical = 0x1002262; /* U+2262 NOT IDENTICAL TO */ public static final int XK_stricteq = 0x1002263; /* U+2263 STRICTLY EQUIVALENT TO */ public static final int XK_braille_dot_1 = 0xfff1; public static final int XK_braille_dot_2 = 0xfff2; public static final int XK_braille_dot_3 = 0xfff3; public static final int XK_braille_dot_4 = 0xfff4; public static final int XK_braille_dot_5 = 0xfff5; public static final int XK_braille_dot_6 = 0xfff6; public static final int XK_braille_dot_7 = 0xfff7; public static final int XK_braille_dot_8 = 0xfff8; public static final int XK_braille_dot_9 = 0xfff9; public static final int XK_braille_dot_10 = 0xfffa; public static final int XK_braille_blank = 0x1002800; /* U+2800 BRAILLE PATTERN BLANK */ public static final int XK_braille_dots_1 = 0x1002801; /* U+2801 BRAILLE PATTERN DOTS-1 */ public static final int XK_braille_dots_2 = 0x1002802; /* U+2802 BRAILLE PATTERN DOTS-2 */ public static final int XK_braille_dots_12 = 0x1002803; /* U+2803 BRAILLE PATTERN DOTS-12 */ public static final int XK_braille_dots_3 = 0x1002804; /* U+2804 BRAILLE PATTERN DOTS-3 */ public static final int XK_braille_dots_13 = 0x1002805; /* U+2805 BRAILLE PATTERN DOTS-13 */ public static final int XK_braille_dots_23 = 0x1002806; /* U+2806 BRAILLE PATTERN DOTS-23 */ public static final int XK_braille_dots_123 = 0x1002807; /* U+2807 BRAILLE PATTERN DOTS-123 */ public static final int XK_braille_dots_4 = 0x1002808; /* U+2808 BRAILLE PATTERN DOTS-4 */ public static final int XK_braille_dots_14 = 0x1002809; /* U+2809 BRAILLE PATTERN DOTS-14 */ public static final int XK_braille_dots_24 = 0x100280a; /* U+280a BRAILLE PATTERN DOTS-24 */ public static final int XK_braille_dots_124 = 0x100280b; /* U+280b BRAILLE PATTERN DOTS-124 */ public static final int XK_braille_dots_34 = 0x100280c; /* U+280c BRAILLE PATTERN DOTS-34 */ public static final int XK_braille_dots_134 = 0x100280d; /* U+280d BRAILLE PATTERN DOTS-134 */ public static final int XK_braille_dots_234 = 0x100280e; /* U+280e BRAILLE PATTERN DOTS-234 */ public static final int XK_braille_dots_1234 = 0x100280f; /* U+280f BRAILLE PATTERN DOTS-1234 */ public static final int XK_braille_dots_5 = 0x1002810; /* U+2810 BRAILLE PATTERN DOTS-5 */ public static final int XK_braille_dots_15 = 0x1002811; /* U+2811 BRAILLE PATTERN DOTS-15 */ public static final int XK_braille_dots_25 = 0x1002812; /* U+2812 BRAILLE PATTERN DOTS-25 */ public static final int XK_braille_dots_125 = 0x1002813; /* U+2813 BRAILLE PATTERN DOTS-125 */ public static final int XK_braille_dots_35 = 0x1002814; /* U+2814 BRAILLE PATTERN DOTS-35 */ public static final int XK_braille_dots_135 = 0x1002815; /* U+2815 BRAILLE PATTERN DOTS-135 */ public static final int XK_braille_dots_235 = 0x1002816; /* U+2816 BRAILLE PATTERN DOTS-235 */ public static final int XK_braille_dots_1235 = 0x1002817; /* U+2817 BRAILLE PATTERN DOTS-1235 */ public static final int XK_braille_dots_45 = 0x1002818; /* U+2818 BRAILLE PATTERN DOTS-45 */ public static final int XK_braille_dots_145 = 0x1002819; /* U+2819 BRAILLE PATTERN DOTS-145 */ public static final int XK_braille_dots_245 = 0x100281a; /* U+281a BRAILLE PATTERN DOTS-245 */ public static final int XK_braille_dots_1245 = 0x100281b; /* U+281b BRAILLE PATTERN DOTS-1245 */ public static final int XK_braille_dots_345 = 0x100281c; /* U+281c BRAILLE PATTERN DOTS-345 */ public static final int XK_braille_dots_1345 = 0x100281d; /* U+281d BRAILLE PATTERN DOTS-1345 */ public static final int XK_braille_dots_2345 = 0x100281e; /* U+281e BRAILLE PATTERN DOTS-2345 */ public static final int XK_braille_dots_12345 = 0x100281f; /* U+281f BRAILLE PATTERN DOTS-12345 */ public static final int XK_braille_dots_6 = 0x1002820; /* U+2820 BRAILLE PATTERN DOTS-6 */ public static final int XK_braille_dots_16 = 0x1002821; /* U+2821 BRAILLE PATTERN DOTS-16 */ public static final int XK_braille_dots_26 = 0x1002822; /* U+2822 BRAILLE PATTERN DOTS-26 */ public static final int XK_braille_dots_126 = 0x1002823; /* U+2823 BRAILLE PATTERN DOTS-126 */ public static final int XK_braille_dots_36 = 0x1002824; /* U+2824 BRAILLE PATTERN DOTS-36 */ public static final int XK_braille_dots_136 = 0x1002825; /* U+2825 BRAILLE PATTERN DOTS-136 */ public static final int XK_braille_dots_236 = 0x1002826; /* U+2826 BRAILLE PATTERN DOTS-236 */ public static final int XK_braille_dots_1236 = 0x1002827; /* U+2827 BRAILLE PATTERN DOTS-1236 */ public static final int XK_braille_dots_46 = 0x1002828; /* U+2828 BRAILLE PATTERN DOTS-46 */ public static final int XK_braille_dots_146 = 0x1002829; /* U+2829 BRAILLE PATTERN DOTS-146 */ public static final int XK_braille_dots_246 = 0x100282a; /* U+282a BRAILLE PATTERN DOTS-246 */ public static final int XK_braille_dots_1246 = 0x100282b; /* U+282b BRAILLE PATTERN DOTS-1246 */ public static final int XK_braille_dots_346 = 0x100282c; /* U+282c BRAILLE PATTERN DOTS-346 */ public static final int XK_braille_dots_1346 = 0x100282d; /* U+282d BRAILLE PATTERN DOTS-1346 */ public static final int XK_braille_dots_2346 = 0x100282e; /* U+282e BRAILLE PATTERN DOTS-2346 */ public static final int XK_braille_dots_12346 = 0x100282f; /* U+282f BRAILLE PATTERN DOTS-12346 */ public static final int XK_braille_dots_56 = 0x1002830; /* U+2830 BRAILLE PATTERN DOTS-56 */ public static final int XK_braille_dots_156 = 0x1002831; /* U+2831 BRAILLE PATTERN DOTS-156 */ public static final int XK_braille_dots_256 = 0x1002832; /* U+2832 BRAILLE PATTERN DOTS-256 */ public static final int XK_braille_dots_1256 = 0x1002833; /* U+2833 BRAILLE PATTERN DOTS-1256 */ public static final int XK_braille_dots_356 = 0x1002834; /* U+2834 BRAILLE PATTERN DOTS-356 */ public static final int XK_braille_dots_1356 = 0x1002835; /* U+2835 BRAILLE PATTERN DOTS-1356 */ public static final int XK_braille_dots_2356 = 0x1002836; /* U+2836 BRAILLE PATTERN DOTS-2356 */ public static final int XK_braille_dots_12356 = 0x1002837; /* U+2837 BRAILLE PATTERN DOTS-12356 */ public static final int XK_braille_dots_456 = 0x1002838; /* U+2838 BRAILLE PATTERN DOTS-456 */ public static final int XK_braille_dots_1456 = 0x1002839; /* U+2839 BRAILLE PATTERN DOTS-1456 */ public static final int XK_braille_dots_2456 = 0x100283a; /* U+283a BRAILLE PATTERN DOTS-2456 */ public static final int XK_braille_dots_12456 = 0x100283b; /* U+283b BRAILLE PATTERN DOTS-12456 */ public static final int XK_braille_dots_3456 = 0x100283c; /* U+283c BRAILLE PATTERN DOTS-3456 */ public static final int XK_braille_dots_13456 = 0x100283d; /* U+283d BRAILLE PATTERN DOTS-13456 */ public static final int XK_braille_dots_23456 = 0x100283e; /* U+283e BRAILLE PATTERN DOTS-23456 */ public static final int XK_braille_dots_123456 = 0x100283f; /* U+283f BRAILLE PATTERN DOTS-123456 */ public static final int XK_braille_dots_7 = 0x1002840; /* U+2840 BRAILLE PATTERN DOTS-7 */ public static final int XK_braille_dots_17 = 0x1002841; /* U+2841 BRAILLE PATTERN DOTS-17 */ public static final int XK_braille_dots_27 = 0x1002842; /* U+2842 BRAILLE PATTERN DOTS-27 */ public static final int XK_braille_dots_127 = 0x1002843; /* U+2843 BRAILLE PATTERN DOTS-127 */ public static final int XK_braille_dots_37 = 0x1002844; /* U+2844 BRAILLE PATTERN DOTS-37 */ public static final int XK_braille_dots_137 = 0x1002845; /* U+2845 BRAILLE PATTERN DOTS-137 */ public static final int XK_braille_dots_237 = 0x1002846; /* U+2846 BRAILLE PATTERN DOTS-237 */ public static final int XK_braille_dots_1237 = 0x1002847; /* U+2847 BRAILLE PATTERN DOTS-1237 */ public static final int XK_braille_dots_47 = 0x1002848; /* U+2848 BRAILLE PATTERN DOTS-47 */ public static final int XK_braille_dots_147 = 0x1002849; /* U+2849 BRAILLE PATTERN DOTS-147 */ public static final int XK_braille_dots_247 = 0x100284a; /* U+284a BRAILLE PATTERN DOTS-247 */ public static final int XK_braille_dots_1247 = 0x100284b; /* U+284b BRAILLE PATTERN DOTS-1247 */ public static final int XK_braille_dots_347 = 0x100284c; /* U+284c BRAILLE PATTERN DOTS-347 */ public static final int XK_braille_dots_1347 = 0x100284d; /* U+284d BRAILLE PATTERN DOTS-1347 */ public static final int XK_braille_dots_2347 = 0x100284e; /* U+284e BRAILLE PATTERN DOTS-2347 */ public static final int XK_braille_dots_12347 = 0x100284f; /* U+284f BRAILLE PATTERN DOTS-12347 */ public static final int XK_braille_dots_57 = 0x1002850; /* U+2850 BRAILLE PATTERN DOTS-57 */ public static final int XK_braille_dots_157 = 0x1002851; /* U+2851 BRAILLE PATTERN DOTS-157 */ public static final int XK_braille_dots_257 = 0x1002852; /* U+2852 BRAILLE PATTERN DOTS-257 */ public static final int XK_braille_dots_1257 = 0x1002853; /* U+2853 BRAILLE PATTERN DOTS-1257 */ public static final int XK_braille_dots_357 = 0x1002854; /* U+2854 BRAILLE PATTERN DOTS-357 */ public static final int XK_braille_dots_1357 = 0x1002855; /* U+2855 BRAILLE PATTERN DOTS-1357 */ public static final int XK_braille_dots_2357 = 0x1002856; /* U+2856 BRAILLE PATTERN DOTS-2357 */ public static final int XK_braille_dots_12357 = 0x1002857; /* U+2857 BRAILLE PATTERN DOTS-12357 */ public static final int XK_braille_dots_457 = 0x1002858; /* U+2858 BRAILLE PATTERN DOTS-457 */ public static final int XK_braille_dots_1457 = 0x1002859; /* U+2859 BRAILLE PATTERN DOTS-1457 */ public static final int XK_braille_dots_2457 = 0x100285a; /* U+285a BRAILLE PATTERN DOTS-2457 */ public static final int XK_braille_dots_12457 = 0x100285b; /* U+285b BRAILLE PATTERN DOTS-12457 */ public static final int XK_braille_dots_3457 = 0x100285c; /* U+285c BRAILLE PATTERN DOTS-3457 */ public static final int XK_braille_dots_13457 = 0x100285d; /* U+285d BRAILLE PATTERN DOTS-13457 */ public static final int XK_braille_dots_23457 = 0x100285e; /* U+285e BRAILLE PATTERN DOTS-23457 */ public static final int XK_braille_dots_123457 = 0x100285f; /* U+285f BRAILLE PATTERN DOTS-123457 */ public static final int XK_braille_dots_67 = 0x1002860; /* U+2860 BRAILLE PATTERN DOTS-67 */ public static final int XK_braille_dots_167 = 0x1002861; /* U+2861 BRAILLE PATTERN DOTS-167 */ public static final int XK_braille_dots_267 = 0x1002862; /* U+2862 BRAILLE PATTERN DOTS-267 */ public static final int XK_braille_dots_1267 = 0x1002863; /* U+2863 BRAILLE PATTERN DOTS-1267 */ public static final int XK_braille_dots_367 = 0x1002864; /* U+2864 BRAILLE PATTERN DOTS-367 */ public static final int XK_braille_dots_1367 = 0x1002865; /* U+2865 BRAILLE PATTERN DOTS-1367 */ public static final int XK_braille_dots_2367 = 0x1002866; /* U+2866 BRAILLE PATTERN DOTS-2367 */ public static final int XK_braille_dots_12367 = 0x1002867; /* U+2867 BRAILLE PATTERN DOTS-12367 */ public static final int XK_braille_dots_467 = 0x1002868; /* U+2868 BRAILLE PATTERN DOTS-467 */ public static final int XK_braille_dots_1467 = 0x1002869; /* U+2869 BRAILLE PATTERN DOTS-1467 */ public static final int XK_braille_dots_2467 = 0x100286a; /* U+286a BRAILLE PATTERN DOTS-2467 */ public static final int XK_braille_dots_12467 = 0x100286b; /* U+286b BRAILLE PATTERN DOTS-12467 */ public static final int XK_braille_dots_3467 = 0x100286c; /* U+286c BRAILLE PATTERN DOTS-3467 */ public static final int XK_braille_dots_13467 = 0x100286d; /* U+286d BRAILLE PATTERN DOTS-13467 */ public static final int XK_braille_dots_23467 = 0x100286e; /* U+286e BRAILLE PATTERN DOTS-23467 */ public static final int XK_braille_dots_123467 = 0x100286f; /* U+286f BRAILLE PATTERN DOTS-123467 */ public static final int XK_braille_dots_567 = 0x1002870; /* U+2870 BRAILLE PATTERN DOTS-567 */ public static final int XK_braille_dots_1567 = 0x1002871; /* U+2871 BRAILLE PATTERN DOTS-1567 */ public static final int XK_braille_dots_2567 = 0x1002872; /* U+2872 BRAILLE PATTERN DOTS-2567 */ public static final int XK_braille_dots_12567 = 0x1002873; /* U+2873 BRAILLE PATTERN DOTS-12567 */ public static final int XK_braille_dots_3567 = 0x1002874; /* U+2874 BRAILLE PATTERN DOTS-3567 */ public static final int XK_braille_dots_13567 = 0x1002875; /* U+2875 BRAILLE PATTERN DOTS-13567 */ public static final int XK_braille_dots_23567 = 0x1002876; /* U+2876 BRAILLE PATTERN DOTS-23567 */ public static final int XK_braille_dots_123567 = 0x1002877; /* U+2877 BRAILLE PATTERN DOTS-123567 */ public static final int XK_braille_dots_4567 = 0x1002878; /* U+2878 BRAILLE PATTERN DOTS-4567 */ public static final int XK_braille_dots_14567 = 0x1002879; /* U+2879 BRAILLE PATTERN DOTS-14567 */ public static final int XK_braille_dots_24567 = 0x100287a; /* U+287a BRAILLE PATTERN DOTS-24567 */ public static final int XK_braille_dots_124567 = 0x100287b; /* U+287b BRAILLE PATTERN DOTS-124567 */ public static final int XK_braille_dots_34567 = 0x100287c; /* U+287c BRAILLE PATTERN DOTS-34567 */ public static final int XK_braille_dots_134567 = 0x100287d; /* U+287d BRAILLE PATTERN DOTS-134567 */ public static final int XK_braille_dots_234567 = 0x100287e; /* U+287e BRAILLE PATTERN DOTS-234567 */ public static final int XK_braille_dots_1234567 = 0x100287f; /* U+287f BRAILLE PATTERN DOTS-1234567 */ public static final int XK_braille_dots_8 = 0x1002880; /* U+2880 BRAILLE PATTERN DOTS-8 */ public static final int XK_braille_dots_18 = 0x1002881; /* U+2881 BRAILLE PATTERN DOTS-18 */ public static final int XK_braille_dots_28 = 0x1002882; /* U+2882 BRAILLE PATTERN DOTS-28 */ public static final int XK_braille_dots_128 = 0x1002883; /* U+2883 BRAILLE PATTERN DOTS-128 */ public static final int XK_braille_dots_38 = 0x1002884; /* U+2884 BRAILLE PATTERN DOTS-38 */ public static final int XK_braille_dots_138 = 0x1002885; /* U+2885 BRAILLE PATTERN DOTS-138 */ public static final int XK_braille_dots_238 = 0x1002886; /* U+2886 BRAILLE PATTERN DOTS-238 */ public static final int XK_braille_dots_1238 = 0x1002887; /* U+2887 BRAILLE PATTERN DOTS-1238 */ public static final int XK_braille_dots_48 = 0x1002888; /* U+2888 BRAILLE PATTERN DOTS-48 */ public static final int XK_braille_dots_148 = 0x1002889; /* U+2889 BRAILLE PATTERN DOTS-148 */ public static final int XK_braille_dots_248 = 0x100288a; /* U+288a BRAILLE PATTERN DOTS-248 */ public static final int XK_braille_dots_1248 = 0x100288b; /* U+288b BRAILLE PATTERN DOTS-1248 */ public static final int XK_braille_dots_348 = 0x100288c; /* U+288c BRAILLE PATTERN DOTS-348 */ public static final int XK_braille_dots_1348 = 0x100288d; /* U+288d BRAILLE PATTERN DOTS-1348 */ public static final int XK_braille_dots_2348 = 0x100288e; /* U+288e BRAILLE PATTERN DOTS-2348 */ public static final int XK_braille_dots_12348 = 0x100288f; /* U+288f BRAILLE PATTERN DOTS-12348 */ public static final int XK_braille_dots_58 = 0x1002890; /* U+2890 BRAILLE PATTERN DOTS-58 */ public static final int XK_braille_dots_158 = 0x1002891; /* U+2891 BRAILLE PATTERN DOTS-158 */ public static final int XK_braille_dots_258 = 0x1002892; /* U+2892 BRAILLE PATTERN DOTS-258 */ public static final int XK_braille_dots_1258 = 0x1002893; /* U+2893 BRAILLE PATTERN DOTS-1258 */ public static final int XK_braille_dots_358 = 0x1002894; /* U+2894 BRAILLE PATTERN DOTS-358 */ public static final int XK_braille_dots_1358 = 0x1002895; /* U+2895 BRAILLE PATTERN DOTS-1358 */ public static final int XK_braille_dots_2358 = 0x1002896; /* U+2896 BRAILLE PATTERN DOTS-2358 */ public static final int XK_braille_dots_12358 = 0x1002897; /* U+2897 BRAILLE PATTERN DOTS-12358 */ public static final int XK_braille_dots_458 = 0x1002898; /* U+2898 BRAILLE PATTERN DOTS-458 */ public static final int XK_braille_dots_1458 = 0x1002899; /* U+2899 BRAILLE PATTERN DOTS-1458 */ public static final int XK_braille_dots_2458 = 0x100289a; /* U+289a BRAILLE PATTERN DOTS-2458 */ public static final int XK_braille_dots_12458 = 0x100289b; /* U+289b BRAILLE PATTERN DOTS-12458 */ public static final int XK_braille_dots_3458 = 0x100289c; /* U+289c BRAILLE PATTERN DOTS-3458 */ public static final int XK_braille_dots_13458 = 0x100289d; /* U+289d BRAILLE PATTERN DOTS-13458 */ public static final int XK_braille_dots_23458 = 0x100289e; /* U+289e BRAILLE PATTERN DOTS-23458 */ public static final int XK_braille_dots_123458 = 0x100289f; /* U+289f BRAILLE PATTERN DOTS-123458 */ public static final int XK_braille_dots_68 = 0x10028a0; /* U+28a0 BRAILLE PATTERN DOTS-68 */ public static final int XK_braille_dots_168 = 0x10028a1; /* U+28a1 BRAILLE PATTERN DOTS-168 */ public static final int XK_braille_dots_268 = 0x10028a2; /* U+28a2 BRAILLE PATTERN DOTS-268 */ public static final int XK_braille_dots_1268 = 0x10028a3; /* U+28a3 BRAILLE PATTERN DOTS-1268 */ public static final int XK_braille_dots_368 = 0x10028a4; /* U+28a4 BRAILLE PATTERN DOTS-368 */ public static final int XK_braille_dots_1368 = 0x10028a5; /* U+28a5 BRAILLE PATTERN DOTS-1368 */ public static final int XK_braille_dots_2368 = 0x10028a6; /* U+28a6 BRAILLE PATTERN DOTS-2368 */ public static final int XK_braille_dots_12368 = 0x10028a7; /* U+28a7 BRAILLE PATTERN DOTS-12368 */ public static final int XK_braille_dots_468 = 0x10028a8; /* U+28a8 BRAILLE PATTERN DOTS-468 */ public static final int XK_braille_dots_1468 = 0x10028a9; /* U+28a9 BRAILLE PATTERN DOTS-1468 */ public static final int XK_braille_dots_2468 = 0x10028aa; /* U+28aa BRAILLE PATTERN DOTS-2468 */ public static final int XK_braille_dots_12468 = 0x10028ab; /* U+28ab BRAILLE PATTERN DOTS-12468 */ public static final int XK_braille_dots_3468 = 0x10028ac; /* U+28ac BRAILLE PATTERN DOTS-3468 */ public static final int XK_braille_dots_13468 = 0x10028ad; /* U+28ad BRAILLE PATTERN DOTS-13468 */ public static final int XK_braille_dots_23468 = 0x10028ae; /* U+28ae BRAILLE PATTERN DOTS-23468 */ public static final int XK_braille_dots_123468 = 0x10028af; /* U+28af BRAILLE PATTERN DOTS-123468 */ public static final int XK_braille_dots_568 = 0x10028b0; /* U+28b0 BRAILLE PATTERN DOTS-568 */ public static final int XK_braille_dots_1568 = 0x10028b1; /* U+28b1 BRAILLE PATTERN DOTS-1568 */ public static final int XK_braille_dots_2568 = 0x10028b2; /* U+28b2 BRAILLE PATTERN DOTS-2568 */ public static final int XK_braille_dots_12568 = 0x10028b3; /* U+28b3 BRAILLE PATTERN DOTS-12568 */ public static final int XK_braille_dots_3568 = 0x10028b4; /* U+28b4 BRAILLE PATTERN DOTS-3568 */ public static final int XK_braille_dots_13568 = 0x10028b5; /* U+28b5 BRAILLE PATTERN DOTS-13568 */ public static final int XK_braille_dots_23568 = 0x10028b6; /* U+28b6 BRAILLE PATTERN DOTS-23568 */ public static final int XK_braille_dots_123568 = 0x10028b7; /* U+28b7 BRAILLE PATTERN DOTS-123568 */ public static final int XK_braille_dots_4568 = 0x10028b8; /* U+28b8 BRAILLE PATTERN DOTS-4568 */ public static final int XK_braille_dots_14568 = 0x10028b9; /* U+28b9 BRAILLE PATTERN DOTS-14568 */ public static final int XK_braille_dots_24568 = 0x10028ba; /* U+28ba BRAILLE PATTERN DOTS-24568 */ public static final int XK_braille_dots_124568 = 0x10028bb; /* U+28bb BRAILLE PATTERN DOTS-124568 */ public static final int XK_braille_dots_34568 = 0x10028bc; /* U+28bc BRAILLE PATTERN DOTS-34568 */ public static final int XK_braille_dots_134568 = 0x10028bd; /* U+28bd BRAILLE PATTERN DOTS-134568 */ public static final int XK_braille_dots_234568 = 0x10028be; /* U+28be BRAILLE PATTERN DOTS-234568 */ public static final int XK_braille_dots_1234568 = 0x10028bf; /* U+28bf BRAILLE PATTERN DOTS-1234568 */ public static final int XK_braille_dots_78 = 0x10028c0; /* U+28c0 BRAILLE PATTERN DOTS-78 */ public static final int XK_braille_dots_178 = 0x10028c1; /* U+28c1 BRAILLE PATTERN DOTS-178 */ public static final int XK_braille_dots_278 = 0x10028c2; /* U+28c2 BRAILLE PATTERN DOTS-278 */ public static final int XK_braille_dots_1278 = 0x10028c3; /* U+28c3 BRAILLE PATTERN DOTS-1278 */ public static final int XK_braille_dots_378 = 0x10028c4; /* U+28c4 BRAILLE PATTERN DOTS-378 */ public static final int XK_braille_dots_1378 = 0x10028c5; /* U+28c5 BRAILLE PATTERN DOTS-1378 */ public static final int XK_braille_dots_2378 = 0x10028c6; /* U+28c6 BRAILLE PATTERN DOTS-2378 */ public static final int XK_braille_dots_12378 = 0x10028c7; /* U+28c7 BRAILLE PATTERN DOTS-12378 */ public static final int XK_braille_dots_478 = 0x10028c8; /* U+28c8 BRAILLE PATTERN DOTS-478 */ public static final int XK_braille_dots_1478 = 0x10028c9; /* U+28c9 BRAILLE PATTERN DOTS-1478 */ public static final int XK_braille_dots_2478 = 0x10028ca; /* U+28ca BRAILLE PATTERN DOTS-2478 */ public static final int XK_braille_dots_12478 = 0x10028cb; /* U+28cb BRAILLE PATTERN DOTS-12478 */ public static final int XK_braille_dots_3478 = 0x10028cc; /* U+28cc BRAILLE PATTERN DOTS-3478 */ public static final int XK_braille_dots_13478 = 0x10028cd; /* U+28cd BRAILLE PATTERN DOTS-13478 */ public static final int XK_braille_dots_23478 = 0x10028ce; /* U+28ce BRAILLE PATTERN DOTS-23478 */ public static final int XK_braille_dots_123478 = 0x10028cf; /* U+28cf BRAILLE PATTERN DOTS-123478 */ public static final int XK_braille_dots_578 = 0x10028d0; /* U+28d0 BRAILLE PATTERN DOTS-578 */ public static final int XK_braille_dots_1578 = 0x10028d1; /* U+28d1 BRAILLE PATTERN DOTS-1578 */ public static final int XK_braille_dots_2578 = 0x10028d2; /* U+28d2 BRAILLE PATTERN DOTS-2578 */ public static final int XK_braille_dots_12578 = 0x10028d3; /* U+28d3 BRAILLE PATTERN DOTS-12578 */ public static final int XK_braille_dots_3578 = 0x10028d4; /* U+28d4 BRAILLE PATTERN DOTS-3578 */ public static final int XK_braille_dots_13578 = 0x10028d5; /* U+28d5 BRAILLE PATTERN DOTS-13578 */ public static final int XK_braille_dots_23578 = 0x10028d6; /* U+28d6 BRAILLE PATTERN DOTS-23578 */ public static final int XK_braille_dots_123578 = 0x10028d7; /* U+28d7 BRAILLE PATTERN DOTS-123578 */ public static final int XK_braille_dots_4578 = 0x10028d8; /* U+28d8 BRAILLE PATTERN DOTS-4578 */ public static final int XK_braille_dots_14578 = 0x10028d9; /* U+28d9 BRAILLE PATTERN DOTS-14578 */ public static final int XK_braille_dots_24578 = 0x10028da; /* U+28da BRAILLE PATTERN DOTS-24578 */ public static final int XK_braille_dots_124578 = 0x10028db; /* U+28db BRAILLE PATTERN DOTS-124578 */ public static final int XK_braille_dots_34578 = 0x10028dc; /* U+28dc BRAILLE PATTERN DOTS-34578 */ public static final int XK_braille_dots_134578 = 0x10028dd; /* U+28dd BRAILLE PATTERN DOTS-134578 */ public static final int XK_braille_dots_234578 = 0x10028de; /* U+28de BRAILLE PATTERN DOTS-234578 */ public static final int XK_braille_dots_1234578 = 0x10028df; /* U+28df BRAILLE PATTERN DOTS-1234578 */ public static final int XK_braille_dots_678 = 0x10028e0; /* U+28e0 BRAILLE PATTERN DOTS-678 */ public static final int XK_braille_dots_1678 = 0x10028e1; /* U+28e1 BRAILLE PATTERN DOTS-1678 */ public static final int XK_braille_dots_2678 = 0x10028e2; /* U+28e2 BRAILLE PATTERN DOTS-2678 */ public static final int XK_braille_dots_12678 = 0x10028e3; /* U+28e3 BRAILLE PATTERN DOTS-12678 */ public static final int XK_braille_dots_3678 = 0x10028e4; /* U+28e4 BRAILLE PATTERN DOTS-3678 */ public static final int XK_braille_dots_13678 = 0x10028e5; /* U+28e5 BRAILLE PATTERN DOTS-13678 */ public static final int XK_braille_dots_23678 = 0x10028e6; /* U+28e6 BRAILLE PATTERN DOTS-23678 */ public static final int XK_braille_dots_123678 = 0x10028e7; /* U+28e7 BRAILLE PATTERN DOTS-123678 */ public static final int XK_braille_dots_4678 = 0x10028e8; /* U+28e8 BRAILLE PATTERN DOTS-4678 */ public static final int XK_braille_dots_14678 = 0x10028e9; /* U+28e9 BRAILLE PATTERN DOTS-14678 */ public static final int XK_braille_dots_24678 = 0x10028ea; /* U+28ea BRAILLE PATTERN DOTS-24678 */ public static final int XK_braille_dots_124678 = 0x10028eb; /* U+28eb BRAILLE PATTERN DOTS-124678 */ public static final int XK_braille_dots_34678 = 0x10028ec; /* U+28ec BRAILLE PATTERN DOTS-34678 */ public static final int XK_braille_dots_134678 = 0x10028ed; /* U+28ed BRAILLE PATTERN DOTS-134678 */ public static final int XK_braille_dots_234678 = 0x10028ee; /* U+28ee BRAILLE PATTERN DOTS-234678 */ public static final int XK_braille_dots_1234678 = 0x10028ef; /* U+28ef BRAILLE PATTERN DOTS-1234678 */ public static final int XK_braille_dots_5678 = 0x10028f0; /* U+28f0 BRAILLE PATTERN DOTS-5678 */ public static final int XK_braille_dots_15678 = 0x10028f1; /* U+28f1 BRAILLE PATTERN DOTS-15678 */ public static final int XK_braille_dots_25678 = 0x10028f2; /* U+28f2 BRAILLE PATTERN DOTS-25678 */ public static final int XK_braille_dots_125678 = 0x10028f3; /* U+28f3 BRAILLE PATTERN DOTS-125678 */ public static final int XK_braille_dots_35678 = 0x10028f4; /* U+28f4 BRAILLE PATTERN DOTS-35678 */ public static final int XK_braille_dots_135678 = 0x10028f5; /* U+28f5 BRAILLE PATTERN DOTS-135678 */ public static final int XK_braille_dots_235678 = 0x10028f6; /* U+28f6 BRAILLE PATTERN DOTS-235678 */ public static final int XK_braille_dots_1235678 = 0x10028f7; /* U+28f7 BRAILLE PATTERN DOTS-1235678 */ public static final int XK_braille_dots_45678 = 0x10028f8; /* U+28f8 BRAILLE PATTERN DOTS-45678 */ public static final int XK_braille_dots_145678 = 0x10028f9; /* U+28f9 BRAILLE PATTERN DOTS-145678 */ public static final int XK_braille_dots_245678 = 0x10028fa; /* U+28fa BRAILLE PATTERN DOTS-245678 */ public static final int XK_braille_dots_1245678 = 0x10028fb; /* U+28fb BRAILLE PATTERN DOTS-1245678 */ public static final int XK_braille_dots_345678 = 0x10028fc; /* U+28fc BRAILLE PATTERN DOTS-345678 */ public static final int XK_braille_dots_1345678 = 0x10028fd; /* U+28fd BRAILLE PATTERN DOTS-1345678 */ public static final int XK_braille_dots_2345678 = 0x10028fe; /* U+28fe BRAILLE PATTERN DOTS-2345678 */ public static final int XK_braille_dots_12345678 = 0x10028ff; /* U+28ff BRAILLE PATTERN DOTS-12345678 */ } sikulix-1.1.1/API/src/main/resources/000077500000000000000000000000001315726130400173425ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/resources/ImagesAPI.sikuli/000077500000000000000000000000001315726130400224005ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/resources/ImagesAPI.sikuli/ImagesAPI.py000066400000000000000000000002141315726130400245060ustar00rootroot00000000000000"SikuliLogo.png" "btnnightly.png" "netblogo.png" "nightly.png" "quickstart.png" "raimanlogo.png" "sxpower.png" "netbicons.png" "firefox.png"sikulix-1.1.1/API/src/main/resources/ImagesAPI.sikuli/SikuliLogo.png000066400000000000000000000206021315726130400251670ustar00rootroot00000000000000PNG  IHDR5<Ie iCCPICC ProfileH wTSFI)w@J 6BH(1ؐXQDDPtQDU)ĂE},**@EKo9ws}3͜s `D)tqX7}QL,@0؜434t6vFM+Yu\^(qR>XG$N膫E2""FpHӼo'"Al8R;38Ha!W xpl$6Y2A48?0/f'yz/țȇ}iꩇg"A5tT$g_ y,?Ea3,HgE0_9ÒH ' 4$13ͰxE?-#\g}$dZ[?K W*_0e|/ b/~3qr?kF(eLSeyKy #9}>@RA  '|VVt:7mX GQEBPV(E%ĨTՀjCunWOh,n@t$^^.BW77 bXED*L> Si\ aưX `"l#ۋĎp8 cq]#s>#x!>_???OFWBKXM(& nDe ѝAL"n I$>Ʌ$ 'HT9ه,!o!$S(c%NB\<|T)X+ *^+O(P|DP2VQb+UT:ԯ4LSUQNU.R>|U9G5Q<~ E38K! K%IPJʈ*UA5J5SRT fRKQ+V;vG,YYYg55>[K^ި~[]O#YcF#MBU{5/i2m6gvkZZaZZFuEڻ/hQI)93K}AW3)rE^DZ[oBD?R?WQрa`Pja0bk80ǰa7ii4nlbmɸ $ˤ)ti-3,lY9lh74a[8Y,XZb,],5Vd+UUՀ<\s 6s7G6lAm8vv)[:X8:u9wI4l۹e1`\]ֹvz[asM;vvz=OuᮭT+nWzW6ڽy^{ >[P\c\S?cQ:eZWY[XPءuuu#K=`PݨXx {[ow88p&ZSA3Լy"mi=tͭw;]yFLYټ΍_OMȢⷓk?ChԱ}b|Ī//_;} p2urRj ;%R?)hFGdtM,@jhĂb4@(2Fx^n*ki vSZҤlr=R#?9929>6]g˼J B{j:9¥IDAThY xTս̽d2d#Jv "T uAZ`_U[?WjkkmkUZAqąD@$$agf2{' Xiדɽ{?ҪRRI3 ~3ONhTCZv(M3,{>2T3765?_`f4MS`)X<Ԕ;O'Ԕ+JS&>X[~rʕ0>"D.Z'aɤR(>O{"u|O 99c`hVEXQC<}(o22o0p أ*łO jjYㄎ&;'a"G1h#S!%K"r:ڳ?J =PM"Jh3L{,{UPIS)%AdGO-2rҌ(G|C*:44b m߻dO4Yl_0i(D]o!mh.E%G!/Ϙ_0LS<5mU)iQ3g9_ta=dײ7$ w]g()58 |-Z 0<0Gr* T%a$IeioXYA);ڊ2oߠd@JsB`3EMc6R:fkWnjӧ}Mi^`{< :}Y[@/$;+_-^TUhAN&9!r"WD\Ѫ啡`iCͳ54=n/(eZ7S 4}OV%'ɓ~{fYP' 8wm֪ ]sxjt$06F/Y|x^MWhS>SLѶbɯo~W+V(Mz=C7ޖE5u":!69|)%29 XJD5.gR1a Y9Z0@T* 5|{#cwKlMG2hd|hgQp27`t8P t27-l[&*yTTB֐Pʰ3J*P-H$ #LDLh$+Rz>(nj,V^0P"yH8 k=nikk]B#1e[L4 g?Vh~K{ҵcܝD*xNǧ#׋q/-W_12b^_7}cWeKmbVb(ў $P6'+` k (lWωF5e_֚_h/Y4y)3(%"  οv=II&DOeaqS>W:CA2=?c.-:5E8QqZTf0!PsXG$; WZY p 8K3Q$n̫**Q avrhPh#K]mO.tE2z)Yac8?iSpӂ8p~f0;kׯlR``'Iq?*&DPO>ڭ,bW2 9>-|J;%g('# ӑl׮;F+Bs$̯Y3 'dw؈9&d:B) 5%O2<FEEXΐr,u^:y`WP TƦ]'z$Fdi{H<6*JaPҊr=֐S=wSy:&OR'}cѮȑa,o<װx֡ z7~m5F0BtU.Yd᱉Ȉ"C-Ċ:yE4IfYe0`J;pu@=:3VIy']o kntayƦN?(o()vD!dnh;y"poYTY|x6,ZnmI.L%O yT-cE)_@J+ɉ2ULRQ LX lÍN7_<\j}L:ΤGF=':~[~~G_Mb&lî64o+!I0Q O!ÑݑhNGXT?~ L<پ`5XK/~ 9}|uN]k־GOhnޝ{\f5~{:LLl2}9{/@Z'D=FvuPL_ȼnXg4=1vQ'h9/-QpqKNv7^tqEcnT&)#1g(9ɢ>)4XNQ`KO=q*> WE^w;Ȫs_=jb됽BqJNN6Zy3,!j)[EvLLI%v"޾m6d e`@d0tbƅVG}Ikݻv߈L2IYz=HL*/ovE"bnGiѺug *-N%S/k޳'b::*"I4u ڊs3li2;mymeOU3Hqԛ{BiYHx3Yr%ÖD'|nWQyA=}|_Eު6HIwt-%14d^˦⑰A-gnk^W^|B&U뮱,xb6<ND9|X+{ko\\Y]XQY\Uo{Oo W_]g^OZFm +g2ȾN}pCh75HI)\oD2! F0{ ):>|,ݐ> EB;_~NJw|_f%WniQN._9c.km[}eJ-CEJڪp*Fq eӭ/G;zӿ~mJ&C0V4DBR7, æi68t:/x:|$Eo;1N?RnM%ˡ956iB ENd9c+0dSݒ+V9Rb^J;)kHQp4X,z0#д2'_$pB%ZcTl64@^Ϗo˽ՌO^ح[&itU)RZid*}!7n.2@e>76ϗY0py^_UĦ̆>1YRdDd-j^{G;.h(N 5ef;Q /:+IFv ă9M thID-.Zc`$A؟2l%)Mrm%D*Ka6?n`` et<ٴfo%@+EZgqDLa7nI?m} e, rPNHP'#@p%??yzYcj*sn† 幹?kH~/o4jE1"fg,1/dr-G$$k5N(jATZy*EQF㼒L296>W4Sv#} /N) ȡ-Dd>B0RA3jڅA3NeJ!1Zr+jԢMUNqfC(pxȍQڈ2lj|EBjHo)(U547W47s[Sq mꆂGOO ְiӦO1v[>ORyEU5657[!983k4j<R4ϳP5k+g,>=+Уu3v,w uRc$[ TVV\.p[FA>4:'fl?=zG>de˖ALD??3aLTE7Z-8I;6s|NB}UTثoYƫ )Iv;lӧIƞ=xEUUUsd9Fxaƻv:|-ܢ,spN(bS'OݱcG[[`Gm ǏUW,2Y?XgcICIENDB`sikulix-1.1.1/API/src/main/resources/ImagesAPI.sikuli/btnnightly.png000066400000000000000000000056401315726130400252750ustar00rootroot00000000000000PNG  IHDR.a, gIDATxSIXڲeU_8"b$  ±8uP\|߲$4&BR5d& KYߟɄ S@E!QQQȨ(dT2** _555eKJJ322R0 @`@}VkMM͡Crrr ϟ?埩0 @`@T j:77'VEEE'OP< P0~s8X SSSQ]qqѣG|0 *Zr2auuuIII'NSI5ɩS ,âֆ\*DP6 3oྙ#G(`H!*766VZZ(`x@ro"ȜNgooobb"?RQ}H22vl*y 7fXfWoC =*wIT۰8tL &UwcZ>TKE?aXu5ir 0֜ʔy] T{FH Mt<)ݘNT*y8Uy#Ff_> c㍂2.);ap-!푑a.=ugd\o\D7eʂEӷ]<n)">YmmmZZZEpmGpAW9ܓo3.QkO-7IoK=vQf9MryUK٫l.i'6>DNBzk B ff'oKQX7i &| Vj%wZeNZ=" 8r ̙3X.M&ѣ1qM$v.[{ oOff#M&r)#mŦ*J%6B9 Ė1λ 6#"A修#.j1 2wldRH_ ؂,_0Pv\W '- r37n~:&1H,\ў-+1._I>p݌%p"wr88upFZR}n!Dri'N9Kn1'SlR>@NUo655_~ݱTT$p~i///*OH/-Eoٲe1TTs<@ yZݽ{wʕ۶mPQ,lܹ#\%nhhʕ+˖-O*T <@hƿĹ܋OX|yDD?f *|Ͻn߾iӦuEEE>!g8bkii9zUnذbtt4܅, Wo0t:ŋ"##0.Z0 < Pۧy,mmmz, |G>iۇfZ0 1yzj߿w:X'&&Ʃ0 @bՋBFT"IENDB`sikulix-1.1.1/API/src/main/resources/ImagesAPI.sikuli/firefox.png000066400000000000000000001142321315726130400245530ustar00rootroot00000000000000PNG  IHDR.IDATxX[7{HJqbݩ{K]R5ZJKR-@$Ҏ]&zd fo 󷚿nu__uUVW +j3QZn[IzTpp/AAzz:66IRWW[uu5#}_k]%ݥw3,Ϥ՟N;RwmCI5oj~Scɵ'R%fiui2ꭳ.gέ,ɑTTUfeHk\ɪ@3Ժcok'|ɵ֙usjʿ-rk\嶸+XhJ=^w#@?-ZV 45H#uC{0 Lb38{cLņp_>JY룠AE*_j]TEjz~m6/_|c4.w܂yϰ2-p?4P^uFUf צIڗ- OLItFfmTՑ UVr**Ǖ"Wr=~L=z\||ժ/ku$kIX`;_WuQUS<OɮW?^{UXu1|UvA @DoC!_ÑWp#K9%Ÿa˗ɗ}bib8T-o^["K\B~px)G 0ي0j|m|CbSTrp[ j̪E_jªK gQŖh(Hr , -͇ et[6-ϖ-- - /勀ZyX QSg/\*_J- }P)cH熔, ,f- [rvơگkeT onŐIhېwbF&u2p<8rYl̐\6׌``8!X@|>[ W|!Ea T S,D@>s>YJ@sCdsW%D:#Iff![Ke+äk8 Ib[L9zqu8XRJie_Tquz테z2]9R!\&T-Y"Z6`ɤ` Ʉ@ @`bjRdrPٔ 8l(@6-*GV|~b|>*6~y 05 ߱ނ> >I>)~™9!UR!_8Ȭ:\"ϽI\QmF)u[ 5Z)B. -/ -NM  T(\cP/8e :!%YPllPP&.ő(#|pN|v(lV(z p&*uaI'M  @wh`'o'oo7`X\>Nl,"BU3eSؒqPg0| o>~0xtҾN^yY8ܹh.EsJ9J= yy5[9Rh0%V]~S]!gIl8PH  s=KgOu-\8ީ`sWJJhz\(O'țDŽ  + eCSi T5φ G5 T5iC.#kNlvStvT\C$ؠV2!hpu/ uK/YOrfr?e8IO/դwjEօDȷǔ:XȯSe}IRFyjݎ l[$zqxp-\8)1_p)Rr)븕h(O\C7exqY|c?A??A ѐ@ `!)lPt %N*UnKfˑ)I!PB$,, d=qO%ėJj?|y]>D1L $\9Wscj; .Ou7 nVƓ'R+2Ėo +[$Z_]:ϽxKԗ iӜOF< cCzY9YzN/g:t+V]{,xUlz}F*a_@*ɨq I!(SpR@*3ɦ$\ )\6*X2K $((%k@`p/OߥPQL4T˯XN9"T%x!Q.Dl.Sq2N+AѠڊ#* nTsݨW,Z}&nK Ӭ/悤Y.Ӝ&=4{2:>}}*>a iӌ >H s~G®O;?ZٵGIRM/7OǛ#00)Ɉ `p  rJ1]YjPb@CAAAMdKLJ )$I$h`$?^ !θfd`t1aü:u.!FK|D#*RP-T4ZY\.V!n]O^_oTιSmcTsըQ_~2WVeV{2biup/oG/';я2F8:nJ}eٽeڽ۽5{kx*En}}ֽT{S^Wv9m݊;wr/QÓKhnDQă$ BD PB!#-1!Ke#ʆIWQ@aaq/`|:ܣ ^%܊̣=0FƸB@abd8=xJ$޽&ާ$'+7sd{"e/]<Y5aOë/U]2hKP0}\,Z[U:ϵh)s&<0cCPAvoNkIJMb&o'N2Mҿ{;IvTjڽq7vs;Oɹg6n큔GIW^\Mo_(uРA1q!!ȓbz(*GBO U9xDl|lllttd0=tp$c!0D P<_/eppx {?ҥ`KgFR]QLK! ow1DC$A|_g"jgݧ$)w}k+'=UVFVFis*My{k} ݋8O;iָG O|'>Y777n&LԻ{3Q֛7hJҺy;ݔ3Zk"S p)R*Q h>9F ` pD D`T5ÚTSCS0Il9$($2 Z'|`'@1$|x><T\=XzҽKޥƞ] ? e~&H4:Apg;'|#‰z6QI>.9}Gzp#9{@]FSHaէt&,hG[>3Dq1~W|?g|YNSeMx1~oNx+ ֻZZP%hLq;}J'Yms[?EON?8ùsOBiPӴu 'iHd\`JAЭTM&b=d?+% IRHA1Dt?!tUC?@ϗÇh򁌄AskW 9˼>nE9p2o˼O2mL}ó3t09s:QDuĻxL.E6H%Trg"ffq)5%OS HFRsԌVv&18EӶ>sg8fO~9!mۡwLw=ϵL48qWc^վj^6qM0ӃvDQJԇU/zm$Trf,&aCY' :HӴ4qMx.{iZ]Eƕ[m /*YZ0)wIKq'iȭ^c]c\5cp9Fr1/rոw;OI<=B,Nj7׼(u{Wqk"j˜ }O7ĉTJU`e8FR%nb$I1! /^O/^O^7/.t.N’ӽDý{[zzzz^UZVx{Ѽ3eNy:>y7]w9u)nedok}!BJ昧R%ʠ"U1BUIF(QqB ׫9vJ~r-ѵG.E\:tо)_e|×LO?H0}#T#dz9mL,D Ɖ01Q'|U y,lC02Ѫ RlJ{Ia!hNCu~Es)U/;>(,{::ҵk<"TN|6A [ao^ˆD @|Wz@4TKws|8m]hFO+Ǽ^y,|;(EG^^^Ռ%E̔LX޿{ը֜~Ìφ2,FgB̓u͂{i8T`Nv;d@]O"]z=ˍ7szzyaO0?ILjow\0Cr!ʃMJ$8`z6*j nd&!I|X=Sӫ"$Z=p'ӭd "=nu#QFDmLp3aୄAǂ>}i$Ϯ8MF+Dc-El+(>v[_F2FRz!ҪWDY<]S HB& 5(Tg3f2g01{ʹ [7`~g%g$[̽4{՘UrO}\F>d{LJ~>p_yGgzWzؓNSvevyJjl7N)r1.Ep!?pPQȬRC .MxLqdTËpJ1`=mluؼ67ijl^_{Z܈x3aWO__saZzpkN~(?(;MT'<D>H0r3nBµj“lx6(3`3z#u!% HL|=7;bSNfN3Pf 9Ct!>gH钫4M# F@D(!>{~玞i_C>D 2å *(mԋ ɨ3FҶnHKy?9涻 F@ӵW{^yq98Kqz/]_c^5jll];Gyl-M簗~W}.6!JLfcD7Z)=Ko_?6:)D>r)<@ڄ%m`ded,g-b,`3i:3a Hb,ȧL!L1TLIb̙y"̆VvHjo(bs$ސX_DO'=<9/׸R\뇙p8x0f2U*Ai rCV'<[J#w^+Go* 6qVӑ L=/i^eemcovь3,pX}?h~) b1jo,;Xv 1 mSfog󔥃7HQ L L) s73S H1X8=S!:dL1XT@%>I) 4졡Ql5yz/wTzrR0| BSe/5c`%ӳL*$RՏj`[`UBjM2r+&I(e,719xlJL]nzpLc45G:|H ""VLea s&.+RU w=rF Sd$Dy*Fާ>LHYzV߄SY!fH߳Z_T Rlft$ [xfXged 2D ¤R}W@ zPzLj${` q+rO<+=.ԼV~ GZޏ**P#U*"a"mV؍gT2)%F@3|֏K`VxAuX͋15/Dk8iѲ9;?1:ǡ``''OgIG^f">ˉOBtyl}Jc}gb'έ d >67=ema-e,f,d$eEeR8ff(4?w2D0faߝhB%nҤT`~BIBׯ=-<>,5(RTPnREf,慄CSkƈn$di6|wm̓:DkPeVP=τkj[ ?fhF g b`jĕ׵$#ty0lmil %:?wo\ScΠ[!*IDZF2Hx3? eH͌ɂ ^:„ j'|B-.w$UxrTzrk)o`sh 탌63[?lMRʪ&XTbo$2>HHIXo t S:`jw/ w{vMrS5a<e6c뙳 ٴ3 01?Q߅#>~ҒTWQWi[pS`uzS}&.h%J–EMìN:GH*_$2u3q3a3$gFeFdri.G !$Tz<1TObJO!Z=^n=ݬetȃ6*UPjWĢʹEJ#%#2~Fp,=ʄV}Z`f>X<<u2XT`C`#y07l[O%0͍%D+C>xh!ˆڀ'{(y'O\o20c&1>::!ރ${qW<6䤏'ot.P{[=.a$hHQתrH,rf" )))g'MזwSa ӥ8Xjj :D@SAFP<JU|>{Z_[][0O%%QDR-v$9h/ThS1qSc̰a, { +gCWi ''jBNe@zTz'IllXIA<g#x.Q>F+}/Fƒ9sQD'וΟq'_UO>+=:͌([LzjX[-O{=8d0˗c7pNɴ!$KI[`?(I%Qzɶ U<竓fBKH˸oBeP,&^ߨ lL@j)jphq00HTr~/`Ҷ蘇뚅YpXr06r"k6FBDfWJT='*v)KҬT?)wA,p]jJpSB^5O|zI˞. d<9P𞫝rK R["*J m.XK*0}X*e'>j.VBaɓv&DmBa <VyP{${(+g5]v&=&Xv1d" {c1(O ̘ 鲚'Ֆ#-WM[of2R0V0ѣƏ剃REv@rRnK) #k*U'' 4$KBӒZ,Q:& xDd4ߍvzJ m; p+݈Dcž(I(.ʓthJrM1vE;xCAD a#"=O_m| Je#Ɍ${ZO/BeD_r۷=k8/G pCX &[MکoJ P3FjݍXR!L(Ltdx,h ډ`Ɖ`֩`1^~_ɫaL06U,"j'0oQqĉDH, IBZ;񟝟.Qcdw0;Q vQ`~AP;l( - `W'EDKHKbGzn aU#kԱ's&g?n>O7ۺfK7 kYw 4jHDJzMmV%<0c>K 08`t4~:a Tnuԃ57D1RݔXBoǤ}Ч~o>z>|]NcRZ`JD](Å;y %;޸&qp*;w O[O`rNĤI ;'ɩ-~a$F#U7r4wpSf0<C*Bqx 3tlǢvORN X:h;Ҷ&OHvtχAa-_oɿy[#x dBk"^Qner1y;,Nc.c0n3n5.*\ ]1lq;Lv-^%"aCԪM9q4:<q|q˃LzQvhWUoTWni8Nhƴo(n䉿-o{R-κ܅ NN#R7ƗQh(,5an,,X]Z=8-ZsYQCVR65uFdk2 %%i 8'5<e`1jhL$;^An=.zf~HCmJ&+ZӉ!O|@N$[zj(w7z*;7 ' T*1mUzڅHOݓW=}K|RŅAC,RÙQc0Kr/y.zM2&*U30W2s23|.R~røqS*1Ni Qb)FKxN8BI:q$4yЧz>b`"1liwn۝n}m;5vXo=ϡp430=ǘQ/(}'j= nBk"%DbF[&T0lA鉯ۣzUޡ?:Bv:1Ǚ6dUaCYP{=TL[̅ >:<4U%^;iΚJ_. )gm0*,٘QsI{߳>T'BLz䩹C.[:o~u^jns]w+>HL1%BhBg.)!DQKOD]ʬV$~0&6:`ࣵ߫`ڣN&69v} w6y*6„OH%}JȓcKOK[2 }hHxY*-3oW4*'kF2U&L8$S$InJI=A^{{v JvDzjKMbr0m#1 1u'7=56<NC43cY< V#OćLB%&/vOeUQ8N.TǴa⚨yU^._y{~~}2O1 fwUeȍhu95YGʣ.6Ee7^ Nbk@JTbTnEPE?^L³vS }- UD@ט5ݮ0uTbjqu:.}K))Pz7xI!XGşˈ߻NN'6g?w2Xt((nt zH!# *$UV'Uޠ='f cթӦ&Lm8˝^Zpq ;|kLFD},>E#"}\cڊpb1$AF| Q}/$`VN;n  N RFRO;Ctqgе茊)!ه-ILeue0FPPWR <)4qt]4t&*֫yܴe Ԇ; 2X>Ъu+UB7=֡ÚWkʾʻWuZX&5L< S)mZaoC& -1&FFbsIR~D202>S4!ibr`3!؄03)롵F ;؂fsr\Q-oly_k>uŦBQ(nev2p^W e`K:hH%,SE6ޓ{.gRǍdwSIb=Ҫ{v[v2[&aZ{k9׋ h lvnP%UkQj,|)nUNz6PUM8~^O"ϓ{|ɝIzv먜LtXj0!Ijf7.mۡ z DBm6L:f.SN9%4zC,mVxWћoR^r]<9lT@;L짡N߄<](]䉻SisO^9oׯ_ʛ.7ƁzD+ދ^Q樿ux gqa@zJ8V?AJ!rOܖ;ݷ;sb ,vͶ]x6󯶙wKyVqگP]j'~0$MBf_ wU:„ħ"c^/mMIY\ZxOy)L4v^"IÞLЖA馦"Oo7[ީ{~^Ž&t7?Twn78̽x!Gu @ ]:V?E>|-tGe3BЉ9Mt RHZCu75IwU )]oЦ͂m_!1c ?. ~XzŶ_PX{>cY˔jpKW}jDbZtMf_'>3|.h-Yqox>J=I̘4)]NX#FjJΧR5O_ySjeD/6Eõź<ꨅw %h',p@>K >6[}k*n6?Y.b ʂ72s r]·i#=63M*S'UGFWejSg^^pŲx0/0楥U yhrHF=q))%lp$hpߌhiօV34IZb[\,Q58%7©ݘC#h>%KrxIcse+&>fڗs;*%=+e.df/fcf6O`hYNV1JV"Ua*W.'ND ~gDCD3z7'^û!-u^&g)2zluSWXǙ<#7.N3ނzevԜtRrT"3DNK$K㔘Z8?.A~Z|E7ο"3ۏ>w^ɭQD:U,}.Y >-R |c:!x)NPq؛4v63u3g=6y$:?Jv& MlвvV#OxZ'$'Zrt޿<=|ۇC@ᄃ_P٢x ZQOWJm-Оlj 2,mJImIlpba:z%% XuO]ǙLK68t{(9%SѢO Й.L 5EmO09⑼wKg}JUV;:5;8Ux.K<7mG1ќ 5ؓRTQ$)#=mEQ=ށZN8Mv|OFvΰ^5K5w. k:|r $,FJjMI4ݒܮl3 mݸ+P{L\"XGҧ}̟ ptoĻ`njdKA_gsmȰq̈ I?Iζ`V' [Mҍuhx" Vy%Q5yR?1=q=.WS ͻ^'p8Ny% ٯVܧj ʶ-FKm;lxaS0׆ $4qSu+K_Ih=0n3HyV󮴚mfgD{{EWo];w3m C,}̟1oǼJLIhfJ%\w}L17hՕ'f2#2 '%ۛ%O vxzz1 ZtuMtj~_D5.WS]t̛w pn|YMPMԅadJgjښ }K`xRjd752@{2\m)lVdk& 3 ڮ<NĞjFuڭ漰0*TE=|n2QI_I%o5"&l3]f6Lδm~W.g^+#'3`QNT&*vEt rf|ȞIp 0 y/fR4r|8p՚݇ЀTB^(n@gmoCx) '{*\!V O(<$<# *LݝxW߶ -Z$:3%$ q%,wbm)P(m)K[% !F@BsVBJ\aY~+쬏kWLf7\KxdT3jBOua?TC:,a&\Y,;R8+Q /Hb5h2H2ʨrhIDI$k<|Ӡ~~<)d'mǛ\|N3sתzɓs~=qՐ)$ҶflK*V"^3{1:̒8+I0YeO'0zތ%$ucAT4| ;Ftf~LOC2|?nt#f]|ScqW!o)ytSxsѨ'ǴOR@ Ccyu/4\$9y*vu IL-:ӥxOmx"#zT=]KI([ Iٖ6zvx'k h, Z#Jw"&> PERĤN}7~BXT^m1Ȕ*͠*g5@!bE^{#kjRi{ 1I I)p -L+0ӧI|?|GTI1梡u1mc FnL7~&-€go1Ϟvkp ȷv.O8?'OwϺҗIjr U&fܷ8J`([e)8{aHoDMy=#$}XIo$i;u^M{#=>402c@8櫯Ldۥ2ho~8s ?It޿˩Ѿf8?:{Crzq:8HN4<(|<Rt Թlt_(iiiGz&xϣ‰0a[wS/7@_oތ/*MӖjy%^)B6z-|M)z uTEbs).%I6H"o2LѸe7 \ЃnGs~9?|'||B]/C `қ/ 8g_[E Wh~/6w  x:q9y>mNw.#8$ॱ/02<;:xYҥD[*8ii7FhKͅ©lM!LԗTI`X_C@~#0AaK0YJDNGC& &FԐlT7 AST׿w|׫ǻ5M2煛"莿9?Ҝ $9oTeOe e=Wx4Hs`sAosAgяqn11p&x;S&G{|N ev2&,v8*%"CMTLb)4Ez2p_8(m!NG p-떅BEPtR--&IDJ"(E-?FWe">%SLDHz[䨤 `Kzuܦ9Ӝ[t70*|dt?d29p#Ėʜ D>ޯSch˩ Mauww=ڥ9y2aV'.)%!OjBH&5P<(O!~Ew&qw]u1zl{ -.ďva ,LˊeDdȘSӦVw#`"*YgR&hb*jJ˥4/@Q|,l;TѳKEα7*3_{$yhKo֯=9]wf.$0MީXs^NSmG{uɜCގk wwԽ-YIraJ1leT`IlEY%eEgK|| WPX3-71M7TvH0iHRuOI`AhK&;UrO-DZ{I d7-](ONUx|G'4؉խHRK,X(os],_O'~sG<ܪwlstywfG0'pQK4(Ld`8'6%nՓ,*`j *&0/`3C˲!ʆN$TYK[ / Ԝ=[OR&|[AcGl#':tm1jh0IWIhKhK&JnʙA)|[ZA{|G4͟`ԞX4[Šy`ulCN &$ۨy/OC <CDjbQRr"ZTƟ F$7I22aY|ZG>]&]o4<ؠT|#9y mm\8Z !O1$NHeQ@J-V/ ]H?`(&k5cU*'Xk|%$Wc{N,!FȐ5F^HbER ж f\p *(jdYv(Qh}Ѝ0$A{|3bՍ*{AkE_?Wi*^y\lť!L)Z8ΟDH9xJ`axJ(`4®c-\ $3I0eWz kz4ar Ac*V9K$LҩOס/o1of 1ɥT$u$*B*|BƇ* CU !=<恳Pv,PձhZ씂tˀ=77*$n v\[kV0m<ev<K` r.Xx;C.)A+ᑷёUb ]Q4l{XbTѭ#HK[e4$V$$c'ҿ咉+,kv?R|o W8_GHde}WBE΅s㩯׵?\1Bkt$HDNB= 0N=vl_zٝcA)MGBYIɓϬ=^'caB rDoG=[G]աsīp;`hxxh[C]j2л[\PQE ٵ@Hb%HDIj5 s.#ߢ#-qV?N֜ '= Fi$ړ^8CgOY|f\?{$t&dsp2D8G 9Z"Hђk`Z]Dp.TƐފM0nsrU(=l|cACkmiL荣ߜP.Q\wEJIeUk W닷mדDj TZm.VɻW*a]#$/_w2ߟ8w8](jPe0t1x\2R:Hdat,=]сJ?~QhP\\xz?r)t2? -<t:̆E9%SJUR(Er6D.]Gx*Ml>ԎK)Z}|$(- uQѝ[(UEkQi Vj h Vi4oS#cg KhhņTC*6 um%ΕTeːd@GvUş""3\Kzw2؜a :e !?eNUxr[+f丿_,]kF &&HA0GODvrJH!Id64{vճ<tTLyDBN/“qZLrPȘn@O7Pٹ.UtoɆ5BJU s'3C+P+ hrR C20PjآK O =߷8jKy/QCO!wXtwW]ѩ{/O0dp9"x %U/Bi-z ." @2 ㉒K%_l\b:cQM%FEwncA,U$ZpŚ9\%kBHRO6 >TMsvN͏5-(o&'0HоVёACtp`(ң#\87:oE}g,.(d" y: y0* ǃТgۢZy, юؖ=B''V=@ZvHTH٬z D][qON,韝R*"~)*٨G8chcccX)ܷS+o ːOޠUkwʵ6$hKd=Xrf4͝ fb97 -!7ā@`j8lfd=(K Oz<;ֆ? dǚw*Yuգ8qآ2Ȧ?2Mg[HP,Jஏ1f%Gq_ai #e˹\Lݝ\Mp1{ہN  & VG5ܒɆHD;UVyq95nOJ2#)t ^_ϖAbTU=:woW%Nye}A4T\\Wٝ G8[k@Ts$t몇+bsjQ RYdߢmN ؃ Z|R6Ս1A|z"=k"VV0MUMN5AITkw>`1NԭZnZoSdkhgwѢr'nzr$0N'ϗ}rr#i/c ոfZkmsY{S=U$_4> F::^<E!O-X;mQbRVO]Ж2~)J|VWx!g+}q|2A8s®6O\HL}tXMR"5{'  +P]Pq 61zϙ*bT!˪~3Y{ʞnlНy7r&U1aȡxB.X13ݝO ]|wH/W>RʪjIUOTHu4º.h VTu2(a=r4Cd.퀬DI$(p'Cqdݴƹ֑%\LC5ejWԮB}-MMvУȊ!N #[ *I᠘)uN[Yh=])FckUAnPNH0TΩ`ShoUxd˺DpqP#LBjp4@iQ6u*!KH%ƃ;3V.w/&>+Xwyb1v(3kKZau;H:ĖfK~.褢G@ %F5\;pAc5M<=S9T\p=xM:/qЂ_ @ V9˝oPOu}g)x54 -)Xe],Jͭ{xذ|z{,LB%/bDP|0>O&Dq_jz% VԅN0ߨ Pي +Dj۵a1I V&HOU1*\P;]Jb4 aBO:Z]uC<эFrg~ ^́j[ađXQʤDGICJ Iʜ8ls~#vB@;NWL^ڂ[GB*e L28h/)z, 7 4L ن;T\Xx>?(j"UM1k RAFs>RrPRt:,uW6VeG]AECiʺyרk]kHRJrt ʂ!GxemL-MT-M$(Z#C [g %5.C^"FI&;rsgQ?MԮ7Gn1˴1zs_.OXC];3>_ ]!.EA4!e=EXBzBըΑ G]>KXGf-=xqqqR{.A 57"[:baDU!8{Ӭ$ 4do܋6jh?sFC"M,-@#){~x>|g|0w/6 `T "!ՄurJAfseܳ)HHoQ9Q夋9RK9ߺX `|XHF$t⩫b ! -(cX `zt~2hɍ5Ŵ +XNƠuCv½[Gj@/gJxa< l.bHŖ)YϬ} 3YS"iVJit}}s< p:{5k|F8x`|BxZxFxV(cd4 SŨ4ـ&!ԵF׆pԜF6F/sP/6e}u# ڟ!S#3R tupeyښ6[+uy<ͼt1NYwz#R'KyH٪}vb븯pv$zgP3x)7 83ȍ-)d 3r z4ͭI9ܓEܓL>M#4-olh(Yax3ܚw+ -~w]Őy FUb&.v]T-p $ROu7vj[y`[r#.N33-i/3c+E4%$ nljOnjzpi܅8.₂1n{yTj&zm,jMl˨lQSӵa+aQj:ށ`Y Qԓ5_~B<} [k**q+UyG'Q? %pQ=C DW`[)ܤ/ ɠHa]W1xa<җ =>ODf)H.|vTJH%>{3KA˽ mjҠM:gvBbw iҽqXߓ]=?i=tMZHx=|яzFws%%~u]w2Z~(9.w'KoFbFUJuuFEf}4gQҺ|+\w<@xۢA lh\pï#ׅٙ4HHn⊧=;=%{=Pre|}ehuzrL]]oåaJKy^0OO < =%>[HY)xrJoTBMzHT?KRHn 犿4MգJ+oʢ-QX!Uʽmhe:LdSt.ݲ ngvImoqE힥Fr8\<"NPJ2ʢj =\$cgD>7a,lk Zp,k0A&2,aJ|gT\\|"s7"ՍHP!e1^.;_QJ %SYО] = ֛9k4X0Xċ5٘G""\+1{'oaF}ԜH5LZ\[jUIDAT"Rac<{ g;T٤ʎ]>[$x͡ܝyc)8cx"^&1N{Ծx [hmK0Eqg"˦=)=jL "щ缿,v${T=~ > FR~k|idT6gWdnp?\.h8;˜;x{$aKJxX򿾌.|vg.L;3r)Ga0%0oӝg71a+2R|) h:#ReQdjYQ='U&X܍܍ADC8t1lqhıDӢuk2\X&k{ /7)I~rr1="0QLbJ L\y2= )q&k S<˨ؕ}T=%ԺsԵk}8p&ЍD-k>R*Q5- l,GJ=e*žč:"I9D-)l$w:;v7?/'X,Hm<1LEB?Xjl1x}t}FJiT֡&Um@]mS0 IԋFՇ?oP(*bWcJՐSezړ +x1'$$qbSpD[޹/t\-y]^&5RDv!3|DqI[*RrU JJ/9T=*G+ \C"^hT- iAqm՝SI̺퀤j,lIc!ygO3ˆ(JnOi('+b=C(U֣-s%*v^R,󖴻pTճ,6 $Sx)L'W,cG?8%OBœ$KyΆouųgzδȃT!EJ4>EORMH'Q):)R%{-#Qu^lS/ŪdԦ5^vXQ*SvL3dV7 P~[ma'N <Ӌ-y'==… {Zi,J}}@8%%*6G'*yU%S)U`ɓaTuP -Ӳ-vjٺ3ZmQtk\kǐD۰ȑ#'8pYxy̙3=>s5zaM&UHQA}ThTHXKMUP -K6.KҘ XeZZI  0d#PjͷMn¦qcwڵ~8ph'c5Pm4r6AlA}]I=XW4ruEyi fX- Z 6dZq.@ #BO x0椭6/a=o\h˖-I6ɓ'SSSSRRv޽euR QOf~۹ϗM8uʢ[AٳpHbP굒C.FֳR⥾?Bd%@bD"Й!km7~zqƸZc\3`֭[±# ǹd]<ܾ*/233qSN=z{ݾ}ƍWG kuMRtqnmf{gSs"(_jNDf hcXڃrxZ1rb5QY J?CD磙II#X nt8-tNc\ <5ha8pT G9y8pgw%?ђu%?lyk׮]9| Fe{ҥUP Q1|垪 Wўt! /H8R5'Dh:%2ЎqV4JY"E0fc T^ L^nߐ%h7Mw:W:-뾜 GR OpixcGK=wP?իW/>}[:~+ɨc>&'`\%́9"JLsT-q930){Y&{hoOx5x;gtE!նǏ_2ҵVFtq樝[Oyqzt *5'8δIO/ؽ`{lTuVXhѢؖ˵*-A_"UVTe6 3G\4*Iz-!@*? ϐwF UDl(I͙x- HCQUA:Gmu-lczji?spQv~/u,=$.^9)*''GJQO>vءCܹV˗/\pI+ZnΈTKʲq*OMMWtyR*Bi!^~ Hu2IJA˂0 cgj%U*ɫJTy7V.KuP@'/aAý Ӣ-DfcxR^TA"g1><ւ'IuA>_Ϧ{7뻨\jy~Imio-o`Er2%Ң=;U*),% @LIIٳg϶m6nܸf͚ ̚5kڴis[/@XʺSX`ztY*Mb\gFE'Zjƛ0IE $l OOn81 ( pc~7%ypdQcG ; 6Ie*x*!K)洟)Q͛7oƌ? ̫&®\"(KX{/iDZ Rm*1>FXRlB;Cmu|rǶp4-D-i?Ԛ/)3jJnIt(F}Qݻwǎ6mjPロ"d~~ưmA:bJWcɥP^ bFT!qPU"U"X4A!(JĠQN#yQC~}~qiEbph1##ْItk1l"eQF%vB8|0-۷oTI^5sq^dBqnaJ/vVṯxc7Q]Ol!i}4,`j! cr!!SJ}6/n%oH|`_֔$8ћXZؒE{0RiTʐXSЖ_jղe.\ @H볻v[q-q֯eW :(;3-N!-= cJ]5n%.䈐䟦7 5It,EJKR~dϨ,ʟ5URxw]a'J0,. Ok^;7 Tl,pҖIJ.TI c ްaK.5?|efA=AѱV˴#njh[u"B1,PrN'ӛYa\bX aIK\:k{! /|k:~An$o4^U4+ njj*xa˗/Ӛ?}yF~z-Xk՟餛]K-ْ#/QY[ܳŃf\c2f x~A=ёGXx1|ZK$MDEN=_STIi®h3P Br@Ǖ!gjgh͚5P8[?sff-"q^N?;'8d:A38M|B![Т&!YOJv%5-EK!dRԴ$oAA"B݋aYnVtz8p3/[tȑ""2r?98OK-YNP"%#܀dx.21J:-dySg>d|FQ7HjY?Hҿ:`NMKbK-&©qI;x \8n3 n|pmmXlݷ߬?n!Yݣ꾽7|< gsu5]$z3'2I Q+):{ '{`Yg, oAuPE݋6pYapj(gPn&7 rK'+Pn;R@H |Zɇ Yg'FK<,6MI5Q‹M]0&2036Βr;mF@ ϢO/Eק;Rzh$e Y[ѿI[^u, 2Q(pHDJ $ױFңt {>/}KM”YFiYE(ѱI@>d1<;IENDB`sikulix-1.1.1/API/src/main/resources/ImagesAPI.sikuli/netbicons.png000066400000000000000000000024711315726130400250760ustar00rootroot00000000000000PNG  IHDRWIDATx{L[u&&&f˦dsMYS-&` ItBa&cL66,Bᡰl^mWpxӔO9- %,e^e fas&VWW[{)fa ) 63P{tNLNsfx 7Bc%{Yq6Y"O hfJ񓿄17Egn>'J: /:sl+` ~ڎk" VV6nk>YOϛ5Z\x:Zh}3 ¤R[ȡPfdj²>Џ[H q` w;Vn }N]˿YM4q IA>ց }C kY|0N19g1 I+q YHlxLr]}[2U60SzLO^ "L~7> 8q:,>0%laq {fXV\BII~Q߸nRY2.]p ,m6 l.[|⿯=/BϿXHNdd+ _e譿}IENDB`sikulix-1.1.1/API/src/main/resources/ImagesAPI.sikuli/netblogo.png000066400000000000000000000014151315726130400247200ustar00rootroot00000000000000PNG  IHDR ]xIDATxOptle225-MISit<阤bxdxy{5E@CDYT4[d[ܞϿm .n'oM)I`nǿ!Ǡ !"Ɩp+OQbWt+qz'*jRaj}Ulׇ͘_8Gq;^Z̕ԬW&?4bVV(1M;Ƣ \2^'j^c)Z͔NGJVbV};:~?.Y[q{{MW@e\웬&w@XL%EoViցlk-b#m,2,6̕ӌ&f9/75 3WZn'SGm l惣']bRWaC+ǕOE5BlUMXrۆpIqI0~+xG$ e,ݴȗ)'ggqy=)QPd^>((_KKVYs? V@"4=>@I= G'0dr躝=68ChS^ \ٙɮʢ*j_u*Dk{(YY4~$P@7m/^<[=/MnۧcQ:TNt(LLBgoN̾cagQ($Ee~VRСI[]8piPL霾M6¶|PƇۡ5#۳#.~>s:KeMkF!)b<RZf"W8z-%E!m5&"`!W(6lm"FaPha(m䊊<`NR= bxcft]r7g<nBG-~ QhCoVMC ӴQ YQH[߳90sFa*0uo)~Y;Ttqٙ?7_S(Dܖ2"n4\Njꁘk>'b< w6ᡐ\ ]+vΊ;Zl`0$Z0t\Y~2f6vXlA> .gE-Ag!BFJEVO&5ؗyqѡM8(2 C 4J̈5&$7HP/R=@[+)z(?](o(2oil5 Rjb`f~gևPin:~"񒥿VvQgD% rMC5 _Ԃa (D.m 뚦0PXrS幭Nx8kb~:ZXDvhGl(4CxNEN~} peοrLL&P5 F.\O؄b.A($yx򵗩 /69@q= Fb|l(DB"1B:Ӓ88}p+^}Sa}x_fקF!'1B"PHtE0 %0Ǡp־ F05bq#.K`5_Bb :p(v NFW3Q2lYI @! )d $>Ɣ$r&1G䒣}J9XȈBj0r1< /C?8~Cg1#jJ ~27,?^iPdi=17in5_A5W0ngQ1($\Lvu*8xybrX8WH'r?VnQWwum3A 9a8Z+!J&nbP01c>_ "N~\}D7{2lGXv(|UwF!U`*t؄F  SEBs/|pμNûRܩ9d4({T8* MÐ(Z Ҡ%nPHHtB\, ,ٓi*e$c`ФaJmWx}Hz#H f@̡jD(Ԯkv[֕[(ۨtz4> o* ߸Q"wO9ʂJ_0(W,⮾rh1juR;L1RU4J;Z-TO)ֺ>>btUHIGT^alo|F?tNPlT ICX+"~ogJ;x&ԕ7c*+ ~ /Fm5=‘z(;~W{m3uv_2vtF۪u^ 'TAZBiw[DpD"hfu,m#*˓]r}S|js4ME=]6ZBRd8IYx G7}ˬp0:'c(4-  n?W{MeV'E&]XC*R[ݑ*¶?;tWȅG߳P3F!;#3̼O9@oilG=XyEB.Zhwl7[r][ŜU^7v䖵nKd:/IXͪ9);rzIHFR>*g(E#@!ch?q H8K:OsZ¹VtrϷ*)QJ2UEsZ+D\Uޭ8Z (Zc~ThvBTŠptPͭ|Ȁ | P %Ta* bUQAX!K@!"7]^s$&VFДO2@B@  GHěIENDB`sikulix-1.1.1/API/src/main/resources/ImagesAPI.sikuli/quickstart.png000066400000000000000000000044331315726130400253040ustar00rootroot00000000000000PNG  IHDRr)fIDATxOY&hwtD,+ ,"EcI^h],c7&L+or\v7W\sfy]___ggg ifKC=^1Ld]WL:S6}Ǥ'Z0 uwܹ{n38mmmjG: ֖t[Lh-dQ3m Ĩ,Yf'\SSeo߾]^h񠛼i"fD566hhh Vыe[kD"j^l۶ ]WW'Ҹ@Vd]mm-4Ե11OMM֭[&e԰dy%Z i1j@:̮j^V{R3U^"KEEEi&iԍBƍ.(3 /BlذAXxBp`ֽ'ꊘDٕ(D D(!dadx3֯_o, aCG 5˼ FښFae}Lƃn:F! E命zYfvZu`z͚5Tl-FhBjtY%̎/@(\ZZ*D,~I̜|ٷo_iLAӧO.^(fLsFFƤIBW`s YrUh(72?ۼZh:uׯgΜw k׮ 5B:2---x;+`999Ԛ3 _ 1liJb"jD1>|xQϦ.\$4XPb;wĉE!2Eƌ7n <ݻiӦM::>%˩5_LII/֘a^`<ӧO_tgϘr0T5*8&TxIb 4?2F,JgϞ8?>}Tdtt%3Mĩ0F "Ĉ!Q/Jĉ{ɓ3fV,s玨xErp8[Y9Fדͮ'NKǏݻ+Isсs֬YfuXVEV+#U /LbKD`b,CZ.!3+.Y 8M(-O)%'WLp\bb&wy⇡I@f. B6f-8w1Rp+Ah4+ SD6G70C#k ]<47avKd럦AGd̜YÇS_6/DzCcycSDP͘Hu|BSS5m25]A3k[ޘ,GfhD SƦN&Ch2|ΡMvͳ::hl1K dg]/*]ƣ2eW݉f؍'{鮴=>YfgvA(ׅ@:ԇ $Ni^ g3`Eju$ev !%7ӽX)28N1tFFZa<4dee y233X$8@Ml@F^)m2@rssEB2oY6>D01DCQ@ȣ.1 ũfEXIENDB`sikulix-1.1.1/API/src/main/resources/ImagesAPI.sikuli/raimanlogo.png000066400000000000000000000425611315726130400252460ustar00rootroot00000000000000PNG  IHDRcd\zE8IDATxTy};uv h Z܊%H INNw' =Lۙ}3yJ/׾oM{ySSӣ}}斉ʉ‰~~>پW|)vRnBw%XSSONNOLL Oh SeSٹc#>c&cj¢<޿lu/0 ݜՉ mR GO @15>>588EON*scQ1n&#J*CFp=9|U?!3AAhB0j`jEF ZؘɠǓ;9F$r&Cl<}gڵsغ}C28 o ,gp19sHuR-|\[k iX?OO L2{&(ɦ)HH|Ⱛۨ3ѫׇ_ ޲|TRAo ek鲞U37mblB߾U[bgO$$P1XZEEgBB׍߭oI5q*A?OWC H!H%̶\R_ZfMƍm[iPosQ?TըN8 M#kj--:h29so%K:fgg;]l532sb)Bפ-b8A}ݭBqDGSf}bWo/US`AÃ^r37ofZqR:K]YR$L_ꯪ!E_Xo~,%6NҘ,[ D( ?hKٲqrzdsWG+^dzghr 9D ߐ'-YY!5n~E )P8CCp}ܶG*F%%=w'>zzfT#A0"(|Mjۡ2N- ?{_bOϟg|q SO&"ڹkL͘:l֙sIqq8񖖆s"|%EU1.Eᦑkٻ d`CkquAض޷}kzB\D;2Ccv{z?&5VwN)2onj^PBq>G,ZDwVZ;<.zfvVOEK>6QQ\g=|Fe,S7}t&=[gIܴԤIut(7j:eNl Oٻ~<-;5<;8ԗ޴ɢ8ڶA8_7X4*dѨoׯL֐؇J [7ozxi h%Ruz?1}S}ݍ+CohjfG&K XG2qH 2L(V3>}hQ yh zWLdw.CZU_gXꚴ#&2SٹSvݳs$&mKZ9ۙ l۷0|Mj ajf}f<#},3s@Agjƾ>հnȜ&֌|Z{Q"ԃЕ,'pw[ZhW/S,qt30bkKٽg<(A|٤rN^\IFD ]Xv_!`ϒbX'Oy:|~?gpH&SvfUWžtUeڡ }=/fpPʹzxB2o̔5V_[Thמ]#6bb_Bbf73H556Vt))A$zBkR=5us۪kް}GNS^+e>{vOv;XbÙHX8}wYeTWXN{ ʓD=pv5*zzzopm?zdPTQ`5L]ݡ:y|#u4)T2Ok@_ߡ< ;55 5zG}[;ȱz)Lc}Ղg ҩ|4[tj(JJ0TYu~=) 4=EBͣs4k5tE. UUvi&\^o宝`IG''bc2I% 69YZ0?>795Ȁ~ ^TPU,)0u4IJ7r$? OTh׮I7]E6) q;0"iGvfyA;{vD*]h.;Ng I R6m ~m677?O{gނEcNW*z𠯬 PuXRV驨pκ5N whldtIRi~Mjztٹt XU4Fe9eҽ^ݻc$߾ .l|ʲeǏ{E~< hnP艈Yl=,+ÐXpJɅsDב]Cp`M;s/\HCcR5AJ"ohTW!7xxP#5>Y!Ů jcY6.4oޗ Gcٿ;88X &EйS!tķݹݓ3UTz[Yؘ /]KMonLlZC?G:˖uvv`.^LoB,TyFM'*;Y#c==eU>tO^03p_prb?*aaF޳sժnP  >48,#X)dP.d8dgQ/_cݷӽ.kˎZ֭kx܎ݼOyhpM}]uM50oitZ6)X K߾Zn#"sJ\\wy|bT][^[GeN7|ۿ+o439<"d qh_S4a˚hk92|<}ddΔa-M:T/EĠz_1r^M;7665cB݋9rsb||ExhwUIqm\䔽|ʊ#n4TD K* A.nZ$S\Z. X:vtt)xG,\ aXK?|hki'z{gAcQQ5X.NccA^-[77:МEuvS+-m(olkcN*%a5uU$ReR") \M$Zx!_W.[d)J5T6d}ڍNtwhhh@llN#clAַ2qJ*jnnnh!Ց/ARk„+P' ut:R&9Ǫ)]gN7OAsJ܁[$$sdN5P;O*%2ҫ kjk8,jث:~R37ldI[-[fûVi=# nz&'fEDDaJM 8(Aba#͋[(vR{Ü=QplqJ݃[׿_VE΋,ssӶnIRR * Hdږv[[rss**;|2 qܽ8ҳ(̣V{~K]uiaQzreS}]?;~*T%)a}㋣wpHU() L Ш bEzo rjhf&̆ L 曙 uMdLf$WQNs4R4SuYѺڊ1̉j'\,ͮ*ͫ.#WpeE2|:&cq9!q>ў)ўQvan^6Z*z$LU><ၥ%+x9 [W]ڹ O>&͓#FzbOԼ _<)66*&<:p }͚,Sh ͱ,J؆`VNDm$UV3QAT]%kd@q OMN4 \o& k s)ģ3QQn1vfA.^6Φ6z2VfbڒoԅyEx>ɭ7\Wo}q7?8+/^:lgQ~DDxG,%rUƯ\hg&J!5 VX;;ocVE|X^FD^ZXˉ qЖrdHLM=PTMyn-]WXo)jb^9>!?JL)޶ڎƊvrFg+M)}w:RoTUKܕ|݃ko}dۯ1練߿}2#R丛T #8/s'b8HZVX SZ/ LQ^\V`'+3%}F@Pm"Hrtk}))-L-+J\R~vTvr`ZGbK*V_HȘȾW|&G޿z7\ݿ=|ɥI5,&sRS>R/z9A=R 繀}]:۫k;H3U7޾`O  0GwK<gܚ tUi.JPŸ$!? hbYYhJB)Y( jKK{~duf뚛G851>W@eG>ށz=C*xNdYYe!5j\m۪]u4_;WGNM2zX~:Nkm(k+m)&KiyYb-|\ZZM JqY:ZiK* I+9YkJZk}ÓO/}~׉6z'sFYAPHvwz ~sHRisH[ϊc,7+1vc#'Di`.$Y] JS@FԊb VeiV9>옂xN Y9{[{[88*8)Δ(Sptޛ^<ѫmvk>W}}a 5:bz A|gM=|6')93{꼜[٩аJa'S"=Ç(&Ckbt"2 [[WQYCĂCas*/Nݥ)tZXnrPV?&-4#;:(k/w|w.=[?M: ?{t902|kU_·p.RUu5EdLF8!?Z`Cl#4W5 j ]-K&b@8 68UV1 ?">3!9.>9%951)@SFbTx.yz儢Kv<8{-y {b5<@gQ@jjh/.lߵvd/jLzxnz8:3 61m4jS3euaE %A(,˭`|pn *4UI@UU+ eD@%xBFExGy%89聬<4t>iHVx%JS{Wج$yS?|c#<2R$%!7'FZU_XRYR^Uey9IAɑ$B^ Fi 5YmezBkp='BPh"YMDTr`a^,54"PHl+V\M\U[[앾ӻ)K}sK.$}et;y;~Dj|'' SkUhgZjcSH&Q `I`ސa;Z*)dX 2Ȝlêȩ%QQPnulGyCqWQNLVq1Snd#jcfj"l$')R[u\zڥk;I?.7ϓcso 6A^~vleQ\+$L*eSG3X+$Fr J "4Z"T ߃Ӂmc"KSD'ByqX0˂ ZJ;)9|k `ҕ9n\\ܶUvYLn(ļߋUaa`Ut($ۆzK!( (r\fAnl!ԕTdA*ʀ9XYZ PQ2̒Ii1^ZNJJ.F&*n֚.>vLTDTD4u>Swϟ:f٭IU;8z/\is*z"9h…+V.pn6e(0/s\VTJyQH,͆~PƤR 7mVRmy YYGy NI 妅ƕsSÜ]LT\Lm5$,,4$B,tltdtekH) qkI1QӒz+8vdM߾Ko\Deߧ~LgbYS6U-ZsXt[H0-3do[cGsC)nr;' -@CoaQifb}MQ%!0j0 UQs b s 㳣 8)z[i* AeHu2QN+ʉJvt2p<)%o nm$*}㓛oܼ{g=x\cӓ?A*4gӦR9Gڕx@m})eK | XS#jwCAnZ&%88JXiHyDWc[];%xn}II5 ̑U…i\wKUZ _1>692___[3>ϬDmܔl`"cNΜ??{޼.̛?-tTM_\?)⮕槏w\V \羥,m,/h,DYRKJ]_#U!nWwŹo߿pT=OTp#XEI<<E:: =z'1˴KSVn<9 jɜ8"BzE0)nyq4J=DI N *W]M* '麙qVl7P]J ӿw~i/<-?( jJʾז~)Z3\IX98G}&JB:Ro^{w܍{_"kI+VA(,G6gzƚ5]Ȗ_!RG$ ^njpyqfYQfZgVb`kc9L⼴Ьx2\ L* Ed< q4VѕP{i P჆dLw?-NOZ݋珉ޗ/}WK89KM W3s5{a"<*B5Dym4td5$>@}%@S҇g\tp/W޹c 3|9FW{}v_oS\LVL qIlRk)Y$X q^)ᲰJoT2bۛt U*^_]jyݛ_9W q;f6)1N!;ZSTUжި}XUOĽ m=LՁ Xi+;˚) ,S7XIԄVommG 7wF>pZ,ŋB]M2"3|!0X߆||+s@^TQB#>1+);<@i-ޯ*5ϴDd<UWUE^) r iT9L ,T  (+Gw}u%I76=}f N^"GQǏ)(j`L!A!wCB CMOw=OeR;i!\Wե?zZŇyB\C`ljIκ }9%yي G]2rRBřE09ţC\\TMT V[OC쥶kO9kGژOOA:=HZ7'+Ԕ_lwlܞ¬ sヨŦ)#=L蘝웟 0DsH`t T#)7sRl*1-uflbI2o+#苛,hr޶nV:RZRzJo$>T%JV8Q%ۧ>ʾg %:׏iGk '-,CPAf/j*-!yٮ.A&}aaJxq]ʊf yʕȰ~ZI~rBCF?8'/#:&ĩ 92LvAid|>#?76L]J듀2:=& ,8MN0WדP-PBcl]ӣ<#lT%(}|ᩢc`Z4Sїyg,+VY X1h-?ph!&(,[毨8b}}i11mvy"ut6V-ѝ#`*-MJp v3ώg1zY 5]MܸP_po0OP԰2LwNiu7hMJ\W[Dώ u3u6V2PA·{R\CSZGV_bsyh_ѓ~PS쥑GE!o:ÏIMJH(. ,p?:8059>sZWg}^N3!A:fNkbD0ъG'$:E{$c3cZHԮb*Zd6[)cÌzK#T &^K**OHkJ":) ?w2 tHu#d)ӊF  "-I>`%RUBELy5Tt&&e,q-^lvJ7{<{juVˁCe,N!y㙝¢]`WkK?Ouu56ٲ(-ʢUuU5VtT6V+ c0YѠv2\UґWTH# !9)566"3)M*Ɍ vla_]!rB ""/MDMD??RHFE ``HCx(P`&Χ!%({㜟H}RSS9VV6!q 0遻s0qHY͒Rs_,a0:TZWv^rPn?>/>=ίesT]e>)Љyxt<$MGo,-B/AOYiH ȿy& SGW_ܒxb+jo :9虪#9cAU`H4S[ZG7ٲ|ޏIy:YvVQ J5KAY̚\XEbd0V큊ɈJ N W^QVJg`J踨;2b@t9Ř!#|mt`Ѕ-+yx| K´m_{]?{}]3UQ&P"T'w-u[W/7 Rӿc}}vo9G#,YYtSJamRIo-*Ho)lnm,k+9}*?;ai%*++J+ʋvPs3Qv3U֒{%boC Ӕ|( JDt=|;8C at5QԒ4ՐzpU+ܸץf?/W7)-@ߺ,)Bws}*94;5XH j+q=<τbL"6329Q|AF$|T4RVNDYXGH~L*+5=lejnc=sGД̇ _[V.0)RĄCԌU~cU_ +.@?CCzaQ:9WėtZ+tzBcuaM$?Pub)>V vjf) Օb_ c(!g(QOꝩ(^l}y% 1n+d-4@Yj"joy`gwn2 ßTuF"˱/PSߓ_ʑ3+9N'džM#ok*tյ45ƥDzh{[kjKH;)8(ehb zZk/UאX6ҮFJbn]޲b1ԕ[wYamdS**t.]A91tr5/lȕزtP*07(/!TKcuD.f_ZCEE;$V j}oOGk3.ˊ>XRNVPn:6z JBnB!Ld$h*b'"v_[q~۰4]]mp0Hq PSs#Yϭ)fZ)ei{&$I@JGHos])#a S顉a-%-$AS.rk]1@%Do-Ku13e!SE-w2^y։߸o{yZ;>);14eo g# X!XA0Sxεu6\޳֡6u03)RԺzׅD+ &`ЭEΌdFR "AI%mdEnl"4T.<.ˎ)ϫ)GT`#\IģrSݢ| ud?yTٱ]o_yqK=Mזjb0DA+RzVZ5 E޳kŒ_ڳݗnp05)R۷>"787XCnv ߽֮+B~2|' qEDp¼X(C\z8:)8*džŤ (mBHE(etbLkm$+=AїN?vɃ:{Cyo$'̽2Ey``+c/k-m"l(JSJ]-k]r~v:І5F?X]\\")@u989 \exի/_9*(EG`h>My4r#*:')(.\@@I;hJtG7zfmyhǮ+n!sΐ[Do&g<w72(//omke􌎌|jkR59D5PyiN%쌵ZJ'6dLVH/#:g8jJkU{}iyaFi~*>7.'1 6!)'#%j09s*! B=9k )+u =ُqkXsȮ;G]Ojjzz8>>('+5@hinf1#?aag-J*Ɍfd(l͞ru,5`?94ȠtԶ5C\bl!U4ڪ%LlVt \/ ~HTý-!.D 5aiejjQSOQ1DQ4s;g/x|ӚW_ܻNvb}0>Πi8UAO`Xt5a_鍫V1"*@V \q^|L}V.;TX]TY]Y"gA'q~1^VP~N^v6Z>V^VΦ*k_Aݙ( (~;֞ٺ Wms|ϝc{l\hnA_MOCehKuAj+0V:R0P^O+-a;]YVFw#C \Ntu_GTpgJ ̈M w q@`ǘ@p+]`҄J4\+ů-g ѣsn]unӛ\ܵ}\۸@*/=YGQ_^77-\0OC% p2}Aṫ(_¢6w4a0ƥG2#uU$BNiQ}ryQZyaji~26; *Bǥ$yC rnfb ð(ۇRou$ < wm:e=ֺXHek񺛪xז@HAB fo+73ue!WxuI- eE6t2vX%87T@IJ vt3sÂBs5U\rSMךb|R/﾿wھ6qp7ŽwOurߵm=ՇLvԓR r gć8?~v&ph_eAn'WU>|w=+%f㊢ fumuXMu%5̄ΘB%81Ez[Jr2Vђr1R6 K u2_y{n}uߖ6?>wƶ RYI1^^Rь q~ ',4D|t`(4T-{[ُ/, Tq%ؔƲZjGmk})U-= )}_NR@fOl0``%e&f*t>ݗ}P[w=t|{]?ƁzzSwmv6T$u=7@M]?vTU|*H?yvCSⵊkbE%dP$)IknU_nDHRг!;%@ADՆhj'=zrMMqү>{GV/}l/;.ur?l[<,b 꺻ZLm!* :3/F^/t1S rLģ)=cXE_ŘlFXUq\C)h3\.{}a}yb9rM.AnX}jMu T !SY^XwW h"D(/. [-U6TVPD  ,&Ivt7u5oijoZWWW%exlyq~YXRX`|8qǑ#G>|8q8qǑ#G~~N8Ww/N ː.&8qǡoᗐ/z=_7q;Vsk8{KӿU7#@8_ Z8|я#Ox‘#!qV>zWgN#Ǒ?pN!83ᛚ~/+k.&6de777;ך|UQ&}y΢}[-윾.N?>x/a[6u[(`;oZXm9+Ap.U#G~_!IilXU@$6g4EF5yzי1z[y{uJʏ+_|ԸR@(& +@F dfVSU#6"3qǑ#G~sӝʦƸzo: F];j+ūyj]jr[PN RB } @$"!2 5%Jp_F~#tf#G>||}^Y\ZZ_Wɬjq߱fʥ*f̪:@dнxEAHOF_„17edoo"WZ՝-}R8q㩗6#UUɔ̬Wzju'OnYqSՋΙS=ir%\Gx ~;ȇg5~VWqg\8qǑ#HlkȬyWzBu9&M0b1cF(gZA``ڃ4Hy>+소cYmu>z>[g?#շtC2 XwY;O#q?|IVW'IHR]DfTFbc0XNa\TNe 86+V:47{:|sՎ#G>Y::75Udݐ a*5lGn@!?@H'>&DhpGpn ںR/OnLarr߾}3`A~soRqHѲxS"յVShQnh4FȚ3d, ɡ `/r!ݞc @vUt?מc*<@rpac!Z o#m{-O-J \?p0׷G>i#Wlwjo |<᩷H zi9۠V:|x?$W2qRɜC)3 ;7\IxUV@~*s@~;su0Qcɓ&N%M^2k6b5ϊ޾--'~Kg"sO/~ڠϜqw#G>WǢ024 E I |fRp)SH3g,+{X"R΋""__bDdIR);\XH))P(Rjyyiee #}Ą|#F1d"|5};}"SskG4w^8<W矊&O/5}VF^\EE_{zDHEEd{Y)qQJJ]Ҝ7J )],yڴ66m[n+S /@>\uE:"Ǒxӿ (V~[PZ,痬__deI-)&T^QA4ҩ4Ӯ6F)/I)ښk Jϝskl5QTrB`SIJ7eo G>Nxӿiq#FÐ> gp X^UE|wh ll /~H;\RS`_v_ikR(J ~~}O߇vq$'<ˑ_`I5}z;/,sWTuM]'FN˟3G=u쭭qǑ'x7볳7lj#3~^'!#ꈧ)ɥ4n/+RlFG۸dsU%C 4Y3(q\ӑs9L^]ώȵ%5sVM,pYV-uϾHA[3ZۧGG>[ɔ/]2lXȐ*ڬ~Fӝuerb~yYqVF#G4uýdKOhHJ^__17|^O= #G>|f8ϭK9y*rРhi?}z_~z{<^*LI7D"b)Tb15?J&wnkL5EF즽tkcs׬F5EwxUY>#G>|x|Acb0@ĠAQLoiE~ P1D]rU3J{EǑ\)EmwϪJ?)L{AOs+( =yD,F ,eKܜhXZ![і-9H> 'ͨQJ-8qǑ'?R#.1xpXH]]*]LG%l3™3bbL*jdQ<=!!{J_5gT*>&nZA~KIlY01s?E]q#e0ҤE?P*+'9sx@85-qRm*D61.>x0~_TTrh |7Osj?\BH`?x𽾮.-#w$ Ce6n }\]jVے?|< OxjrO[4(kܹs .+Yx޼ їhnƼHÆ=}NAR/>A-."++M<~H/ZDܿIE7Q($ѣIoeUy˗US_ ZAx؈P;7R NsFV5 y ?}[UYI7556_G>|< Ox7#|\| Ox {mmn!`I#߮6u)d=]t7귊&M}|~/$D˧Ve3mQJPˑCtY=gΦ|(Aء5Mp"GEgmiCy|,́/5%p!??xie^MNL79RIX$>6x/(KM+ V65$yQSSӧ^ uoSS/XN꺑Ο <8 O]~ 6?Kc]}U~就1ykZѾ[YYu4dh1/);.΢Al%gNZ8d0q^=b>%9\4q wԨ|S@we~ !O_VVOoPJb~000(濍|njYWWA5hNl>ĬǑ2qmbB"yjke}LC^R5xIGv @?[ң%\sǎ-ۯ65͢WUVKF,>q$5J!IϟP<݋S^Qȴښy&fO~H<ɰ?mBO&IiIw|F,bc}|"HUWs8љ l[Y[[Z%w}CC\brtrj1.-='8,-mܶAill,/))HI)LK+P >PӡAyh.-瓾}#fg%%Dr%Gž9,_ B9A7;D"t.=ȑC.;;6IH釲try2Uz*v_Lٍ /A/ef~G,fڑUUͥԆoٕ-CH T៚I'.YTjѰa%)EТCskVփ9Q\n+WvM l)zϟS^^܃w9sSOb~CCd0|9='v'&&t,[-\`T*[7-B[VVK< Rgddݩ󩶪 E>!\\?~C3VV^D^jK>ܹ󵠠LS(v8𱤱z56 P(]V 53C;--ǁkCʶnB ؾ+(Z]SRPZ }r cS KJdϘdQWA 1(z̰a b1߾%YtLoRjZB\\]܇qNɩ+%`?M?zGȑ>'G "w; @`A):%V諪""325u4?9-703@^QIOOmW_|sXwo璝<9&&B8ۭҘc?y3@qqq̠O !uZZZ|/ T'=j[QUTTZc+Ͼ}w6\SY!W^ RTTH C < FÁ +moaaa5xx-[-&ՇZ><{CllcxZ,,{f}cEݢuk6( kvᦦIi쨗V}31"" (@=9=v1s uvr<%@@:D%5jY--1qDJEM=%^~a6vW|)72Que͚7o#Y}]x‡Ϙ| ! {k2zNV~QTTsq:, ( cV]yyևZE~arJ=իG>| <۷;wސ2_UV&9thߨ*/Ǧ 1]Wu2c uYݿpO<'/C~ >!V>_ +AÆYw`5e*?/h @oju\?#`ذӠ^CCC0t;>MJ% znLIMK v1ȷBbE0OY H"""sյ5u~ܼC ?򽽽 ,[f؋?o޹ }!}xo]<Džcݻݻ*-j+W^5Kx ݹs= ה%x,ȇ g;, {[YI F#GLLLDQKps+yР3wXXX W!{۶/^[KC3Ub?hѱ8{ѣop᯿*>|==0Z?D~["_l\ccN.^͂|ի!755 %oTS|O>s]Nr2q&:acN664WUUedllhl|^ _SS[XLrr3pÛj7AKU Vzmq/[v1AAAyyy:=$Լu떜,ߐGHfAˌ|-- ò0DK l^^&//݉JY帉'?|ΥKEDD޽ի׌d`` GQHH?vuNGGǢz8v:Q[ !!?? }O]ݖSgWxQtt_fzO ȏj%**ƆƚjWЎmll>B=9~1;?AO%%Qʧ%FxDx''88~T} B*+H%yiQ~!fVޅ~3bf '@>zj ʦe߫I`i^tyTCI$*>T("+uWQBHQ\u[JB=e%İǵv-zKxx8o@vĀ I$xaPRRyzzzxxDFFkۿ#;, sP9J+WbV;00B`psyI UAx$NNN%%%gg_aO8ϭ^}M-`Kd0Rzz:Xj y^޽2D`{=˘٨e)`%-\~ԩ 8/ d;fB\b1)6&I4a~SM?xp ?;{4]R3"ܾą&ĄP:2 b1urrC]Eq~JJo|gBObOJ|@jB@TKtkL[TKh}]]WOSk#x#oz-U{Š'TDOåz W߷%f؝'ٹZiW/tB.ʠ P3{ɓ'UTT^A^# ~^Xhd-X}@N4 tGc~h'Ojqq?'MsYnDDDYY%M; %P;LfwG'>3̙?kF}e~~___z;&&Nl(Ύ~ʕ=A/JȧDEYYE> ɓM#[V;Zs˩EX;x1P{fmg_>v[[t }ʦCwX {c#LXaoo1X%-\ͭy7K~KO جYnI;t4QQf% F>3:utƌwށmЯ*-U5nh''%?U]B~\YnlD |p j_iaÜ 7oγQ,]i[/\oؘ~q6K4}a&ɻm<55M55M 0~J)1ޱIɱ ɡI!ȕWDcu;ལ'ϯ>Q{qWs ORoJ]R8_wX8sﲹ;ڶhb{T/ ڻӱ1fa`{{{n)Z)I~hb`lnm eWt>'A~'_RF…o? >ކ h%LpI?ݨaCL K*HO~ ]eebb9yE1G@aT;VEGX[G9:f# h=Y669Cz,ˎvpM +χ# ##--y))6+R,T'H'>?bȿpϟAnzRNNZPPeSJ``Ey9`3;,3y,zݎ UWKO#%%%9rd'~+j?F=u] @SwD< ic3$hGWD9ے߼H p v4~-);ׅGBj˛jO HIL v q ">ᣳo}vKҫ.zSUEҼpt.32޾MJˌpwb`ݗVUU4ԖUkkK**H J@r\GX=q~nl:~ѷKw Th膤.ݓV}@-\לٹ u3^>oג)]9GEYQEp#Ɍ)R6(޶1"S]GGQ100%hh'egcWSZy豱n5(EilWom,"ò:u܏Ukio?ܹs𢠠8ˈt"C1b~mt/@j?tRSwn9["-CfM'|Ooի~Dz? $dMQQ Z5A;ԩNh!̞=yv^AEd)pp8B! LE/ax$K:__GoO!!! V[ajQVUN|f-\xM''ػN. py䈂4Br@ߥQRR_G=ɕ_q E Ab;. Cߓa3^I~K$Gx:9}zsJGJrJ-,+/!GeFf$FxF:'GzƆy#gDcxCdS{篯mLujQZjRwDhȿ+B]F٫|GNH sƼ>KKvBuH777lQsOz,YYY-ujUnn- ywXVnMMb|,bidu?9᝗u՘LGyVaҥLôO6of%%yzyq.Ιsa"ٳEg~rqh)Mx1˛6}2Ejƶl\|.hNj0*SE΅ 3+={@k)m;Z.#$g?_)L&////EG u0%K7lܵKd˖VMN-[z|X"˗ 3G2?)$I6ݰč@ٳͻdVDwQvl)))UΫA$]Hx +.dTVU(YګՁ*F,ȿuhcB]ݾ)#RX_AK .23S"NAN1a^P?>+5!0:ԕQce%φZ 5?Pq 9t_JOn_}$FC>Kgٽ6,߾`ƶXs琢޵K*@@>̔yX[Xo,1;ttt{|=yUDDDTTTC} II R^8s&ݼ>p@__%wXV9RekD @dL?_1111YYYeeHxpC$##MM!C.lx9Ȥ]ؼY .Mqq"fMǣ [Chk3#_hܸ8y89Q°Ӄ>L&w/Zk׮I>|۷D]=::̝Gdp+yx W\b))))** {]]]C]_i6M[d=9B'Ǝ9~p;i02/ ׅ3ϟ?fE͍qԔ)'OB'saax롴q%$s9l8W^AI PjPWt 2VQ(-snnno탂pۺ?v-ǐ%( y;<+:bXZYam*YcYGlȐǎj\ȇ ;7u7~'"”)vP4T?`hu*PЬhgCUnkkzVv5YٖK|@>fA>Z_uZN 6MK I? ÝA~˥, V$-- r<x <ݻwU 03| @>r>:4ߖ?_=MI Lu}PUA,++,K.IˊI NJ OKmOMG9&Dz%%ƆDŽ؇3r70wlmǶ:p}yiɫ{\׾y宜8u38/wdݼ{\> r:_SI((x+):vlKV /.Ֆg˖O2 ":'{u"1QQSSSy-6,G7tx3ǖ/h2֭ hᲳeH_@ (ZE`8%%%Ҍ dPK  (@&liuZTaL*_"  ._gF6oe6I׃bT]*/^| H 00[;vִipKRsz*@/4 %z+--wlj_ꢥEggϞ# X߅ګD+cZ>thS-3IP4{@Cĉ0PM^71BK̟HhuILV^3g~͌|~[;g$&NB7/89|Vh9=`ZYYMB3AB=C'Թ'2DtX:3[E>ڠ_c;z =읨(]CzAneǍp- ߿|=<:[2237\7$-z-"?AW;5)$2sDSi~N"GFd&Gdg'}cSb"=}#bhP?*56#%/);65㫏GǯmMu?YD83%߈řY`֧'DŽ{R⣼chS#<ʏ vNMm #)8-! /^-_Z:}wbbTwoouC n 'U;mi{W/[3kҡu $RSI,/@GNg@.&vqa؀eKtD蛭]Bk輟5KB@d`-gс|[ΖXֶgbv2& kӦѣnbBiartDA@C|PVUee???ugVXEIIfx@zw"5JDPĢ ^Ʀ"+gXh0wxOMx1aAe>ʺ({sY__VHᰴGoݺj\ɹkAiF;/ڱQWyx SAƎsA} Cw:|L\\\LxwR.7ry(GCh \S"%e S@ rCLg ?͋(/{cs'q^qa\bQzLGBgzr0XѡiA2Rr"r#q!.Q^V1y"$F=۽pc`ahon#SȾиfAG͇Jׅ.SzA93m\1fyS/r?o-$o@umbb*0ΜW Bgz|3gapeq+WXCWgAÈ{ "сt/XOu0ݖ4qb `ggsܾcAt6333lF6W4%ش}s4glds&n)~j1&N֬QPPbã >$S;=i;e=ئmfw_Oo]']6:v>y3c| a~4]?[@DDظ{V?3wy._|cݻW@@  *gUb4k,F=A>:O?2tG'##ü*=cAu:_.f}e> ?m9j;B~}C>c^ηb;U}bu67~q]Jl(00=)/9')r_WzJxNFTFRmQ?+9'"!"65`)<"/Kww?T־yͫ)iֻE 7.}_ ˄ϝ~޽zzzP+s DH͛ zx1/3!UG=,4M^jX J *ҶeS#pbAZ.ԸX?{|GTg#C|]?8XxY9|1|[Oկqo}w/n>Q{Қ o]=:e#7gC[y75V@3;hf`M~P֬q|ėFx1(v1Xujjj y3>2ZmYTUUQT]Q~H , lL3b8^<<4@{wÆ߼Ývt4.^,**:aKU_6C3^y˗tY?i}[1P:#d 4 Yǂ|*ΝK/ƍ`[c3>atnC+-,,W !Cv >"dCi$];|mտW>r֏C?s<˱G~mU^ R#7C ǖ󿴱U%&OK/:B,?dpsxSD &~JB@fJpj|`fJDVjTQ^Jp Pp G/'wv"ifg7& Uuʿ+RC=EG7>T7zΕ{"(iݸ|󊀊8-ٿqs鼣6xy@ķHb @.wpqȐf*CڳQӜAAA X$24SNݸqǏ . IɈv"&f 1X43~<ݧl^}}y{EG 222ѹ}udfW4͛i[=ϗ@VTT#iN| %%%OOOn;#o߾eNɈ,B>ڠYYט[HPP"z'ru- gKO|]qϴ1ς|7&=fҮ#AH{hoaEDctkIAzIaFlgFJpvzS^ZTUA s⒣}B=""C]B}mLvmhlg]{/)}uM[*<"\|.J-+߱lʩn5{]=\hcvKw~'*%.\`Z|`[=m'#˃di'D>QkN: :m$ o߾a)Nlbl ~d;> UguoV~ጰTZ\ٖ{7>߼I՟'kX6 ΤP:G.} :'>.&N{)Ua^=O#rϝO&+ DW×cMMF|2wʇ<2&֮sNhh(K__MnnnhDa45RUUU[=Ib?A{P9}q;VAc+ -ءC/\DFFFAAAI Y[G]"44nɿa544X6t,=xPD@@\\ի/W V1 k;ynىc0+/_fjBts$:e>{:AH`4|VzRXnV$ZWSZQVL,ɊMK vpDŽ^N탽lކz~q0ๆ;r/޾2=Ow!^&??}CRUε4D D]- :~ʼn+EpZz}=`o|lV9cgܜ=K{/}||--KǏL%%@1b~m:@os֏)Exxx?~ _Te4mۏ+25inLg= KlmcƀU:û{{.~6el?:Ͻ{zC6@ccƠbEV#M<P޵޿oeeb*{D&j= '1o6}ؾ>|9zr%!qt -=z|jֶmѣ_=~ ol+BvBǥKyyyA͂>fdd5 k3f3n+sN"\ ^Q"ѤhTo|tl6s-^0kӠA.ll}66Q##Μ!7VU6ol*0=&פh 1\E!~)/'edĤG{xZ[{;di_ Lt^kܑ/Qy YA 9G*W>t5zsOn]~(rYI[[2.ݕ(sl;ݿEsA&!&7n񇐐4"###͞<py"0o$9ѹxmҤ'O*))8DA}ss F8UIKCY]^'Ob(MÆʲ_g _lkkbe`'NX[[k>Lۋ1g" 0[s ֿr^߸q@]9nL  | >6ҷo P:u=ieE #ЪUz$D#?#8?b/r#˝%[FhQ`h,fׯb++%>\Yή < gE`-hh ]7C>]Z!..~B6Zlg)$L#x8 D!;,/C~y^}7cgw>?Bg;tN:w><2;OSS\H()TD .*̈uvv4r7IHK ggQ791a!>q43u2wi,1}{ /4o^>]KIi{nYxS'ϛ9uW+'*{CR<(zSu*kl[ӊcC>l֥88`D&;?6C(<=zJc4cVa\q8+.czT322?'2 !nt0xO Ӿ;`ڭA| z üXqaɒW/^]پڵknl],$aZOQ/ i΢W\tt _=zU RZvUj=;mg##v͙#""BA eKWNCO2ԩ;9EgbC6j/%$s[H($$e'A&gl#F?  yO@mª},G&(c* up4^L@`Zg3]M?ɱS8b9) ?#w`-N ڿq=ݖǩQwkѢ/X[STضcfE鏘Cr˼E>B;s ֊EPZ+;<<<2=;g&4x-fz;[p jdѱʉggg{[ɈC7]3F~Tٳwa.[FkUΞ&ցȲ@YH,ˤ}gR+/y ˖ V"GݱC^^]oY.ppgxCHH/sHi&32JMMeQ Iz `r2ۿgNt[eV~:ѹŽoOkӒ˓+SڏP^05.oR= <؇goMH%9>֡61!n_^%E{Qr%`7@F<4723xɀf>V5qjhfF^=۱nŹûOnY.z|(S;W_<_KE!a/rnmEDG/z>Ģ4297/3.c?/~։ўq!6fzFOo=nzjejH.JO hTW-9Ws^kjk]Q!{몀er'/s#̩!+x4=yRRg/s VrpqgZ^w? x,'R ?u;~lFT*(9}Yvv6ڈ7·-,ZuT,vd-ruX>EXWkVmZjK!0A{{/qZC?{<<{s{.99|`%$fMXXc.i'Z׮uI)*K"I3uKg|(pJE"q#ICjnemhf'GR'\$ 8ZiiC! <9#!^y9C ".=ىgChZYHsgnn4_Žn-c_i[ML!?ږӀ&ƍ-س96,A͡:\oMvfA0gFNm*Nf֛>[ZZ̑ֆ9@xr< $܊'wT@fN2ՙ6_g&|Ӱah{F$}M[oO?coέ_~)((zxx8AQeB":6l;'CNCܛG ;׮s/TDE.0Ç'O>8G9N͞:4|ѭ[.Uz*횰SIš^{s?VÅP>r|"BBu(Di#y}GMԄcg9Rz+/Y"yy!NJJzF)77ҥP;՝^~n6q=W:a:\T<3?GGeUΊ;w+))-CSO& >?,`d2ynX,֭3>+brwi; A|~6͡~t8s&'+V(}"??T :D1iO#ܽ J *s=7tצMbam@H); '2ھ$Ӑr%{b'C>`* <O3 n H{{/Wf͛Ϟ1/R<wٶ i k'wﶳk:Ýkȿv |whKOLO{OVe`7wo-̆(2x&@koWmk=ACM#ÇB=!X:66*ޮy||P(&&&P[ ۂTAhh(Tӎb`fMyZsٳ<<BBAp蟦9aiiihʞ>-رc.A***Й9eeeءsz nѣSaN?ocYXX J;8r.#$$''/iR?3oa?FvQ2\N,wO?HB 22jjj@P }ڻj-hGfD3rǎIJ c>.*Lڝ|>N`P `Cν> MҲĽsda$[/J<xp9GP6*jjj:hĸ8..̔/EA`@'̴E3ۥtǎ=Vbbb0V0n" Ah;| 9 ȿq=y ^$_[sM&To ۴6,wN*^oGr̲Kr'μia tץYT 56twX.h#n+Lԥ\zvfjdvqJ[st`!vV猪U yT%( I4cVerk| P/b d,b(>9Ub~;sh7$%^c獎+//Ht87 Z`0uplc`.{6G3l)kaa!m="3 * >>T u!Ph:|!X@?-wm07%% 84`a@P=p_ c' K0 {E***f>Zin@hBdeerqq@L'D3]u{}p @zpgX$ m:C8ĔaKfU_ %Ϻtj; +-B?-:ރf.El ”*0ރUVVfug]&"m Nw'p0/~D)OOO =6MxjC  X90 p+!(~z?pda2uײ82Mk kהCl ֥.9'v翱(?oǟ8A'׸$_gՆ55__ܻېă{<{gғJkK:j F(}s=#-a'TmMݭ#rSBS܂]T#

c<'w͡u]([S%+F"脉==P,vF`)xpgv&y! u ܈9cu:.PtL ݯYC^`(h,4a&C@ij_A?Jcw(j ?Ez=':'hߑ#D"q TG#Pgԏ40ᾀjt\WLY(m9ٜL(9?.Ls]ӈ0X.'BkbE}9[zw޶_.@BZ[#yzs~;Cݿ}u _Z56Uw{񡎊t =Ieqz}E^wky[c)09LJTWi/kNt0USrJ%cWj ڙZEzg'%G椄g$f$dDW*K2bb\)z$-I#E!ཹY?Bd~ |ncD@ JbSآ:?3Ǧ-wUyVI8`w0:~bnwZg.q7Yt´ cd*Γc]T Xikj0>ɄT S-0qrֈɬZ%IΓwd>_w?L7J$0C r>?af>??#7R1py~#.w[nXofbttޘX_]oo ]mi>yݻ]dlԔxh9#9>.;%@s ݝ0OwꢞV@}Gs=+b}s +bnRP-+"2|ZP_Snr3I"u]>5SҕhO7ѧ୍f:JWD/Ki K5PtuxQԉg/s},)tuazOc- x&OZɳH/b^_[,=J;x|\\D N|3J:$Ww+&ɒᰗo{:Yn }R[ܽ[t)xG9Z=XS9R_UDgy{G8timt[W%INYɁYqa.U „V+ >ȓbEJ8QtQլƚ⎖򦺢ڻ7楆zӈE35 =)>u3 <CBS#3c}m\-􌕯H]ґ<-+y#k+GRʚJ ߻w:?7ݞ>~||$47Ν#H%%%Od!Gːp8CNO؃{wk=6szc穿 |g:2td_QaY8>{c||QokMK Kh*m6uucjgK=cajwogmI^|ŕCҒ?ǃ|/rP)*9x땓cdK3r&i /WTl -,et$x\ XdnWuĸOHB%#/+)i޲ݻ;w SMM\֭Vra??W:6â'%dzWdxvgdp[{CIIn\Uqjz{ׯuwT5 [%;L$Γx#g*#{1<>))%6^0OKGW Az#eaei?my}eAhH #yMSYb.ޭIv_mqXzգ~KT|w&D/ڿ?HJjׅo~M9qE}/>o1_(IψK p87&1 \w;{:&JfpoPocoG@w}G#"44/('|hWCU~ZD&x]j',4*"*I)qq6֡T3YǑicHR5 :i8ij=$מU3^dsSA|$仹8%g5?!$ֲe|=7LygpCoY2ԇD&NϞ8 7 $?]ZqZ`,klm8/>1;%Ʒ8?ZV42T[XSTKuvH^ ZjSUEjI]tq#C]^De,pZO`D2P&%HJw;Nۚ3=<7O;; K0ԃ9:/88ɓ"Wg럑^D>rpp8s8ȧM" ]FGq>y)!Ɂ̄fVG"^LzrAVdaf$X.ʌp5Vܼ]_U'dAA zW.Q>/q% sj@}E@kHMWHVr.z*|U| !ɓ _`$ς.-t:999!.? ]]׮U9sC?| gQgn-/LʊMˊIK du6vvjgT3JkYY좤ʒ=!&ZWX_^K3%*++]Ă_jgEizyQrqvtjWbS-.͊f3Rj%{Q &f&xa IC$5 Suq=y}i-1^*Q)ڇNنLMEDڲvVj{G5Kߵ1'!#Y@L|7x $  w`` h> !ȯMJz {?q'"<8`|wn_1>p}wɢ'UkW{z;jz=meM5tvI(4?.΍gc\iml/)/L tr2*Coofg'Y+kKդb=h_Z|cu56Æ`gNoZKP d DܲawmXm̟GOqciD *=eM2 oW9rɳ'RccW{:۪ZJKs"JcÝ߷1 KYYlF*$ #;h|pGogM[#3M3{Zĝkgkkdow0Qqrq ieknljNr53Sv*d] >v^!Z^TQ:1Kώ*ʎM .͉$N$͸g|dXVW[ȋi.l7C0~{[┓ln.''&z^#_mr%e  iQѾ4@:HT՚h-pKݱ6:֮ͦtX #3:YA_IkGyp8s30[ՊI6sUBD^H-'0IJxFn4#3M }p_Po}KCY^Z837ZKgӓ݃<#=-2"z BfZz<;y>}[7޴RՑ#yAKI<+Y'ĞN3Vԓ5 b;׬zŻ{68zm^u{ZWa?Gir#|`|'3|c>UϚ/|W(BZځQtzU.Ni̫agJscSBBI$-luy;c]uW[JsØqVabLx)~s/GJ#ٍjlGҷ2$HJfZr2jdDBDz|WNHWއY:[hۛd ݶ|鷟~X{W,EGf!!Y>B92Lרg>!=fkr]sm=-сYY==ׯr>Iow3 2ɢ'fG{=DG35*AњHPv ]ihi*/I-ʉ*+Jq0q3#jګz{*˲*KY ANndG3;SU KmYY~=>=FBDyIӛfldaKJfRlzْްЗ~i%vO9xFS1IOEs򜽷w3 /cWzl[pcŚ۟;'p'vń%Kb~ɁVWD ann̲ᑑ[n=?^`'4Xܔ(?[G+YdžmMҷ3Q5RN16pmst876'5(=;6О{SYDRڊ*ff3(;2ŸOM q 0W.O&J3Q&Z:[hJRtdtwYk#_m]/fo7(GGGGG94Tlenn痓Q]Q5>6v޽~mO@o[#JAzx;;6Ёj`-j;q4$?w89!ގBVabb[F/TWt IDATXZ1®feIJI1@~m/ՕK3ۙzQ6Q>4w+}!^X[}Kmix\>kOZe>_yǭ'w|^ 1k׮ܹ}{؆?_˝̴(:2Ndmfa"fWܭ ܬ|l]9r]7oBR(/L rI `Uu42yܺ F*8 q>4G3'R;9`J3EPi=*QPQ^:2RZںg~k[۾wt5!!@B>B>Bk%)DyxTBYABsMQ//BRҌdu3:<Pg6лYE5Cwa^Yɑ a.3>1!9.!ҏfnE37Qғ#E-dTϞݽmڏvm\yrǖ?l27!Ɨ/sѿ ==z>MZD:˿T3IѴ #e*r($?("~qmf^HWW-8Ȏfe6UW2*2+e%y1Ź1LzBf_R[Uul}|iC!Y:4 rgL&ʢٶKm\㚏~޼_վU+ܨV _y/ ˍD#OizosU~MI'acNyXteՄNJ{XuuWU33Y Cuݵ Ui=MU%yE9̢䜔P`qY/.E#zX)R /*?|W ߻~8c>E'Q!(;(lM#ą8Օg%pȕcgeo`֕=w@O_䡜ҌfVS5Pk_o}Wk:[MIaܔ8(?Z/^1^nf.d;#.I]RE脮%iYY>fӁ-klYͻ۳l5B>Gȟ?;B1=;'1 ?%(;/>``D lL"yUSWN_Q;~nW|ںu<6=oYf?C"-ze|AO+2Sf=%~'fw#멉pP鲞e;3kTۛ#cWz;[J&%xE;%$9$8yQ>޶F=g3M@̹ (U|FC_fٻG_ٶ+Oj/*B>B>B+! BB~Jm+BY̬ff%gEB< aN^?0/ fnC5RZiI >M-9S}EZ־U|Z>kl.z)EG_@ኒ恞R:XWQ]^*)'+*`jH+-Ϋbw7w^_ݽ=~Ձց(it4u67MXSQ.bWT1je,Fi^X/^VD/Ӌ,FQUYAhfא #(! Bp ] 211ILLlkksSC:/ !RVVvƍz9J৵3ed۬};kᯀB9/!A#ÇUE %%%w!!! |$H-/H #y?|||$H˼x] AGLX>OÇ翀upȟ!h\#A!!G B>||||$H򑼴GGG!ay) ؂F)|x!A!! B>B>B>B>|$H^?G7TIENDB`sikulix-1.1.1/API/src/main/resources/JavaScript/000077500000000000000000000000001315726130400214105ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/resources/JavaScript/commands.js000066400000000000000000000071211315726130400235500ustar00rootroot00000000000000RUNTIME = RunTime.get(); USEJSON = false; jsonOn = function() { USEJSON = true; Debug.log(3, "JSON: Now returning JSONized objects"); }; jsonOff = function() { USEJSON = false; Debug.log(3, "JSON: Now returning normal objects"); }; isNull = function(aObj) { if (!Commands.isJSON(aObj)) { return aObj == null; } return Commands.fromJSON(aObj) == null; }; json = function(aObj) { if (!Commands.isJSON(aObj)) { return aObj; } return Commands.fromJSON(aObj); }; getArgsForJ = function(args) { var jargs; if (Commands.isNashorn()) { jargs = Java.to(args); } else { jargs = java.lang.reflect.Array.newInstance(java.lang.Object, args.length); for(n = 0; n < args.length; n++) { jargs[n] = args[n]; } } return jargs; }; makeRetVal = function(aObj) { Commands.restoreUsed(); return makeRetValDo(aObj); }; makeRetValDo = function(aObj) { if (!USEJSON) { return aObj; } else { try { return aObj.toJSON(); } catch (ex) { } try { return "[\"" + aObj.getClass().getName() + ", \"" + aObj.toString + "\"]"; } catch (ex) { return "[\"NULL\"]"; } } }; run = function() { if (arguments.length < 1) { return; } return makeRetValDo(Commands.call("run", getArgsForJ(arguments))); } use = function() { return makeRetValDo(Commands.call("use", getArgsForJ(arguments))); }; use1 = function() { return makeRetValDo(Commands.call("use1", getArgsForJ(arguments))); }; wait = function() { return makeRetVal(Commands.call("wait", getArgsForJ(arguments))); }; waitVanish = function() { return makeRetVal(Commands.call("waitVanish", getArgsForJ(arguments))); }; exists = function() { return makeRetVal(Commands.call("exists", getArgsForJ(arguments))); }; click = function() { return makeRetVal(Commands.call("click", getArgsForJ(arguments))); }; doubleClick = function() { return makeRetVal(Commands.call("doubleClick", getArgsForJ(arguments))); }; rightClick = function() { return makeRetVal(Commands.call("rightClick", getArgsForJ(arguments))); }; hover = function() { return makeRetVal(Commands.call("hover", getArgsForJ(arguments))); }; type = function() { return makeRetVal(Commands.call("type", getArgsForJ(arguments))); }; write = function() { return makeRetVal(Commands.call("write", getArgsForJ(arguments))); }; paste = function() { return makeRetVal(Commands.call("paste", getArgsForJ(arguments))); }; closeApp = function() { if (RunTime.get().runningMac) { write("#M.q"); } else if (RunTime.get().runningWindows) { write("#A.#F4."); } else { write("#C.q"); }; }; closeBrowserWindow = function() { if (RunTime.get().runningMac) { write("#M.w"); } else { write("#C.w"); }; }; circle = function() { return makeRetVal(Commands.call("circle", getArgsForJ(arguments))); }; rectangle = function() { return makeRetVal(Commands.call("rectangle", getArgsForJ(arguments))); }; text = function() { return makeRetVal(Commands.call("text", getArgsForJ(arguments))); }; tooltip = function() { return makeRetVal(Commands.call("tooltip", getArgsForJ(arguments))); }; flag = function() { return makeRetVal(Commands.call("flag", getArgsForJ(arguments))); }; callout = function() { return makeRetVal(Commands.call("callout", getArgsForJ(arguments))); }; image = function() { return makeRetVal(Commands.call("image", getArgsForJ(arguments))); }; arrow = function() { return makeRetVal(Commands.call("arrow", getArgsForJ(arguments))); }; bracket = function() { return makeRetVal(Commands.call("bracket", getArgsForJ(arguments))); }; button = function() { return makeRetVal(Commands.call("button", getArgsForJ(arguments))); }; sikulix-1.1.1/API/src/main/resources/Lib/000077500000000000000000000000001315726130400200505ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/resources/Lib/HTMLTestRunner.py000066400000000000000000000632551315726130400232330ustar00rootroot00000000000000""" A TestRunner for use with the Python unit testing framework. It generates a HTML report to show the result at a glance. The simplest way to use this is to invoke its main method. E.g. import unittest import HTMLTestRunner ... define your tests ... if __name__ == '__main__': HTMLTestRunner.main() For more customization options, instantiate a HTMLTestRunner object. HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. # output to a file fp = file('my_report.html', 'wb') # setup a TestRunner runner = HTMLTestRunner.HTMLTestRunner( stream=fp, title='My unit test', description='This demonstrates the report output by HTMLTestRunner.' ) # Use an external stylesheet. # See the Template_mixin class for more customizable options runner.STYLESHEET_TMPL = '' # run the test runner.run(my_test_suite) # close file in interactive environments fp.close() ------------------------------------------------------------------------ Copyright (c) 2004-2007, Wai Yip Tung All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Wai Yip Tung nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ # URL: http://tungwaiyip.info/software/HTMLTestRunner.html __author__ = "Wai Yip Tung" __version__ = "0.8.2" """ Change History Version 0.8.2 * Show output inline instead of popup window (Viorel Lupu). Version in 0.8.1 * Validated XHTML (Wolfgang Borgert). * Added description of test classes and test cases. Version in 0.8.0 * Define Template_mixin class for customization. * Workaround a IE 6 bug that it does not treat %(heading)s %(report)s %(ending)s """ # variables: (title, generator, stylesheet, heading, report, ending) # ------------------------------------------------------------------------ # Stylesheet # # alternatively use a for external style sheet, e.g. # STYLESHEET_TMPL = """ """ # ------------------------------------------------------------------------ # Heading # HEADING_TMPL = """

%(title)s

%(parameters)s

%(description)s

""" # variables: (title, parameters, description) HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

""" # variables: (name, value) # ------------------------------------------------------------------------ # Report # REPORT_TMPL = """

Show Summary Failed All

%(test_list)s
Test Group/Test case Count Pass Fail Error View
Total %(count)s %(Pass)s %(fail)s %(error)s  
""" # variables: (test_list, count, Pass, fail, error) REPORT_CLASS_TMPL = r""" %(desc)s %(count)s %(Pass)s %(fail)s %(error)s Detail """ # variables: (style, desc, count, Pass, fail, error, cid) REPORT_TEST_WITH_OUTPUT_TMPL = r"""
%(desc)s
%(status)s """ # variables: (tid, Class, style, desc, status) REPORT_TEST_WITH_OUTPUT_TMPL_SHOT = r"""
%(desc)s
%(status)s     ScreenShot """ # variables: (tid, Class, style, desc, status, screenshot) REPORT_TEST_NO_OUTPUT_TMPL = r"""
%(desc)s
%(status)s """ # variables: (tid, Class, style, desc, status) REPORT_TEST_OUTPUT_TMPL = r""" %(id)s: %(output)s """ # variables: (id, output) # ------------------------------------------------------------------------ # ENDING # ENDING_TMPL = """
 
""" # -------------------- The end of the Template class ------------------- TestResult = unittest.TestResult class _TestResult(TestResult): # note: _TestResult is a pure representation of results. # It lacks the output and reporting ability compares to unittest._TextTestResult. # modified by RaiMan def __init__(self, verbosity=1, dirTestScreenshots = None): TestResult.__init__(self) self.stdout0 = None self.stderr0 = None self.success_count = 0 self.failure_count = 0 self.error_count = 0 self.verbosity = verbosity self.dirTestScreenshots = dirTestScreenshots # added by RaiMan # result is a list of result in 4 tuple # ( # result code (0: success; 1: fail; 2: error), # TestCase object, # Test output (byte string), # stack trace, # ) self.result = [] def startTest(self, test): TestResult.startTest(self, test) # just one buffer for both stdout and stderr self.outputBuffer = StringIO.StringIO() stdout_redirector.fp = self.outputBuffer stderr_redirector.fp = self.outputBuffer self.stdout0 = sys.stdout self.stderr0 = sys.stderr sys.stdout = stdout_redirector sys.stderr = stderr_redirector def complete_output(self): """ Disconnect output redirection and return buffer. Safe to call multiple times. """ if self.stdout0: sys.stdout = self.stdout0 sys.stderr = self.stderr0 self.stdout0 = None self.stderr0 = None return self.outputBuffer.getvalue() def stopTest(self, test): # Usually one of addSuccess, addError or addFailure would have been called. # But there are some path in unittest that would bypass this. # We must disconnect stdout in stopTest(), which is guaranteed to be called. self.complete_output() def addSuccess(self, test): self.success_count += 1 TestResult.addSuccess(self, test) output = self.complete_output() self.result.append((0, test, output, '', '')) if self.verbosity > 1: sys.stderr.write('ok ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('.') def addError(self, test, err): self.error_count += 1 TestResult.addError(self, test, err) _, _exc_str = self.errors[-1] output = self.complete_output() self.result.append((2, test, output, _exc_str, '')) if self.verbosity > 1: sys.stderr.write('E ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('E') def addFailure(self, test, err): self.failure_count += 1 TestResult.addFailure(self, test, err) _, _exc_str = self.failures[-1] output = self.complete_output() self.result.append((1, test, output, _exc_str, self.generateTestScreenshot(test))) # modified by RaiMan if self.verbosity > 1: sys.stderr.write('F ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('F') # added by RaiMan def generateTestScreenshot(self, test): imgdir = self.dirTestScreenshots imgpath = '' if imgdir: x = str(test).split() tf = x[0] tc = x[1].split('.')[1][:-1] imgpath = os.path.join(imgdir, "%s-%s.png"%(tc, tf)) shutil.move(capture(SCREEN), imgpath) return imgpath class HTMLTestRunner(Template_mixin): """ """ # modified by RaiMan def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None, dirTestScreenshots = None): self.stream = stream self.verbosity = verbosity if title is None: self.title = self.DEFAULT_TITLE else: self.title = title if description is None: self.description = self.DEFAULT_DESCRIPTION else: self.description = description self.startTime = datetime.datetime.now() self.dirTestScreenshots = dirTestScreenshots # added by RaiMan def run(self, test): "Run the given test case or test suite." result = _TestResult(self.verbosity, self.dirTestScreenshots) # modified by RaiMan test(result) self.stopTime = datetime.datetime.now() #print >>sys.stderr, result.result self.generateReport(test, result) print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) return result def sortResult(self, result_list): # unittest does not seems to run in any particular order. # Here at least we want to group them together by class. rmap = {} classes = [] for n,t,o,e,p in result_list: cls = t.__class__ if not rmap.has_key(cls): rmap[cls] = [] classes.append(cls) rmap[cls].append((n,t,o,e,p)) r = [(cls, rmap[cls]) for cls in classes] return r def getReportAttributes(self, result): """ Return report attributes as a list of (name, value). Override this to add custom attributes. """ startTime = str(self.startTime)[:19] duration = str(self.stopTime - self.startTime) status = [] if result.success_count: status.append('Pass %s' % result.success_count) if result.failure_count: status.append('Failure %s' % result.failure_count) if result.error_count: status.append('Error %s' % result.error_count ) if status: status = ' '.join(status) else: status = 'none' return [ ('Start Time', startTime), ('Duration', duration), ('Status', status), ] def generateReport(self, test, result): report_attrs = self.getReportAttributes(result) generator = 'HTMLTestRunner %s' % __version__ stylesheet = self._generate_stylesheet() heading = self._generate_heading(report_attrs) report = self._generate_report(result) ending = self._generate_ending() output = self.HTML_TMPL % dict( title = saxutils.escape(self.title), generator = generator, stylesheet = stylesheet, heading = heading, report = report, ending = ending, ) self.stream.write(output.encode('utf8')) def _generate_stylesheet(self): return self.STYLESHEET_TMPL def _generate_heading(self, report_attrs): a_lines = [] for name, value in report_attrs: line = self.HEADING_ATTRIBUTE_TMPL % dict( name = saxutils.escape(name), value = saxutils.escape(value), ) a_lines.append(line) heading = self.HEADING_TMPL % dict( title = saxutils.escape(self.title), parameters = ''.join(a_lines), description = saxutils.escape(self.description), ) return heading def _generate_report(self, result): rows = [] sortedResult = self.sortResult(result.result) for cid, (cls, cls_results) in enumerate(sortedResult): # subtotal for a class np = nf = ne = 0 for n,t,o,e,p in cls_results: if n == 0: np += 1 elif n == 1: nf += 1 else: ne += 1 # format class description if cls.__module__ == "__main__": name = cls.__name__ else: name = "%s.%s" % (cls.__module__, cls.__name__) doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" desc = doc and '%s: %s' % (name, doc) or name row = self.REPORT_CLASS_TMPL % dict( style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', desc = desc, count = np+nf+ne, Pass = np, fail = nf, error = ne, cid = 'c%s' % (cid+1), ) rows.append(row) for tid, (n,t,o,e,p) in enumerate(cls_results): self._generate_report_test(rows, cid, tid, n, t, o, e, p) report = self.REPORT_TMPL % dict( test_list = ''.join(rows), count = str(result.success_count+result.failure_count+result.error_count), Pass = str(result.success_count), fail = str(result.failure_count), error = str(result.error_count), ) return report def _generate_report_test(self, rows, cid, tid, n, t, o, e, p): # e.g. 'pt1.1', 'ft1.1', etc has_output = bool(o or e) tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) name = t.id().split('.')[-1] doc = t.shortDescription() or "" desc = doc and ('%s: %s' % (name, doc)) or name tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL tmpl = bool(p) and self.REPORT_TEST_WITH_OUTPUT_TMPL_SHOT or tmpl # o and e should be byte string because they are collected from stdout and stderr? if isinstance(o,str): # TODO: some problem with 'string_escape': it escape \n and mess up formating # uo = unicode(o.encode('string_escape')) uo = o.decode('latin-1') else: uo = o if isinstance(e,str): # TODO: some problem with 'string_escape': it escape \n and mess up formating # ue = unicode(e.encode('string_escape')) ue = e.decode('latin-1') else: ue = e script = self.REPORT_TEST_OUTPUT_TMPL % dict( id = tid, output = saxutils.escape(uo+ue), ) row = tmpl % dict( tid = tid, Class = (n == 0 and 'hiddenRow' or 'none'), style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), desc = desc, script = script, status = self.STATUS[n], screenshot = p ) rows.append(row) if not has_output: return def _generate_ending(self): return self.ENDING_TMPL ############################################################################## # Facilities for running tests from the command line ############################################################################## # Note: Reuse unittest.TestProgram to launch test. In the future we may # build our own launcher to support more specific command line # parameters like test title, CSS, etc. class TestProgram(unittest.TestProgram): """ A variation of the unittest.TestProgram. Please refer to the base class for command line parameters. """ def runTests(self): # Pick HTMLTestRunner as the default test runner. # base class's testRunner parameter is not useful because it means # we have to instantiate HTMLTestRunner before we know self.verbosity. if self.testRunner is None: self.testRunner = HTMLTestRunner(verbosity=self.verbosity) unittest.TestProgram.runTests(self) main = TestProgram ############################################################################## # Executing this module from the command line ############################################################################## if __name__ == "__main__": main(module=None) sikulix-1.1.1/API/src/main/resources/Lib/XMLTestRunner1.py000066400000000000000000000154751315726130400232110ustar00rootroot00000000000000""" XML Test Runner for PyUnit """ # Written by Sebastian Rittau and placed in # the Public Domain. With contributions by Paolo Borelli and others. # downloaded from: http://www.rittau.org/python/xmlrunner.py # modified by RaiMan at sikulix.com # removed self runnable and self test from __future__ import with_statement __version__ = "0.1" import os.path import re import sys import time import traceback import unittest from xml.sax.saxutils import escape try: from StringIO import StringIO except ImportError: from io import StringIO class _TestInfo(object): """Information about a particular test. Used by _XMLTestResult. """ def __init__(self, test, time): (self._class, self._method) = test.id().rsplit(".", 1) self._time = time self._error = None self._failure = None @staticmethod def create_success(test, time): """Create a _TestInfo instance for a successful test.""" return _TestInfo(test, time) @staticmethod def create_failure(test, time, failure): """Create a _TestInfo instance for a failed test.""" info = _TestInfo(test, time) info._failure = failure return info @staticmethod def create_error(test, time, error): """Create a _TestInfo instance for an erroneous test.""" info = _TestInfo(test, time) info._error = error return info def print_report(self, stream): """Print information about this test case in XML format to the supplied stream. """ stream.write(' ' % \ { "class": self._class, "method": self._method, "time": self._time, }) if self._failure is not None: self._print_error(stream, 'failure', self._failure) if self._error is not None: self._print_error(stream, 'error', self._error) stream.write('\n') def _print_error(self, stream, tagname, error): """Print information from a failure or error to the supplied stream.""" text = escape(str(error[1])) stream.write('\n') stream.write(' <%s type="%s">%s\n' \ % (tagname, _clsname(error[0]), text)) tb_stream = StringIO() traceback.print_tb(error[2], None, tb_stream) stream.write(escape(tb_stream.getvalue())) stream.write(' \n' % tagname) stream.write(' ') def _clsname(cls): return cls.__module__ + "." + cls.__name__ class _XMLTestResult(unittest.TestResult): """A test result class that stores result as XML. Used by XMLTestRunner. """ def __init__(self, classname): unittest.TestResult.__init__(self) self._test_name = classname self._start_time = None self._tests = [] self._error = None self._failure = None def startTest(self, test): unittest.TestResult.startTest(self, test) self._error = None self._failure = None self._start_time = time.time() def stopTest(self, test): time_taken = time.time() - self._start_time unittest.TestResult.stopTest(self, test) if self._error: info = _TestInfo.create_error(test, time_taken, self._error) elif self._failure: info = _TestInfo.create_failure(test, time_taken, self._failure) else: info = _TestInfo.create_success(test, time_taken) self._tests.append(info) def addError(self, test, err): unittest.TestResult.addError(self, test, err) self._error = err def addFailure(self, test, err): unittest.TestResult.addFailure(self, test, err) self._failure = err def print_report(self, stream, time_taken, out, err): """Prints the XML report to the supplied stream. The time the tests took to perform as well as the captured standard output and standard error streams must be passed in.a """ stream.write('\n' % \ { "n": self._test_name, "t": self.testsRun, "time": time_taken, }) for info in self._tests: info.print_report(stream) stream.write(' \n' % out) stream.write(' \n' % err) stream.write('\n') class XMLTestRunner(object): """A test runner that stores results in XML format compatible with JUnit. XMLTestRunner(stream=None) -> XML test runner The XML file is written to the supplied stream. If stream is None, the results are stored in a file called TEST-..xml in the current working directory (if not overridden with the path property), where and are the module and class name of the test class. """ def __init__(self, stream=None): self._stream = stream self._path = "." def run(self, test): """Run the given test case or test suite.""" class_ = test.__class__ classname = class_.__module__ + "." + class_.__name__ if self._stream == None: filename = "TEST-%s.xml" % classname stream = file(os.path.join(self._path, filename), "w") stream.write('\n') else: stream = self._stream result = _XMLTestResult(classname) start_time = time.time() with _fake_std_streams(): test(result) try: out_s = sys.stdout.getvalue() except AttributeError: out_s = "" try: err_s = sys.stderr.getvalue() except AttributeError: err_s = "" time_taken = time.time() - start_time result.print_report(stream, time_taken, out_s, err_s) if self._stream is None: stream.close() return result def _set_path(self, path): self._path = path path = property(lambda self: self._path, _set_path, None, """The path where the XML files are stored. This property is ignored when the XML file is written to a file stream.""") class _fake_std_streams(object): def __enter__(self): self._orig_stdout = sys.stdout self._orig_stderr = sys.stderr sys.stdout = StringIO() sys.stderr = StringIO() def __exit__(self, exc_type, exc_val, exc_tb): sys.stdout = self._orig_stdout sys.stderr = self._orig_stderr sikulix-1.1.1/API/src/main/resources/Lib/XMLTestRunner2.py000066400000000000000000000166021315726130400232030ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ XML Test Runner for PyUnit """ # Written by Sebastian Rittau and placed in # the Public Domain. With contributions by Paolo Borelli and others. # downloaded from: http://www.rittau.org/python/xmlrunner.py # modified by RaiMan at sikulix.com # removed self runnable and self test from __future__ import unicode_literals __version__ = "0.2" import os.path import re import sys import time import traceback import unittest import unittest.util from xml.sax.saxutils import escape from io import StringIO, BytesIO class _TestInfo(object): """Information about a particular test. Used by _XMLTestResult. """ def __init__(self, test, time): (self._class, self._method) = test.id().rsplit(".", 1) self._time = time self._error = None self._failure = None @staticmethod def create_success(test, time): """Create a _TestInfo instance for a successful test.""" return _TestInfo(test, time) @staticmethod def create_failure(test, time, failure): """Create a _TestInfo instance for a failed test.""" info = _TestInfo(test, time) info._failure = failure return info @staticmethod def create_error(test, time, error): """Create a _TestInfo instance for an erroneous test.""" info = _TestInfo(test, time) info._error = error return info def print_report(self, stream): """Print information about this test case in XML format to the supplied stream. """ tag_template = (' ') stream.write(tag_template.format(class_=self._class, method=self._method, time=self._time)) if self._failure is not None: self._print_error(stream, 'failure', self._failure) if self._error is not None: self._print_error(stream, 'error', self._error) stream.write('\n') @staticmethod def _print_error(stream, tag_name, error): """Print information from a failure or error to the supplied stream.""" str_ = str if sys.version_info[0] >= 3 else unicode io_class = StringIO if sys.version_info[0] >= 3 else BytesIO text = escape(str_(error[1])) class_name = unittest.util.strclass(error[0]) stream.write('\n') stream.write(' <{tag} type="{class_}">{text}\n'.format( tag=tag_name, class_= class_name, text=text)) tb_stream = io_class() traceback.print_tb(error[2], None, tb_stream) tb_string = tb_stream.getvalue() if sys.version_info[0] < 3: tb_string = tb_string.decode("utf-8") stream.write(escape(tb_string)) stream.write(' \n'.format(tag=tag_name)) stream.write(' ') def _clsname(cls): return cls.__module__ + "." + cls.__name__ class _XMLTestResult(unittest.TestResult): """A test result class that stores result as XML. Used by XMLTestRunner. """ def __init__(self, class_name): unittest.TestResult.__init__(self) self._test_name = class_name self._start_time = None self._tests = [] self._error = None self._failure = None def startTest(self, test): unittest.TestResult.startTest(self, test) self._error = None self._failure = None self._start_time = time.time() def stopTest(self, test): time_taken = time.time() - self._start_time unittest.TestResult.stopTest(self, test) if self._error: info = _TestInfo.create_error(test, time_taken, self._error) elif self._failure: info = _TestInfo.create_failure(test, time_taken, self._failure) else: info = _TestInfo.create_success(test, time_taken) self._tests.append(info) def addError(self, test, err): unittest.TestResult.addError(self, test, err) self._error = err def addFailure(self, test, err): unittest.TestResult.addFailure(self, test, err) self._failure = err def print_report(self, stream, time_taken, out, err): """Prints the XML report to the supplied stream. The time the tests took to perform as well as the captured standard output and standard error streams must be passed in.a """ tag_template = ('\n') stream.write(tag_template.format(name=self._test_name, total=self.testsRun, errors=len(self.errors), failures=len(self.failures), time=time_taken)) for info in self._tests: info.print_report(stream) stream.write(' \n'.format( out)) stream.write(' \n'.format( err)) stream.write('\n') class XMLTestRunner(object): """A test runner that stores results in XML format compatible with JUnit. XMLTestRunner(stream=None) -> XML test runner The XML file is written to the supplied stream. If stream is None, the results are stored in a file called TEST-..xml in the current working directory (if not overridden with the path property), where and are the module and class name of the test class. """ def __init__(self, stream=None): self._stream = stream self._path = "." def run(self, test): """Run the given test case or test suite.""" class_ = test.__class__ class_name = class_.__module__ + "." + class_.__name__ if self._stream is None: filename = "TEST-{0}.xml".format(class_name) stream = open(os.path.join(self._path, filename), "w") stream.write('\n') else: stream = self._stream result = _XMLTestResult(class_name) start_time = time.time() with _FakeStdStreams(): test(result) try: out_s = sys.stdout.getvalue() except AttributeError: out_s = "" try: err_s = sys.stderr.getvalue() except AttributeError: err_s = "" time_taken = time.time() - start_time result.print_report(stream, time_taken, out_s, err_s) if self._stream is None: stream.close() return result def _set_path(self, path): self._path = path path = property( lambda self: self._path, _set_path, None, """The path where the XML files are stored. This property is ignored when the XML file is written to a file stream.""") class _FakeStdStreams(object): def __enter__(self): self._orig_stdout = sys.stdout self._orig_stderr = sys.stderr sys.stdout = StringIO() sys.stderr = StringIO() def __exit__(self, exc_type, exc_val, exc_tb): sys.stdout = self._orig_stdout sys.stderr = self._orig_stderr sikulix-1.1.1/API/src/main/resources/Lib/guide/000077500000000000000000000000001315726130400211455ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/resources/Lib/guide/__init__.py000066400000000000000000000000251315726130400232530ustar00rootroot00000000000000from guide import * sikulix-1.1.1/API/src/main/resources/Lib/guide/guide.py000066400000000000000000000402431315726130400226170ustar00rootroot00000000000000from sikuli import * from org.sikuli.script import Pattern from org.sikuli.script import Region from org.sikuli.basics import Debug from org.sikuli.guide import Guide from org.sikuli.guide import Visual from org.sikuli.guide.Visual import Layout from org.sikuli.guide import SxAnchor from org.sikuli.guide import SxArea from org.sikuli.guide import SxArrow from org.sikuli.guide import SxBracket from org.sikuli.guide import SxButton from org.sikuli.guide import SxCallout from org.sikuli.guide import SxCircle from org.sikuli.guide import SxClickable from org.sikuli.guide import SxFlag from org.sikuli.guide import SxHotspot from org.sikuli.guide import SxImage from org.sikuli.guide import SxRectangle from org.sikuli.guide import SxSpotlight from org.sikuli.guide import SxText _g = Guide() ####################### # Core API # ####################### #================ # Basic Elements #================ def rectangle(target, **kwargs): comp = _g.rectangle() return _addComponentHelper(comp, target, side = 'over', **kwargs) def circle(target, **kwargs): comp = _g.circle() return _addComponentHelper(comp, target, side = 'over', **kwargs) def text(target, txt, **kwargs): comp = _g.text(txt) s = kwargs.pop("side", 'right') f = kwargs.pop("fontsize", 16) return _addComponentHelper(comp, target, side = s, fontsize = f, **kwargs) def tooltip(target, txt, **kwargs): return text(target, txt, fontsize = 8, **kwargs) def flag(target, txt, **kwargs): comp = _g.flag(txt) s = kwargs.pop("side", 'right') f = kwargs.pop("fontsize", 12) return _addComponentHelper(comp, target, side = s, fontsize = f, **kwargs) def callout(target, txt, **kwargs): comp = _g.callout(txt) s = kwargs.pop("side", 'right') f = kwargs.pop("fontsize", 16) return _addComponentHelper(comp, target, side = s, fontsize = f, **kwargs) def image(target, img, **kwargs): comp = _g.image(img) return _addComponentHelper(comp, target, side = "over", **kwargs) def bracket(target, **kwargs): comp = _g.bracket() s = kwargs.pop("side", 'right') return _addComponentHelper(comp, target, side = s, **kwargs) def arrow(src, dest, **kwargs): comp = _g.arrow(src, dest) return _addComponentHelper(comp, None, **kwargs) #----------- not yet checked def spotlight(target, **kwargs): comp = SxSpotlight(None) shp = kwargs.pop("shape", 'circle') if shp == 'rectangle': comp.setShape(SxSpotlight.RECTANGLE) elif shp == 'circle': comp.setShape(SxSpotlight.CIRCLE) s = kwargs.pop("side", 'over') return _addComponentHelper(comp, target, side = s, **kwargs) #===================== # Interactive Elements #===================== def button(target, name, **kwargs): comp = _g.button(name) s = kwargs.pop("side", 'bottom') return _addComponentHelper(comp, target, side = s, **kwargs) def addCloseButton(a): button(target, "Close", side="left", offset = (200,0)) def addNextButton(a): button(target, "Next", side="left", offset = (60,0)) def addPreviousButton(a): button(target, "Previous", side ="left", offset = (0,0)) #----------- not yet checked def clickable(target, name = "", **kwargs): comp = SxClickable(None) comp.setName(name) return _addComponentHelper(comp, target, side = 'over', **kwargs) def hotspot(target, message, side = 'right'): # TODO allow hotspot's positions to be automatically updated r = _getRegionFromTarget(target) txtcomp = SxCallout(message) r1 = Region(r) r1.x -= 10 r1.w += 20 _setLocationRelativeToRegion(txtcomp,r1,side) txtcomp.setShadow(10,2) comp = SxHotspot(r, txtcomp, _g) _g.addToFront(comp) return comp #===================== # Positioning Elements #====================== def anchor(target): if isinstance(target, Pattern): pattern = target elif isinstance(target, str): pattern = Pattern(target) comp = SxAnchor(pattern) _g.addToFront(comp) return comp def area(targets): patterns = [Pattern(target) for target in targets] comp = SxArea() for pattern in patterns: anchor = SxAnchor(pattern) _g.addToFront(anchor) comp.addLandmark(anchor) _g.addToFront(comp) return comp #################### # Helper functions # #################### """ positional parameters: target the element, that defines the place, where to show this element (region or other element) txt text to be shown in the element name the name of a button src, dest the targets for the arrow: points from src to dest optional keyword parameters (not every keyword might be supported by every element): side = "layout" where to place the element related to others margin = value space added around the element offset = (x_value, y_value) offset added to the standard top left of the element font = "fontname" fontsize = value width = value color keywords back = colorSpec fill color for the element frame = colorSpec color of a border text = colorSpec color of contained text front = colorSpec color of element aspects other than back or frame layout variants: right position to the right left position to the left top position above (towards upper screen edge) bottom position below (towards lower screen edge) over position on top centered (eventually hiding all or parts of the target) colorSpec: (R,G,B) integer list as RGB spec (0 .. 255) """ def _addComponentHelper(comp, target, side = 'best', margin = 0, offset = (0,0), horizontalalignment = 'center', verticalalignment = 'center', font = None, fontsize = 0, width = 0, shadow = 'default', front = None, back = None, frame = None, text = None): # set the component's colors comp.setColors(front, back, frame, text) # set the component's font comp.setFont(font, fontsize) # set the components width if width > 0: comp.setMaxWidth(width) # Margin if margin: if isinstance(margin, tuple): (dt,dl,db,dr) = margin else: (dt,dl,db,dr) = (margin,margin,margin,margin) comp.setMargin(dt,dl,db,dr) # Offset if offset: (x,y) = offset comp.setOffset(x,y) # Side sideConstant = None; if (side == 'right'): sideConstant = Layout.RIGHT elif (side == 'top'): sideConstant = Layout.TOP elif (side == 'bottom'): sideConstant = Layout.BOTTOM elif (side == 'left'): sideConstant = Layout.LEFT elif (side == 'inside'): sideConstant = Layout.INSIDE elif (side == 'over'): sideConstant = Layout.OVER # Alignment # if (horizontalalignment == 'left'): # comp.setHorizontalAlignmentWithRegion(r,0.0) # elif (horizontalalignment == 'right'): # if (verticalalignment == 'top'): # comp.setVerticalAlignmentWithRegion(r,0.0) # elif (verticalalignment == 'bottom'): # comp.setVerticalAlignmentWithRegion(r,1.0) # target and position if target: comp.setTarget(target) if sideConstant: comp.setLayout(sideConstant) # if isinstance(target, Region): # # absolute location wrt a Region # comp.setLocationRelativeToRegion(target, sideConstant) # elif isinstance(target, tuple): # # absolute location wrt a point (specified as (x,y)) # comp.setLocationRelativeToRegion(Region(target[0], target[1],1,1), Layout.RIGHT) # else: # targetComponent = None # if isinstance(target, str): # # relative location to a string (image filename) # targetComponent = anchor(Pattern(target)) # targetComponent.setOpacity(0) # elif isinstance(target, Pattern): # # relative location to a pattern # targetComponent = anchor(target) # targetComponent.setOpacity(0) # elif isinstance(target, Visual): # targetComponent = target # if targetComponent: # comp.setLocationRelativeToComponent(targetComponent, sideConstant) # else: # Debug.error("GuideComponentSetup: invalid target: ", target) # return None # set shadow, different sizes for different types of components #TODO shadow handling if shadow == 'default': if (isinstance(comp, SxCircle) or \ isinstance(comp, SxRectangle) or \ isinstance(comp, SxBracket)): comp.setShadow(5,2) elif not (isinstance(comp, SxSpotlight)): comp.setShadow(10,2) # add the component to guide comp.updateComponent() # _g.addToFront(comp) return comp #===================== # Show the Elements #===================== def show(arg = None, timeout = 5): global _g cmd = "" # show a list of steps if isinstance(arg, list) or isinstance(arg, tuple): _show_steps(arg, timeout) # show a single step elif callable(arg): arg() cmd = _g.showNow(timeout) # show for some period of time elif isinstance(arg, float) or isinstance(arg, int): cmd = _g.showNow(arg) # show else: cmd = _g.showNow() _g = Guide() return cmd def setDefaultTimeout(timeout): _g.setDefaultTimeout(timeout) # showing steps, that are defined in a list of functions def _show_steps(steps, timeout = None): # only keep callables steps = filter(lambda x: callable(x), steps) print steps n = len(steps) i = 0 while True: step = steps[i] step() msg = "Step %d of %d" % (i+1, n) a = rectangle(Region(100,100,0,0)) text((10,50), msg, fontsize = 10) if n == 1: # only one step addCloseButton(a) elif i == 0: # first step addNextButton(a) addCloseButton(a) elif i < n - 1: # between addPreviousButton(a) addNextButton(a) addCloseButton(a) elif i == n - 1: # final step addPreviousButton(a) addCloseButton(a) ret = _g.showNow() if (ret == "Previous" and i > 0): i = i - 1 elif (ret == "Next" and i < n - 1): i = i + 1 elif (ret == None and i < n - 1): # timeout i = i + 1 elif (ret == "Close"): return else: # some other transitions happened if (i < n - 1): i = i + 1 else: return ######################### # Cursor Enhancement # ######################### def beam(target): r = s.getRegionFromTarget(target) c = _g.addBeam(r) return c def magnet(arg): m = Magnet(_g) def addTarget(x): if (isinstance(x, Pattern)): pattern = x elif (isinstance(x, str)): pattern = Pattern(x) m.addTarget(pattern) if isinstance(arg, list) or isinstance(arg, tuple): for x in arg: addTarget(x) else: addTarget(x) _g.addTransition(m) def _setLocationRelativeToRegion(comp, r_, side='left', offset=(0,0), expand=(0,0,0,0), \ horizontalalignment = 'center', \ verticalalignment = 'center'): r = Region(r_) # Offset (dx,dy) = offset r.x += dx r.y += dy # Side if (side == 'right'): comp.setLocationRelativeToRegion(r, Layout.RIGHT); elif (side == 'top'): comp.setLocationRelativeToRegion(r, Layout.TOP); elif (side == 'bottom'): comp.setLocationRelativeToRegion(r, Layout.BOTTOM); elif (side == 'left'): comp.setLocationRelativeToRegion(r, Layout.LEFT); elif (side == 'inside'): comp.setLocationRelativeToRegion(r, Layout.INSIDE); # Alignment if (horizontalalignment == 'left'): comp.setHorizontalAlignmentWithRegion(r,0.0) elif (horizontalalignment == 'right'): comp.setHorizontalAlignmentWithRegion(r,1.0) if (verticalalignment == 'top'): comp.setVerticalAlignmentWithRegion(r,0.0) elif (verticalalignment == 'bottom'): comp.setVerticalAlignmentWithRegion(r,1.0) def _getRegionFromTarget(target): if isinstance(target, Visual): return Region(target.getBounds()) else: return Screen().getRegionFromTarget(target) """ # RaiMan currently not used # def _addSideComponentToTarget(comp, target, **kwargs): r = _getRegionFromTarget(target) _setLocationRelativeToRegion(comp,r,**kwargs) if isinstance(target, str): _g.addTracker(Pattern(target),r,comp) elif isinstance(target, Pattern): _g.addTracker(target,r,comp) elif isinstance(target, Visual): target.addFollower(comp) _g.addComponent(comp) return comp def _addAraeComponentToTarget(comp_func, target, **kwargs): r = _getRegionFromTarget(target) r1 = _adjustRegion(r, **kwargs) comp = comp_func(r1) if isinstance(target, str): _g.addTracker(Pattern(target),r1,comp) elif isinstance(target, Pattern): _g.addTracker(target,r1,comp) elif isinstance(target, Visual): target.addFollower(comp) _g.addComponent(comp) return comp def _adjustRegion(r_, offset = (0,0), expand=(0,0,0,0)) r = Region(r_) # Offset (dx,dy) = offset r.x += dx r.y += dy # Expansion if isinstance(expand, tuple): (dt,dl,db,dr) = expand else: (dt,dl,db,dr) = (expand,expand,expand,expand) r.x -= dl r.y -= dt r.w = r.w + dl + dr r.h = r.h + dt + db return r # RaiMan: seems not to be used anymore # # functions for showing def _show_steps_old(steps, timeout = None): # only keep callables steps = filter(lambda x: callable(x), steps) print steps n = len(steps) i = 0 while True: step = steps[i] step() d = TransitionDialog() text = "Step %d of %d" % (i+1, n) d.setText(text) if n == 1: # only one step d.addButton("Close") elif i == 0: # first step d.addButton("Next") d.addButton("Close") elif i < n - 1: # between d.addButton("Previous") d.addButton("Next") d.addButton("Close") elif i == n - 1: # final step d.addButton("Previous") d.addButton("Close") d.setLocationToUserPreferredLocation() if timeout: d.setTimeout(timeout*1000) _g.setTransition(d) ret = _g.showNow() if (ret == "Previous" and i > 0): i = i - 1 elif (ret == "Next" and i < n - 1): i = i + 1 elif (ret == None and i < n - 1): # timeout i = i + 1 elif (ret == "Close"): return else: return RaiMan: Temporarily switched off ######################### # Experimental Features # ######################### def portal(targets): p = Portal(_g) for target in targets: r = s.getRegionFromTarget(target) p.addEntry("",r) _g.addSingleton(p) def magnifier(target): r = s.getRegionFromTarget(target) _g.addMagnifier(r) def parse_model(gui, level=0): for i in range(0,level): print "----", n = gui[0] ps,name = n node_n = GUINode(Pattern(ps).similar(0.75)) node_n.setName(name) print node_n children = gui[1:] for c in children: node_c = parse_model(c, level+1) node_n.add(node_c) return node_n def do_search(guidefs, guide): root = GUINode(None) model = GUIModel(root) for guidef in guidefs: root.add(parse_model(guidef)) search = TreeSearchDialog(guide, model) search.setLocationRelativeTo(None) search.setAlwaysOnTop(True) guide.setSearchDialog(search) guide.showNow() h = dict() def addEntry(target, keys): r = s.getRegionFromTarget(target) for k in keys: if isinstance(k, tuple): h[k[0]] = k[1] _g.addSearchEntry(k[0], r) else: _g.addSearchEntry(k, r) def gui_search(guidefs, keyword): root = GUINode(None) model = GUIModel(root) for guidef in guidefs: root.add(parse_model(guidef)) model.drawPathTo(_g, keyword); _g.showNow(3); def search(model = None): if model: do_search(model, _g) else: ret = _g.showNow() if ret in h: h[ret]() """sikulix-1.1.1/API/src/main/resources/Lib/sikuli/000077500000000000000000000000001315726130400213505ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/resources/Lib/sikuli/Env.py000077500000000000000000000007711315726130400224620ustar00rootroot00000000000000# Copyright 2010-2014, Sikuli.org, sikulix.com # Released under the MIT License. from org.sikuli.basics import HotkeyListener from org.sikuli.basics import HotkeyManager from org.sikuli.script import Env as JEnv class Env(JEnv): @classmethod def addHotkey(cls, key, modifiers, handler): class AnonyListener(HotkeyListener): def hotkeyPressed(self, event): handler(event) return HotkeyManager.getInstance().addHotkey(key, modifiers, AnonyListener()) sikulix-1.1.1/API/src/main/resources/Lib/sikuli/Finder.py000077500000000000000000000007221315726130400231350ustar00rootroot00000000000000# Copyright 2010-2014, Sikuli.org, sikulix.com # Released under the MIT License. # created by RaiMan 2013 from org.sikuli.script import Finder as JFinder class Finder(JFinder): # TODO make as Python (support for with) def __init__(self): pass def __enter__(self): return super def __exit__(self, type, value, trackback): super.destroy() def __del__(self, type, value, trackback): super.destroy() sikulix-1.1.1/API/src/main/resources/Lib/sikuli/Region.py000077500000000000000000000035041315726130400231520ustar00rootroot00000000000000# Copyright 2010-2016, Sikuli.org, sikulix.com # Released under the MIT License. # modified RaiMan 2016 import org.sikuli.script.Region as JRegion from org.sikuli.script.Constants import FOREVER import sys import inspect from org.sikuli.basics import Debug as JDebug import Sikuli class Debug(JDebug): pass class Region(JRegion): # support for with: # override all global sikuli functions by this region's methods. def __enter__(self): Debug.logj("Region: with __enter__: %s", self.toStringShort()) Sikuli.use(self, fromWith=True) return self # exclude_list = ['ROI'] # self._global_funcs = {} # dict = sys.modules['__main__'].__dict__ # for name in dir(self): # if name in exclude_list: continue # try: # if not inspect.ismethod(getattr(self, name)): # continue # except: # continue # if dict.has_key(name): # self._global_funcs[name] = dict[name] # dict[name] = eval("self." + name) # return self def __exit__(self, type, value, traceback): Debug.logj("Region: with __exit__: %s", self.toStringShort()) Sikuli.use(fromWith=True) return # dict = sys.modules['__main__'].__dict__ # for name in self._global_funcs.keys(): # dict[name] = self._global_funcs[name] # self._global_funcs = None ####################################################################### # ---- SIKULI PUBLIC API ####################################################################### # the new Region.text() feature (Tesseract 3) returns utf8 def text(self): return JRegion.text(self).encode("utf8") # still needed, to be backwards compatible def observe(self, waitTime=FOREVER, background=False): if background : return self.observeInBackground(waitTime) else: return self.observeInLine(waitTime); sikulix-1.1.1/API/src/main/resources/Lib/sikuli/Screen.py000077500000000000000000000002271315726130400231450ustar00rootroot00000000000000# Copyright 2010-2016, Sikuli.org, sikulix.com # Released under the MIT License. # since version 1.1.2: no longer used class Screen(Region): pass sikulix-1.1.1/API/src/main/resources/Lib/sikuli/Sikuli.py000077500000000000000000000450701315726130400231730ustar00rootroot00000000000000# Copyright 2010-2016, Sikuli.org, sikulix.com # Released under the MIT License. # modified RaiMan 2016 from __future__ import with_statement from org.sikuli.basics import Debug as JDebug class Debug(JDebug): pass Debug.log(3, "Jython: sikuli: Sikuli: starting init") import time import __builtin__ # import __main__ import types import sys import os import inspect Debug.log(4, "Jython: sikuli: Sikuli: backports from Version 2: Do") import org.sikuli.script.Do as Do Debug.log(4, "Jython: sikuli: Sikuli: RunTime, Setting, Debug") import org.sikuli.script.RunTime as JRunTime class RunTime(JRunTime): pass RUNTIME = RunTime.get() import org.sikuli.basics.Settings as Settings Debug.log(4, "Jython: sikuli: Sikuli: constants") import org.sikuli.script.FindFailed as FindFailed from org.sikuli.script.FindFailedResponse import * from org.sikuli.script.Constants import * import org.sikuli.script.Button as Button from org.sikuli.script.Button import WHEEL_UP, WHEEL_DOWN from org.sikuli.basics import OS Debug.log(4, "Jython: sikuli: Sikuli: import Region") from Region import * Debug.log(4, "Jython: sikuli: Sikuli: import Screen") # from Screen import * import org.sikuli.script.Screen as JScreen class Screen(JScreen): pass SCREEN = None def capture(*args): return SCREEN.cmdCapture(args).getFile() # Python wait() needs to be here because Java Object has a final method: wait(long timeout). # If we want to let Sikuli users use wait(int/long timeout), we need this Python method. def wait(target, timeout=None): if isinstance(target, int) or isinstance(target, long): target = float(target) if timeout == None: return SCREEN.wait(target) else: return SCREEN.wait(target, timeout) Debug.log(4, "Jython: sikuli: Sikuli: import ScreenUnion") from org.sikuli.script import ScreenUnion Debug.log(4, "Jython: sikuli: Sikuli: import Location") import org.sikuli.script.Location as JLocation class Location(JLocation): pass Debug.log(4, "Jython: sikuli: Sikuli: import Finder") import org.sikuli.script.Finder as JFinder class Finder(JFinder): pass Debug.log(4, "Jython: sikuli: Sikuli: import Match") from org.sikuli.script import Match as JMatch class Match(JMatch): pass Debug.log(4, "Jython: sikuli: Sikuli: import ImagePath") from org.sikuli.script import ImagePath Debug.log(4, "Jython: sikuli: Sikuli: import Pattern") from org.sikuli.script import Pattern as JPattern class Pattern(JPattern): pass Debug.log(4, "Jython: sikuli: Sikuli: import Image") import org.sikuli.script.Image as JImage class Image(JImage): pass Debug.log(4, "Jython: sikuli: Sikuli: Env.addHotkey") from Env import * Debug.log(4, "Jython: sikuli: Sikuli: import App") import org.sikuli.script.App as JApp class App(JApp): pass Debug.log(4, "Jython: sikuli: Sikuli: import KeyBoard/Mouse") from org.sikuli.script import Key as JKey class Key(JKey): pass from org.sikuli.script import KeyModifier from org.sikuli.script.KeyModifier import KEY_CTRL, KEY_SHIFT, KEY_META, KEY_CMD, KEY_WIN, KEY_ALT from org.sikuli.script import Device from org.sikuli.script import Mouse def at(): return Mouse.at() Debug.log(4, "Jython: sikuli: Sikuli: import from compare") from org.sikuli.script.compare import DistanceComparator from org.sikuli.script.compare import VerticalComparator from org.sikuli.script.compare import HorizontalComparator Debug.log(4, "Jython: sikuli: Sikuli: init SikuliImporter") import SikuliImporter Debug.log(4, "Jython: sikuli: Sikuli: import Sikulix") from org.sikuli.script import Sikulix Debug.log(4, "Jython: sikuli: Sikuli: import ScriptingSupport") SCRIPT_SUPPORT = True try: from org.sikuli.scriptrunner import ScriptingSupport except: SCRIPT_SUPPORT = False import org.sikuli.script.Runner as Runner import org.sikuli.util.JythonHelper as JythonHelper def load(jar): """ loads a Sikuli extension (.jar) from 1. user's sikuli data path 2. bundle path 3. Java classpath :param jar: jar filename or absolute path :return: True if success, False otherwise """ return JythonHelper.get().load(jar) def prepareRobot(): return JythonHelper.get().prepareRobot() def show(): RUNTIME.show() ## # a token to check the availability # SIKULIX_IS_WORKING = sys.version.split("(")[0] ## # public for options handling (property file) ## def makeOpts(): return RUNTIME.makeOpts() def loadOpts(filePath): return RUNTIME.loadOpts(filePath) def getOpts(props): return RUNTIME.getOpts(props) def hasOpts(props): return RUNTIME.hasOpts(props) def setOpts(props, adict): return RUNTIME.setOpts(props, adict) def delOpts(props): return RUNTIME.delOpts(props) def saveOpts(props, filePath = None): if not filePath: return RUNTIME.saveOpts(props) else: return RUNTIME.saveOpts(props, filePath) def hasOpt(props, key): return RUNTIME.hasOpt(props, key) def getOpt(props, key, deflt = ""): return RUNTIME.getOpt(props, key, deflt) def getOptNum(props, key, deflt = 0): return RUNTIME.getOptNum(props, key, deflt) def setOpt(props, key, value): return RUNTIME.setOpt(props, key, value) def setOptNum(props, key, value): return RUNTIME.setOptNum(props, key, value) def delOpt(props, key): return RUNTIME.delOpt(props, key) ## # some support for handling unicode and strings # ## use instead of print if unicode strings present # usage: uprint(s1, u1, u2, u3, s3, ...) # def uprint(*args): for e in args[:-1]: if isinstance(e, str): print e, else: print e.encode("utf8"), if isinstance(args[-1], str): print args[-1] else: print args[-1].encode("utf8") ## # to make an utf8-encoded string from a str object # def unicd(s): return ucode(s) def ucode(s): return (unicode(s, "utf8")) ## # unzips the given input (string) to the given folder (string) # relative paths are resolved against the working folder # def unzip(zip, target): import org.sikuli.basics.FileManager as FM return FM.unzip(str(zip), str(target)) ## ---------------------------------------------------------------------- # append the given path sys.path if not yet contained # def addImportPath(path): _addModPath(path) ## # append the given path image path list if not yet contained # def addImagePath(path): ImagePath.add(path) ## # return the current image path list # def getImagePath(): return [e for e in ImagePath.get()] ## # remove the given path from the image path # def removeImagePath(path): ImagePath.remove(path) ## # reset the image path, so it only contains the bundlepath # def resetImagePath(path=None): if not path: path = getBundlePath(); ImagePath.reset(path) ## ---------------------------------------------------------------------- # Sets the path for searching images in all Sikuli Script methods.
# Sikuli IDE sets this to the path of the bundle of source code (.sikuli) # automatically. If you write Sikuli scripts using Sikuli IDE, you should # know what you are doing. # returns true if path is valid and exists, false otherwise (no changes) # def setBundlePath(path): return ImagePath.setBundlePath(path) ## # return the current bundlepath (usually the folder .sikuli) # or None if no bundlepath is defined # no trailing path sep # def getBundlePath(): return ImagePath.getBundlePath() ## # return the current bundlepath (usually the folder .sikuli) # or None if no bundlepath is defined # with a trailing path separator (for string concatenation) # def getBundleFolder(): path = ImagePath.getBundlePath() if not path: return None return path + Settings.getFilePathSeperator(); ## # return the parent folder of the current bundlepath # (usually the folder containing the current script folder.sikuli) # or None if no bundlepath is defined # no trailing path sep # def getParentPath(): path = ImagePath.getBundlePath() if not path: return None return os.path.dirname(makePath(getBundlePath())); ## # return the parent folder of the current bundlepath # (usually the folder containing the current script folder.sikuli) # or None if no bundlepath is defined # no trailing path sep # def getParentFolder(): path = getParentPath() if not path: return None return path + Settings.getFilePathSeperator(); ## # make a valid path by by using os.path.join() with the given elements # always without a trailing path separator # def makePath(*paths): if len(paths) == 0: return None path = paths[0] if len(paths) > 1: for p in paths[1:]: path = os.path.join(path, p) if path[-1] == Settings.getFilePathSeperator(): return os.path.dirname(path) return path ## # make a valid path by by using os.path.join() with the given elements # with a trailing path separator (for string concatenation) # def makeFolder(*paths): path = makePath(*paths) if not path: return None path = path + Settings.getFilePathSeperator() return path ## ---------------------------------------------------------------------- # Sikuli shows actions (click, dragDrop, ... etc.) if this flag is set to True. # The default setting is False. # def setShowActions(flag): Settings.setShowActions(flag) def highlightOff(): import org.sikuli.util.ScreenHighlighter as SH SH.closeAll() ## ---------------------------------------------------------------------- # set location, where the center of the pop... should be # no-args: use center screen where SikuliX is running (default) # is used until changed again def popat(*args): if len(args) == 0: return Sikulix.popat() elif len(args) > 1: return Sikulix.popat(args[0], args[1]) else: return Sikulix.popat(args[0]) # Shows a message dialog containing the given message. # @param msg The given message string. # @param title gets the window title. def popup(msg, title="Sikuli Info"): Sikulix.popup(msg, title) # Show error popup (special icon) containing the given message. # @param msg The given message string. # @param title gets the window title. def popError(msg, title="Sikuli Error"): Sikulix.popError(msg, title) # Show a popup containing the given message asking for yes or no # @param msg The given message string. # @param title gets the window title. # @return True if answered Yes, else False def popAsk(msg, title="Sikuli Decision"): return Sikulix.popAsk(msg, title) ## # Shows a question-message dialog requesting input from the user. # @param msg The message to display. # @param default The preset text of the input field (default empty). # @param title the title for the dialog (default: Sikuli input request) # @param hidden =true makes the dialog run as a password input (input hidden with bullets) # @return The user's input string. # def input(msg="", default="", title="Sikuli Input", hidden=False): Debug.log(3, "Sikuli.py: input") if (hidden): default = "" return Sikulix.input(msg, default, title, hidden) ## # Shows a dialog request to enter text in a multiline text field # Though not all text might be visible, everything entered is delivered with the returned text # The main purpose for this feature is to allow pasting text from somewhere # @param msg the message to display. # @param title the title for the dialog (default: Sikuli input request) # @param lines the maximum number of lines visible in the text field (default 9) # @param width the maximum number of characters visible in one line (default 20) # @return The user's input including the line breaks. def inputText(msg="", title="", lines=0, width=0, text=""): return Sikulix.inputText(msg, title, lines, width, text) ## # Shows a dialog requesting to select an entry from the drop down list # @param msg the message to display. # @param title the title for the dialog def select(msg="", title="Sikuli Selection", options=(), default=None): optionsLen = len(options) if optionsLen == 0: return "" try: default = 0 + default; if default > -1 and default < optionsLen: default = options[default] else: default = None except: pass return Sikulix.popSelect(msg, title, options, default) def popFile(title = "Select File or Folder"): return Sikulix.popFile(title) ## ---------------------------------------------------------------------- # set the default screen to given or primary screen # # TODO where else to remember an opened remote screen? remoteScreen = None def use(scr=None, remote=False, fromWith = False): global SCREEN if remote or fromWith: theGlobals = inspect.currentframe().f_back.f_back.f_globals else: theGlobals = inspect.currentframe().f_back.f_globals global remoteScreen if remoteScreen: remoteScreen.close() remoteScreen = None if not scr: newScreen = JScreen() else: newScreen = scr if (newScreen.isValid()): SCREEN = newScreen Debug.log(3, "Jython: requested to use as default region: " + SCREEN.toStringShort()) globals()['SIKULISAVED'] = _exposeAllMethods(SCREEN, globals().get('SIKULISAVED'), theGlobals, None) theGlobals['SCREEN'] = SCREEN if remote: remoteScreen = SCREEN return SCREEN ## # set the default screen to given remote screen # def useRemote(adr, port=0): global remoteScreen import org.sikuli.script.ScreenRemote as SR SCREEN = SR(adr, str(port)) if SCREEN.isValid(): return use(SCREEN, True) else: return None ## ----------------------------------------------------------------------- # convenience for a VNCScreen connection # ip the server IP (default: 127.0.0.1) # port the port number (default 5900) # connectionTimeout seconds to wait for a valid connection (default 10) # timeout the timout value in milli-seconds during normal operation (default 1000) # returns a VNCScreen object # use theVNCScreen.stop() to stop this connection again (auto-stopped at script end) def useVnc(ip="127.0.0.1", port=5900, connectionTimeout=10, timeout=1000, password=None): use(Sikulix.vncStart(ip, port, password, connectionTimeout, timeout), True) def vncStart(ip="127.0.0.1", port=5900, connectionTimeout=10, timeout=1000, password=None): return Sikulix.vncStart(ip, port, password, connectionTimeout, timeout) ## ---------------------------------------------------------------------- # Switches the frontmost application to the given application. # If the given application is not running, it will be launched by openApp() # automatically.
# Note: On Windows, Sikule searches in the text on the title bar # instead of the application name. # @param app The name of the application. (case-insensitive) # def switchApp(app): return App.focus(app) ## # Opens the given application.
# @param app The name of an application if it is in the environment variable PATH, or the full path to an application. # def openApp(app): return App.open(app) ## # Closes the given application.
# @param app The name of the application. (case-insensitive) # def closeApp(app): return App.close(app) ## # Sleeps until the given amount of time in seconds has elapsed. # @param sec The amount of sleeping time in seconds. def sleep(sec): time.sleep(sec) ## ---------------------------------------------------------------------- def reset(): JScreen.resetMonitors(); use(); ALL = SCREEN.all().getRegion() ## # shutdown and return given exit code # def exit(code=0): global remoteScreen if remoteScreen: remoteScreen.close() remoteScreen = None Sikulix.cleanUp(code) sys.exit(code) ## ---------------------------------------------------------------------- # Runs the given string command. # @param cmd The given string command. # @return Returns the output from the executed command. def run(cmd): return Sikulix.run(cmd) # Runs the script given by absolute or relative path (./ same folder as calling script) # @param script The given script path. # @args the parameters for the called script (sys.argv) # @return returns the scripts return code given with exit(n) def runScript(script, *args): if SCRIPT_SUPPORT: return ScriptingSupport.run(unicd(script), args) else: return Runner.run(unicd(script), args) def getLastReturnCode(): if SCRIPT_SUPPORT: return ScriptingSupport.getLastReturnCode() else: return Runner.getLastReturnCode() ## # helper functions, that can be used when sorting lists of regions # def byDistanceTo(x, y=None): """ Method to compare two Region objects by distance of their top left. or a regions top left to the given point by coordinates""" return DistanceComparator(x, y) def byX(m): """ Method to compare two Region objects by x value. """ return HorizontalComparator().compare def byY(m): """ Method to compare two Region objects by y value. """ return VerticalComparator().compare def verticalComparator(): """ Method to compare two Region objects by y value. """ return VerticalComparator().compare def horizontalComparator(): """ Method to compare two Region objects by x value. """ return HorizontalComparator().compare def distanceComparator(x, y=None): """ Method to compare two Region objects by distance of their top left. or a regions top left to the given point by coordinates""" if y is None: return DistanceComparator(x).compare # x is Region or Location return DistanceComparator(x, y).compare # x/y as coordinates ## ################## internal use only ########################################### # def _addModPath(path): if path[-1] == Settings.getFilePathSeperator(): path = path[:-1] if not path in sys.path: sys.path.append(path) def _exposeAllMethods(anyObject, saved, theGlobals, exclude_list): if not exclude_list: exclude_list = ['class', 'classDictInit', 'clone', 'equals', 'finalize', 'getClass', 'hashCode', 'notify', 'notifyAll', 'toGlobalCoord', 'getLocationFromPSRML', 'getRegionFromPSRM', 'create', 'observeInBackground', 'waitAll', 'updateSelf', 'findNow', 'findAllNow', 'getEventManager', 'lastMatch', 'lastMatches', 'lastScreenImage', 'lastScreenImageFile', 'capture', 'wait' ] # Debug.log(3, "Sikuli: _exposeAllMethods: %s called from: %s", anyObject, theGlobals['__name__']) tosave = [] if not saved: saved = [] for name in dir(anyObject): if name in exclude_list: continue try: if not inspect.ismethod(getattr(anyObject, name)): continue except: continue if name[0] != '_' and name[:7] != 'super__': try: saved.remove(name) except: pass tosave.append(name) # print "added:", name theGlobals[name] = eval("anyObject." + name) if name == 'checkWith': Debug.log(3, "%s %s", name, str(dict[name])[1:]) for name in saved: if name in theGlobals: # print "removed:", name theGlobals.pop(name) return tosave ############### set SCREEN as primary screen at startup ################ use() ALL = JScreen.all().getRegion() Debug.log(3, "Jython: sikuli: Sikuli: ending init") sikulix-1.1.1/API/src/main/resources/Lib/sikuli/SikuliImporter.py000066400000000000000000000035401315726130400247060ustar00rootroot00000000000000# Copyright 2010-2014, Sikuli.org, sikulix.com # Released under the MIT License. # modified RaiMan 2013 import imp import sys import Sikuli from org.sikuli.script import ImagePath import org.sikuli.util.JythonHelper as JH import os def _stripPackagePrefix(module_name): pdot = module_name.rfind('.') if pdot >= 0: return module_name[pdot+1:] return module_name class SikuliImporter: class SikuliLoader: def __init__(self, path): self.path = path def _load_module(self, fullname): try: (file, pathname, desc) = imp.find_module(fullname) except: etype, evalue, etb = sys.exc_info() evalue = etype(fullname + ".sikuli has no " + fullname + ".py") raise etype, evalue, etb try: return imp.load_module(fullname, file, pathname, desc) except: etype, evalue, etb = sys.exc_info() evalue = etype("!!WHILE IMPORTING!! %s" % evalue) raise etype, evalue, etb finally: if file: file.close() def load_module(self, module_name): module_name = JH.get().loadModulePrepare(module_name, self.path) return self._load_module(module_name) def _find_module(self, module_name, fullpath): fullpath = fullpath + "/" + module_name + ".sikuli" if os.path.exists(fullpath): return self.SikuliLoader(fullpath) return None def find_module(self, module_name, package_path): module_path = JH.get().findModule(module_name, package_path, sys.path) if not module_path: return None else: return self.SikuliLoader(module_path) sys.meta_path.append(SikuliImporter()) del SikuliImporter sikulix-1.1.1/API/src/main/resources/Lib/sikuli/__init__.py000077500000000000000000000002421315726130400234620ustar00rootroot00000000000000# Copyright 2010-2014, Sikuli.org, sikulix.com # Released under the MIT License. # modified RaiMan 2013 from org.sikuli.basics import Debug from Sikuli import * sikulix-1.1.1/API/src/main/resources/Settings/000077500000000000000000000000001315726130400211425ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/resources/Settings/sikulixversion.txt000066400000000000000000000005261315726130400250040ustar00rootroot00000000000000sikulixvmaj=${sikulixvmaj} sikulixvmin=${sikulixvmin} sikulixvsub=${sikulixvsub} sikulixbeta=${sikulixbeta} sikulixbuild=${timestamp} sikulixdev=${sikulixdev} sikulixvused=${sikuli.usedversion} sikulixvproject=${project.version} sikulixvjython=${sikulixjythonversion} sikulixvjruby=${sikulixjrubyversion} sikulixlocalrepo=${sikulixlocalrepo} sikulix-1.1.1/API/src/main/resources/Support/000077500000000000000000000000001315726130400210165ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/resources/Support/Linux/000077500000000000000000000000001315726130400221155ustar00rootroot00000000000000sikulix-1.1.1/API/src/main/resources/Support/Linux/runBuild000066400000000000000000000023751315726130400236330ustar00rootroot00000000000000work=`pwd` opencvinclude= tesseractinclude= # ---- internal - do not change #work# #jdkdir# #extrainclude# #opencvcore# #opencvimgproc# #opencvhighgui# #tesseractlib# # ---- internal - #jdkdir=/usr/lib/jvm/java-7-openjdk-amd64 #opencvlib=/usr/lib/x86_64-linux-gnu #opencvcore=$opencvlib/libopencv_core.so.2.4 #opencvimgproc=$opencvlib/libopencv_imgproc.so.2.4 #opencvhighgui=$opencvlib/libopencv_highgui.so.2.4 #tesseractlib=/usr/lib/libtesseract.so.3 source=$work/Source build=$work/Target rm -R -f $build mkdir $build stuff=$build/Stuff rm -R -f $stuff mkdir $stuff includeplus= if [ "$extrainclude" != "" ]; then includeplus="-I$extrainclude " fi includefinal="-I/usr/include -I/usr/local/include $includeplus " echo ----------- COMPILING mods= for mod in cvgui.cpp finder.cpp pyramid-template-matcher.cpp sikuli-debug.cpp \ tessocr.cpp vision.cpp visionJAVA_wrap.cxx do echo ----- $mod g++ -c -O3 -fPIC -MMD -MP \ -I$jdkdir/include \ -I$jdkdir/include/linux \ $includefinal \ -MF $stuff/$mod.o.d \ -o $stuff/$mod.o \ $source/$mod mods="$mods $stuff/$mod.o " done echo ----------- LINKING g++ -shared -s -fPIC -dynamic $mods \ $opencvcore \ $opencvimgproc \ $opencvhighgui \ $tesseractlib \ -o $build/libVisionProxy.so sikulix-1.1.1/IDE/000077500000000000000000000000001315726130400136055ustar00rootroot00000000000000sikulix-1.1.1/IDE/README.md000077500000000000000000000006751315726130400150770ustar00rootroot00000000000000Sikuli IDE 2014 (version 1.1.x) === Implements a GUI using Java, that allows to edit and run Sikuli scripts (currently Jython and JRuby are supported). It is an easy to use IDE focusing on the handling of the screenshots and images used in the typical Sikuli workflows. The ready-to-use package `sikulix.jar` is the package containing all other options. It can be used on commandline to run scripts or open interactive scripting sessions. sikulix-1.1.1/IDE/pom.xml000077500000000000000000000101021315726130400151170ustar00rootroot00000000000000 com.sikulix sikulix1 1.1.1 ../ 4.0.0 com.sikulix sikulix 1.1.1 com.sikulix sikulixapi ${project.version} commons-cli commons-cli 1.2 org.python jython-standalone ${sikulixjythonversion} org.jruby jruby-complete ${sikulixjrubyversion} com.googlecode.json-simple json-simple 1.1.1 org.kohsuke.metainf-services metainf-services 1.3 jar compile true org.swinglabs.swingx swingx-autocomplete 1.6.5-1 compile org.swinglabs.swingx swingx-action 1.6.5-1 compile org.swinglabs.swingx swingx-plaf 1.6.5-1 compile org.swinglabs.swingx swingx-graphics 1.6.5-1 jar compile org.swinglabs.swingx swingx-painters 1.6.5-1 compile org.swinglabs.swingx swingx-mavensupport 1.6.5-1 compile true com.jgoodies forms 1.2.1 com.explodingpixels mac_widgets 0.9.5 com.sikulix sxjygments ${project.version} org.swinglabs swing-layout 1.0.3 maven-jar-plugin 2.4 org.sikuli.ide.Sikulix sikulix-1.1.1/IDE/src/000077500000000000000000000000001315726130400143745ustar00rootroot00000000000000sikulix-1.1.1/IDE/src/main/000077500000000000000000000000001315726130400153205ustar00rootroot00000000000000sikulix-1.1.1/IDE/src/main/java/000077500000000000000000000000001315726130400162415ustar00rootroot00000000000000sikulix-1.1.1/IDE/src/main/java/org/000077500000000000000000000000001315726130400170305ustar00rootroot00000000000000sikulix-1.1.1/IDE/src/main/java/org/sikuli/000077500000000000000000000000001315726130400203305ustar00rootroot00000000000000sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/000077500000000000000000000000001315726130400210715ustar00rootroot00000000000000sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/AutoUpdater.java000066400000000000000000000176771315726130400242130ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import javax.swing.*; import javax.swing.event.*; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.basics.Settings; public class AutoUpdater { private String details, bdetails; private String server = ""; private String bserver = ""; private int major, minor, sub; private int bmajor, bminor, beta; private int smajor, sminor, ssub, sbeta; private String name; public static int MAJOR = 1; public static int MINOR = 2; public static int SUB = 3; public static int SOMEBETA = 10; public static int BETA = 5; public static int FINAL = 6; private int available = 0; private boolean notAvailable = false; public String whatUpdate; public String getServer() { return server; } public String getVersion() { if (available > 0) { return String.format("%s-%d.%d.%d", name, major, minor, sub); } return ""; } public String getVersionNumber() { if (available > 0) { return String.format("%d.%d.%d", major, minor, sub); } return ""; } public String getBeta() { if (available == BETA || available >= SOMEBETA) { return String.format("%s-%d.%d-Beta%d", name, bmajor, bminor, beta); } return ""; } public String getBetaVersion() { if (beta > 0) { return String.format("%d.%d-Beta%d", bmajor, bminor, beta); } else { return ""; } } public String getDetails() { return details; } public String getBetaDetails() { return bdetails; } public int checkUpdate() { for (String s : SikuliIDE.runTime.ServerList) { try { if (checkUpdate(s)) { smajor = SikuliIDE.runTime.SikuliVersionMajor; sminor = SikuliIDE.runTime.SikuliVersionMinor; ssub = SikuliIDE.runTime.SikuliVersionSub; sbeta = SikuliIDE.runTime.SikuliVersionBetaN; if (sbeta > 0) { if (smajor == major && sminor == minor) { available = FINAL; whatUpdate = "The final version is available: " + getVersion(); Debug.info(whatUpdate); } else if (smajor == bmajor && sminor == bminor && beta > sbeta) { available = BETA; whatUpdate = "A new beta version is available: " + getVersion(); Debug.info(whatUpdate); } } else { if (major > smajor) { available = MAJOR; whatUpdate = "A new major version is available: " + getVersion(); Debug.info(whatUpdate); } else if (major == smajor && minor > sminor) { available = MINOR; whatUpdate = "A new minor version is available: " + getVersion(); Debug.info(whatUpdate); } else if (major == smajor && minor == sminor && sub > ssub) { available = SUB; whatUpdate = "A new service update is available: " + getVersion(); Debug.info(whatUpdate); } } if (beta > 0 && (bmajor > smajor || (bmajor == smajor && bminor > sminor))) { available += SOMEBETA; Debug.info("A beta version is available: " + getVersion()); } } } catch (Exception e) { notAvailable = true; } if (notAvailable) { Debug.log(2, "No version info available at " + s); return 0; } } return available; } private boolean checkUpdate(String s) throws IOException, MalformedURLException { // contents of latestversion //SikuliX 1 0 0 1 0 999 //DOWNLOAD https://launchpad.net/sikuli/+download //BETA https://dl.dropboxusercontent.com/u/42895525/SikuliX/index.html URL url = new URL(s + "/latestversion"); URLConnection conn; if (FileManager.getProxy() != null) { conn = url.openConnection(FileManager.getProxy()); } else { conn = url.openConnection(); } BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; if ((line = in.readLine()) != null) { String[] vinfo = line.trim().split(" "); if (vinfo.length > 6) { name = vinfo[0]; major = Integer.parseInt(vinfo[1]); minor = Integer.parseInt(vinfo[2]); sub = Integer.parseInt(vinfo[3]); bmajor = Integer.parseInt(vinfo[4]); bminor = Integer.parseInt(vinfo[5]); beta = Integer.parseInt(vinfo[6]); } else { notAvailable = true; return false; } details = ""; if ((line = in.readLine()) != null) { if (line.startsWith("DOWNLOAD")) { server = line.split(" ")[1]; details += "Please download at: " + server + "
"; details += "-------------------------------------------------------------------------"; details += "

"; } else { details += line; } } bdetails = ""; while ((line = in.readLine()) != null) { if (line.startsWith("BETA")) { if (beta > 0) bdetails = line; break; } details += line; } if (beta > 0) { if (! "".equals(bdetails)) { bserver = bdetails.split(" ")[1]; bdetails = "Please download at: " + bserver + "
"; bdetails += "-------------------------------------------------------------------------"; bdetails += "

"; } while ((line = in.readLine()) != null) { bdetails += line; } } in.close(); return true; } return false; } public JFrame showUpdateFrame(String title, String text, int whatUpdate) { if (whatUpdate < 0) { return new UpdateFrame(title, text, null); } else { if (whatUpdate == BETA) { return new UpdateFrame(title, text, bserver); } else { return new UpdateFrame(title, text, server); } } } } class UpdateFrame extends JFrame { public UpdateFrame(String title, String text, String server) { setTitle(title); setSize(300, 200); setLocationRelativeTo(getRootPane()); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); JEditorPane p = new JEditorPane("text/html", text); p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); p.setEditable(false); cp.add(new JScrollPane(p), BorderLayout.CENTER); JButton btnOK = new JButton("ok"); if (server != null) { p.addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { try { FileManager.openURL(e.getURL().toString()); } catch (Exception ex) { ex.printStackTrace(); } } } }); JPanel buttonPane = new JPanel(); btnOK.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { UpdateFrame.this.dispose(); } }); JButton btnGo = new JButton("download"); btnGo.setToolTipText(server); btnGo.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { FileManager.openURL(((JButton) ae.getSource()).getToolTipText()); UpdateFrame.this.dispose(); } }); buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); buttonPane.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); buttonPane.add(Box.createHorizontalGlue()); buttonPane.add(btnGo); buttonPane.add(btnOK); buttonPane.add(Box.createHorizontalGlue()); getRootPane().setDefaultButton(btnOK); cp.add(buttonPane, BorderLayout.PAGE_END); } cp.doLayout(); pack(); setVisible(true); btnOK.requestFocus(); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/BevelDividerPainter.java000077500000000000000000000026101315726130400256250ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import javax.swing.*; import org.jdesktop.swingx.JXMultiSplitPane.DividerPainter; import org.jdesktop.swingx.MultiSplitLayout.Divider; class BevelDividerPainter extends DividerPainter { private JComponent owner; public BevelDividerPainter( JComponent c ) { owner = c; } @Override public void doPaint(Graphics2D g, Divider divider, int width, int height) { Color c = owner.getBackground(); g.setColor( c ); g.fillRect(0, 0, width, height); int size = 1; if ( divider.isVertical()) { size = Math.max( size, ( width / 5 ) -1 ); g.setColor( c.brighter()); g.fillRect( 1, 0, size, height); g.setColor( c.darker()); g.fillRect( width-size-1, 0, size, height); g.setColor( c.darker().darker()); g.drawLine( 0, 0, 0, height); g.drawLine( width-1, 0, width-1, height); } else { size = Math.max( size, height / 5 ); g.setColor( c.brighter()); g.fillRect( 0, 1, width, size ); g.setColor( c.darker()); g.fillRect( 0, height-size-1, width, size ); g.setColor( c.darker().darker()); g.drawLine( 0, 0, width, 0 ); g.drawLine( 0, height-1, width, height-1); } } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/ButtonCapture.java000077500000000000000000000227741315726130400245520ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import org.sikuli.android.ADBScreen; import org.sikuli.basics.PreferencesUser; import java.awt.*; import java.awt.event.*; import java.io.File; import java.net.*; import javax.swing.*; import javax.swing.text.*; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.script.*; import org.sikuli.script.Sikulix; import org.sikuli.util.OverlayCapturePrompt; import org.sikuli.basics.Settings; import org.sikuli.util.EventObserver; import org.sikuli.util.EventSubject; import static org.sikuli.script.Sikulix.popup; class ButtonCapture extends ButtonOnToolbar implements ActionListener, Cloneable, EventObserver { private static final String me = "ButtonCapture: "; protected Element _line; protected EditorPane _codePane; private boolean captureCancelled = false; private EditorPatternLabel _lbl = null; private String givenName = ""; public static boolean debugTrace = true; public ButtonCapture() { super(); URL imageURL = SikuliIDE.class.getResource("/icons/camera-icon.png"); setIcon(new ImageIcon(imageURL)); PreferencesUser pref = PreferencesUser.getInstance(); String strHotkey = Key.convertKeyToText( pref.getCaptureHotkey(), pref.getCaptureHotkeyModifiers()); setToolTipText(SikuliIDE._I("btnCaptureHint", strHotkey)); setText(SikuliIDE._I("btnCaptureLabel")); //setBorderPainted(false); //setMaximumSize(new Dimension(26,26)); addActionListener(this); _line = null; } public ButtonCapture(EditorPane codePane, Element elmLine) { this(); _line = elmLine; _codePane = codePane; setUI(UIManager.getUI(this)); setBorderPainted(true); setCursor(new Cursor(Cursor.HAND_CURSOR)); setText(null); URL imageURL = SikuliIDE.class.getResource("/icons/capture-small.png"); setIcon(new ImageIcon(imageURL)); } public ButtonCapture(EditorPatternLabel lbl) { // for internal use with the image label __CLICK-TO-CAPTURE__ super(); _line = null; _codePane = null; _lbl = lbl; } @Override public void actionPerformed(ActionEvent e) { Debug.log(3, "ButtonCapture: capture!"); captureWithAutoDelay(); } public void captureWithAutoDelay() { PreferencesUser pref = PreferencesUser.getInstance(); int delay = (int) (pref.getCaptureDelay() * 1000.0) + 1; capture(delay); } IScreen defaultScreen = null; ScreenImage sImgNonLocal = null; public void capture(final int delay) { String line = ""; SikuliIDE ide = SikuliIDE.getInstance(); ide.setVisible(false); EditorPane codePane = ide.getCurrentCodePane(); line = codePane.getLineTextAtCaret(); givenName = codePane.parseLineText("#" + line.trim()); Debug.log(3, "ButtonCapture: doPrompt for %s", givenName); RunTime.pause(((float) delay)/1000); defaultScreen = SikuliIDE.getDefaultScreen(); if (defaultScreen == null) { Screen.doPrompt("Select an image", this); } else { Sikulix.popup("Android capture"); Thread adbCapture = new Thread() { @Override public void run() { ADBScreen aScr = (ADBScreen) defaultScreen; aScr.wakeUp(2); sImgNonLocal = aScr.userCapture(""); ButtonCapture.this.update((EventSubject) null); } }; adbCapture.start(); } } @Override public void update(EventSubject es) { Debug.log(3, "ButtonCapture: update"); ScreenImage simg = null; OverlayCapturePrompt ocp = null; if (null == es) { simg = sImgNonLocal; } else { ocp = (OverlayCapturePrompt) es; simg = ocp.getSelection(); Screen.closePrompt(); } String filename = null; String fullpath = null; EditorPane pane = SikuliIDE.getInstance().getCurrentCodePane(); boolean saveOverwrite = Settings.OverwriteImages; if (simg != null) { if (!givenName.isEmpty()) { filename = givenName + ".png"; Settings.OverwriteImages = true; } else { int naming = PreferencesUser.getInstance().getAutoNamingMethod(); if (naming == PreferencesUser.AUTO_NAMING_TIMESTAMP) { filename = Settings.getTimestamp(); } else if (naming == PreferencesUser.AUTO_NAMING_OCR) { filename = PatternPaneNaming.getFilenameFromImage(simg.getImage()); if (filename == null || filename.length() == 0) { filename = Settings.getTimestamp(); } } else { String nameOCR = ""; try { nameOCR = PatternPaneNaming.getFilenameFromImage(simg.getImage()); } catch (Exception e) { } filename = getFilenameFromUser(nameOCR); } } if (filename != null) { fullpath = FileManager.saveImage(simg.getImage(), filename, pane.getSrcBundle()); if (fullpath != null) { fullpath = FileManager.slashify(fullpath, false); } } } Settings.OverwriteImages = saveOverwrite; captureCompleted(fullpath); if (ocp != null) { Screen.resetPrompt(ocp); } SikuliIDE.showAgain(); } public void captureCompleted(String imgFullPath) { Element src = getSrcElement(); if (imgFullPath != null) { Debug.log(3, "captureCompleted: " + imgFullPath); if (src == null) { if (_codePane == null) { if (_lbl == null) { insertAtCursor(SikuliIDE.getInstance().getCurrentCodePane(), imgFullPath); } else { _lbl.setFile(imgFullPath); } } else { insertAtCursor(_codePane, imgFullPath); } } else { replaceButton(src, imgFullPath); } } else { Debug.log(3, "ButtonCapture: Capture cancelled"); if (src != null) { captureCancelled = true; replaceButton(src, ""); captureCancelled = false; } } } // /*public boolean hasNext() { * return false; * }*/ /*public CaptureButton getNextDiffButton() { * return null; * }*/ /*public void setParentPane(SikuliPane parent) { * _codePane = parent; * }*/ /*public void setDiffMode(boolean flag) { * }*/ /*public void setSrcElement(Element elmLine) { * _line = elmLine; * }*/ // private String getFilenameFromUser(String hint) { return (String) JOptionPane.showInputDialog( _codePane, SikuliIDEI18N._I("msgEnterScreenshotFilename"), SikuliIDEI18N._I("dlgEnterScreenshotFilename"), JOptionPane.PLAIN_MESSAGE, null, null, hint); } private Element getSrcElement() { return _line; } private boolean replaceButton(Element src, String imgFullPath) { if (captureCancelled) { if (_codePane.showThumbs && PreferencesUser.getInstance().getPrefMoreImageThumbs() || !_codePane.showThumbs) { return true; } } int start = src.getStartOffset(); int end = src.getEndOffset(); int old_sel_start = _codePane.getSelectionStart(), old_sel_end = _codePane.getSelectionEnd(); try { StyledDocument doc = (StyledDocument) src.getDocument(); String text = doc.getText(start, end - start); Debug.log(3, text); for (int i = start; i < end; i++) { Element elm = doc.getCharacterElement(i); if (elm.getName().equals(StyleConstants.ComponentElementName)) { AttributeSet attr = elm.getAttributes(); Component com = StyleConstants.getComponent(attr); boolean isButton = com instanceof ButtonCapture; boolean isLabel = com instanceof EditorPatternLabel; if (isButton || isLabel && ((EditorPatternLabel) com).isCaptureButton()) { Debug.log(5, "button is at " + i); int oldCaretPos = _codePane.getCaretPosition(); _codePane.select(i, i + 1); if (!_codePane.showThumbs) { _codePane.insertString((new EditorPatternLabel(_codePane, imgFullPath, true)).toString()); } else { if (PreferencesUser.getInstance().getPrefMoreImageThumbs()) { com = new EditorPatternButton(_codePane, imgFullPath); } else { if (captureCancelled) { com = new EditorPatternLabel(_codePane, ""); } else { com = new EditorPatternLabel(_codePane, imgFullPath, true); } } _codePane.insertComponent(com); } _codePane.setCaretPosition(oldCaretPos); break; } } } } catch (BadLocationException ble) { Debug.error(me + "Problem inserting Button!\n%s", ble.getMessage()); } _codePane.select(old_sel_start, old_sel_end); _codePane.requestFocus(); return true; } protected void insertAtCursor(EditorPane pane, String imgFilename) { String img = "\"" + (new File(imgFilename)).getName() + "\""; if (!pane.showThumbs) { pane.insertString(img); } else { if (PreferencesUser.getInstance().getPrefMoreImageThumbs()) { EditorPatternButton comp = EditorPatternButton.createFromFilename(pane, imgFilename, null); if (comp != null) { pane.insertComponent(comp); } } else { pane.insertComponent(new EditorPatternLabel(pane, imgFilename, true)); } } //TODO set Caret pane.requestFocus(); } @Override public String toString() { return "\"__CLICK-TO-CAPTURE__\""; } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/ButtonGenCommand.java000077500000000000000000000125411315726130400251460ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import org.sikuli.basics.PreferencesUser; import javax.swing.*; import java.awt.event.*; import javax.swing.text.*; import java.net.URL; import org.sikuli.basics.Debug; public class ButtonGenCommand extends JButton implements ActionListener, MouseListener { String _cmd; String[] _params; String _desc; EditorPane pane; PreferencesUser pref; final static String DefaultStyle = "color:black;font-family:monospace;font-size:10px; font-weight:normal", HoverStyle = "color:#3333ff;font-family:monospace;font-size:10px; font-weight:bold;", PressedStyle = "color:#3333ff;font-family:monospace;font-size:10px; font-weight:bold;text-decoration:underline;"; public ButtonGenCommand(String cmd, String desc, String... params) { super(getRichRepresentation(DefaultStyle, cmd, desc, params, false)); _cmd = cmd; _params = params; _desc = desc; setToolTipText(getRichRepresentation(DefaultStyle, cmd, desc, params, true)); setHorizontalAlignment(SwingConstants.LEFT); addActionListener(this); addMouseListener(this); setBorderPainted(false); setBorder(BorderFactory.createEmptyBorder(1, 2, 2, 1)); setContentAreaFilled(false); } static String getParamHTML(String p, boolean first, boolean showOptParam) { URL imgPattern = SikuliIDE.class.getResource("/icons/capture-small.png"); String item = ""; if (!first) { item += ", "; } if (p.equals("PATTERN")) { item += ""; } else { if (p.startsWith("[") && p.endsWith("]")) { if (showOptParam) { item += "" + p + ""; } } else { if (p.startsWith("_")) { item += "" + p.substring(1, 2) + "" + p.substring(2); } else { item += p; } } } return !item.equals(", ") ? item : ""; } static String getRichRepresentation(String style, String cmd, String desc, String[] params, boolean showOptParam) { String ret = "
" + "" + cmd + "("; int count = 0; for (String p : params) { String item = getParamHTML(p, count == 0, showOptParam); if (!item.equals("")) { ret += "" + item; } count++; } ret += ")
"; if (showOptParam) { ret += "

" + desc; } return ret; } static String getTextRepresentation(String cmd, String[] params) { String ret = "" + cmd + "("; int count = 0; for (String p : params) { ret += p; if (++count < params.length) { ret += ", "; } } ret += ")"; return ret; } @Override public String toString() { return getTextRepresentation(_cmd, _params); } @Override public void actionPerformed(ActionEvent ae) { SikuliIDE ide = SikuliIDE.getInstance(); pane = ide.getCurrentCodePane(); pref = PreferencesUser.getInstance(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { insertCommand(); } }); } public void insertCommand() { pref = PreferencesUser.getInstance(); int endPos = -1, endPosLen = 0; boolean first = true; ButtonCapture btnCapture; Element line; pane.insertString(_cmd + "("); for (String p : _params) { if (p.equals("PATTERN")) { line = pane.getLineAtCaret(-1); if (!first) { pane.insertString(", "); } else { first = false; } if (pref.getAutoCaptureForCmdButtons()) { btnCapture = new ButtonCapture(pane, line); pane.insertComponent(btnCapture); btnCapture.captureWithAutoDelay(); } else { if (pane.showThumbs && pref.getPrefMoreImageThumbs()) { pane.insertComponent(new ButtonCapture(pane, line)); } else { pane.insertComponent(new EditorPatternLabel(pane, "")); } } continue; } if (!p.startsWith("[")) { if (!first) { pane.insertString(", "); } if (p.startsWith("_")) { endPos = pane.getCaretPosition(); p = p.substring(1); } endPosLen = p.length(); pane.insertString(p); first = false; } } pane.insertString(")"); (new SikuliEditorKit.InsertBreakAction()).insertBreak(pane); if (endPos >= 0) { pane.requestFocus(); pane.setCaretPosition(endPos); pane.setSelectionStart(endPos); pane.setSelectionEnd(endPos + endPosLen); Debug.log(5, "sel: " + pane.getSelectedText()); } pane.requestFocus(); } @Override public void mouseClicked(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { setText(getRichRepresentation(HoverStyle, _cmd, _desc, _params, false)); } @Override public void mouseExited(MouseEvent e) { setText(getRichRepresentation(DefaultStyle, _cmd, _desc, _params, false)); } @Override public void mousePressed(MouseEvent e) { setText(getRichRepresentation(PressedStyle, _cmd, _desc, _params, false)); } @Override public void mouseReleased(MouseEvent e) { setText(getRichRepresentation(HoverStyle, _cmd, _desc, _params, false)); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/ButtonOnToolbar.java000077500000000000000000000014341315726130400250340ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import com.explodingpixels.macwidgets.plaf.UnifiedToolbarButtonUI; import javax.swing.BorderFactory; import javax.swing.JButton; public class ButtonOnToolbar extends JButton { public ButtonOnToolbar(){ setBorderPainted(false); putClientProperty("JButton.buttonType", "textured"); //setIconTextGap(8); /* setVerticalTextPosition(SwingConstants.BOTTOM); setHorizontalTextPosition(SwingConstants.CENTER); Font f = new Font(null, Font.PLAIN, 10); setFont(f); */ setUI(new UnifiedToolbarButtonUI()); //setMaximumSize(new Dimension(32,32)); setBorder(BorderFactory.createEmptyBorder(3,10,3,10)); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/CloseableModernTabbedPaneUI.java000077500000000000000000000277041315726130400271530ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicTabbedPaneUI; public class CloseableModernTabbedPaneUI extends BasicTabbedPaneUI { private static final String TABBED_PANE_UI_LOGGER = "TabbedPaneUI"; private static final Logger LOGGER = Logger.getLogger(TABBED_PANE_UI_LOGGER); private int TAB_WIDTH = 0; private static int TAB_HEIGHT = 24; private static BufferedImage tabSelectedPressedEnd; private static BufferedImage tabSelectedPressed; private static BufferedImage tabSelectedEnd; private static BufferedImage tabSelected; @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) private static BufferedImage tabClosePressed; @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) private static BufferedImage tabCloseRollover; @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) private static BufferedImage tabClose; private static BufferedImage tabRolloverEnd; private static BufferedImage tabRollover; private static BufferedImage tabEnd; private static BufferedImage tab; private int tabPressed = -1; private int width; static { try { tabSelectedPressedEnd = ImageIO.read( CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-aqua-highlight-sep.png")); tabSelectedPressed = ImageIO.read( CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-aqua-highlight.png")); tabSelectedEnd = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-aqua-sep.png")); tabSelected = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-aqua.png")); /* tabClosePressed = ImageIO.read(ModernTabbedPaneUI.class.getResource( "/icons/tab/tab-close-pressed.png")); tabCloseRollover = ImageIO.read( ModernTabbedPaneUI.class.getResource( "/icons/tab/tab-close-rollover.png")); tabClose = ImageIO.read(ModernTabbedPaneUI.class.getResource( "/icons/tab/tab-close.png")); */ tabRolloverEnd = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-normal-highlight-sep.png")); tabRollover = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-normal-highlight.png")); tabEnd = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-normal-sep.png")); tab = ImageIO.read(CloseableModernTabbedPaneUI.class.getResource( "/icons/tab/tab-normal.png")); } catch (IOException e) { LOGGER.warning("Could not load SliderUI images"); } } // TODO Paint & handle close buttons but on first tab public CloseableModernTabbedPaneUI(int width) { TAB_WIDTH = width; } @Override public void installUI(JComponent c) { JTabbedPane tabPane = (JTabbedPane) c; tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); // TODO Test on Windows, this is a Mac OS X workaround Constructor constructor = null; try { Class aClass = Class.forName( "javax.swing.plaf.basic.BasicTabbedPaneUI$Actions"); constructor = aClass.getDeclaredConstructor(String.class); constructor.setAccessible(true); } catch (ClassNotFoundException e) { getLogger().warning("Cannot access tabbed pane UI actions"); } catch (NoSuchMethodException e) { getLogger().warning("Constructor does not exist"); } if (constructor != null) { ActionMap map = tabPane.getActionMap(); try { map.put("scrollTabsBackwardAction", (Action) constructor.newInstance("scrollTabsBackwardAction")); map.put("scrollTabsForwardAction", (Action) constructor.newInstance("scrollTabsForwardAction")); } catch (InstantiationException e) { getLogger().warning("Cannot instantiate action"); } catch (IllegalAccessException e) { getLogger().warning("Action cannot be accessed"); } catch (InvocationTargetException e) { getLogger().warning("Cannot instantiate action"); } } super.installUI(c); } @Override protected void installDefaults() { UIManager.put("TabbedPane.tabAreaInsets", new Insets(0, 0, 0, 0)); UIManager.put("TabbedPane.font", ((Font) UIManager.get("TabbedPane.font")).deriveFont(Font.BOLD)); /* UIManager.put("TabbedPane.font", new Font("Thoma",Font.BOLD,12)); */ UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0)); UIManager.put("TabbedPane.selectedTabPadInsets", new Insets(0, 0, 0, 0)); super.installDefaults(); } @Override protected void installListeners() { super.installListeners(); tabPane.addMouseListener(new TabPressedTracker()); } @Override protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) { return TAB_HEIGHT; } @Override protected int calculateMaxTabHeight(int tabPlacement) { return TAB_HEIGHT; } /* @Override protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) { return TAB_WIDTH; } @Override protected int calculateMaxTabWidth(int tabPlacement) { return TAB_WIDTH; } */ @Override protected int getTabRunIndent(int tabPlacement, int run) { return 0; } @Override protected void setRolloverTab(int index) { int oldIndex = getRolloverTab(); super.setRolloverTab(index); if (oldIndex != index) { if (oldIndex != -1 && oldIndex < tabPane.getTabCount()) { tabPane.repaint(getTabBounds(tabPane, oldIndex)); } if (index != -1 && index < tabPane.getTabCount()) { tabPane.repaint(getTabBounds(tabPane, index)); } } } @Override protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) { return rects[tabIndex].width % 2; } @Override protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) { return 0; } @Override public void paint(Graphics g, JComponent c) { int tabPlacement = tabPane.getTabPlacement(); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Insets insets = c.getInsets(); Dimension size = c.getSize(); if (tabPane.getTabPlacement() == TOP) { g2d.drawImage(tab, insets.left, insets.top, size.width - insets.right - insets.left - 1, calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight), null); g2d.setColor(Color.gray); g2d.drawLine(size.width - 1, insets.top + 2, size.width - 1, insets.top + calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)); } /*System.out.println("Tab Height"+calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight));*/ super.paint(g2d, c); } @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { BufferedImage background; BufferedImage end; Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if (isSelected) { if (tabPressed == tabIndex) { background = tabSelectedPressed; end = tabSelectedPressedEnd; } else { background = tabSelected; end = tabSelectedEnd; } } else { if (getRolloverTab() == tabIndex) { background = tabRollover; end = tabRolloverEnd; } else { background = tab; end = tabEnd; } } if (x < 0) { x = 0; } if (y < 0) { y = 0; } g2d.drawImage(background, x + 1, y + 1, w - 1, TAB_HEIGHT, null); //g2d.drawLine(end.getWidth(), x + w- end.getWidth(),end.getWidth(),TAB_HEIGHT); //g2d.drawImage(end, x + w - end.getWidth()+1, TAB_HEIGHT, null); } private class TabPressedTracker extends MouseAdapter { @Override public void mousePressed(MouseEvent e) { if (tabPane == null || !tabPane.isEnabled()) { return; } tabPressed = tabForCoordinate(tabPane, e.getX(), e.getY()); if (tabPressed != -1) { tabPane.repaint(getTabBounds(tabPane, tabPressed)); } } @Override public void mouseReleased(MouseEvent e) { int oldTabPressed = tabPressed; tabPressed = -1; if (oldTabPressed != -1) { tabPane.repaint(getTabBounds(tabPane, oldTabPressed)); } } } // Methods below are overriden to get rid of the painting @Override protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected) { } @Override protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { } @Override protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { } private static Logger getLogger() { return LOGGER; } } class AquaCloseableTabbedPaneUI extends CloseableModernTabbedPaneUI { /** * the horizontal position of the text */ private int horizontalTextPosition = SwingUtilities.LEFT; /** * Creates a new instance of * CloseableTabbedPaneUI */ public AquaCloseableTabbedPaneUI() { super(100); } /** * Creates a new instance of * CloseableTabbedPaneUI * * @param horizontalTextPosition the horizontal position of the text (e.g. * SwingUtilities.TRAILING or SwingUtilities.LEFT) */ public AquaCloseableTabbedPaneUI(int horizontalTextPosition) { this(); this.horizontalTextPosition = horizontalTextPosition; } Color darkTabColor = new Color(200, 200, 200, 100); Color borderColor = new Color(150, 150, 150); @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { super.paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected); if (!isSelected) { g.setColor(darkTabColor); g.fillRect(x + 1, y + 2, w - 1, h - 1); } g.setColor(borderColor); g.drawLine(x + w, y + 2, x + w, y + h); g.drawLine(x, y + 2, x, y + h); if (isSelected) { g.setColor(Color.white); g.drawLine(x, y + h, x + w, y + h); } } /** * Layouts the label * * @param tabPlacement the placement of the tabs * @param metrics the font metrics * @param tabIndex the index of the tab * @param title the title of the tab * @param icon the icon of the tab * @param tabRect the tab boundaries * @param iconRect the icon boundaries * @param textRect the text boundaries * @param isSelected true whether the tab is selected, false otherwise */ @Override protected void layoutLabel(int tabPlacement, FontMetrics metrics, int tabIndex, String title, Icon icon, Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected) { textRect.x = textRect.y = iconRect.x = iconRect.y = 0; javax.swing.text.View v = getTextViewForTab(tabIndex); if (v != null) { tabPane.putClientProperty("html", v); } SwingUtilities.layoutCompoundLabel((JComponent) tabPane, metrics, title, icon, SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.CENTER, //SwingUtilities.TRAILING, horizontalTextPosition, tabRect, iconRect, textRect, textIconGap + 2); tabPane.putClientProperty("html", null); int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); iconRect.x += xNudge; iconRect.y += yNudge; textRect.x += xNudge; textRect.y += yNudge; } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/CloseableTabbedPane.java000077500000000000000000000507751315726130400255540ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.Color; import java.awt.Component; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JTabbedPane; import javax.swing.JViewport; import javax.swing.SwingUtilities; import javax.swing.event.EventListenerList; import javax.swing.plaf.basic.BasicTabbedPaneUI; import javax.swing.plaf.metal.MetalTabbedPaneUI; /** * A JTabbedPane which has a close ('X') icon on each tab. * * To add a tab, use the method addTab(String, Component) * * To have an extra icon on each tab (e.g. like in JBuilder, showing the file type) use the method * addTab(String, Component, Icon). Only clicking the 'X' closes the tab. */ public class CloseableTabbedPane extends JTabbedPane implements MouseListener, MouseMotionListener { /** * The * EventListenerList. */ private EventListenerList listenerList = null; /** * The viewport of the scrolled tabs. */ private JViewport headerViewport = null; /** * The normal closeicon. */ private Icon normalCloseIcon = SikuliIDE.getIconResource("/icons/close-normal.gif"); /** * The closeicon when the mouse is over. */ private Icon hooverCloseIcon = SikuliIDE.getIconResource("/icons/close-hover.gif"); /** * The closeicon when the mouse is pressed. */ private Icon pressedCloseIcon = SikuliIDE.getIconResource("/icons/close-pressed.gif"); private SikuliIDEPopUpMenu popMenuTab = null; private String lastClosed = null; public boolean isLastClosedByMove = false; /** * Creates a new instance of * CloseableTabbedPane */ public CloseableTabbedPane() { super(); init(SwingUtilities.LEFT); } /** * Creates a new instance of * CloseableTabbedPane * * @param horizontalTextPosition the horizontal position of the text (e.g. SwingUtilities.TRAILING * or SwingUtilities.LEFT) */ public CloseableTabbedPane(int horizontalTextPosition) { super(); init(horizontalTextPosition); } /** * Initializes the * CloseableTabbedPane * * @param horizontalTextPosition the horizontal position of the text (e.g. SwingUtilities.TRAILING * or SwingUtilities.LEFT) */ private void init(int horizontalTextPosition) { listenerList = new EventListenerList(); addMouseListener(this); addMouseMotionListener(this); //setUI(new AquaCloseableTabbedPaneUI(horizontalTextPosition)); if (getUI() instanceof MetalTabbedPaneUI) { setUI(new CloseableMetalTabbedPaneUI(horizontalTextPosition)); } /* else setUI(new CloseableTabbedPaneUI(horizontalTextPosition)); */ popMenuTab = new SikuliIDEPopUpMenu("POP_TAB", this); if (!popMenuTab.isValidMenu()) { popMenuTab = null; } } /** * Allows setting own closeicons. * * @param normal the normal closeicon * @param hoover the closeicon when the mouse is over * @param pressed the closeicon when the mouse is pressed */ public void setCloseIcons(Icon normal, Icon hoover, Icon pressed) { normalCloseIcon = normal; hooverCloseIcon = hoover; pressedCloseIcon = pressed; } /** * Adds a * Component represented by a title and no icon. * * @param title the title to be displayed in this tab * @param component the component to be displayed when this tab is clicked */ @Override public void addTab(String title, Component component) { addTab(title, component, null, -1); } public void addTab(String title, Component component, int position) { addTab(title, component, null, position); } /** * Adds a * Component represented by a title and an icon. * * @param title the title to be displayed in this tab * @param component the component to be displayed when this tab is clicked * @param extraIcon the icon to be displayed in this tab */ public void addTab(String title, Component component, Icon extraIcon, int position) { boolean doPaintCloseIcon = true; try { Object prop = null; if ((prop = ((JComponent) component). getClientProperty("isClosable")) != null) { doPaintCloseIcon = (Boolean) prop; } } catch (Exception ignored) {/*Could probably be a ClassCastException*/ } if (position < 0) { super.addTab(title, doPaintCloseIcon ? new CloseTabIcon(extraIcon) : null, component, "RightClick for actions"); } else { super.insertTab(title, doPaintCloseIcon ? new CloseTabIcon(extraIcon) : null, component, "RightClick for actions", position); } if (headerViewport == null) { for (Component c : getComponents()) { if ("TabbedPane.scrollableViewport".equals(c.getName())) { headerViewport = (JViewport) c; } } } } /** * Invoked when the mouse button has been clicked (pressed and released) on a component. * * @param e the MouseEvent */ @Override public void mouseClicked(MouseEvent e) { processMouseEvents(e); } /** * Invoked when the mouse enters a component. * * @param e the MouseEvent */ @Override public void mouseEntered(MouseEvent e) { } /** * Invoked when the mouse exits a component. * * @param e the MouseEvent */ @Override public void mouseExited(MouseEvent e) { for (int i = 0; i < getTabCount(); i++) { CloseTabIcon icon = (CloseTabIcon) getIconAt(i); if (icon != null) { icon.mouseover = false; } } repaint(); } /** * Invoked when a mouse button has been pressed on a component. * * @param e the MouseEvent */ @Override public void mousePressed(MouseEvent e) { processMouseEvents(e); } /** * Invoked when a mouse button has been released on a component. * * @param e the MouseEvent */ @Override public void mouseReleased(MouseEvent e) { processMouseEvents(e); } /** * Invoked when a mouse button is pressed on a component and then dragged. * MOUSE_DRAGGED events will continue to be delivered to the component where the drag * originated until the mouse button is released (regardless of whether the mouse position is * within the bounds of the component).
*
* Due to platform-dependent Drag&Drop implementations, * MOUSE_DRAGGED events may not be delivered during a native Drag&Drop operation. * * @param e the MouseEvent */ @Override public void mouseDragged(MouseEvent e) { processMouseEvents(e); } /** * Invoked when the mouse cursor has been moved onto a component but no buttons have been pushed. * * @param e the MouseEvent */ @Override public void mouseMoved(MouseEvent e) { processMouseEvents(e); } /** * Processes all caught * MouseEvents. * * @param e the MouseEvent */ private void processMouseEvents(MouseEvent e) { int tabNumber = getUI().tabForCoordinate(this, e.getX(), e.getY()); if (tabNumber < 0) { return; } //Debug.log("click tab: " + tabNumber + " cur: " + getSelectedIndex()); if (e.isPopupTrigger()) { if (popMenuTab != null) { popMenuTab.doShow(this, e); } return; } CloseTabIcon icon = (CloseTabIcon) getIconAt(tabNumber); if (icon != null) { Rectangle rect = icon.getBounds(); Point pos = headerViewport == null ? new Point() : headerViewport.getViewPosition(); Rectangle drawRect = new Rectangle( rect.x - pos.x, rect.y - pos.y, rect.width, rect.height); if (e.getID() == e.MOUSE_PRESSED) { icon.mousepressed = e.getModifiers() == e.BUTTON1_MASK; repaint(drawRect); } else if (e.getID() == e.MOUSE_MOVED || e.getID() == e.MOUSE_DRAGGED || e.getID() == e.MOUSE_CLICKED) { pos.x += e.getX(); pos.y += e.getY(); if (rect.contains(pos)) { if (e.getID() == e.MOUSE_CLICKED) { if (!fireCloseTab(e, tabNumber)) { icon.mouseover = false; icon.mousepressed = false; repaint(drawRect); } } else { icon.mouseover = true; icon.mousepressed = e.getModifiers() == e.BUTTON1_MASK; } } else { icon.mouseover = false; } repaint(drawRect); } } } /** * Adds an * CloseableTabbedPaneListener to the tabbedpane. * * @param l the CloseableTabbedPaneListener to be added */ public void addCloseableTabbedPaneListener(CloseableTabbedPaneListener l) { listenerList.add(CloseableTabbedPaneListener.class, l); } /** * Removes an * CloseableTabbedPaneListener from the tabbedpane. * * @param l the listener to be removed */ public void removeCloseableTabbedPaneListener(CloseableTabbedPaneListener l) { listenerList.remove(CloseableTabbedPaneListener.class, l); } /** * Returns an array of all the * SearchListeners added to this * SearchPane with addSearchListener(). * * @return all of the SearchListeners added or an empty array if no listeners have * been added */ public CloseableTabbedPaneListener[] getCloseableTabbedPaneListener() { return listenerList.getListeners(CloseableTabbedPaneListener.class); } /** * Notifies all listeners that have registered interest for notification on this event type. * * @param tabIndexToClose the index of the tab which should be closed * @return true if the tab can be closed, false otherwise */ protected boolean fireCloseTab(MouseEvent me, int tabIndexToClose) { boolean closeit = true; // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); for (Object i : listeners) { if (i instanceof CloseableTabbedPaneListener) { if (!((CloseableTabbedPaneListener) i).closeTab(tabIndexToClose)) { closeit = false; break; } } } if (closeit) { if (tabIndexToClose > 0) { Rectangle rec = getUI().getTabBounds(this, tabIndexToClose); MouseEvent event = new MouseEvent((Component) me.getSource(), me.getID() + 1, System.currentTimeMillis(), me.getModifiers(), rec.x, rec.y, me.getClickCount(), false, me.getButton()); dispatchEvent(event); } remove(tabIndexToClose); } return closeit; } public void setLastClosed(String bundle) { lastClosed = bundle; } public String getLastClosed() { return lastClosed; } public void resetLastClosed() { lastClosed = null; isLastClosedByMove = false; } /** * The class which generates the 'X' icon for the tabs. The constructor accepts an icon which is * extra to the 'X' icon, so you can have tabs like in JBuilder. This value is null if no extra * icon is required. */ class CloseTabIcon implements Icon { /** * the x position of the icon */ private int x_pos; /** * the y position of the icon */ private int y_pos; /** * the width the icon */ private int width; /** * the height the icon */ private int height; /** * the additional fileicon */ private Icon fileIcon; /** * true whether the mouse is over this icon, false otherwise */ private boolean mouseover = false; /** * true whether the mouse is pressed on this icon, false otherwise */ private boolean mousepressed = false; /** * Creates a new instance of * CloseTabIcon * * @param fileIcon the additional fileicon, if there is one set */ public CloseTabIcon(Icon fileIcon) { this.fileIcon = fileIcon; width = 16; height = 16; } /** * Draw the icon at the specified location. Icon implementations may use the Component argument * to get properties useful for painting, e.g. the foreground or background color. * * @param c the component which the icon belongs to * @param g the graphic object to draw on * @param x the upper left point of the icon in the x direction * @param y the upper left point of the icon in the y direction */ public void paintIcon(Component c, Graphics g, int x, int y) { boolean doPaintCloseIcon = true; try { // JComponent.putClientProperty("isClosable", new Boolean(false)); JTabbedPane tabbedpane = (JTabbedPane) c; int tabNumber = tabbedpane.getUI().tabForCoordinate(tabbedpane, x, y); JComponent curPanel = (JComponent) tabbedpane.getComponentAt(tabNumber); Object prop = null; if ((prop = curPanel.getClientProperty("isClosable")) != null) { doPaintCloseIcon = (Boolean) prop; } } catch (Exception ignored) {/*Could probably be a ClassCastException*/ } if (doPaintCloseIcon) { x_pos = x; y_pos = y; int y_p = y + 1; if (normalCloseIcon != null && !mouseover) { normalCloseIcon.paintIcon(c, g, x, y_p); } else if (hooverCloseIcon != null && mouseover && !mousepressed) { hooverCloseIcon.paintIcon(c, g, x, y_p); } else if (pressedCloseIcon != null && mousepressed) { pressedCloseIcon.paintIcon(c, g, x, y_p); } else { y_p++; Color col = g.getColor(); if (mousepressed && mouseover) { g.setColor(Color.WHITE); g.fillRect(x + 1, y_p, 12, 13); } g.setColor(Color.GRAY); /* g.drawLine(x+1, y_p, x+12, y_p); g.drawLine(x+1, y_p+13, x+12, y_p+13); g.drawLine(x, y_p+1, x, y_p+12); g.drawLine(x+13, y_p+1, x+13, y_p+12); g.drawLine(x+3, y_p+3, x+10, y_p+10); */ if (mouseover) { g.setColor(Color.RED); } g.drawLine(x + 3, y_p + 4, x + 9, y_p + 10); g.drawLine(x + 4, y_p + 3, x + 10, y_p + 9); g.drawLine(x + 10, y_p + 3, x + 3, y_p + 10); g.drawLine(x + 10, y_p + 4, x + 4, y_p + 10); g.drawLine(x + 9, y_p + 3, x + 3, y_p + 9); g.setColor(col); if (fileIcon != null) { fileIcon.paintIcon(c, g, x + width, y_p); } } } } /** * Returns the icon's width. * * @return an int specifying the fixed width of the icon. */ public int getIconWidth() { return width + (fileIcon != null ? fileIcon.getIconWidth() : 0); } /** * Returns the icon's height. * * @return an int specifying the fixed height of the icon. */ public int getIconHeight() { return height; } /** * Gets the bounds of this icon in the form of a * Rectangle * object. The bounds specify this icon's width, height, and location * relative to its parent. * * @return a rectangle indicating this icon's bounds */ public Rectangle getBounds() { return new Rectangle(x_pos, y_pos, width, height); } } /** * A specific * BasicTabbedPaneUI. */ class CloseableTabbedPaneUI extends BasicTabbedPaneUI { /** * the horizontal position of the text */ private int horizontalTextPosition = SwingUtilities.LEFT; /** * Creates a new instance of * CloseableTabbedPaneUI */ public CloseableTabbedPaneUI() { } /** * Creates a new instance of * CloseableTabbedPaneUI * * @param horizontalTextPosition the horizontal position of the text (e.g. * SwingUtilities.TRAILING or SwingUtilities.LEFT) */ public CloseableTabbedPaneUI(int horizontalTextPosition) { this.horizontalTextPosition = horizontalTextPosition; } Color darkTabColor = new Color(200, 200, 200); @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { super.paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected); if (!isSelected) { g.setColor(darkTabColor); g.fillRect(x + 1, y + 1, w - 1, h - 1); } } /** * Layouts the label * * @param tabPlacement the placement of the tabs * @param metrics the font metrics * @param tabIndex the index of the tab * @param title the title of the tab * @param icon the icon of the tab * @param tabRect the tab boundaries * @param iconRect the icon boundaries * @param textRect the text boundaries * @param isSelected true whether the tab is selected, false otherwise */ @Override protected void layoutLabel(int tabPlacement, FontMetrics metrics, int tabIndex, String title, Icon icon, Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected) { textRect.x = textRect.y = iconRect.x = iconRect.y = 0; javax.swing.text.View v = getTextViewForTab(tabIndex); if (v != null) { tabPane.putClientProperty("html", v); } SwingUtilities.layoutCompoundLabel((JComponent) tabPane, metrics, title, icon, SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.CENTER, //SwingUtilities.TRAILING, horizontalTextPosition, tabRect, iconRect, textRect, textIconGap + 2); tabPane.putClientProperty("html", null); int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); iconRect.x += xNudge; iconRect.y += yNudge; textRect.x += xNudge; textRect.y += yNudge; } } /** * A specific * MetalTabbedPaneUI. */ class CloseableMetalTabbedPaneUI extends MetalTabbedPaneUI { /** * the horizontal position of the text */ private int horizontalTextPosition = SwingUtilities.LEFT; /** * Creates a new instance of * CloseableMetalTabbedPaneUI */ public CloseableMetalTabbedPaneUI() { } /** * Creates a new instance of * CloseableMetalTabbedPaneUI * * @param horizontalTextPosition the horizontal position of the text (e.g. * SwingUtilities.TRAILING or SwingUtilities.LEFT) */ public CloseableMetalTabbedPaneUI(int horizontalTextPosition) { this.horizontalTextPosition = horizontalTextPosition; } /** * Layouts the label * * @param tabPlacement the placement of the tabs * @param metrics the font metrics * @param tabIndex the index of the tab * @param title the title of the tab * @param icon the icon of the tab * @param tabRect the tab boundaries * @param iconRect the icon boundaries * @param textRect the text boundaries * @param isSelected true whether the tab is selected, false otherwise */ @Override protected void layoutLabel(int tabPlacement, FontMetrics metrics, int tabIndex, String title, Icon icon, Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected) { textRect.x = textRect.y = iconRect.x = iconRect.y = 0; javax.swing.text.View v = getTextViewForTab(tabIndex); if (v != null) { tabPane.putClientProperty("html", v); } SwingUtilities.layoutCompoundLabel((JComponent) tabPane, metrics, title, icon, SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.CENTER, //SwingUtilities.TRAILING, horizontalTextPosition, tabRect, iconRect, textRect, textIconGap + 2); tabPane.putClientProperty("html", null); int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); iconRect.x += xNudge; iconRect.y += yNudge; textRect.x += xNudge; textRect.y += yNudge; } } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/CloseableTabbedPaneListener.java000077500000000000000000000011471315726130400272470ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.util.EventListener; /** * The listener that's notified when an tab should be closed in the * CloseableTabbedPane. */ public interface CloseableTabbedPaneListener extends EventListener { /** * Informs all CloseableTabbedPaneListeners when a tab should be * closed * @param tabIndexToClose the index of the tab which should be closed * @return true if the tab can be closed, false otherwise */ boolean closeTab(int tabIndexToClose); } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorConsolePane.java000066400000000000000000000203411315726130400253110ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; // // A simple Java Console for your application (Swing version) // Requires Java 1.1.5 or higher // // Disclaimer the use of this source is at your own risk. // // Permision to use and distribute into your own applications // // RJHM van den Bergh , rvdb@comweb.nl import org.sikuli.basics.PreferencesUser; import java.awt.*; import java.awt.event.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.io.*; import java.util.Arrays; import java.util.regex.*; import javax.swing.*; import javax.swing.text.*; import javax.swing.text.html.*; import javax.swing.JMenuItem; import org.sikuli.basics.Debug; import org.sikuli.scriptrunner.IScriptRunner; import org.sikuli.basics.Settings; import org.sikuli.scriptrunner.ScriptingSupport; public class EditorConsolePane extends JPanel implements Runnable { private static final String me = "EditorConsolePane: "; static boolean ENABLE_IO_REDIRECT = true; static { String flag = System.getProperty("sikuli.console"); if (flag != null && flag.equals("false")) { ENABLE_IO_REDIRECT = false; } } private int NUM_PIPES; private JTextPane textArea; private Thread[] reader; private boolean quit; private PipedInputStream[] pin; private JPopupMenu popup; Thread errorThrower; // just for testing (Throws an Exception at this Console) class PopupListener extends MouseAdapter { JPopupMenu popup; PopupListener(JPopupMenu popupMenu) { popup = popupMenu; } public void mousePressed(MouseEvent e) { maybeShowPopup(e); } public void mouseReleased(MouseEvent e) { maybeShowPopup(e); } private void maybeShowPopup(MouseEvent e) { if (e.isPopupTrigger()) { popup.show(e.getComponent(), e.getX(), e.getY()); } } } public EditorConsolePane() { super(); textArea = new JTextPane(); textArea.setEditorKit(new HTMLEditorKit()); textArea.setTransferHandler(new JTextPaneHTMLTransferHandler()); String css = PreferencesUser.getInstance().getConsoleCSS(); ((HTMLEditorKit) textArea.getEditorKit()).getStyleSheet().addRule(css); textArea.setEditable(false); setLayout(new BorderLayout()); add(new JScrollPane(textArea), BorderLayout.CENTER); if (ENABLE_IO_REDIRECT) { Debug.log(3, "EditorConsolePane: starting redirection to message area"); int npipes = 2; NUM_PIPES = npipes * ScriptingSupport.scriptRunner.size(); pin = new PipedInputStream[NUM_PIPES]; reader = new Thread[NUM_PIPES]; for (int i = 0; i < NUM_PIPES; i++) { pin[i] = new PipedInputStream(); } int irunner = 0; for (IScriptRunner srunner : ScriptingSupport.scriptRunner.values()) { Debug.log(3, "EditorConsolePane: redirection for %s", srunner.getName()); if (srunner.doSomethingSpecial("redirect", Arrays.copyOfRange(pin, irunner*npipes, irunner*npipes+2))) { Debug.log(3, "EditorConsolePane: redirection success for %s", srunner.getName()); quit = false; // signals the Threads that they should exit //TODO Hack to avoid repeated redirect of stdout/err ScriptingSupport.systemRedirected = true; // Starting two seperate threads to read from the PipedInputStreams for (int i = irunner * npipes; i < irunner * npipes + npipes; i++) { reader[i] = new Thread(this); reader[i].setDaemon(true); reader[i].start(); } irunner++; } } } //Create the popup menu. popup = new JPopupMenu(); JMenuItem menuItem = new JMenuItem("Clear messages"); // Add ActionListener that clears the textArea menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { textArea.setText(""); } }); popup.add(menuItem); //Add listener to components that can bring up popup menus. MouseListener popupListener = new PopupListener(popup); textArea.addMouseListener(popupListener); } private void appendMsg(String msg) { HTMLDocument doc = (HTMLDocument) textArea.getDocument(); HTMLEditorKit kit = (HTMLEditorKit) textArea.getEditorKit(); try { kit.insertHTML(doc, doc.getLength(), msg, 0, 0, null); } catch (Exception e) { Debug.error(me + "Problem appending text to message area!\n%s", e.getMessage()); } } /* public synchronized void windowClosed(WindowEvent evt) { quit=true; this.notifyAll(); // stop all threads try { reader.join(1000);pin.close(); } catch (Exception e){} try { reader2.join(1000);pin2.close(); } catch (Exception e){} System.exit(0); } public synchronized void windowClosing(WindowEvent evt) { frame.setVisible(false); // default behaviour of JFrame frame.dispose(); } */ static final String lineSep = System.getProperty("line.separator"); private String htmlize(String msg) { StringBuilder sb = new StringBuilder(); Pattern patMsgCat = Pattern.compile("\\[(.+?)\\].*"); msg = msg.replace("&", "&").replace("<", "<").replace(">",">"); for (String line : msg.split(lineSep)) { Matcher m = patMsgCat.matcher(line); String cls = "normal"; if (m.matches()) { cls = m.group(1); } line = "" + line + ""; sb.append(line).append("
"); } return sb.toString(); } @Override public synchronized void run() { try { for (int i = 0; i < NUM_PIPES; i++) { while (Thread.currentThread() == reader[i]) { try { this.wait(100); } catch (InterruptedException ie) { } if (pin[i].available() != 0) { String input = this.readLine(pin[i]); appendMsg(htmlize(input)); if (textArea.getDocument().getLength() > 0) { textArea.setCaretPosition(textArea.getDocument().getLength() - 1); } } if (quit) { return; } } } } catch (Exception e) { Debug.error(me + "Console reports an internal error:\n%s", e.getMessage()); } } public synchronized String readLine(PipedInputStream in) throws IOException { String input = ""; do { int available = in.available(); if (available == 0) { break; } byte b[] = new byte[available]; in.read(b); input = input + new String(b, 0, b.length); } while (!input.endsWith("\n") && !input.endsWith("\r\n") && !quit); return input; } public void clear() { textArea.setText(""); } } class JTextPaneHTMLTransferHandler extends TransferHandler { private static final String me = "EditorConsolePane: "; public JTextPaneHTMLTransferHandler() { } @Override public void exportToClipboard(JComponent comp, Clipboard clip, int action) { super.exportToClipboard(comp, clip, action); } @Override public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } @Override protected Transferable createTransferable(JComponent c) { JTextPane aTextPane = (JTextPane) c; HTMLEditorKit kit = ((HTMLEditorKit) aTextPane.getEditorKit()); StyledDocument sdoc = aTextPane.getStyledDocument(); int sel_start = aTextPane.getSelectionStart(); int sel_end = aTextPane.getSelectionEnd(); int i = sel_start; StringBuilder output = new StringBuilder(); while (i < sel_end) { Element e = sdoc.getCharacterElement(i); Object nameAttr = e.getAttributes().getAttribute(StyleConstants.NameAttribute); int start = e.getStartOffset(), end = e.getEndOffset(); if (nameAttr == HTML.Tag.BR) { output.append("\n"); } else if (nameAttr == HTML.Tag.CONTENT) { if (start < sel_start) { start = sel_start; } if (end > sel_end) { end = sel_end; } try { String str = sdoc.getText(start, end - start); output.append(str); } catch (BadLocationException ble) { Debug.error(me + "Copy-paste problem!\n%s", ble.getMessage()); } } i = end; } return new StringSelection(output.toString()); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorCurrentLineHighlighter.java000077500000000000000000000074541315726130400275310ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import javax.swing.JTextPane; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.text.*; import org.sikuli.basics.Debug; public class EditorCurrentLineHighlighter implements CaretListener { private static final String me = "EditorCurrentLineHighlighter: "; static final Color DEFAULT_COLOR = new Color(230, 230, 210); static final Color ERROR_COLOR = new Color(255, 105, 105); private Highlighter.HighlightPainter painter; private Object highlight = null; public EditorCurrentLineHighlighter(JTextPane textPane) { this(textPane, null); } public EditorCurrentLineHighlighter(JTextPane textPane, Color highlightColor) { Color c = highlightColor != null ? highlightColor : DEFAULT_COLOR; MyHighlighter h = new MyHighlighter(); textPane.setHighlighter(h); painter = new DefaultHighlighter.DefaultHighlightPainter(c); } @Override public void caretUpdate(CaretEvent evt) { JTextComponent comp = (JTextComponent) evt.getSource(); if (comp != null) { if (comp.getSelectionStart() != comp.getSelectionEnd()) { // cancel line highlighting if selection exists removeLineHighlight(comp); comp.repaint(); return; } int pos = comp.getCaretPosition(); Element elem = Utilities.getParagraphElement(comp, pos); int start = elem.getStartOffset(); int end = elem.getEndOffset(); Document doc = comp.getDocument(); Element root = doc.getDefaultRootElement(); int line = root.getElementIndex(pos); Debug.log(5, "LineHighlight: Caret at " + pos + " line " + line + " for " + start + "-" + end); if (SikuliIDE.getStatusbar() != null) { SikuliIDE.getStatusbar().setCaretPosition(line + 1, pos - start + 1); } removeLineHighlight(comp); try { highlight = comp.getHighlighter().addHighlight(start, end, painter); comp.repaint(); } catch (BadLocationException ex) { Debug.error(me + "Problem while highlighting line %d\n%s", pos, ex.getMessage()); } } } private void removeLineHighlight(JTextComponent comp) { if (highlight != null) { comp.getHighlighter().removeHighlight(highlight); highlight = null; } } } // class MyHighlighter extends DefaultHighlighter { private JTextComponent component; private Rectangle a = null; @Override public final void install(final JTextComponent c) { super.install(c); this.component = c; } @Override public final void deinstall(final JTextComponent c) { super.deinstall(c); this.component = null; } @Override public final void paint(final Graphics g) { final Highlighter.Highlight[] highlights = getHighlights(); final int len = highlights.length; if (len == 0) { return; } if (highlights[0].getClass().getName().indexOf("LayeredHighlightInfo") > -1) { Debug.log(6, "LineHighlight: painting enter for " + len); a = this.component.getBounds(); final Insets insets = this.component.getInsets(); a.x = insets.left; Debug.log(6, "LineHighlight: painting 0"); highlights[0].getPainter().paint(g, highlights[0].getStartOffset(), highlights[0].getEndOffset(), a, this.component); } for (int i = 1; i < len; i++) { if (highlights[i].getClass().getName().indexOf("LayeredHighlightInfo") > -1) { Debug.log(6, "LineHighlight: painting " + i); highlights[i].getPainter().paint(g, highlights[i].getStartOffset(), highlights[i].getEndOffset(), a, this.component); } } } } // sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorLineNumberView.java000077500000000000000000000230371315726130400260060ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import java.awt.event.*; import java.beans.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; import org.sikuli.basics.Debug; public class EditorLineNumberView extends JComponent implements MouseListener { private static ImageIcon ERROR_ICON = SikuliIDE.getIconResource("/icons/error_icon.gif"); // This is for the border to the right of the line numbers. // There's probably a UIDefaults value that could be used for this. private static final Color BORDER_COLOR = new Color(155, 155, 155); private static Color FG_COLOR = Color.GRAY; private static Color BG_COLOR = new Color(241, 241, 241); private static Color selBG_COLOR = new Color(220, 220, 220); private static final int WIDTH_TEMPLATE = 999; private static final int MARGIN = 5; private FontMetrics viewFontMetrics; private int maxNumberWidth; private int componentWidth; private int textTopInset; private int textFontAscent; private int textFontHeight; private EditorPane text; private SizeSequence sizes; private int startLine = 0; private boolean structureChanged = true; private Set errLines = new HashSet(); private int line; private SikuliIDEPopUpMenu popMenuLineNumber = null; private boolean wasPopup = false; public EditorLineNumberView(JTextComponent text) { /** * Construct a LineNumberView and attach it to the given text component. The LineNumberView will * listen for certain kinds of events from the text component and update itself accordingly. */ if (text == null) { throw new IllegalArgumentException("Text component required! Cannot be null!"); } this.text = (EditorPane) text; updateCachedMetrics(); UpdateHandler handler = new UpdateHandler(); text.getDocument().addDocumentListener(handler); text.addPropertyChangeListener(handler); text.addComponentListener(handler); setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, BORDER_COLOR)); setForeground(FG_COLOR); setBackground(BG_COLOR); init(); } private void init() { addMouseListener(this); setToolTipText("RightClick for options - left to select the line"); popMenuLineNumber = new SikuliIDEPopUpMenu("POP_LINE", this); if (!popMenuLineNumber.isValidMenu()) { popMenuLineNumber = null; } } @Override public Dimension getPreferredSize() { return new Dimension(componentWidth, text.getHeight()); } @Override public void setFont(Font font) { super.setFont(font); updateCachedMetrics(); } private void updateCachedMetrics() { // Cache some values that are used a lot in painting or size calculations. Font textFont = text.getFont(); FontMetrics fm = getFontMetrics(textFont); textFontHeight = fm.getHeight(); textFontAscent = fm.getAscent(); textTopInset = text.getInsets().top; Font viewFont = getFont(); boolean changed = false; if (viewFont == null) { viewFont = UIManager.getFont("Label.font"); viewFont = viewFont.deriveFont(Font.PLAIN); changed = true; } if (viewFont.getSize() > textFont.getSize()) { viewFont = viewFont.deriveFont(textFont.getSize2D()); changed = true; } viewFontMetrics = getFontMetrics(viewFont); maxNumberWidth = viewFontMetrics.stringWidth(String.valueOf(WIDTH_TEMPLATE)); componentWidth = 2 * MARGIN + maxNumberWidth; if (changed) { super.setFont(viewFont); } } @Override public void paintComponent(Graphics g) { updateSizes(); Rectangle clip = g.getClipBounds(); g.setColor(getBackground()); g.fillRect(clip.x, clip.y, clip.width, clip.height); if (sizes == null) { return; } // draw line numbers g.setColor(getForeground()); int base = clip.y - textTopInset; int first = sizes.getIndex(base); int last = sizes.getIndex(base + clip.height); String lnum; lnum = ""; for (int i = first; i < last; i++) { lnum = String.valueOf(i + 1); int x = MARGIN + maxNumberWidth - viewFontMetrics.stringWidth(lnum); int y = (sizes.getPosition(i) + sizes.getPosition(i + 1)) / 2 + textFontAscent / 2 + textTopInset / 2; if (errLines.contains(i + 1)) { final int h = 12; g.drawImage(ERROR_ICON.getImage(), 0, y - h + 1, h, h, null); g.setColor(Color.RED); } else { g.setColor(getForeground()); } g.drawString(lnum, x, y); } } private void updateSizes() { // Update the line heights as needed. if (startLine < 0) { return; } if (structureChanged) { int count = getAdjustedLineCount(); sizes = new SizeSequence(count); for (int i = 0; i < count; i++) { sizes.setSize(i, getLineHeight(i)); } structureChanged = false; } else { if (sizes != null) { sizes.setSize(startLine, getLineHeight(startLine)); } } startLine = -1; } private int getAdjustedLineCount() { // There is an implicit break being modeled at the end of the // document to deal with boundary conditions at the end. This // is not desired in the line count, so we detect it and remove // its effect if throwing off the count. Element root = text.getDocument().getDefaultRootElement(); int n = root.getElementCount(); Element lastLine = root.getElement(n - 1); if ((lastLine.getEndOffset() - lastLine.getStartOffset()) >= 1) { return n; } return n - 1; } private int getLineHeight(int index) { // Get the height of a line from the JTextComponent. Element e; int lastPos = sizes.getPosition(index) + textTopInset; Element l = text.getDocument().getDefaultRootElement().getElement(index); Rectangle r = null; Rectangle r1; int h = textFontHeight; int max_h = 0; try { if (l.getElementCount() < 2) { r = text.modelToView(l.getEndOffset() - 1); } else { for (int i = 0; i < l.getElementCount(); i++) { e = l.getElement(i); if ("component".equals(e.getName())) { r1 = text.modelToView(e.getStartOffset()); if (max_h < r1.height) { max_h = r1.height; r = r1; } } } } if (r == null) { r = text.modelToView(l.getEndOffset() - 1); } h = (r.y - lastPos) + r.height; } catch (Exception ex) { } return h; } public void addErrorMark(int line) { errLines.add(line); } public void resetErrorMark() { errLines.clear(); } // @Override public void mouseEntered(MouseEvent me) { setBackground(selBG_COLOR); } @Override public void mouseExited(MouseEvent me) { setBackground(BG_COLOR); } @Override public void mouseClicked(MouseEvent me) { if (wasPopup) { wasPopup = false; return; } ((EditorPane) text).jumpTo(sizes.getIndex(me.getY()) + 1); if (me.getClickCount() == 2) { ((EditorPane) text).getDocument(); } } @Override public void mousePressed(MouseEvent me) { checkPopup(me); } @Override public void mouseReleased(MouseEvent me) { checkPopup(me); } private void checkPopup(MouseEvent me) { if (me.isPopupTrigger()) { if (popMenuLineNumber != null) { wasPopup = true; popMenuLineNumber.show(this, me.getX(), me.getY()); } return; } } // // private void viewChanged(int startLine, boolean structureChanged) { // Schedule a repaint because one or more line heights may have changed. // triggered by UpdateHandler this.startLine = startLine; this.structureChanged |= structureChanged; revalidate(); repaint(); } class UpdateHandler extends ComponentAdapter implements PropertyChangeListener, DocumentListener { @Override public void componentResized(ComponentEvent evt) { // all lines invalidated viewChanged(0, true); } @Override public void propertyChange(PropertyChangeEvent evt) { // a doc prop changed - invalidate all lines Object oldValue = evt.getOldValue(); Object newValue = evt.getNewValue(); String propertyName = evt.getPropertyName(); if ("document".equals(propertyName)) { if (oldValue != null && oldValue instanceof Document) { ((Document) oldValue).removeDocumentListener(this); } if (newValue != null && newValue instanceof Document) { ((Document) newValue).addDocumentListener(this); } } updateCachedMetrics(); viewChanged(0, true); } @Override public void insertUpdate(DocumentEvent evt) { update(evt, "insert"); // Text was inserted into the document. } @Override public void removeUpdate(DocumentEvent evt) { update(evt, "remove"); //Text was removed from the document. } @Override public void changedUpdate(DocumentEvent evt) { // update(evt); //done by insert / remove already } private void update(DocumentEvent evt, String msg) { // invalidate one or all lines Element map = text.getDocument().getDefaultRootElement(); int line = map.getElementIndex(evt.getOffset()); DocumentEvent.ElementChange ec = evt.getChange(map); Debug.log(6, "LineNumbers: " + msg + " update - struct changed: " + (ec != null)); viewChanged(line, ec != null); } } // } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorMyDocument.java000077500000000000000000000011241315726130400251700ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import org.sikuli.basics.Debug; class EditorMyDocument extends DefaultStyledDocument { @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { str = str.replaceAll("\t", " "); super.insertString(offs, str, a); Debug.log(5, "insertString: " + str); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorPane.java000077500000000000000000001316731315726130400240040ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import org.sikuli.basics.PreferencesUser; import java.awt.*; import java.awt.datatransfer.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.util.List; import java.util.prefs.PreferenceChangeEvent; import java.util.prefs.PreferenceChangeListener; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.*; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.*; import java.nio.charset.Charset; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.sikuli.basics.Settings; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.idesupport.IIndentationLogic; import org.sikuli.script.Location; import org.sikuli.script.Image; import org.sikuli.script.ImagePath; import org.sikuli.script.Runner; import org.sikuli.script.Sikulix; import org.sikuli.scriptrunner.IScriptRunner; import org.sikuli.scriptrunner.ScriptingSupport; import org.sikuli.syntaxhighlight.ResolutionException; import org.sikuli.syntaxhighlight.grammar.Lexer; import org.sikuli.syntaxhighlight.grammar.Token; import org.sikuli.syntaxhighlight.grammar.TokenType; import org.sikuli.util.SikulixFileChooser; public class EditorPane extends JTextPane implements KeyListener, CaretListener { private static final String me = "EditorPane: "; private static final int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private static TransferHandler transferHandler = null; private static final Map lexers = new HashMap(); private PreferencesUser pref; private File scriptSource = null; private File scriptParent = null; private File scriptImages = null; private String scriptType = null; private boolean scriptIsTemp = false; private boolean scriptIsDirty = false; private DirtyHandler dirtyHandler; private File _editingFile; // private String scriptType = null; private String _srcBundlePath = null; private boolean _srcBundleTemp = false; private String sikuliContentType; private EditorCurrentLineHighlighter _highlighter = null; private EditorUndoManager _undo = null; // TODO: move to SikuliDocument ???? private IIndentationLogic _indentationLogic = null; private boolean hasErrorHighlight = false; public boolean showThumbs; static Pattern patPngStr = Pattern.compile("(\"[^\"]+?\\.(?i)(png|jpg|jpeg)\")"); static Pattern patCaptureBtn = Pattern.compile("(\"__CLICK-TO-CAPTURE__\")"); static Pattern patPatternStr = Pattern.compile( "\\b(Pattern\\s*\\(\".*?\"\\)(\\.\\w+\\([^)]*\\))+)"); static Pattern patRegionStr = Pattern.compile( "\\b(Region\\s*\\((-?[\\d\\s],?)+\\))"); static Pattern patLocationStr = Pattern.compile( "\\b(Location\\s*\\((-?[\\d\\s],?)+\\))"); //TODO what is it for??? private int _caret_last_x = -1; private boolean _can_update_caret_last_x = true; private SikuliIDEPopUpMenu popMenuImage; private SikuliEditorKit editorKit; private EditorViewFactory editorViewFactory; private SikuliIDE sikuliIDE = null; private int caretPosition = -1; // public EditorPane(SikuliIDE ide) { pref = PreferencesUser.getInstance(); showThumbs = !pref.getPrefMorePlainText(); sikuliIDE = ide; log(lvl, "EditorPane: creating new pane (constructor)"); } public void saveCaretPosition() { caretPosition = getCaretPosition(); } public void restoreCaretPosition() { if (caretPosition < 0) { return; } if (caretPosition < getDocument().getLength()) { setCaretPosition(caretPosition); } else { setCaretPosition(getDocument().getLength() - 1); } caretPosition = -1; } public void initBeforeLoad(String scriptType) { initBeforeLoad(scriptType, false); } public void reInit(String scriptType) { initBeforeLoad(scriptType, true); } private void initBeforeLoad(String scriptType, boolean reInit) { String scrType = null; boolean paneIsEmpty = false; log(lvl, "initBeforeLoad: %s", scriptType); if (scriptType == null) { scriptType = Runner.EDEFAULT; paneIsEmpty = true; } if (Runner.EPYTHON.equals(scriptType)) { scrType = Runner.CPYTHON; _indentationLogic = SikuliIDE.getIDESupport(scriptType).getIndentationLogic(); _indentationLogic.setTabWidth(pref.getTabWidth()); } else if (Runner.ERUBY.equals(scriptType)) { scrType = Runner.CRUBY; _indentationLogic = null; } //TODO should know, that scripttype not changed here to avoid unnecessary new setups if (scrType != null) { sikuliContentType = scrType; editorKit = new SikuliEditorKit(); editorViewFactory = (EditorViewFactory) editorKit.getViewFactory(); setEditorKit(editorKit); setContentType(scrType); if (_indentationLogic != null) { pref.addPreferenceChangeListener(new PreferenceChangeListener() { @Override public void preferenceChange(PreferenceChangeEvent event) { if (event.getKey().equals("TAB_WIDTH")) { _indentationLogic.setTabWidth(Integer.parseInt(event.getNewValue())); } } }); } } if (transferHandler == null) { transferHandler = new MyTransferHandler(); } setTransferHandler(transferHandler); if (_highlighter == null) { _highlighter = new EditorCurrentLineHighlighter(this); addCaretListener(_highlighter); initKeyMap(); addKeyListener(this); addCaretListener(this); } setFont(new Font(pref.getFontName(), Font.PLAIN, pref.getFontSize())); setMargin(new Insets(3, 3, 3, 3)); setBackground(Color.WHITE); if (!Settings.isMac()) { setSelectionColor(new Color(170, 200, 255)); } updateDocumentListeners("initBeforeLoad"); popMenuImage = new SikuliIDEPopUpMenu("POP_IMAGE", this); if (!popMenuImage.isValidMenu()) { popMenuImage = null; } if (paneIsEmpty || reInit) { // this.setText(String.format(Settings.TypeCommentDefault, getSikuliContentType())); this.setText(""); } SikuliIDE.getStatusbar().setCurrentContentType(getSikuliContentType()); log(lvl, "InitTab: (%s)", getSikuliContentType()); if (!ScriptingSupport.hasTypeRunner(getSikuliContentType())) { Sikulix.popup("No installed runner supports (" + getSikuliContentType() + ")\n" + "Trying to run the script will crash IDE!", "... serious problem detected!"); } } public String getSikuliContentType() { return sikuliContentType; } public SikuliIDEPopUpMenu getPopMenuImage() { return popMenuImage; } private void updateDocumentListeners(String source) { log(lvl, "updateDocumentListeners from: %s", source); if (dirtyHandler == null) { dirtyHandler = new DirtyHandler(); } getDocument().addDocumentListener(dirtyHandler); getDocument().addUndoableEditListener(getUndoManager()); } public EditorUndoManager getUndoManager() { if (_undo == null) { _undo = new EditorUndoManager(); } return _undo; } public IIndentationLogic getIndentationLogic() { return _indentationLogic; } private void initKeyMap() { InputMap map = this.getInputMap(); int shift = InputEvent.SHIFT_MASK; int ctrl = InputEvent.CTRL_MASK; map.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, shift), SikuliEditorKit.deIndentAction); map.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, ctrl), SikuliEditorKit.deIndentAction); } @Override public void keyPressed(java.awt.event.KeyEvent ke) { } @Override public void keyReleased(java.awt.event.KeyEvent ke) { } @Override public void keyTyped(java.awt.event.KeyEvent ke) { //TODO implement code completion * checkCompletion(ke); } // public String loadFile(boolean accessingAsFile) throws IOException { File file = new SikulixFileChooser(sikuliIDE, accessingAsFile).load(); if (file == null) { return null; } String fname = FileManager.slashify(file.getAbsolutePath(), false); int i = sikuliIDE.isAlreadyOpen(fname); if (i > -1) { log(lvl, "loadFile: Already open in IDE: " + fname); return null; } loadFile(fname); if (_editingFile == null) { return null; } return _editingFile.getParent(); } public void loadFile(String filename) { log(lvl, "loadfile: %s", filename); filename = FileManager.slashify(filename, false); File script = new File(filename); _editingFile = Runner.getScriptFile(script); if (_editingFile != null) { setSrcBundle(FileManager.slashify(_editingFile.getParent(), true)); scriptType = _editingFile.getAbsolutePath().substring(_editingFile.getAbsolutePath().lastIndexOf(".") + 1); initBeforeLoad(scriptType); if (!readScript(_editingFile)) { _editingFile = null; } updateDocumentListeners("loadFile"); setDirty(false); } if (_editingFile == null) { _srcBundlePath = null; } else { _srcBundleTemp = false; } } private boolean readScript(Object script) { InputStreamReader isr; try { if (script instanceof String) { isr = new InputStreamReader( new ByteArrayInputStream(((String) script).getBytes(Charset.forName("utf-8"))), Charset.forName("utf-8")); } else if (script instanceof File) { isr = new InputStreamReader( new FileInputStream((File) script), Charset.forName("utf-8")); } else { log(-1, "readScript: not supported %s as %s", script, script.getClass()); return false; } this.read(new BufferedReader(isr), null); } catch (Exception ex) { log(-1, "read returned %s", ex.getMessage()); return false; } return true; } @Override public void read(Reader in, Object desc) throws IOException { super.read(in, desc); Document doc = getDocument(); Element root = doc.getDefaultRootElement(); parse(root); restoreCaretPosition(); } public boolean hasEditingFile() { return _editingFile != null; } public String saveFile() throws IOException { if (_editingFile == null) { return saveAsFile(Settings.isMac()); } else { writeSrcFile(); return getCurrentShortFilename(); } } public String saveAsFile(boolean accessingAsFile) throws IOException { File file = new SikulixFileChooser(sikuliIDE, accessingAsFile).save(); if (file == null) { return null; } String bundlePath = FileManager.slashify(file.getAbsolutePath(), false); if (!file.getAbsolutePath().endsWith(".sikuli")) { bundlePath += ".sikuli"; } if (FileManager.exists(bundlePath)) { int res = JOptionPane.showConfirmDialog( null, SikuliIDEI18N._I("msgFileExists", bundlePath), SikuliIDEI18N._I("dlgFileExists"), JOptionPane.YES_NO_OPTION); if (res != JOptionPane.YES_OPTION) { return null; } FileManager.deleteFileOrFolder(bundlePath); } FileManager.mkdir(bundlePath); try { saveAsBundle(bundlePath, (sikuliIDE.getCurrentFileTabTitle())); if (Settings.isMac()) { if (!Settings.handlesMacBundles) { makeBundle(bundlePath, accessingAsFile); } } } catch (IOException iOException) { } return getCurrentShortFilename(); } private void makeBundle(String path, boolean asFile) { String isBundle = asFile ? "B" : "b"; String result = Sikulix.run(new String[]{"#SetFile", "-a", isBundle, path}); if (!result.isEmpty()) { log(-1, "makeBundle: return: " + result); } if (asFile) { if (!FileManager.writeStringToFile("/Applications/SikuliX-IDE.app", (new File(path, ".LSOverride")).getAbsolutePath())) { log(-1, "makeBundle: not possible: .LSOverride"); } } else { new File(path, ".LSOverride").delete(); } } private void saveAsBundle(String bundlePath, String current) throws IOException { //TODO allow other file types log(lvl, "saveAsBundle: " + getSrcBundle()); bundlePath = FileManager.slashify(bundlePath, true); if (_srcBundlePath != null) { if (!ScriptingSupport.transferScript(_srcBundlePath, bundlePath)) { log(-1, "saveAsBundle: did not work - "); } } ImagePath.remove(_srcBundlePath); if (_srcBundleTemp) { FileManager.deleteTempDir(_srcBundlePath); _srcBundleTemp = false; } setSrcBundle(bundlePath); _editingFile = createSourceFile(bundlePath, "." + Runner.typeEndings.get(sikuliContentType)); writeSrcFile(); reparse(); } private File createSourceFile(String bundlePath, String ext) { if (ext != null) { String name = new File(bundlePath).getName(); name = name.substring(0, name.lastIndexOf(".")); return new File(bundlePath, name + ext); } else { return new File(bundlePath); } } private void writeSrcFile() throws IOException { log(lvl, "writeSrcFile: " + _editingFile.getName()); this.write(new BufferedWriter(new OutputStreamWriter( new FileOutputStream(_editingFile.getAbsolutePath()), "UTF8"))); if (PreferencesUser.getInstance().getAtSaveMakeHTML()) { convertSrcToHtml(getSrcBundle()); } else { String snameDir = new File(_editingFile.getAbsolutePath()).getParentFile().getName(); String sname = snameDir.replace(".sikuli", "") + ".html"; (new File(snameDir, sname)).delete(); } if (PreferencesUser.getInstance().getAtSaveCleanBundle()) { if (!sikuliContentType.equals(Runner.CPYTHON)) { log(lvl, "delete-not-used-images for %s using Python string syntax", sikuliContentType); } cleanBundle(); } setDirty(false); } private void cleanBundle() { log(3, "cleanBundle"); Set foundImages = parseforImages().keySet(); if (foundImages.contains("uncomplete_comment_error")) { log(-1, "cleanBundle aborted (uncomplete_comment_error)"); } else { FileManager.deleteNotUsedImages(getBundlePath(), foundImages); log(3, "cleanBundle finished"); } } private Map> parseforImages() { String pbundle = FileManager.slashify(getSrcBundle(), false); log(3, "parseforImages: in \n%s", pbundle); String scriptText = getText(); Lexer lexer = getLexer(); Map> images = new HashMap>(); parseforImagesWalk(pbundle, lexer, scriptText, 0, images, 0); log(3, "parseforImages finished"); return images; } private void parseforImagesWalk(String pbundle, Lexer lexer, String text, int pos, Map> images, Integer line) { //log(3, "parseforImagesWalk"); Iterable tokens = lexer.getTokens(text); boolean inString = false; String current; String innerText; String[] possibleImage = new String[]{""}; String[] stringType = new String[]{""}; for (Token t : tokens) { current = t.getValue(); if (current.endsWith("\n")) { line++; if (inString) { boolean answer = Sikulix.popAsk(String.format("Possible incomplete string in line %d\n" + "\"%s\"\n" + "Yes: No images will be deleted!\n" + "No: Ignore and continue", line, text), "Delete images on save"); if (answer) { log(-1, "DeleteImagesOnSave: possible incomplete string in line %d", line); images.clear(); images.put("uncomplete_comment_error", null); } break; } } if (t.getType() == TokenType.Comment) { //log(3, "parseforImagesWalk::Comment"); innerText = t.getValue().substring(1); parseforImagesWalk(pbundle, lexer, innerText, t.getPos() + 1, images, line); continue; } if (t.getType() == TokenType.String_Doc) { //log(3, "parseforImagesWalk::String_Doc"); innerText = t.getValue().substring(3, t.getValue().length() - 3); parseforImagesWalk(pbundle, lexer, innerText, t.getPos() + 3, images, line); continue; } if (!inString) { inString = parseforImagesGetName(current, inString, possibleImage, stringType); continue; } if (!parseforImagesGetName(current, inString, possibleImage, stringType)) { inString = false; parseforImagesCollect(pbundle, possibleImage[0], pos + t.getPos(), images); continue; } } } private boolean parseforImagesGetName(String current, boolean inString, String[] possibleImage, String[] stringType) { //log(3, "parseforImagesGetName (inString: %s) %s", inString, current); if (!inString) { if (!current.isEmpty() && (current.contains("\"") || current.contains("'"))) { possibleImage[0] = ""; stringType[0] = current.substring(current.length() - 1, current.length()); return true; } } if (!current.isEmpty() && "'\"".contains(current) && stringType[0].equals(current)) { return false; } if (inString) { possibleImage[0] += current; } return inString; } private void parseforImagesCollect(String pbundle, String img, int pos, Map> images) { String fimg; //log(3, "parseforImagesCollect"); if (img.endsWith(".png") || img.endsWith(".jpg") || img.endsWith(".jpeg")) { fimg = FileManager.slashify(img, false); if (fimg.contains("/")) { if (!fimg.contains(pbundle)) { return; } img = new File(fimg).getName(); } if (images.containsKey(img)) { images.get(img).add(pos); } else { List poss = new ArrayList(); poss.add(pos); images.put(img, poss); } } } private Lexer getLexer() { //TODO this only works for cleanbundle to find the image strings String scriptType = "python"; if (null != lexers.get(scriptType)) { return lexers.get(scriptType); } try { Lexer lexer = Lexer.getByName(scriptType); lexers.put(scriptType, lexer); return lexer; } catch (ResolutionException ex) { return null; } } public String exportAsZip() { File file = new SikulixFileChooser(sikuliIDE).export(); if (file == null) { return null; } String zipPath = file.getAbsolutePath(); if (!file.getAbsolutePath().endsWith(".skl")) { zipPath += ".skl"; } if (new File(zipPath).exists()) { if (!Sikulix.popAsk(String.format("Overwrite existing file?\n%s", zipPath), "Exporting packed SikuliX Script")) { return null; } } String pSource = _editingFile.getParent(); try { writeSrcFile(); zipDir(pSource, zipPath, _editingFile.getName()); log(lvl, "Exported packed SikuliX Script to:\n%s", zipPath); } catch (Exception ex) { log(-1, "Exporting packed SikuliX Script did not work:\n%s", zipPath); return null; } return zipPath; } private static void zipDir(String dir, String zipPath, String fScript) throws IOException { ZipOutputStream zos = null; try { zos = new ZipOutputStream(new FileOutputStream(zipPath)); File zipDir = new File(dir); String[] dirList = zipDir.list(); byte[] readBuffer = new byte[1024]; int bytesIn; ZipEntry anEntry = null; String ending = fScript.substring(fScript.length() - 3); String sName = new File(zipPath).getName(); sName = sName.substring(0, sName.length() - 4) + ending; for (int i = 0; i < dirList.length; i++) { File f = new File(zipDir, dirList[i]); if (f.isFile()) { if (fScript.equals(f.getName())) { anEntry = new ZipEntry(sName); } else { anEntry = new ZipEntry(f.getName()); } FileInputStream fis = new FileInputStream(f); zos.putNextEntry(anEntry); while ((bytesIn = fis.read(readBuffer)) != -1) { zos.write(readBuffer, 0, bytesIn); } fis.close(); } } } catch (Exception ex) { String msg = ""; msg = ex.getMessage() + ""; } finally { zos.close(); } } public boolean close() throws IOException { log(lvl, "Tab close clicked"); if (isDirty()) { Object[] options = {SikuliIDEI18N._I("yes"), SikuliIDEI18N._I("no"), SikuliIDEI18N._I("cancel")}; int ans = JOptionPane.showOptionDialog(this, SikuliIDEI18N._I("msgAskSaveChanges", getCurrentShortFilename()), SikuliIDEI18N._I("dlgAskCloseTab"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); if (ans == JOptionPane.CANCEL_OPTION || ans == JOptionPane.CLOSED_OPTION) { return false; } else if (ans == JOptionPane.YES_OPTION) { if (saveFile() == null) { return false; } } else { // sikuliIDE.getTabPane().resetLastClosed(); } setDirty(false); } if (_srcBundlePath != null) { ImagePath.remove(_srcBundlePath); if (_srcBundleTemp) { FileManager.deleteTempDir(_srcBundlePath); } } return true; } private void setSrcBundle(String newBundlePath) { try { newBundlePath = new File(newBundlePath).getCanonicalPath(); } catch (Exception ex) { return; } _srcBundlePath = newBundlePath; ImagePath.setBundlePath(_srcBundlePath); } public String getSrcBundle() { if (_srcBundlePath == null) { File tmp = FileManager.createTempDir(); setSrcBundle(FileManager.slashify(tmp.getAbsolutePath(), true)); _srcBundleTemp = true; } return _srcBundlePath; } // used at ButtonRun.run public String getBundlePath() { return _srcBundlePath; } public boolean isSourceBundleTemp() { return _srcBundleTemp; } public String getCurrentSrcDir() { if (_srcBundlePath != null) { if (_editingFile == null || _srcBundleTemp) { return FileManager.normalize(_srcBundlePath); } else { return _editingFile.getParent(); } } else { return null; } } public String getCurrentShortFilename() { if (_srcBundlePath != null) { File f = new File(_srcBundlePath); return f.getName(); } return "Untitled"; } public File getCurrentFile() { return getCurrentFile(true); } public File getCurrentFile(boolean shouldSave) { if (shouldSave && _editingFile == null && isDirty()) { try { saveAsFile(Settings.isMac()); } catch (IOException e) { log(-1, "getCurrentFile: Problem while trying to save %s\n%s", _editingFile.getAbsolutePath(), e.getMessage()); } } return _editingFile; } public String getCurrentFilename() { if (_editingFile == null) { return null; } return _editingFile.getAbsolutePath(); } //TODO convertSrcToHtml has to be completely revised private void convertSrcToHtml(String bundle) { IScriptRunner runner = ScriptingSupport.getRunner(null, "jython"); if (runner != null) { runner.doSomethingSpecial("convertSrcToHtml", new String[]{bundle}); } } public File copyFileToBundle(String filename) { File f = new File(filename); String bundlePath = getSrcBundle(); if (f.exists()) { try { File newFile = FileManager.smartCopy(filename, bundlePath); return newFile; } catch (IOException e) { log(-1, "copyFileToBundle: Problem while trying to save %s\n%s", filename, e.getMessage()); return f; } } return null; } public Image getImageInBundle(String filename) { return Image.createThumbNail(filename); } // public boolean isDirty() { return scriptIsDirty; } public void setDirty(boolean flag) { if (scriptIsDirty == flag) { return; } scriptIsDirty = flag; sikuliIDE.setCurrentFileTabTitleDirty(scriptIsDirty); } private class DirtyHandler implements DocumentListener { @Override public void changedUpdate(DocumentEvent ev) { log(lvl + 1, "change update"); //setDirty(true); } @Override public void insertUpdate(DocumentEvent ev) { log(lvl + 1, "insert update"); setDirty(true); } @Override public void removeUpdate(DocumentEvent ev) { log(lvl + 1, "remove update"); setDirty(true); } } // // //TODO not used @Override public void caretUpdate(CaretEvent evt) { /* seems not to be used * if (_can_update_caret_last_x) { * _caret_last_x = -1; * } else { * _can_update_caret_last_x = true; * } */ } public int getLineNumberAtCaret(int caretPosition) { Element root = getDocument().getDefaultRootElement(); return root.getElementIndex(caretPosition) + 1; } public Element getLineAtCaret(int caretPosition) { Element root = getDocument().getDefaultRootElement(); Element result; if (caretPosition == -1) { result = root.getElement(root.getElementIndex(getCaretPosition())); } else { result = root.getElement(root.getElementIndex(root.getElementIndex(caretPosition))); } return result; } public String getLineTextAtCaret() { Element elem = getLineAtCaret(-1); Document doc = elem.getDocument(); Element subElem; String text; String line = ""; int start = elem.getStartOffset(); int end = elem.getEndOffset(); for (int i = 0; i < elem.getElementCount(); i++) { text = ""; subElem = elem.getElement(i); start = subElem.getStartOffset(); end = subElem.getEndOffset(); if (subElem.getName().contains("component")) { text = StyleConstants.getComponent(subElem.getAttributes()).toString(); } else { try { text = doc.getText(start, end - start); } catch (Exception ex) { } } line += text; } return line.trim(); } public Element getLineAtPoint(MouseEvent me) { Point p = me.getLocationOnScreen(); Point pp = getLocationOnScreen(); p.translate(-pp.x, -pp.y); int pos = viewToModel(p); Element root = getDocument().getDefaultRootElement(); int e = root.getElementIndex(pos); if (e == -1) { return null; } return root.getElement(e); } public boolean jumpTo(int lineNo, int column) { log(lvl + 1, "jumpTo pos: " + lineNo + "," + column); try { int off = getLineStartOffset(lineNo - 1) + column - 1; int lineCount = getDocument().getDefaultRootElement().getElementCount(); if (lineNo < lineCount) { int nextLine = getLineStartOffset(lineNo); if (off >= nextLine) { off = nextLine - 1; } } if (off >= 0) { setCaretPosition(off); } } catch (BadLocationException ex) { jumpTo(lineNo); return false; } return true; } public boolean jumpTo(int lineNo) { log(lvl + 1, "jumpTo line: " + lineNo); try { setCaretPosition(getLineStartOffset(lineNo - 1)); } catch (BadLocationException ex) { return false; } return true; } public int getLineStartOffset(int line) throws BadLocationException { // line starting from 0 Element map = getDocument().getDefaultRootElement(); if (line < 0) { throw new BadLocationException("Negative line", -1); } else if (line >= map.getElementCount()) { throw new BadLocationException("No such line", getDocument().getLength() + 1); } else { Element lineElem = map.getElement(line); return lineElem.getStartOffset(); } } // public void jumpTo(String funcName) throws BadLocationException { log(lvl + 1, "jumpTo function: " + funcName); Element root = getDocument().getDefaultRootElement(); int pos = getFunctionStartOffset(funcName, root); if (pos >= 0) { setCaretPosition(pos); } else { throw new BadLocationException("Can't find function " + funcName, -1); } } private int getFunctionStartOffset(String func, Element node) throws BadLocationException { Document doc = getDocument(); int count = node.getElementCount(); Pattern patDef = Pattern.compile("def\\s+" + func + "\\s*\\("); for (int i = 0; i < count; i++) { Element elm = node.getElement(i); if (elm.isLeaf()) { int start = elm.getStartOffset(), end = elm.getEndOffset(); String line = doc.getText(start, end - start); Matcher matcher = patDef.matcher(line); if (matcher.find()) { return start; } } else { int p = getFunctionStartOffset(func, elm); if (p >= 0) { return p; } } } return -1; } // // // public boolean reparse(String oldName, String newName, boolean fileOverWritten) { boolean success; if (fileOverWritten) { Image.unCacheBundledImage(newName); } Map> images = parseforImages(); oldName = new File(oldName).getName(); List poss = images.get(oldName); if (images.containsKey(oldName) && poss.size() > 0) { Collections.sort(poss, new Comparator() { @Override public int compare(Integer o1, Integer o2) { if (o1 > o2) return -1; return 1; } }); reparseRenameImages(poss, oldName, new File(newName).getName()); } return reparse(); } private boolean reparseRenameImages(List poss, String oldName, String newName) { StringBuilder text = new StringBuilder(getText()); int lenOld = oldName.length(); for (int pos : poss) { text.replace(pos - lenOld, pos, newName); } setText(text.toString()); return true; } public boolean reparse() { String paneContent = this.getText(); if (paneContent.length() < 7) { return true; } if (readScript(paneContent)) { updateDocumentListeners("reparse"); return true; } return false; } //TODO not clear what this is for LOL (SikuliIDEPopUpMenu.doSetType) public boolean reparseCheckContent() { if (this.getText().contains(ScriptingSupport.TypeCommentToken)) { return true; } return false; } private void parse(Element node) { if (!showThumbs) { // do not show any thumbnails return; } int count = node.getElementCount(); for (int i = 0; i < count; i++) { Element elm = node.getElement(i); log(lvl + 1, elm.toString()); if (elm.isLeaf()) { int start = elm.getStartOffset(), end = elm.getEndOffset(); parseRange(start, end); } else { parse(elm); } } } public String parseLineText(String line) { if (line.startsWith("#")) { Pattern aName = Pattern.compile("^#[A-Za-z0-9_]+ =$"); Matcher mN = aName.matcher(line); if (mN.find()) { return line.substring(1).split(" ")[0]; } return ""; } Matcher mR = patRegionStr.matcher(line); String asOffset = ".asOffset()"; if (mR.find()) { if (line.length() >= mR.end() + asOffset.length()) { if (line.substring(mR.end()).contains(asOffset)) { return line.substring(mR.start(), mR.end()) + asOffset; } } return line.substring(mR.start(), mR.end()); } Matcher mL = patLocationStr.matcher(line); if (mL.find()) { return line.substring(mL.start(), mL.end()); } Matcher mP = patPatternStr.matcher(line); if (mP.find()) { return line.substring(mP.start(), mP.end()); } Matcher mI = patPngStr.matcher(line); if (mI.find()) { return line.substring(mI.start(), mI.end()); } return ""; } private int parseRange(int start, int end) { if (!showThumbs) { // do not show any thumbnails return end; } try { end = parseLine(start, end, patCaptureBtn); end = parseLine(start, end, patPatternStr); end = parseLine(start, end, patRegionStr); end = parseLine(start, end, patPngStr); } catch (BadLocationException e) { log(-1, "parseRange: Problem while trying to parse line\n%s", e.getMessage()); } return end; } private int parseLine(int startOff, int endOff, Pattern ptn) throws BadLocationException { if (endOff <= startOff) { return endOff; } Document doc = getDocument(); while (true) { String line = doc.getText(startOff, endOff - startOff); Matcher m = ptn.matcher(line); //System.out.println("["+line+"]"); if (m.find()) { int len = m.end() - m.start(); boolean replaced = replaceWithImage(startOff + m.start(), startOff + m.end(), ptn); if (replaced) { startOff += m.start() + 1; endOff -= len - 1; } else { startOff += m.end() + 1; } } else { break; } } return endOff; } private boolean replaceWithImage(int startOff, int endOff, Pattern ptn) throws BadLocationException { Document doc = getDocument(); String imgStr = doc.getText(startOff, endOff - startOff); JComponent comp = null; if (ptn == patPatternStr || ptn == patPngStr) { if (pref.getPrefMoreImageThumbs()) { comp = EditorPatternButton.createFromString(this, imgStr, null); } else { comp = EditorPatternLabel.labelFromString(this, imgStr); } } else if (ptn == patRegionStr) { if (pref.getPrefMoreImageThumbs()) { comp = EditorRegionButton.createFromString(this, imgStr); } else { comp = EditorRegionLabel.labelFromString(this, imgStr); } } else if (ptn == patCaptureBtn) { comp = EditorPatternLabel.labelFromString(this, ""); } if (comp != null) { this.select(startOff, endOff); this.insertComponent(comp); return true; } return false; } public String getRegionString(int x, int y, int w, int h) { return String.format("Region(%d,%d,%d,%d)", x, y, w, h); } public String getPatternString(String ifn, float sim, Location off, Image img) { //TODO ifn really needed?? if (ifn == null) { return "\"" + EditorPatternLabel.CAPTURE + "\""; } String imgName = new File(ifn).getName(); if (img != null) { imgName = img.getName(); } String pat = "Pattern(\"" + imgName + "\")"; String ret = ""; if (sim > 0) { if (sim >= 0.99F) { ret += ".exact()"; } else if (sim != 0.7F) { ret += String.format(Locale.ENGLISH, ".similar(%.2f)", sim); } } if (off != null && (off.x != 0 || off.y != 0)) { ret += ".targetOffset(" + off.x + "," + off.y + ")"; } if (!ret.equals("")) { ret = pat + ret; } else { ret = "\"" + imgName + "\""; } return ret; } // // public void insertString(String str) { int sel_start = getSelectionStart(); int sel_end = getSelectionEnd(); if (sel_end != sel_start) { try { getDocument().remove(sel_start, sel_end - sel_start); } catch (BadLocationException e) { log(-1, "insertString: Problem while trying to insert\n%s", e.getMessage()); } } int pos = getCaretPosition(); insertString(pos, str); int new_pos = getCaretPosition(); int end = parseRange(pos, new_pos); setCaretPosition(end); } private void insertString(int pos, String str) { Document doc = getDocument(); try { doc.insertString(pos, str, null); } catch (Exception e) { log(-1, "insertString: Problem while trying to insert at pos\n%s", e.getMessage()); } } //TODO not used public void appendString(String str) { Document doc = getDocument(); try { int start = doc.getLength(); doc.insertString(doc.getLength(), str, null); int end = doc.getLength(); //end = parseLine(start, end, patHistoryBtnStr); } catch (Exception e) { log(-1, "appendString: Problem while trying to append\n%s", e.getMessage()); } } // // /* * public int search(Pattern pattern){ * return search(pattern, true); * } * * public int search(Pattern pattern, boolean forward){ * if(!pattern.equals(_lastSearchPattern)){ * _lastSearchPattern = pattern; * Document doc = getDocument(); * int pos = getCaretPosition(); * Debug.log("caret: " + pos); * try{ * String body = doc.getText(pos, doc.getLength()-pos); * _lastSearchMatcher = pattern.matcher(body); * } * catch(BadLocationException e){ * e.printStackTrace(); * } * } * return continueSearch(forward); * } */ /* * public int search(String str){ * return search(str, true); * } */ public int search(String str, int pos, boolean forward) { boolean isCaseSensitive = true; String toSearch = str; if (str.startsWith("!")) { str = str.substring(1).toUpperCase(); isCaseSensitive = false; } int ret = -1; Document doc = getDocument(); Debug.log(lvl, "search: %s from %d forward: %s", str, pos, forward); try { String body; int begin; if (forward) { int len = doc.getLength() - pos; body = doc.getText(pos, len > 0 ? len : 0); begin = pos; } else { body = doc.getText(0, pos); begin = 0; } if (!isCaseSensitive) { body = body.toUpperCase(); } Pattern pattern = Pattern.compile(Pattern.quote(str)); Matcher matcher = pattern.matcher(body); ret = continueSearch(matcher, begin, forward); if (ret < 0) { if (forward && pos != 0) { return search(toSearch, 0, forward); } if (!forward && pos != doc.getLength()) { return search(toSearch, doc.getLength(), forward); } } } catch (BadLocationException e) { log(-1, "search: did not work:\n" + e.getStackTrace()); } return ret; } protected int continueSearch(Matcher matcher, int pos, boolean forward) { boolean hasNext = false; int start = 0, end = 0; if (!forward) { while (matcher.find()) { hasNext = true; start = matcher.start(); end = matcher.end(); } } else { hasNext = matcher.find(); if (!hasNext) { return -1; } start = matcher.start(); end = matcher.end(); } if (hasNext) { Document doc = getDocument(); getCaret().setDot(pos + end); getCaret().moveDot(pos + start); getCaret().setSelectionVisible(true); return pos + start; } return -1; } // // private class MyTransferHandler extends TransferHandler { private static final String me = "EditorPaneTransferHandler: "; Map _copiedImgs = new HashMap(); @Override public void exportToClipboard(JComponent comp, Clipboard clip, int action) { super.exportToClipboard(comp, clip, action); } @Override protected void exportDone(JComponent source, Transferable data, int action) { if (action == TransferHandler.MOVE) { JTextPane aTextPane = (JTextPane) source; int sel_start = aTextPane.getSelectionStart(); int sel_end = aTextPane.getSelectionEnd(); Document doc = aTextPane.getDocument(); try { doc.remove(sel_start, sel_end - sel_start); } catch (BadLocationException e) { log(-1, "MyTransferHandler: exportDone: Problem while trying to remove text\n%s", e.getMessage()); } } } @Override public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } @Override protected Transferable createTransferable(JComponent c) { JTextPane aTextPane = (JTextPane) c; SikuliEditorKit kit = ((SikuliEditorKit) aTextPane.getEditorKit()); Document doc = aTextPane.getDocument(); int sel_start = aTextPane.getSelectionStart(); int sel_end = aTextPane.getSelectionEnd(); StringWriter writer = new StringWriter(); try { _copiedImgs.clear(); kit.write(writer, doc, sel_start, sel_end - sel_start, _copiedImgs); return new StringSelection(writer.toString()); } catch (Exception e) { log(-1, "MyTransferHandler: createTransferable: Problem creating text to copy\n%s", e.getMessage()); } return null; } @Override public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) { for (int i = 0; i < transferFlavors.length; i++) { //System.out.println(transferFlavors[i]); if (transferFlavors[i].equals(DataFlavor.stringFlavor)) { return true; } } return false; } @Override public boolean importData(JComponent comp, Transferable t) { DataFlavor htmlFlavor = DataFlavor.stringFlavor; if (canImport(comp, t.getTransferDataFlavors())) { try { String transferString = (String) t.getTransferData(htmlFlavor); EditorPane targetTextPane = (EditorPane) comp; for (Map.Entry entry : _copiedImgs.entrySet()) { String imgName = entry.getKey(); String imgPath = entry.getValue(); File destFile = targetTextPane.copyFileToBundle(imgPath); String newName = destFile.getName(); if (!newName.equals(imgName)) { String ptnImgName = "\"" + imgName + "\""; newName = "\"" + newName + "\""; transferString = transferString.replaceAll(ptnImgName, newName); Debug.info("MyTransferHandler: importData:" + ptnImgName + " exists. Rename it to " + newName); } } targetTextPane.insertString(transferString); } catch (Exception e) { log(-1, "MyTransferHandler: importData: Problem pasting text\n%s", e.getMessage()); } return true; } return false; } } // // private String _tabString = " "; private void setTabSize(int charactersPerTab) { FontMetrics fm = this.getFontMetrics(this.getFont()); int charWidth = fm.charWidth('w'); int tabWidth = charWidth * charactersPerTab; TabStop[] tabs = new TabStop[10]; for (int j = 0; j < tabs.length; j++) { int tab = j + 1; tabs[j] = new TabStop(tab * tabWidth); } TabSet tabSet = new TabSet(tabs); SimpleAttributeSet attributes = new SimpleAttributeSet(); StyleConstants.setFontSize(attributes, 18); StyleConstants.setFontFamily(attributes, "Osaka-Mono"); StyleConstants.setTabSet(attributes, tabSet); int length = getDocument().getLength(); getStyledDocument().setParagraphAttributes(0, length, attributes, true); } private void setTabs(int spaceForTab) { String t = ""; for (int i = 0; i < spaceForTab; i++) { t += " "; } _tabString = t; } private void expandTab() throws BadLocationException { int pos = getCaretPosition(); Document doc = getDocument(); doc.remove(pos - 1, 1); doc.insertString(pos - 1, _tabString, null); } private Class _historyBtnClass; private void setHistoryCaptureButton(ButtonCapture btn) { _historyBtnClass = btn.getClass(); } private void indent(int startLine, int endLine, int level) { Document doc = getDocument(); String strIndent = ""; if (level > 0) { for (int i = 0; i < level; i++) { strIndent += " "; } } else { Debug.error("negative indentation not supported yet!!"); } for (int i = startLine; i < endLine; i++) { try { int off = getLineStartOffset(i); if (level > 0) { doc.insertString(off, strIndent, null); } } catch (Exception e) { e.printStackTrace(); } } } private void checkCompletion(java.awt.event.KeyEvent ke) throws BadLocationException { Document doc = getDocument(); Element root = doc.getDefaultRootElement(); int pos = getCaretPosition(); int lineIdx = root.getElementIndex(pos); Element line = root.getElement(lineIdx); int start = line.getStartOffset(), len = line.getEndOffset() - start; String strLine = doc.getText(start, len - 1); Debug.log(9, "[" + strLine + "]"); if (strLine.endsWith("find") && ke.getKeyChar() == '(') { ke.consume(); doc.insertString(pos, "(", null); ButtonCapture btnCapture = new ButtonCapture(this, line); insertComponent(btnCapture); doc.insertString(pos + 2, ")", null); } } // } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorPatternButton.java000066400000000000000000000273311315726130400257220ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import org.sikuli.basics.PreferencesUser; import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; import java.io.*; import javax.imageio.*; import javax.swing.*; import org.sikuli.script.Location; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.script.Image; class EditorPatternButton extends JButton implements ActionListener, Serializable, MouseListener { public static final int DEFAULT_NUM_MATCHES = 50; static final float DEFAULT_SIMILARITY = 0.7f; private String _imgFilename, _thumbFname, _imgFilenameSaved; private Image _image; private JLabel patternImageIcon = null; private EditorPane _pane; private float _similarity, _similaritySaved; private int _numMatches = DEFAULT_NUM_MATCHES; private boolean _exact, _exactSaved; private Location _offset = new Location(0, 0), _offsetSaved; private int _imgW, _imgH; private float _scale = 1f; private static PatternWindow pwin = null; private static Font textFont = new Font("arial", Font.BOLD, 12); private Color oldC; private String buttonFname = ""; private String buttonSimilar = ""; private String buttonOffset = ""; private EditorPatternLabel _lbl; protected EditorPatternButton(EditorPane pane) { this._image = null; init(pane, null, null); } public EditorPatternButton(EditorPane pane, String imgFilename) { this._image = null; init(pane, imgFilename, null); } private EditorPatternButton(EditorPane pane, Image img) { this._image = null; init(pane, null, img); } protected EditorPatternButton(EditorPatternLabel lbl) { super(); this._image = null; _lbl = lbl; _imgFilename = _lbl.getFile(); _exact = false; _similarity = _lbl.getSimilarity(); _offset = _lbl.getTargetOffset(); _numMatches = DEFAULT_NUM_MATCHES; _pane = _lbl.getPane(); } private void init(EditorPane pane, String imgFilename, Image img) { //TODO thumbMax = PreferencesUser.getInstance().getDefaultThumbHeight() == 0 ? false : true; _pane = pane; _exact = false; _similarity = DEFAULT_SIMILARITY; _numMatches = DEFAULT_NUM_MATCHES; if (imgFilename != null) { setFilename(imgFilename); } else if (img != null) { setFilename(img); } setMargin(new Insets(0, 0, 0, 0)); setBorderPainted(true); setCursor(new Cursor(Cursor.HAND_CURSOR)); addActionListener(this); setButtonText(); } public BufferedImage createThumbnailImage(int maxHeight) { return createThumbnailImage(_imgFilename, maxHeight); } public static EditorPatternButton createFromFilename(EditorPane parentPane, String str, EditorPatternLabel lbl) { return createFromString(parentPane, "\"" + str + "\"", lbl); } public static EditorPatternButton createFromString(EditorPane parentPane, String str, EditorPatternLabel lbl) { if (!str.startsWith("Pattern")) { str = str.substring(1, str.length() - 1); str = FileManager.slashify(str, false); Image img = Image.createThumbNail(str); if (img.isValid() && img.isBundled()) { return new EditorPatternButton(parentPane, img); } return null; } EditorPatternButton btn = new EditorPatternButton(parentPane); String[] tokens = str.split("\\)\\s*\\.?"); for (String tok : tokens) { //System.out.println("token: " + tok); if (tok.startsWith("exact")) { btn.setExact(true); btn.setSimilarity(0.99f); } else if (tok.startsWith("Pattern")) { String filename = FileManager.slashify(tok.substring( tok.indexOf("\"") + 1, tok.lastIndexOf("\"")), false); Image img = Image.createThumbNail(filename); if (img.isValid() && img.isBundled()) { btn.setFilename(img); } else { return null; } } else if (tok.startsWith("similar")) { String strArg = tok.substring(tok.lastIndexOf("(") + 1); try { btn.setSimilarity(Float.valueOf(strArg)); } catch (NumberFormatException e) { return null; } } else if (tok.startsWith("firstN")) { // FIXME: replace with limit/max String strArg = tok.substring(tok.lastIndexOf("(") + 1); btn._numMatches = Integer.valueOf(strArg); } else if (tok.startsWith("targetOffset")) { String strArg = tok.substring(tok.lastIndexOf("(") + 1); String[] args = strArg.split(","); try { Location offset = new Location(0, 0); offset.x = Integer.valueOf(args[0]); offset.y = Integer.valueOf(args[1]); btn.setTargetOffset(offset); } catch (NumberFormatException e) { return null; } } } btn.setButtonText(); return btn; } @Override public void actionPerformed(ActionEvent e) { Debug.log(3, "ThumbButtonLabel: open Pattern Settings"); _pane.saveCaretPosition(); if (pwin == null) { _offsetSaved = new Location(_offset); _similaritySaved = _similarity; _exactSaved = _similarity >= 0.99f; _imgFilenameSaved = _imgFilename.substring(0); pwin = new PatternWindow(this, _exactSaved, _similarity, _numMatches); pwin.setTargetOffset(_offset); } pwin.requestFocus(); } @Override public Point getLocationOnScreen() { if (_lbl == null) { return super.getLocationOnScreen(); } else { return _lbl.getLocationOnScreen(); } } @Override public void mouseEntered(MouseEvent me) { } @Override public void mouseExited(MouseEvent me) { } public PatternWindow getWindow() { return pwin; } public void resetWindow() { pwin = null; } public String getFilename() { File img = new File(_imgFilename); String oldBundle = img.getParent(); String newBundle = _pane.getSrcBundle(); Debug.log(4, "ImageButton.getFilename: old: " + oldBundle + "\nnew: " + newBundle); if (FileManager.pathEquals(newBundle, oldBundle)) { return _imgFilename; } setFilename(new File(newBundle, img.getName()).getAbsolutePath()); return _imgFilename; } public void setFilename(String fileName) { _image = _pane.getImageInBundle(fileName); _imgFilename = _image.getFilename(); setIcon(new ImageIcon(createThumbnailImage(_imgFilename, PreferencesUser.getInstance().getDefaultThumbHeight()))); setButtonText(); } private void setFilename(Image img) { _image = img; _imgFilename = _image.getFilename(); setIcon(new ImageIcon(createThumbnailImage(_imgFilename, PreferencesUser.getInstance().getDefaultThumbHeight()))); setButtonText(); } private String createThumbnail(String imgFname) { return createThumbnail(imgFname, PreferencesUser.getInstance().getDefaultThumbHeight()); } private String createThumbnail(String imgFname, int maxHeight) { BufferedImage thumb = createThumbnailImage(imgFname, maxHeight); return FileManager.saveTmpImage(thumb); } private BufferedImage createThumbnailImage(String imgFname, int maxHeight) { try { BufferedImage img; if (_image != null) { img = _image.get(); } else { //TODO ???? Debug.error("EditorPatternButton: createThumbnailImage: not using Image for: " + imgFname); img = ImageIO.read(new File(imgFname)); } int w = img.getWidth(null), h = img.getHeight(null); _imgW = w; _imgH = h; if (maxHeight == 0 || maxHeight >= h) { return img; } _scale = (float) maxHeight / h; w *= _scale; h *= _scale; h = (int) h ; BufferedImage thumb = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = thumb.createGraphics(); g2d.drawImage(img, 0, 0, w, h, null); g2d.dispose(); return thumb; } catch (IOException e) { Debug.error("Can't read file: " + e.getMessage()); return null; } } public boolean setParameters(boolean exact, float similarity, int numMatches) { boolean dirty = false; Debug.log(3, "ThumbButtonLabel: setParameters: " + exact + "," + similarity + "," + numMatches); dirty |= setExact(exact); dirty |= setSimilarity(similarity); setButtonText(); return dirty; } public void resetParameters() { setFilename(_imgFilenameSaved); setParameters(_exactSaved, _similaritySaved, DEFAULT_NUM_MATCHES); setTargetOffset(_offsetSaved); } public boolean setExact(boolean exact) { if (_exact != exact) { _exact = exact; return true; } return false; } public boolean setSimilarity(float val) { float sim; if (val < 0) { sim = 0; } else if (val >= 1) { sim = 0.99f; } else { sim = val; } if (sim != _similarity) { _similarity = sim; return true; } return false; } public float getSimilarity() { return _similarity; } public boolean setTargetOffset(Location offset) { Debug.log(3, "ThumbButtonLabel: setTargetOffset: " + offset.toStringShort()); if (!_offset.equals(offset)) { _offset = offset; setButtonText(); return true; } return false; } public Location getTargetOffset() { return _offset; } public String getFileName() { return _imgFilename; } // @Override public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D)g; drawText(g2d); if( useThumbnail() ){ g2d.setColor( new Color(0, 128, 128, 128) ); g2d.drawRoundRect(3, 3, getWidth()-7, getHeight()-7, 5, 5); } } private void drawText(Graphics2D g2d) { String strSim = null, strOffset = null; if (_similarity != DEFAULT_SIMILARITY) { if (_exact) { strSim = "99"; } else { strSim = String.format("%d", (int) (_similarity * 100)); } } if (_offset != null && (_offset.x != 0 || _offset.y != 0)) { strOffset = _offset.toStringShort(); } if (strOffset == null && strSim == null) { return; } final int fontH = g2d.getFontMetrics().getMaxAscent(); final int x = getWidth(), y = 0; drawText(g2d, strSim, x, y); if (_offset != null) { drawCross(g2d); } } private void drawText(Graphics2D g2d, String str, int x, int y) { if (str == null) { return; } final int w = g2d.getFontMetrics().stringWidth(str); final int fontH = g2d.getFontMetrics().getMaxAscent(); final int borderW = 3; g2d.setFont(textFont); g2d.setColor(new Color(0, 128, 0, 128)); g2d.fillRoundRect(x - borderW * 2 - w - 1, y, w + borderW * 2 + 1, fontH + borderW * 2 + 1, 3, 3); g2d.setColor(Color.white); g2d.drawString(str, x - w - 3, y + fontH + 3); } private void drawCross(Graphics2D g2d) { int x, y; final String cross = "+"; final int w = g2d.getFontMetrics().stringWidth(cross); final int h = g2d.getFontMetrics().getMaxAscent(); if (_offset.x > _imgW / 2) { x = getWidth() - w; } else if (_offset.x < -_imgW / 2) { x = 0; } else { x = (int) (getWidth() / 2 + _offset.x * _scale - w / 2); } if (_offset.y > _imgH / 2) { y = getHeight() + h / 2 - 3; } else if (_offset.y < -_imgH / 2) { y = h / 2 + 2; } else { y = (int) (getHeight() / 2 + _offset.y * _scale + h / 2); } g2d.setFont(textFont); g2d.setColor(new Color(0, 0, 0, 180)); g2d.drawString(cross, x + 1, y + 1); g2d.setColor(new Color(255, 0, 0, 180)); g2d.drawString(cross, x, y); } private boolean useThumbnail() { return !_imgFilename.equals(_thumbFname); } // @Override public String toString() { return _pane.getPatternString(_imgFilename, _similarity, _offset, _image); } private void setButtonText() { if (_lbl == null) { setToolTipText(toString()); } else { _lbl.resetLabel(_imgFilename, _similarity, _offset); } } // @Override public void mouseClicked(MouseEvent me) {} @Override public void mousePressed(MouseEvent me) {} @Override public void mouseReleased(MouseEvent me) {} // } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorPatternLabel.java000077500000000000000000000226221315726130400254670ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.Color; import java.awt.Container; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.File; import java.util.Locale; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.border.Border; import javax.swing.text.Element; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.script.Image; import org.sikuli.script.Location; public class EditorPatternLabel extends EditorRegionLabel { public static String CAPTURE = "__CLICK-TO-CAPTURE__"; public static String NOTFOUND = "!? "; private String lblText; private EditorPane pane; private float sim; private Location off; private String imgName = null; private String imgNameShort = null; private String imgFile; private Image image; private JFrame imgpop = null; private boolean onImagePath = true; private boolean isFromCapture; private Border pbrd = BorderFactory.createEmptyBorder(5, 5, 5, 5); private Border ibrd = BorderFactory.createLineBorder(Color.BLACK); private Border brd = BorderFactory.createCompoundBorder(pbrd, ibrd); //TODO clickToShow: make a user pref private boolean clickToShow = false; private SikuliIDEPopUpMenu popMenu = null; private boolean wasPopup = false; public EditorPatternLabel(EditorPane parentPane, String str) { super(parentPane, str); initLabel(parentPane); } public EditorPatternLabel(EditorPane parentPane, String str, boolean fromCapture) { super(parentPane, str); isFromCapture = fromCapture; initLabel(parentPane); } public EditorPatternLabel(EditorPane parentPane, String str, String oldString) { super(parentPane, str, oldString); isFromCapture = true; initLabel(parentPane); } public EditorPatternLabel(EditorPane parentPane, EditorPatternButton btn) { super(parentPane, btn.toString()); initLabel(parentPane); } private void initLabel(EditorPane parentPane) { pane = parentPane; sim = 0.7F; off = new Location(0, 0); if ("".equals(pyText)) { lblText = CAPTURE; pyText = "\"" + lblText + "\""; } else if (pyText.startsWith("Pattern")) { String[] tokens = pyText.split("\\)\\s*\\.?"); for (String tok : tokens) { //System.out.println("token: " + tok); if (tok.startsWith("exact")) { sim = 0.99F; } else if (tok.startsWith("Pattern")) { setFileNames(tok.substring(tok.indexOf("\"") + 1, tok.lastIndexOf("\""))); if (lblText == null) { break; } } else if (tok.startsWith("similar")) { String strArg = tok.substring(tok.lastIndexOf("(") + 1); try { sim = Float.valueOf(strArg); } catch (NumberFormatException e) { sim = 0.7F; } } else if (tok.startsWith("targetOffset")) { String strArg = tok.substring(tok.lastIndexOf("(") + 1); String[] args = strArg.split(","); try { off = new Location(0, 0); off.x = Integer.valueOf(args[0]); off.y = Integer.valueOf(args[1]); } catch (NumberFormatException e) { } } } if (lblText != null) { setLabelText(); } } else { setFileNames(pyText.replaceAll("\"", "")); } if (lblText == null) { onImagePath = false; } else { setText(lblText); setLabelPyText(); } } @Override public boolean isRegionLabel() { return false; } private void setFileNames(String givenName) { lblText = null; if (!new File(givenName).isAbsolute() && FileManager.isFilenameDotted(givenName)) { return; } Image img = pane.getImageInBundle(givenName); if (img.isValid()) { if (isFromCapture || !img.isAbsolute() || img.isBundled()) { image = img; imgFile = img.getFilename(); imgName = img.getName(); imgNameShort = imgName.replaceFirst(".png", "").replaceFirst(".jpg", ""); lblText = imgNameShort; } } } public boolean isOnImagePath() { return onImagePath; } public void showPopup(boolean show) { if (show) { if (imgpop == null) { BufferedImage img = image.get(); if (img == null) { Debug.log(4, "EditorPatternLabel: mouseEntered: not found " + this.imgName); return; } imgpop = new JFrame(); imgpop.setAlwaysOnTop(true); imgpop.setUndecorated(true); imgpop.setResizable(false); imgpop.setFocusableWindowState(false); imgpop.setBackground(Color.WHITE); Container p = imgpop.getContentPane(); p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); JLabel lbl = new JLabel(); lbl.setIcon(new ImageIcon(img)); lbl.setBorder(brd); p.add(Box.createHorizontalGlue()); p.add(lbl); p.add(Box.createHorizontalGlue()); imgpop.pack(); } Point p = getLocationOnScreen(); Rectangle r = (new Location(p)).getScreen().getRect(); Point p1 = new Point(); if (p.y < (r.y + r.height) / 2) { p1.y = p.y + getHeight() + 3; } else { p1.y = p.y - 3 - imgpop.getHeight(); } if (p.x < (r.x + r.width) / 2) { p1.x = p.x; } else { p1.x = p.x - imgpop.getWidth() + getWidth(); } imgpop.setLocation(p1); imgpop.setVisible(true); } else { if (imgpop != null) { imgpop.setVisible(false); } } } public boolean isCaptureButton() { return (CAPTURE.equals(lblText) || lblText.startsWith(NOTFOUND)); } public void resetLabel(String givenFileName, float sim, Location off) { imgName = (new File(givenFileName)).getName(); image = Image.createThumbNail(imgName); imgFile = image.getFilename(); imgNameShort = imgName.replaceFirst(".png", "").replaceFirst(".jpg", ""); this.sim = sim; this.off = off; setLabelText(); setLabelPyText(); } public void setLabelText() { String buttonSimilar = ""; if (sim != 0.7F) { buttonSimilar = String.format(Locale.ENGLISH, " .%d", (int) (sim * 100F)); } String buttonOffset = ""; if (off != null && (off.x != 0 || off.y != 0)) { buttonOffset = String.format(" (%d,%d)", off.x, off.y); } lblText = imgNameShort + buttonSimilar + buttonOffset; setText(lblText); } public void setLabelPyText() { if (!lblText.startsWith(NOTFOUND)) { pyText = pane.getPatternString(imgName, sim, off, image); } } public void setFile(String imgFile) { imgName = (new File(imgFile)).getName(); pyText = "\"" + imgName + "\""; imgNameShort = pyText.replaceAll("\"", "").replaceFirst(".png", "").replaceFirst(".jpg", ""); lblText = imgNameShort; setText(lblText); } public String getFile() { return imgFile; } public void setFileName(String img) { this.imgName = img; } public void setTargetOffset(Location off) { this.off = off; } public Location getTargetOffset() { return off; } public void setSimilarity(float sim) { this.sim = sim; } public float getSimilarity() { return sim; } public EditorPane getPane() { return pane; } public static EditorPatternLabel labelFromString(EditorPane parentPane, String str) { EditorPatternLabel reg = new EditorPatternLabel(parentPane, str, false); if (reg.isOnImagePath()) { return reg; } else { reg = null; return null; } } @Override public void mousePressed(MouseEvent me) { checkPopup(me); } @Override public void mouseReleased(MouseEvent me) { checkPopup(me); } private void checkPopup(MouseEvent me) { if (me.isPopupTrigger()) { wasPopup = true; popMenu = pane.getPopMenuImage(); if (popMenu != null) { if (imgpop != null && imgpop.isShowing()) { showPopup(false); } popMenu.show(this, me.getX(), me.getY()); } return; } } @Override public void mouseClicked(MouseEvent me) { if (wasPopup) { wasPopup = false; return; } if (clickToShow) { if (imgpop != null) { if (!imgpop.isShowing()) { showPopup(true); return; } else { showPopup(false); } } else { showPopup(true); return; } } else { if (imgpop != null && imgpop.isShowing()) { showPopup(false); } } if (!CAPTURE.equals(lblText) && !lblText.startsWith(NOTFOUND)) { (new EditorPatternButton(this)).actionPerformed(null); } else { Element x = pane.getLineAtPoint(me); //TODO recapture not found image if (x == null || lblText.startsWith(NOTFOUND)) { return; } (new ButtonCapture(pane, x)).actionPerformed(null); } } @Override public void mouseEntered(MouseEvent me) { super.mouseEntered(me); if (CAPTURE.equals(lblText) || lblText.startsWith(NOTFOUND)) { return; } if (!clickToShow) { showPopup(true); } } @Override public void mouseExited(MouseEvent me) { super.mouseExited(me); if (CAPTURE.equals(lblText)) { return; } showPopup(false); } @Override public String toString() { return super.toString(); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorRegionButton.java000077500000000000000000000066651315726130400255420ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.swing.*; import org.sikuli.util.OverlayCapturePrompt; import org.sikuli.script.IScreen; import org.sikuli.script.Region; import org.sikuli.script.ScreenImage; import org.sikuli.basics.Debug; import org.sikuli.script.Screen; import org.sikuli.util.EventObserver; import org.sikuli.util.EventSubject; class EditorRegionButton extends JButton implements ActionListener, EventObserver { private static final String me = "EditorRegionButton: "; EditorPane _pane; int _x, _y, _w, _h; public EditorRegionButton(EditorPane pane, int x, int y, int w, int h) { _pane = pane; _x = x; _y = y; _w = w; _h = h; setIcon(new ImageIcon(getRegionImage(x, y, w, h))); setBorderPainted(true); setToolTipText(this.toString()); addActionListener(this); } @Override public void actionPerformed(ActionEvent ae) { SikuliIDE ide = SikuliIDE.getInstance(); ide.setVisible(false); Screen.doPrompt(SikuliIDE._I("msgCapturePrompt"), this); } @Override public void update(EventSubject es) { OverlayCapturePrompt ocp = (OverlayCapturePrompt) es; ScreenImage simg = ocp.getSelection(); Screen.closePrompt(); if (simg != null) { try { Thread.sleep(300); } catch (InterruptedException ie) { } Rectangle roi = simg.getROI(); _x = (int) roi.getX(); _y = (int) roi.getY(); _w = (int) roi.getWidth(); _h = (int) roi.getHeight(); BufferedImage img = getRegionImage(_x, _y, _w, _h); setIcon(new ImageIcon(img)); setToolTipText(this.toString()); } Screen.resetPrompt(ocp); SikuliIDE.showAgain(); } private BufferedImage getRegionImage(int x, int y, int w, int h) { Region region = Region.create(x, y, w, h); IScreen _screen = region.getScreen(); ScreenImage simg = _screen.capture(); int scr_w = simg.w, scr_h = simg.h; int max_h = 80; // FIXME: put max_h in UserPreferences float scale = (float) max_h / scr_h; scr_w *= scale; scr_h *= scale; BufferedImage screen = new BufferedImage(scr_w, scr_h, BufferedImage.TYPE_INT_RGB); Graphics2D screen_g2d = screen.createGraphics(); try { screen_g2d.drawImage(simg.getImage(), 0, 0, scr_w, scr_h, null); int sx = (int) ((x - simg.x) * scale), sy = (int) ((y - simg.y) * scale), sw = (int) (w * scale), sh = (int) (h * scale); screen_g2d.setColor(new Color(255, 0, 0, 150)); screen_g2d.fillRect(sx, sy, sw, sh); } catch (RasterFormatException e) { Debug.error(me + "getRegionImage: Problem making image\n%s", e.getMessage()); } screen_g2d.dispose(); return screen; } public static EditorRegionButton createFromString(EditorPane parentPane, String str) { String[] tokens = str.split("[(),]"); try { int x = Integer.valueOf(tokens[1].trim()), y = Integer.valueOf(tokens[2].trim()), w = Integer.valueOf(tokens[3].trim()), h = Integer.valueOf(tokens[4].trim()); return new EditorRegionButton(parentPane, x, y, w, h); } catch (Exception e) { Debug.error(me + "createFromString: Problem parsing region expression\n%s", e.getMessage()); } return null; } @Override public String toString() { return String.format("Region(%d,%d,%d,%d)", _x, _y, _w, _h); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorRegionLabel.java000077500000000000000000000075071315726130400253020ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.Color; import java.awt.Cursor; import java.awt.Font; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.border.Border; import org.sikuli.script.RunTime; import org.sikuli.script.Screen; import org.sikuli.util.EventSubject; import org.sikuli.util.OverlayCapturePrompt; import org.sikuli.script.ScreenImage; import org.sikuli.util.EventObserver; /** * * @author rhocke */ public class EditorRegionLabel extends JLabel implements MouseListener, EventObserver { protected String pyText; protected String oldPyText = null; private EditorPane editor; private final Color bc = Color.BLACK; private final Color bcs = Color.RED; private Color fc; private final Color fcs = Color.RED; private final Border paddingBorder = BorderFactory.createEmptyBorder(0, 4, 0, 3); private final Border border = BorderFactory.createLineBorder(bc); private final Border borders = BorderFactory.createLineBorder(bcs); private final Border bfinal = BorderFactory.createCompoundBorder(paddingBorder, border); private final Border bfinals = BorderFactory.createCompoundBorder(paddingBorder, borders); private SikuliIDEPopUpMenu popMenu = null; private boolean wasPopup = false; EditorRegionLabel() { } EditorRegionLabel(EditorPane pane, String lblText) { init(pane, lblText); } EditorRegionLabel(EditorPane pane, String lblText, String oldText) { oldPyText = oldText; init(pane, lblText); } private void init(EditorPane pane, String lblText) { editor = pane; pyText = lblText; setFont(new Font(editor.getFont().getFontName(), Font.PLAIN, editor.getFont().getSize())); setBorder(bfinal); setCursor(new Cursor(Cursor.HAND_CURSOR)); addMouseListener(this); setText(pyText.replaceAll("Region", "").replaceAll("\\(", "").replaceAll("\\)", "")); } public boolean isRegionLabel() { return true; } public static EditorRegionLabel labelFromString(EditorPane parentPane, String str) { EditorRegionLabel reg = new EditorRegionLabel(parentPane, str); return reg; } @Override public String toString() { return pyText; } @Override public void mousePressed(MouseEvent me) { checkPopup(me); } @Override public void mouseReleased(MouseEvent me) { checkPopup(me); } private void checkPopup(MouseEvent me) { if (me.isPopupTrigger()) { popMenu = editor.getPopMenuImage(); if (popMenu != null) { wasPopup = true; popMenu.show(this, me.getX(), me.getY()); } return; } } @Override public void mouseClicked(MouseEvent me) { if (wasPopup) { wasPopup = false; return; } SikuliIDE ide = SikuliIDE.getInstance(); ide.setVisible(false); setForeground(fc); setBorder(bfinal); RunTime.pause(0.5f); Screen.doPrompt(SikuliIDE._I("msgCapturePrompt"), this); } @Override public void update(EventSubject es) { OverlayCapturePrompt ocp = (OverlayCapturePrompt) es; ScreenImage simg = ocp.getSelection(); Screen.closePrompt(); if (simg != null) { try { Thread.sleep(300); } catch (InterruptedException ie) { } Rectangle roi = simg.getROI(); pyText = String.format("%d,%d,%d,%d", (int) roi.x, (int) roi.y, (int) roi.width, (int) roi.height); setText(pyText); pyText = "Region(" + pyText + ")"; } Screen.resetPrompt(ocp); SikuliIDE.showAgain(); } @Override public void mouseEntered(MouseEvent me) { setForeground(fcs); setBorder(bfinals); } @Override public void mouseExited(MouseEvent me) { setForeground(fc); setBorder(bfinal); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/EditorUndoManager.java000077500000000000000000000074521315726130400253160ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import javax.swing.event.DocumentEvent; import javax.swing.event.UndoableEditEvent; import javax.swing.event.UndoableEditListener; import javax.swing.text.AbstractDocument; import javax.swing.text.BadLocationException; import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.CannotUndoException; import javax.swing.undo.CompoundEdit; import javax.swing.undo.UndoableEdit; import java.util.ArrayList; import org.sikuli.basics.Debug; class MyCompoundEdit extends CompoundEdit { boolean isUnDone=false; public int getLength() { return edits.size(); } @Override public void undo() throws CannotUndoException { super.undo(); isUnDone=true; } @Override public void redo() throws CannotUndoException { super.redo(); isUnDone=false; } @Override public boolean canUndo() { return edits.size()>0 && !isUnDone; } @Override public boolean canRedo() { return edits.size()>0 && isUnDone; } } public class EditorUndoManager extends AbstractUndoableEdit implements UndoableEditListener { private static final String me = "EditorUndoManager: "; String lastEditName=null; ArrayList edits=new ArrayList(); MyCompoundEdit current; int pointer=-1; @Override public void undoableEditHappened(UndoableEditEvent e) { UndoableEdit edit=e.getEdit(); if (edit instanceof AbstractDocument.DefaultDocumentEvent) { AbstractDocument.DefaultDocumentEvent event=(AbstractDocument.DefaultDocumentEvent)edit; int start=event.getOffset(); int len=event.getLength(); Debug.log(9, "undoableEditHappened " + start + "," + len); boolean isNeedStart=false; if(event.getType().equals(DocumentEvent.EventType.CHANGE) || event.getType().equals(DocumentEvent.EventType.INSERT) ){ try { String text=event.getDocument().getText(start, len); if (text.contains("\n")) isNeedStart=true; } catch (BadLocationException e1) { Debug.error(me + "undoableEditHappened: Problem getting text\n%s", e1.getMessage()); } } if (current==null) { isNeedStart=true; } else if (lastEditName==null || !lastEditName.equals(edit.getPresentationName())) { isNeedStart=true; } while (pointer0) { current= new MyCompoundEdit(); } edits.add(current); pointer++; } @Override public void undo() throws CannotUndoException { if (!canUndo()) { throw new CannotUndoException(); } MyCompoundEdit u=edits.get(pointer); u.undo(); pointer--; refreshControls(); } @Override public void redo() throws CannotUndoException { if (!canRedo()) { throw new CannotUndoException(); } pointer++; MyCompoundEdit u=edits.get(pointer); u.redo(); refreshControls(); } @Override public boolean canUndo() { return pointer>=0; } @Override public boolean canRedo() { return edits.size()>0 && pointer class SectionBoxView extends BoxView { public SectionBoxView(Element elem, int axis) { super(elem, axis); } @Override protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { super.layoutMajorAxis(targetSpan, axis, offsets, spans); int count = getViewCount(); if (count == 0) { return; } int offset = 0; offsets[0] = 0; spans[0] = (int) getView(0).getMinimumSpan(View.Y_AXIS); for (int i = 1; i < count; i++) { View view = getView(i); spans[i] = (int) view.getMinimumSpan(View.Y_AXIS); offset += spans[i - 1]; offsets[i] = offset; } return; } @Override protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { super.layoutMinorAxis(targetSpan, axis, offsets, spans); int count = getViewCount(); for (int i = 0; i < count; i++) { offsets[i] = 0; } } } // // class LineBoxView extends BoxView { public LineBoxView(Element elem, int axis) { super(elem, axis); } // for Utilities.getRowStart @Override public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { Rectangle r = a.getBounds(); View v = getViewAtPosition(pos, r); if ((v != null) && (!v.getElement().isLeaf())) { // Don't adjust the height if the view represents a branch. return super.modelToView(pos, a, b); } int height = r.height; int y = r.y; Shape loc = super.modelToView(pos, a, b); r = loc.getBounds(); r.height = height; r.y = y; return r; } @Override protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { super.layoutMinorAxis(targetSpan, axis, offsets, spans); int maxH = 0; for (int i = 0; i < spans.length; i++) { if (spans[i] > maxH) { maxH = spans[i]; } } for (int i = 0; i < offsets.length; i++) { offsets[i] = (maxH - spans[i]) / 2; } } } // // class SyntaxHighlightLabelView extends LabelView { static FontMetrics _fMetrics = null; static String tabStr = nSpaces(PreferencesUser.getInstance().getTabWidth()); private static Map patternColors; private static Map patternColorsPython; private static Map patternColorsRuby; private static Map patternColorsSikuli; private static Font fontParenthesis; // private static String[] keywordsPython = { "and", "del", "for", "is", "raise", "assert", "elif", "from", "lambda", "return", "break", "else", "global", "not", "try", "class", "except", "if", "or", "while", "continue", "exec", "import", "pass", "yield", "def", "finally", "in", "print", "with", "self" }; private static String[] keywordsRuby = { "return", "break", "else", "class", "if", "nil", "begin", "end", "rescue", "next", "def", "puts", "java_import", "eval", "then", "alias", "and", "case", "defined?", "do", "elsif", "ensure", "false", "for", "in", "module", "not", "or", "redo", "retry", "self", "super", "true", "undef", "unless", "until", "when", "while", "yield", "BEGIN", "END", "__ENCODING__", "__END__", "__FILE__", "__LINE__", "require" }; private static String[] keywordsSikuliClass = { "Screen", "Region", "Location", "Match", "Pattern", "Env", "Key", "Button", "Finder", "App", "KeyModifier", "Mouse", "Keys", "Image", "ImagePath", "ImageGroup", "ImageFind", "ImageFinder", "Settings", }; private static String[] keywordsSikuli = { "find", "wait", "findAll", "findText", "findAllText", "waitVanish", "exists", "text", "click", "doubleClick", "rightClick", "hover", "wheel", "delayClick", "type", "paste", "write", "delayType", "dragDrop", "drag", "dropAt", "mouseMove", "mouseDown", "mouseUp", "keyDown", "keyUp", "onAppear", "onVanish", "onChange", "observe", "observeInBackground", "stopObserver", "isObserving", "popup", "input", "sleep", "run", "runScript", "switchApp", "openApp", "closeApp", "use", "useRemote", "ucode", "load", "capture", "selectRegion", "getOS", "getMouseLocation", "exit", //Region "right", "rightAt", "left", "leftAt", "above", "aboveAt", "below", "belowAt", "nearby", "inside", "grow", "union", "intersection", "getScreen", "getCenter", "setCenter", "setSize", "setLocation", "setX", "setY", "setW", "setH", "setRect", "setROI", "getX", "getY", "getW", "getH", "getRect", "getROI", "highlight", "add", "getLastScreenImageFile", "getNumberScreens", "getBounds", "contains", "containsMouse", "atMouse", "getTopLeft", "setTopLeft", "getTopRight", "setTopRight", "getBottomLeft", "setBottomLeft", "getBottomRight", "setBottomRight", "get", "setRows", "getRows", "setCols", "setCols", "getRowH", "getColW", "setRaster", "getRow", "getCol", "getCell", "getImage", //Event "repeat", //Pattern "similar", "targetOffset", "getLastMatch", "getLastMatches", "getTargetOffset", "getFilename", //global "setAutoWaitTimeout", "setBundlePath", "setShowActions", "setThrowException", "getAutoWaitTimeout", "getBundlePath", "getShowActions", "getThrowException", "highlightOff", "setFindFailedResponse", "getFindFailedResponse", "setWaitScanRate", "getWaitScanRate", "setObserveScanRate", "getObserveScanRate", "setWaitForVanish", "getWaitForVanish", "showScreens", "resetScreens", "showMonitors", "resetMonitors", "hasNext", "next", "destroy", "exact", "offset", "getOSVersion", "getScore", "getTarget", "getClipboard", "addImagePath", "removeImagePath", "getImagePath", "addImportPath", "resetImagePath", "getParentPath", "makePath", //App class "open", "close", "focus", "window", "focusedWindow",}; private static String[] constantsSikuli = { "FOREVER", "KEY_SHIFT", "KEY_CTRL", "KEY_META", "KEY_ALT", "KEY_CMD", "KEY_WIN", "ENTER", "BACKSPACE", "TAB", "ESC", "UP", "RIGHT", "DOWN", "LEFT", "PAGE_UP", "PAGE_DOWN", "DELETE", "END", "HOME", "INSERT", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "SHIFT", "CTRL", "ALT", "META", "CMD", "WIN", "SCREEN", "MIDDLE", "WHEEL_UP", "WHEEL_DOWN", "PRINTSCREEN", "SCROLL_LOCK", "PAUSE", "CAPS_LOCK", "NUM0", "NUM1", "NUM2", "NUM3", "NUM4", "NUM5", "NUM6", "NUM7", "NUM8", "NUM9", "SEPARATOR", "NUM_LOCK", "ADD", "MINUS", "MULTIPLY", "DIVIDE" }; private String sikuliContentType; // static { PreferencesUser.getInstance().addPreferenceChangeListener( new PreferenceChangeListener() { @Override public void preferenceChange(PreferenceChangeEvent event) { //TODO: need to reposition images if (event.getKey().equals("TAB_WIDTH")) { tabStr = nSpaces(Integer.parseInt(event.getNewValue())); } } }); fontParenthesis = new Font("Osaka-Mono", Font.PLAIN, 30); // NOTE: the order is important! patternColors = new HashMap(); patternColorsPython = new HashMap(); patternColorsRuby = new HashMap(); patternColorsSikuli = new HashMap(); patternColors.put(Pattern.compile("(#:.*$)"), new Color(220, 220, 220)); patternColors.put(Pattern.compile("(#.*$)"), new Color(138, 140, 193)); patternColors.put(Pattern.compile("(\"[^\"]*\"?)"), new Color(128, 0, 0)); patternColors.put(Pattern.compile("(\'[^\']*\'?)"), new Color(128, 0, 0)); patternColors.put(Pattern.compile("\\b([0-9]+)\\b"), new Color(128, 64, 0)); patternColorsPython.putAll(patternColors); patternColorsRuby.putAll(patternColors); for (int i = 0; i < keywordsPython.length; i++) { patternColorsPython.put(Pattern.compile( "\\b(" + keywordsPython[i] + ")\\b"), Color.blue); } for (int i = 0; i < keywordsRuby.length; i++) { patternColorsRuby.put(Pattern.compile( "\\b(" + keywordsRuby[i] + ")\\b"), Color.blue); } for (int i = 0; i < keywordsSikuli.length; i++) { patternColorsSikuli.put(Pattern.compile( "\\b(" + keywordsSikuli[i] + ")\\b"), new Color(63, 127, 127)); } for (int i = 0; i < keywordsSikuliClass.length; i++) { patternColorsSikuli.put(Pattern.compile( "\\b(" + keywordsSikuliClass[i] + ")\\b"), new Color(215, 41, 56)); } for (int i = 0; i < constantsSikuli.length; i++) { patternColorsSikuli.put(Pattern.compile( "\\b(" + constantsSikuli[i] + ")\\b"), new Color(128, 64, 0)); } patternColorsPython.putAll(patternColorsSikuli); patternColorsRuby.putAll(patternColorsSikuli); patternColorsSikuli = null; } public SyntaxHighlightLabelView(Element elm, String contentType) { super(elm); sikuliContentType = contentType; if (Runner.CPYTHON.equals(sikuliContentType)) { patternColors = patternColorsPython; } else if (Runner.CRUBY.equals(sikuliContentType)) { patternColors = patternColorsRuby; } } private static String nSpaces(int n) { char[] s = new char[n]; Arrays.fill(s, ' '); return new String(s); } // @Override public float getMinimumSpan(int axis) { float f = super.getMinimumSpan(axis); if (axis == View.X_AXIS) { f = tabbedWidth(); } return f; } @Override public float getMaximumSpan(int axis) { float f = super.getMaximumSpan(axis); if (axis == View.X_AXIS) { f = tabbedWidth(); } return f; } @Override public float getPreferredSpan(int axis) { float f = super.getPreferredSpan(axis); if (axis == View.X_AXIS) { f = tabbedWidth(); } return f; } private float tabbedWidth() { String str = getText(getStartOffset(), getEndOffset()).toString(); int tab = countTab(str); if (Settings.isMac()) { return stringWidth(str) + getRealTabWidth() * tab; } else { return stringWidth(str) + getTabWidth() * tab; } } private int countTab(String str) { int pos = -1; int count = 0; while ((pos = str.indexOf('\t', pos + 1)) != -1) { count++; } return count; } private int stringWidth(String str) { if (_fMetrics == null) { _fMetrics = getGraphics().getFontMetrics(); } return _fMetrics.stringWidth(str); } private float getRealTabWidth() { final int tabCharWidth; if (Settings.isMac()) { tabCharWidth = stringWidth("\t"); } else { tabCharWidth = stringWidth(" "); } return getTabWidth() - tabCharWidth /* + 1f */; //still buggy } private int getTabWidth() { return stringWidth(tabStr); } // @Override public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) { bias[0] = Position.Bias.Forward; Debug.log(9, "viewToModel: " + fx + " " + fy); String str = getText(getStartOffset(), getEndOffset()).toString(); int left = getStartOffset(), right = getEndOffset(); int pos = 0; while (left < right) { Debug.log(9, "viewToModel: " + left + " " + right + " " + pos); pos = (left + right) / 2; try { Shape s = modelToView(pos, a, bias[0]); float sx = s.getBounds().x; if (sx > fx) { right = pos; } else if (sx < fx) { left = pos + 1; } else { break; } } catch (BadLocationException ble) { break; } } pos = left - 1 >= getStartOffset() ? left - 1 : getStartOffset(); try { Debug.log(9, "viewToModel: try " + pos); Shape s1 = modelToView(pos, a, bias[0]); Shape s2 = modelToView(pos + 1, a, bias[0]); if (Math.abs(s1.getBounds().x - fx) <= Math.abs(s2.getBounds().x - fx)) { return pos; } else { return pos + 1; } } catch (BadLocationException ble) { } return pos; } @Override public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { int start = getStartOffset(), end = getEndOffset(); Debug.log(9, "[modelToView] start: " + start + " end: " + end + " pos:" + pos); String strHead = getText(start, pos).toString(); String strTail = getText(pos, end).toString(); Debug.log(9, "[modelToView] [" + strHead + "]-pos-[" + strTail + "]"); int tabHead = countTab(strHead), tabTail = countTab(strTail); Debug.log(9, "[modelToView] " + tabHead + " " + tabTail); Shape s = super.modelToView(pos, a, b); Rectangle ret = s.getBounds(); Debug.log(9, "[modelToView] super.bounds: " + ret); if (pos != end) { ret.x += tabHead * getRealTabWidth(); } //ret.width += tabTail*tabWidth; Debug.log(9, "[modelToView] new bounds: " + ret); return ret; } // @Override public void paint(Graphics g, Shape shape) { Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); //super.paint(g, shape); // for drawing selection String text = getText(getStartOffset(), getEndOffset()).toString(); //System.out.println("draw " + text); SortedMap posMap = new TreeMap(); SortedMap colorMap = new TreeMap(); buildColorMaps(text, posMap, colorMap); if (_fMetrics == null) { _fMetrics = g2d.getFontMetrics(); } Rectangle alloc = (shape instanceof Rectangle) ? (Rectangle) shape : shape.getBounds(); int sx = alloc.x; int sy = alloc.y + alloc.height - _fMetrics.getDescent(); int i = 0; for (Map.Entry entry : posMap.entrySet()) { int start = entry.getKey(); int end = entry.getValue(); if (i <= start) { g2d.setColor(Color.black); String str = text.substring(i, start); sx = drawString(g2d, str, sx, sy); } else { break; } g2d.setColor(colorMap.get(start)); i = end; String str = text.substring(start, i); /* * if( str.equals("(") || str.equals(")") ) * sx = drawParenthesis(g2d, str, sx, sy); * else */ sx = drawString(g2d, str, sx, sy); } // Paint possible remaining text black if (i < text.length()) { g2d.setColor(Color.black); String str = text.substring(i, text.length()); drawString(g2d, str, sx, sy); } } int drawString(Graphics2D g2d, String str, int x, int y) { if (str.length() == 0) { return x; } int tabPos = str.indexOf('\t'); if (tabPos != -1) { x = drawString(g2d, str.substring(0, tabPos), x, y); x = drawTab(g2d, x, y); x = drawString(g2d, str.substring(tabPos + 1), x, y); } else { g2d.drawString(str, x, y); x += stringWidth(str); } return x; } int drawTab(Graphics2D g2d, int x, int y) { return drawString(g2d, tabStr, x, y); } int drawParenthesis(Graphics2D g2d, String str, int x, int y) { Font origFont = g2d.getFont(); g2d.setFont(fontParenthesis); g2d.drawString(str, x, y); x += g2d.getFontMetrics().stringWidth(str); g2d.setFont(origFont); return x; } void buildColorMaps(String text, Map posMap, Map colorMap) { // Match all regexes on this snippet, store positions for (Map.Entry entry : patternColors.entrySet()) { Matcher matcher = entry.getKey().matcher(text); while (matcher.find()) { posMap.put(matcher.start(1), matcher.end()); colorMap.put(matcher.start(1), entry.getValue()); } } } // } // // class ButtonView extends ComponentView { public ButtonView(Element elem) { super(elem); Debug.log(6,"ViewCreate: Button"); } @Override public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { return super.modelToView(pos, a, b); } @Override public void paint(Graphics g, Shape shape) { JComponent comp = (JComponent) getComponent(); Rectangle alloc = (shape instanceof Rectangle) ? (Rectangle) shape : shape.getBounds(); } } // sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/ExtensionItem.java000077500000000000000000000132651315726130400245410ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.BorderLayout; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.Border; import javax.swing.border.EtchedBorder; import org.sikuli.basics.Debug; import org.sikuli.basics.ExtensionManager; import org.sikuli.basics.Settings; import org.sikuli.ide.SikuliIDEI18N; import org.sikuli.ide.SikuliIDEI18N; class ExtensionItem extends JPanel implements ActionListener { JButton _installCtrl; JButton _infoCtrl; String _name; String _infourl; String _jarurl; String _version; String _description; boolean _installed; final int NOT_INSTALLED = 0; final int INSTALLED = 1; final int OUT_OF_DATE = 2; int _status = NOT_INSTALLED; JPanel _controls; JPanel _content; JLabel _htmlLabel; public ExtensionItem(String name, String version, String description, String imgurl, String infourl, String jarurl) { this._name = name; this._version = version; this._infourl = infourl; this._infourl = infourl; this._jarurl = jarurl; this._description = description; this._status = getStatus(); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); Border loweredetched = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); setBorder(loweredetched); _content = new JPanel(); Image image = null; URL url; try { // Read from a URL url = new URL(imgurl); image = ImageIO.read(url); } catch (Exception e) { } if (image == null) { try { url = new URL(SikuliIDE.runTime.SikuliRepo + "extensionImage.jpg"); image = ImageIO.read(url); } catch (Exception e) { } } JLabel iconLabel = new JLabel(); iconLabel.setIcon(new ImageIcon(image)); iconLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); _content.setLayout(new BorderLayout(5, 5)); _content.add(iconLabel, BorderLayout.LINE_START); _htmlLabel = new JLabel(renderHTML()); _content.add(_htmlLabel); add(_content); JButton btn = new JButton(SikuliIDEI18N._I("extBtnInstall")); btn.addActionListener(this); btn.setActionCommand("Install"); _installCtrl = btn; _installCtrl.setFocusable(false); btn = new JButton(SikuliIDEI18N._I("extBtnInfo")); btn.addActionListener(this); btn.setActionCommand("Info"); _infoCtrl = btn; _infoCtrl.setFocusable(false); _controls = new JPanel(); _controls.setLayout(new BorderLayout(5, 5)); _controls.add(_infoCtrl, BorderLayout.LINE_START); _controls.add(_installCtrl, BorderLayout.LINE_END); add(_controls); updateControls(); addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { ExtensionManagerFrame.getInstance().select((ExtensionItem) e.getSource()); } }); } protected void setSelected(boolean selected) { _controls.setVisible(selected); /* Color darkRed = new Color(0.5f,0.0f,0.0f); Color bg,fg; if (selected){ bg = darkRed;//Color.red; fg = Color.white; }else{ bg = null; fg = Color.black; } _controls.setBackground(bg); _content.setBackground(bg); for (Component comp : _content.getComponents()){ comp.setForeground(fg); } */ } private String renderHTML() { String installed_version = ExtensionManager.getInstance().getVersion(_name); if (installed_version == null) { installed_version = "Not installed"; } return "

" + _name + " " + "(" + installed_version + ")" + "
" + _description + "
"; } private void updateControls() { int status = getStatus(); if (status == INSTALLED) { _installCtrl.setEnabled(false); _installCtrl.setText(SikuliIDEI18N._I("extMsgInstalled")); } else if (status == NOT_INSTALLED) { _installCtrl.setEnabled(true); _installCtrl.setText(SikuliIDEI18N._I("extBtnInstallVer", _version)); } else if (status == OUT_OF_DATE) { _installCtrl.setEnabled(true); _installCtrl.setText(SikuliIDEI18N._I("extBtnUpdateVer", _version)); } _htmlLabel.setText(renderHTML()); } private int getStatus() { ExtensionManager extMgr = ExtensionManager.getInstance(); if (!extMgr.isInstalled(_name)) { return NOT_INSTALLED; } else if (extMgr.isOutOfDate(_name, _version)) { return OUT_OF_DATE; } else { return INSTALLED; } } @Override public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if (cmd.equals("Install")) { Debug.log("Installing " + _name + " from " + _jarurl); ExtensionManager extMgr = ExtensionManager.getInstance(); // try to install the extension if (extMgr.install(_name, _jarurl, _version)) { // if successful, change the item's status to installed //_installed = true; updateControls(); } } else if (cmd.equals("Info")) { Debug.log("Openning URL: " + _infourl); openURL(_infourl, _name); } } static void openURL(String url, String name) { try { URL u = new URL(url); java.awt.Desktop.getDesktop().browse(u.toURI()); } catch (Exception ex) { Debug.error("SikuliExtension: " + name + " -- no information available!"); } } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/ExtensionManagerFrame.java000077500000000000000000000123541315726130400261660ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import org.json.simple.JSONValue; import org.sikuli.basics.Debug; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; import org.sikuli.basics.Settings; import org.sikuli.ide.SikuliIDE; public class ExtensionManagerFrame extends JFrame { final static String EXTENSION_LIST_URL = SikuliIDE.runTime.SikuliRepo + "extensions.json"; private static ExtensionManagerFrame _instance = null; private int selected_idx = 0; ArrayList _extensions; static public ExtensionManagerFrame getInstance() { if (_instance == null) { //TODO reactivate extension manager _instance = null; // _instance = new ExtensionManagerFrame(); } return _instance; } private ExtensionManagerFrame() { super(); setTitle("Sikuli Extensions"); setResizable(false); createComponents(); addKeyListener(new SelectExtensionKeyListener()); pack(); setLocationRelativeTo(null); } private void createComponents() { Container pane; pane = getContentPane(); pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); try { // Populate the list of extensions _extensions = retrieveExtensions(); for (ExtensionItem ext : _extensions) { pane.add(ext); } // Select the first one select(0); } catch (IOException io) { String msg = "Unable to load extensions list from: " + EXTENSION_LIST_URL; JTextField txt = new JTextField(msg); txt.setBackground(null); txt.setBorder(null); txt.setEditable(false); pane.add(txt); } JPanel bottomBar = new JPanel(); bottomBar.setLayout(new BorderLayout()); bottomBar.setMinimumSize(new Dimension(400, 20)); JButton closeBtn = new JButton("Close"); closeBtn.addActionListener(new ActionListener() { //TODO: clean extensions: old versions? @Override public void actionPerformed(ActionEvent arg0) { dispose(); } }); closeBtn.setFocusable(false); bottomBar.add(closeBtn, BorderLayout.LINE_END); pane.add(bottomBar); } private ArrayList retrieveExtensions() throws IOException { ArrayList extensions = new ArrayList(); Debug.log(2, "Retrieving from " + EXTENSION_LIST_URL); String json = ExtensionManagerFrame.html2txt(EXTENSION_LIST_URL); Object obj = JSONValue.parse(json); Map map = (Map) obj; Map extension_list = (Map) map.get("extension-list"); List exts = (List) extension_list.get("extensions"); for (Object o : exts) { Map ext = (Map) o; String name = (String) ext.get("name"); String version = (String) ext.get("version"); String description = (String) ext.get("description"); String imgurl = (String) ext.get("imgurl"); String infourl = (String) ext.get("infourl"); String jarurl = (String) ext.get("jarurl"); extensions.add(new ExtensionItem(name, version, description, imgurl, infourl, jarurl)); } return extensions; } private static String html2txt(String urlstring) throws IOException { URL url = new URL(urlstring); URLConnection yc = url.openConnection(); yc.setConnectTimeout(5000); BufferedReader in = new BufferedReader( new InputStreamReader( yc.getInputStream())); String txt = ""; String inputLine; while ((inputLine = in.readLine()) != null) { txt = txt + inputLine; } in.close(); return txt; } protected void select(ExtensionItem ext) { int idx = _extensions.indexOf(ext); select(idx); } private void select(int idx) { _extensions.get(selected_idx).setSelected(false); selected_idx = idx; _extensions.get(selected_idx).setSelected(true); } private void selectPrevious() { if (selected_idx == 0) { return; } select(selected_idx - 1); } private void selectNext() { if (selected_idx == _extensions.size() - 1) { return; } select(selected_idx + 1); } private class SelectExtensionKeyListener implements KeyListener { @Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_UP) { selectPrevious(); } else if (key == KeyEvent.VK_DOWN) { selectNext(); } } @Override public void keyReleased(KeyEvent arg0) { } @Override public void keyTyped(KeyEvent arg0) { } } // public static void main(String[] args) throws IOException { // // ExtensionManagerFrame f = ExtensionManagerFrame.getInstance(); // f.setVisible(true); // } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/GifDecoder.java000066400000000000000000000440771315726130400237430ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import java.awt.image.*; import java.io.*; import java.net.*; import java.util.*; public class GifDecoder { /** * File read status: No errors. */ public static final int STATUS_OK = 0; /** * File read status: Error decoding file (may be partially decoded) */ public static final int STATUS_FORMAT_ERROR = 1; /** * File read status: Unable to open source. */ public static final int STATUS_OPEN_ERROR = 2; protected BufferedInputStream in; protected int status; protected int width; // full image width protected int height; // full image height protected boolean gctFlag; // global color table used protected int gctSize; // size of global color table protected int loopCount = 1; // iterations; 0 = repeat forever protected int[] gct; // global color table protected int[] lct; // local color table protected int[] act; // active color table protected int bgIndex; // background color index protected int bgColor; // background color protected int lastBgColor; // previous bg color protected int pixelAspect; // pixel aspect ratio protected boolean lctFlag; // local color table flag protected boolean interlace; // interlace flag protected int lctSize; // local color table size protected int ix, iy, iw, ih; // current image rectangle protected Rectangle lastRect; // last image rect protected BufferedImage image; // current frame protected BufferedImage lastImage; // previous frame protected byte[] block = new byte[256]; // current data block protected int blockSize = 0; // block size // last graphic control extension info protected int dispose = 0; // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev protected int lastDispose = 0; protected boolean transparency = false; // use transparent color protected int delay = 0; // delay in milliseconds protected int transIndex; // transparent color index protected static final int MaxStackSize = 4096; // max decoder pixel stack size // LZW decoder working arrays protected short[] prefix; protected byte[] suffix; protected byte[] pixelStack; protected byte[] pixels; protected ArrayList frames; // frames read from current file protected int frameCount; static class GifFrame { public GifFrame(BufferedImage im, int del) { image = im; delay = del; } public BufferedImage image; public int delay; } /** * Gets display duration for specified frame. * * @param n int index of frame * @return delay in milliseconds */ public int getDelay(int n) { // delay = -1; if ((n >= 0) && (n < frameCount)) { delay = ((GifFrame) frames.get(n)).delay; } return delay; } /** * Gets the number of frames read from file. * @return frame count */ public int getFrameCount() { return frameCount; } /** * Gets the first (or only) image read. * * @return BufferedImage containing first frame, or null if none. */ public BufferedImage getImage() { return getFrame(0); } /** * Gets the "Netscape" iteration count, if any. * A count of 0 means repeat indefinitiely. * * @return iteration count if one was specified, else 1. */ public int getLoopCount() { return loopCount; } /** * Creates new frame image from current data (and previous * frames as specified by their disposition codes). */ protected void setPixels() { // expose destination image's pixels as int array int[] dest = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); // fill in starting image contents based on last image's dispose code if (lastDispose > 0) { if (lastDispose == 3) { // use image before last int n = frameCount - 2; if (n > 0) { lastImage = getFrame(n - 1); } else { lastImage = null; } } if (lastImage != null) { int[] prev = ((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData(); System.arraycopy(prev, 0, dest, 0, width * height); // copy pixels if (lastDispose == 2) { // fill last image rect area with background color Graphics2D g = image.createGraphics(); Color c = null; if (transparency) { c = new Color(0, 0, 0, 0); // assume background is transparent } else { c = new Color(lastBgColor); // use given background color } g.setColor(c); g.setComposite(AlphaComposite.Src); // replace area g.fill(lastRect); g.dispose(); } } } // copy each source line to the appropriate place in the destination int pass = 1; int inc = 8; int iline = 0; for (int i = 0; i < ih; i++) { int line = i; if (interlace) { if (iline >= ih) { pass++; switch (pass) { case 2 : iline = 4; break; case 3 : iline = 2; inc = 4; break; case 4 : iline = 1; inc = 2; } } line = iline; iline += inc; } line += iy; if (line < height) { int k = line * width; int dx = k + ix; // start of line in dest int dlim = dx + iw; // end of dest line if ((k + width) < dlim) { dlim = k + width; // past dest edge } int sx = i * iw; // start of line in source while (dx < dlim) { // map color and insert in destination int index = ((int) pixels[sx++]) & 0xff; int c = act[index]; if (c != 0) { dest[dx] = c; } dx++; } } } } /** * Gets the image contents of frame n. * * @param n frame number * @return BufferedImage representation of frame, or null if n is invalid. */ public BufferedImage getFrame(int n) { BufferedImage im = null; if ((n >= 0) && (n < frameCount)) { im = ((GifFrame) frames.get(n)).image; } return im; } /** * Gets image size. * * @return GIF image dimensions */ public Dimension getFrameSize() { return new Dimension(width, height); } /** * Reads GIF image from stream * * @param is BufferedInputStream containing GIF file. * @return read status code (0 = no errors) */ public int read(BufferedInputStream is) { init(); if (is != null) { in = is; readHeader(); if (!err()) { readContents(); if (frameCount < 0) { status = STATUS_FORMAT_ERROR; } } } else { status = STATUS_OPEN_ERROR; } try { is.close(); } catch (IOException e) { } return status; } /** * Reads GIF image from stream * * @param is InputStream containing GIF file. * @return read status code (0 = no errors) */ public int read(InputStream is) { init(); if (is != null) { if (!(is instanceof BufferedInputStream)) is = new BufferedInputStream(is); in = (BufferedInputStream) is; readHeader(); if (!err()) { readContents(); if (frameCount < 0) { status = STATUS_FORMAT_ERROR; } } } else { status = STATUS_OPEN_ERROR; } try { is.close(); } catch (IOException e) { } return status; } /** * Reads GIF file from specified file/URL source * (URL assumed if name contains ":/" or "file:") * * @param name String containing source * @return read status code (0 = no errors) */ public int read(String name) { status = STATUS_OK; try { name = name.trim().toLowerCase(); if ((name.indexOf("file:") >= 0) || (name.indexOf(":/") > 0)) { URL url = new URL(name); in = new BufferedInputStream(url.openStream()); } else { in = new BufferedInputStream(new FileInputStream(name)); } status = read(in); } catch (IOException e) { status = STATUS_OPEN_ERROR; } return status; } /** * Decodes LZW image data into pixel array. * Adapted from John Cristy's ImageMagick. */ protected void decodeImageData() { int NullCode = -1; int npix = iw * ih; int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi; if ((pixels == null) || (pixels.length < npix)) { pixels = new byte[npix]; // allocate new pixel array } if (prefix == null) prefix = new short[MaxStackSize]; if (suffix == null) suffix = new byte[MaxStackSize]; if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1]; // Initialize GIF data stream decoder. data_size = read(); clear = 1 << data_size; end_of_information = clear + 1; available = clear + 2; old_code = NullCode; code_size = data_size + 1; code_mask = (1 << code_size) - 1; for (code = 0; code < clear; code++) { prefix[code] = 0; suffix[code] = (byte) code; } // Decode GIF pixel stream. datum = bits = count = first = top = pi = bi = 0; for (i = 0; i < npix;) { if (top == 0) { if (bits < code_size) { // Load bytes until there are enough bits for a code. if (count == 0) { // Read a new data block. count = readBlock(); if (count <= 0) break; bi = 0; } datum += (((int) block[bi]) & 0xff) << bits; bits += 8; bi++; count--; continue; } // Get the next code. code = datum & code_mask; datum >>= code_size; bits -= code_size; // Interpret the code if ((code > available) || (code == end_of_information)) break; if (code == clear) { // Reset decoder. code_size = data_size + 1; code_mask = (1 << code_size) - 1; available = clear + 2; old_code = NullCode; continue; } if (old_code == NullCode) { pixelStack[top++] = suffix[code]; old_code = code; first = code; continue; } in_code = code; if (code == available) { pixelStack[top++] = (byte) first; code = old_code; } while (code > clear) { pixelStack[top++] = suffix[code]; code = prefix[code]; } first = ((int) suffix[code]) & 0xff; // Add a new string to the string table, if (available >= MaxStackSize) break; pixelStack[top++] = (byte) first; prefix[available] = (short) old_code; suffix[available] = (byte) first; available++; if (((available & code_mask) == 0) && (available < MaxStackSize)) { code_size++; code_mask += available; } old_code = in_code; } // Pop a pixel off the pixel stack. top--; pixels[pi++] = pixelStack[top]; i++; } for (i = pi; i < npix; i++) { pixels[i] = 0; // clear missing pixels } } /** * Returns true if an error was encountered during reading/decoding */ protected boolean err() { return status != STATUS_OK; } /** * Initializes or re-initializes reader */ protected void init() { status = STATUS_OK; frameCount = 0; frames = new ArrayList(); gct = null; lct = null; } /** * Reads a single byte from the input stream. */ protected int read() { int curByte = 0; try { curByte = in.read(); } catch (IOException e) { status = STATUS_FORMAT_ERROR; } return curByte; } /** * Reads next variable length block from input. * * @return number of bytes stored in "buffer" */ protected int readBlock() { blockSize = read(); int n = 0; if (blockSize > 0) { try { int count = 0; while (n < blockSize) { count = in.read(block, n, blockSize - n); if (count == -1) break; n += count; } } catch (IOException e) { } if (n < blockSize) { status = STATUS_FORMAT_ERROR; } } return n; } /** * Reads color table as 256 RGB integer values * * @param ncolors int number of colors to read * @return int array containing 256 colors (packed ARGB with full alpha) */ protected int[] readColorTable(int ncolors) { int nbytes = 3 * ncolors; int[] tab = null; byte[] c = new byte[nbytes]; int n = 0; try { n = in.read(c); } catch (IOException e) { } if (n < nbytes) { status = STATUS_FORMAT_ERROR; } else { tab = new int[256]; // max size to avoid bounds checks int i = 0; int j = 0; while (i < ncolors) { int r = ((int) c[j++]) & 0xff; int g = ((int) c[j++]) & 0xff; int b = ((int) c[j++]) & 0xff; tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b; } } return tab; } /** * Main file parser. Reads GIF content blocks. */ protected void readContents() { // read GIF file content blocks boolean done = false; while (!(done || err())) { int code = read(); switch (code) { case 0x2C : // image separator readImage(); break; case 0x21 : // extension code = read(); switch (code) { case 0xf9 : // graphics control extension readGraphicControlExt(); break; case 0xff : // application extension readBlock(); String app = ""; for (int i = 0; i < 11; i++) { app += (char) block[i]; } if (app.equals("NETSCAPE2.0")) { readNetscapeExt(); } else skip(); // don't care break; default : // uninteresting extension skip(); } break; case 0x3b : // terminator done = true; break; case 0x00 : // bad byte, but keep going and see what happens break; default : status = STATUS_FORMAT_ERROR; } } } /** * Reads Graphics Control Extension values */ protected void readGraphicControlExt() { read(); // block size int packed = read(); // packed fields dispose = (packed & 0x1c) >> 2; // disposal method if (dispose == 0) { dispose = 1; // elect to keep old image if discretionary } transparency = (packed & 1) != 0; delay = readShort() * 10; // delay in milliseconds transIndex = read(); // transparent color index read(); // block terminator } /** * Reads GIF file header information. */ protected void readHeader() { String id = ""; for (int i = 0; i < 6; i++) { id += (char) read(); } if (!id.startsWith("GIF")) { status = STATUS_FORMAT_ERROR; return; } readLSD(); if (gctFlag && !err()) { gct = readColorTable(gctSize); bgColor = gct[bgIndex]; } } /** * Reads next frame image */ protected void readImage() { ix = readShort(); // (sub)image position & size iy = readShort(); iw = readShort(); ih = readShort(); int packed = read(); lctFlag = (packed & 0x80) != 0; // 1 - local color table flag interlace = (packed & 0x40) != 0; // 2 - interlace flag // 3 - sort flag // 4-5 - reserved lctSize = 2 << (packed & 7); // 6-8 - local color table size if (lctFlag) { lct = readColorTable(lctSize); // read table act = lct; // make local table active } else { act = gct; // make global table active if (bgIndex == transIndex) bgColor = 0; } int save = 0; if (transparency) { save = act[transIndex]; act[transIndex] = 0; // set transparent color if specified } if (act == null) { status = STATUS_FORMAT_ERROR; // no color table defined } if (err()) return; decodeImageData(); // decode pixel data skip(); if (err()) return; frameCount++; // create new image to receive frame data image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); setPixels(); // transfer pixel data to image frames.add(new GifFrame(image, delay)); // add image to frame list if (transparency) { act[transIndex] = save; } resetFrame(); } /** * Reads Logical Screen Descriptor */ protected void readLSD() { // logical screen size width = readShort(); height = readShort(); // packed fields int packed = read(); gctFlag = (packed & 0x80) != 0; // 1 : global color table flag // 2-4 : color resolution // 5 : gct sort flag gctSize = 2 << (packed & 7); // 6-8 : gct size bgIndex = read(); // background color index pixelAspect = read(); // pixel aspect ratio } /** * Reads Netscape extenstion to obtain iteration count */ protected void readNetscapeExt() { do { readBlock(); if (block[0] == 1) { // loop count sub-block int b1 = ((int) block[1]) & 0xff; int b2 = ((int) block[2]) & 0xff; loopCount = (b2 << 8) | b1; } } while ((blockSize > 0) && !err()); } /** * Reads next 16-bit value, LSB first */ protected int readShort() { // read 16-bit value, LSB first return read() | (read() << 8); } /** * Resets frame state for reading next image. */ protected void resetFrame() { lastDispose = dispose; lastRect = new Rectangle(ix, iy, iw, ih); lastImage = image; lastBgColor = bgColor; dispose = 0; transparency = false; delay = 0; lct = null; } /** * Skips variable length blocks up to and including * next zero length block. */ protected void skip() { do { readBlock(); } while ((blockSize > 0) && !err()); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/LoadingSpinner.java000066400000000000000000000015211315726130400246470ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.image.*; import java.util.Date; public class LoadingSpinner { protected GifDecoder _gif; protected long _start_t = 0; protected int _curFrame_i = 0; protected BufferedImage _curFrame = null; public LoadingSpinner(){ _gif = new GifDecoder(); _gif.read(getClass().getResourceAsStream("/icons/loading.gif")); _curFrame = _gif.getFrame(0); } public BufferedImage getFrame(){ int delay = _gif.getDelay(_curFrame_i); long now = (new Date()).getTime(); if(now - _start_t >= delay){ _start_t = now; _curFrame_i = (_curFrame_i+1) % _gif.getFrameCount(); _curFrame = _gif.getFrame(_curFrame_i); } return _curFrame; } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PatternPaneNaming.java000077500000000000000000000132341315726130400253150ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import org.sikuli.basics.PreferencesUser; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.io.*; import javax.swing.*; import javax.swing.border.*; import org.sikuli.basics.Debug; import org.sikuli.script.TextRecognizer; //RaiMan not used import org.sikuli.script.TextRecognizer; public class PatternPaneNaming extends JPanel { final static int TXT_FILE_EXT_LENGTH = 4; final static int TXT_FILENAME_LENGTH = 20; final static int MAX_OCR_TEXT_LENGTH = 12; final static int THUMB_MAX_HEIGHT = 200; EditorPatternButton _imgBtn; JTextField _txtPath, _txtFileExt; JComboBox _txtFilename; String _oldFilename; static String _I(String key, Object... args) { return SikuliIDEI18N._I(key, args); } public PatternPaneNaming(EditorPatternButton imgBtn, JLabel msgApplied) { super(new GridBagLayout()); init(imgBtn, msgApplied); } private void init(EditorPatternButton imgBtn, JLabel msgApplied) { _imgBtn = imgBtn; JLabel lblPath = new JLabel(_I("lblPath")); JLabel lblFilename = new JLabel(_I("lblFilename")); String filename = _imgBtn.getFilename(); File f = new File(filename); String fullpath = f.getParent(); filename = getFilenameWithoutExt(f); _oldFilename = filename; BufferedImage thumb = _imgBtn.createThumbnailImage(THUMB_MAX_HEIGHT); Border border = LineBorder.createGrayLineBorder(); JLabel lblThumb = new JLabel(new ImageIcon(thumb)); lblThumb.setBorder(border); _txtPath = new JTextField(fullpath, TXT_FILENAME_LENGTH); _txtPath.setEditable(false); _txtPath.setEnabled(false); String[] candidates = new String[]{filename}; // /* String ocrText = getFilenameFromImage(thumb); if(ocrText.length()>0 && !ocrText.equals(filename)) candidates = new String[] {filename, ocrText}; */ // _txtFilename = new AutoCompleteCombo(candidates); _txtFileExt = new JTextField(getFileExt(f), TXT_FILE_EXT_LENGTH); _txtFileExt.setEditable(false); _txtFileExt.setEnabled(false); GridBagConstraints c = new GridBagConstraints(); c.gridy = 0; c.insets = new Insets(100, 0, 0, 0); this.add(new JLabel(""), c); c = new GridBagConstraints(); c.fill = 0; c.gridwidth = 3; c.gridy = 1; c.insets = new Insets(0, 10, 20, 10); this.add(lblThumb, c); c = new GridBagConstraints(); c.fill = 1; c.gridy = 2; this.add(lblPath, c); c.gridx = 1; c.gridwidth = 2; this.add(_txtPath, c); c = new GridBagConstraints(); c.gridy = 3; c.fill = 0; this.add(lblFilename, c); this.add(_txtFilename, c); this.add(_txtFileExt, c); c = new GridBagConstraints(); c.gridy = 4; c.gridx = 1; c.insets = new Insets(200, 0, 0, 0); this.add(msgApplied, c); } protected void updateFilename() { _oldFilename = (String) _txtFilename.getSelectedItem(); } private String getFilenameWithoutExt(File f) { String name = f.getName(); int pos = name.lastIndexOf('.'); if (pos > 0) { return name.substring(0, pos); } return name; } private String getFileExt(File f) { String name = f.getName(); int pos = name.lastIndexOf('.'); if (pos > 0) { return name.substring(pos); } return ""; } public static String getFilenameFromImage(BufferedImage img) { TextRecognizer tr = TextRecognizer.getInstance(); if (!PreferencesUser.getInstance().getPrefMoreTextOCR() || tr == null) { return ""; } String text = tr.recognize(img); text = text.replaceAll("\\W", ""); if (text.length() > MAX_OCR_TEXT_LENGTH) { return text.substring(0, MAX_OCR_TEXT_LENGTH); } return text; } public String getAbsolutePath() { return _txtPath.getText() + File.separatorChar + _txtFilename.getSelectedItem() + _txtFileExt.getText(); } public boolean isDirty() { String newFilename = (String) _txtFilename.getSelectedItem(); return !_oldFilename.equals(newFilename); } } class AutoCompleteCombo extends JComboBox { private static final String me = "PatternPaneNaming: "; final static int TXT_FILENAME_LENGTH = 20; public int caretPos = 0; public JTextField editor = null; public AutoCompleteCombo(final Object items[]) { super(items); this.setEditable(true); setHook(); //hideDropDownButton(); } private void hideDropDownButton() { for (Component component : this.getComponents()) { if (component instanceof AbstractButton && component.isVisible()) { component.setVisible(false); this.revalidate(); } } } @Override public void setSelectedIndex(int ind) { super.setSelectedIndex(ind); editor.setText(getItemAt(ind).toString()); editor.setSelectionEnd(caretPos + editor.getText().length()); editor.moveCaretPosition(caretPos); } public void setHook() { ComboBoxEditor anEditor = this.getEditor(); if (anEditor.getEditorComponent() instanceof JTextField) { editor = (JTextField) anEditor.getEditorComponent(); editor.setColumns(TXT_FILENAME_LENGTH); editor.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent ev) { char key = ev.getKeyChar(); if (!(Character.isLetterOrDigit(key) || Character .isSpaceChar(key))) { return; } caretPos = editor.getCaretPosition(); String text = ""; try { text = editor.getText(0, caretPos); } catch (Exception ex) { Debug.error(me + "setHook: Problem getting image file name\n%s", ex.getMessage()); } int n = getItemCount(); for (int i = 0; i < n; i++) { int ind = ((String) getItemAt(i)).indexOf(text); if (ind == 0) { setSelectedIndex(i); return; } } } }); } } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PatternPaneScreenshot.java000077500000000000000000000223631315726130400262240ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; import org.sikuli.script.Finder; import org.sikuli.script.Match; import org.sikuli.script.Pattern; import org.sikuli.script.Region; import org.sikuli.script.ScreenImage; import org.sikuli.script.ScreenUnion; import org.sikuli.basics.Debug; class PatternPaneScreenshot extends JPanel implements ChangeListener, ComponentListener { private static final String me = "PatternPaneScreenshot: "; static int DEFAULT_H; static int MAX_NUM_MATCHING = EditorPatternButton.DEFAULT_NUM_MATCHES; Region _match_region; int _width, _height; double _scale, _ratio; boolean _runFind = false; float _similarity; int _numMatches; Set _fullMatches = null; final Boolean _fullMatchesSynch = true; ArrayList _showMatches = null; final Boolean _showMatchesSynch = true; protected ScreenImage _simg; protected BufferedImage _screen = null; protected Rectangle _uBound; private JLabel btnSimilar, _lblMatchCount; private JSlider sldSimilar; private JSpinner txtNumMatches; private LoadingSpinner _loading; static String _I(String key, Object... args) { return SikuliIDEI18N._I(key, args); } private JLabel _msgApplied; public PatternPaneScreenshot(ScreenImage simg, Dimension pDim, JLabel msgApplied) { init(simg, pDim, msgApplied); } private void init(ScreenImage simg, Dimension pDim, JLabel msgApplied) { _msgApplied = msgApplied; _match_region = new ScreenUnion(); _ratio = (double) _match_region.w / _match_region.h; _height = pDim.height - 200; _scale = (double) _height / _match_region.h; _width = (int) (_match_region.w * _scale); setPreferredSize(new Dimension(_width, _height)); addComponentListener(this); _simg = simg; _screen = simg.getImage(); //TODO Necessary? MAX_NUM_MATCHING = (int) Vision.getParameter("FindAllMaxReturn"); autoResize(); _loading = new LoadingSpinner(); } public JComponent createControls() { JPanel pane = new JPanel(new GridBagLayout()); btnSimilar = new JLabel(_I("lblSimilarity")); sldSimilar = createSlider(); sldSimilar.setPreferredSize(new Dimension(250, 35)); /* JLabel lblPreNumMatches = new JLabel(_I("lblNumberOfMatches")); _lblMatchCount = new JLabel("0"); Dimension size = _lblMatchCount.getPreferredSize(); size.width *= 2; _lblMatchCount.setPreferredSize(size); SpinnerNumberModel model = new SpinnerNumberModel(10, 0, PatternPaneScreenshot.MAX_NUM_MATCHING, 1); txtNumMatches = new JSpinner(model); lblPreNumMatches.setLabelFor(txtNumMatches); */ GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.gridy = 0; pane.add(sldSimilar, c); pane.add(btnSimilar, c); pane.add(_msgApplied, c); //TODO num Matches needed? /* c.fill = 0; c.gridy = 1; c.gridwidth = 1; pane.add(lblPreNumMatches, c); c.insets = new Insets(0, 10, 0, 0); pane.add(_lblMatchCount, c); c.insets = new Insets(0, 0, 0, 0); pane.add(new JLabel("/"), c); c.insets = new Insets(0, 0, 0, 100); pane.add(txtNumMatches, c); txtNumMatches.addChangeListener(this); */ return pane; } private JSlider createSlider() { sldSimilar = new PatternSimilaritySlider(0, 100, 70, btnSimilar); sldSimilar.setMajorTickSpacing(10); sldSimilar.setPaintTicks(true); Hashtable labelTable = new Hashtable(); labelTable.put(new Integer(0), new JLabel("00")); labelTable.put(new Integer(50), new JLabel("50")); labelTable.put(new Integer(100), new JLabel("99")); sldSimilar.setLabelTable(labelTable); sldSimilar.setPaintLabels(true); sldSimilar.addChangeListener(this); return sldSimilar; } public void setParameters(final String patFilename, final boolean exact, final float similarity, final int numMatches) { if (!_runFind) { _runFind = true; Thread thread = new Thread(new Runnable() { @Override public void run() { try { Finder f = new Finder(_simg, _match_region); f.findAll(new Pattern(patFilename).similar(0.00001f)); _fullMatches = new TreeSet(new Comparator() { @Override public int compare(Object o1, Object o2) { return -1 * ((Comparable) o1).compareTo(o2); } @Override public boolean equals(Object o) { return false; } @Override public int hashCode() { int hash = 3; return hash; } }); int count = 0; while (f.hasNext()) { if (++count > MAX_NUM_MATCHING) { break; } Match m = f.next(); Debug.log(4, me + "f.next(%d): " + m.toString(), count); synchronized (_fullMatchesSynch) { _fullMatches.add(m); } setParameters(exact, similarity, numMatches); } } catch (Exception e) { Debug.error(me + "Problems searching image in ScreenUnion\n%s", e.getMessage()); } } }); thread.start(); } else { setParameters(exact, similarity, numMatches); } } public void setParameters(boolean exact, float similarity, int numMatches) { if (numMatches > MAX_NUM_MATCHING) { numMatches = MAX_NUM_MATCHING; } if (!exact) { _similarity = similarity; } else { _similarity = 0.99f; } _numMatches = numMatches; filterMatches(_similarity, _numMatches); sldSimilar.setValue((int) (similarity * 100)); repaint(); } @Override public void componentHidden(ComponentEvent e) { } @Override public void componentMoved(ComponentEvent e) { } @Override public void componentShown(ComponentEvent e) { } @Override public void componentResized(ComponentEvent e) { autoResize(); } private void autoResize() { _width = getWidth(); if (_width == 0) { _width = (int) getPreferredSize().getWidth(); } _height = (int) ((double) _width / _ratio); _scale = (double) _height / _match_region.h; setPreferredSize(new Dimension(_width, _height)); repaint(); } public boolean isExact() { return _similarity >= 0.99f; } public float getSimilarity() { return _similarity; } public int getNumMatches() { return _numMatches; } public void setSimilarity(float similarity) { _similarity = similarity > 0.99f ? 0.99f : similarity; filterMatches(_similarity, _numMatches); repaint(); } public void setNumMatches(int numMatches) { _numMatches = numMatches; filterMatches(_similarity, _numMatches); repaint(); } void filterMatches(float similarity, int numMatches) { int count = 0; if (_fullMatches != null && numMatches >= 0) { if (_showMatches == null) { _showMatches = new ArrayList(); } synchronized (_showMatchesSynch) { _showMatches.clear(); if (numMatches == 0) { return; } synchronized (_fullMatchesSynch) { for (Match m : _fullMatches) { if (m.getScore() >= similarity) { _showMatches.add(m); if (++count >= numMatches) { break; } } } } } // _lblMatchCount.setText(Integer.toString(count)); Debug.log(4, "filterMatches(%.2f,%d): %d", similarity, numMatches, count); } } @Override public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (_screen != null) { g2d.drawImage(_screen, 0, 0, _width, _height, null); if (_showMatches != null) { paintMatches(g2d); } else { paintOverlay(g2d); } } } void paintOverlay(Graphics2D g2d) { g2d.setColor(new Color(0, 0, 0, 150)); g2d.fillRect(0, 0, _width, _height); BufferedImage spinner = _loading.getFrame(); g2d.drawImage(spinner, null, _width / 2 - spinner.getWidth() / 2, _height / 2 - spinner.getHeight() / 2); repaint(); } void paintMatches(Graphics2D g2d) { synchronized (_showMatchesSynch) { for (Match m : _showMatches) { int x = (int) ((m.x - _match_region.x) * _scale); int y = (int) ((m.y - _match_region.y) * _scale); int w = (int) (m.w * _scale); int h = (int) (m.h * _scale); Color c = PatternSimilaritySlider.getScoreColor(m.getScore()); g2d.setColor(c); g2d.fillRect(x, y, w, h); g2d.drawRect(x, y, w - 1, h - 1); } } } @Override public void stateChanged(javax.swing.event.ChangeEvent e) { Object src = e.getSource(); if (src instanceof JSlider) { JSlider source = (JSlider) e.getSource(); int val = (int) source.getValue(); setSimilarity((float) val / 100); } else if (src instanceof JSpinner) { JSpinner source = (JSpinner) e.getSource(); int val = (Integer) source.getValue(); setNumMatches(val); } } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PatternPaneTargetOffset.java000077500000000000000000000220101315726130400264710ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.io.*; import javax.imageio.*; import javax.swing.*; import javax.swing.event.*; import org.sikuli.basics.Debug; import org.sikuli.script.Finder; import org.sikuli.script.Location; import org.sikuli.script.Match; import org.sikuli.script.Region; import org.sikuli.script.Screen; import org.sikuli.script.ScreenImage; class PatternPaneTargetOffset extends JPanel implements MouseListener, MouseWheelListener, ChangeListener { final static String me = "PatternPaneTargetOffset: "; static int DEFAULT_H = 120; final static float DEFAULT_PATTERN_RATIO = 0.1f; private static final Color COLOR_BG_LINE = new Color(210, 210, 210, 130); ScreenImage _simg; BufferedImage _img; Match _match = null; int _viewX, _viewY, _viewW, _viewH; float _zoomRatio, _ratio; Location _tar = new Location(0, 0); Location _offset = new Location(0, 0); JSpinner txtX, txtY; private LoadingSpinner _loading; private boolean _finding = true; private JLabel _msgApplied; public PatternPaneTargetOffset( ScreenImage simg, String patFilename, Location initOffset, Dimension pDim, JLabel msgApplied) { _msgApplied = msgApplied; _simg = simg; _ratio = DEFAULT_PATTERN_RATIO; setPreferredSize(new Dimension(pDim.width, pDim.height - DEFAULT_H)); addMouseListener(this); addMouseWheelListener(this); _loading = new LoadingSpinner(); findTarget(patFilename, initOffset); } void findTarget(final String patFilename, final Location initOffset) { Thread thread = new Thread(new Runnable() { @Override public void run() { Region screenUnion = Region.create(0, 0, 1, 1); Finder f = new Finder(_simg, screenUnion); try { f.find(patFilename); if (f.hasNext()) { //TODO rewrite completely for ScreenUnion Screen s = (Screen) screenUnion.getScreen(); s.setAsScreenUnion(); _match = f.next(); s.setAsScreen(); if (initOffset != null) { setTarget(initOffset.x, initOffset.y); } else { setTarget(0, 0); } } _img = ImageIO.read(new File(patFilename)); } catch (IOException e) { Debug.error(me + "Can't load " + patFilename); } synchronized (PatternPaneTargetOffset.this) { _finding = false; } repaint(); } }); thread.start(); } static String _I(String key, Object... args) { return SikuliIDEI18N._I(key, args); } public void setTarget(int dx, int dy) { Debug.log(4, me + "new target: " + dx + "," + dy); if (_match != null) { Location center = _match.getCenter(); _tar.x = center.x + dx; _tar.y = center.y + dy; } else { _tar.x = dx; _tar.y = dy; } _offset = new Location(dx, dy); if (txtX != null) { txtX.setValue(new Integer(dx)); txtY.setValue(new Integer(dy)); } repaint(); } @Override public void mousePressed(MouseEvent me) { Location tar = convertViewToScreen(me.getPoint()); Debug.log(4, "click: " + me.getPoint() + " -> " + tar.toStringShort()); if (_match != null) { Location center = _match.getCenter(); setTarget(tar.x - center.x, tar.y - center.y); } else { setTarget(tar.x, tar.y); } } @Override public void mouseWheelMoved(MouseWheelEvent e) { int rot = e.getWheelRotation(); int patW = (int) (getWidth() * _ratio); // float zoomRatio = patW / (float) _img.getWidth(); int patH = (int) (_img.getHeight() * _zoomRatio); if (rot < 0) { if (patW < 2 * getWidth() && patH < 2 * getHeight()) { _ratio *= 1.1; } } else { if (patW > 20 && patH > 20) { _ratio *= 0.9; } } repaint(); } @Override public void mouseClicked(MouseEvent me) { } @Override public void mouseReleased(MouseEvent me) { } @Override public void mouseEntered(MouseEvent me) { } @Override public void mouseExited(MouseEvent me) { } @Override public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (getWidth() > 0 && getHeight() > 0) { if (_match != null) { zoomToMatch(); paintSubScreen(g2d); } else { paintPatternOnly(g2d); } paintMatch(g2d); // paintRulers(g2d); paintTarget(g2d); synchronized (this) { if (_finding) { paintLoading(g2d); } } } } private void zoomToMatch() { _viewW = (int) (_match.w / _ratio); _zoomRatio = getWidth() / (float) _viewW; _viewH = (int) (getHeight() / _zoomRatio); _viewX = _match.x + _match.w / 2 - _viewW / 2; _viewY = _match.y + _match.h / 2 - _viewH / 2; } void paintSubScreen(Graphics g2d) { if (_viewX < 0 || _viewY < 0) { paintBackground(g2d); } int subX = _viewX < 0 ? 0 : _viewX; int subY = _viewY < 0 ? 0 : _viewY; int subW = _viewW - (subX - _viewX); int subH = _viewH - (subY - _viewY); BufferedImage img = _simg.getImage(); if (subX + subW >= img.getWidth()) { subW = img.getWidth() - subX; } if (subY + subH >= img.getHeight()) { subH = img.getHeight() - subY; } BufferedImage clip = img.getSubimage(subX, subY, subW, subH); int destX = (int) ((subX - _viewX) * _zoomRatio), destY = (int) ((subY - _viewY) * _zoomRatio); int destW = (int) (subW * _zoomRatio), destH = (int) (subH * _zoomRatio); g2d.drawImage(clip, destX, destY, destW, destH, null); } void paintMatch(Graphics2D g2d) { int w = (int) (getWidth() * _ratio); int h = (int) ((float) w / _img.getWidth() * _img.getHeight()); int x = getWidth() / 2 - w / 2; int y = getHeight() / 2 - h / 2; Color c = PatternSimilaritySlider.getScoreColor((_match == null ? 1.0 : _match.getScore())); g2d.setColor(c); // g2d.fillRect(x, y, w, h); Stroke savedStroke = g2d.getStroke(); g2d.setStroke(new BasicStroke(5)); g2d.drawRect(x, y, w - 1, h - 1); g2d.setStroke(savedStroke); } void paintBackground(Graphics g2d) { g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, getWidth(), getHeight()); } void paintPatternOnly(Graphics g2d) { int patW = (int) (getWidth() * _ratio); _zoomRatio = patW / (float) _img.getWidth(); int patH = (int) (_img.getHeight() * _zoomRatio); int patX = getWidth() / 2 - patW / 2, patY = getHeight() / 2 - patH / 2; paintBackground(g2d); g2d.drawImage(_img, patX, patY, patW, patH, null); } void paintLoading(Graphics2D g2d) { int w = getWidth(), h = getHeight(); g2d.setColor(new Color(0, 0, 0, 200)); g2d.fillRect(0, 0, w, h); BufferedImage spinner = _loading.getFrame(); g2d.drawImage(spinner, null, w / 2 - spinner.getWidth() / 2, h / 2 - spinner.getHeight() / 2); repaint(); } void paintTarget(Graphics2D g2d) { final int CROSS_LEN = 20 / 2; Point l = convertScreenToView(_tar); g2d.setColor(Color.BLACK); g2d.drawLine(l.x - CROSS_LEN, l.y + 1, l.x + CROSS_LEN, l.y + 1); g2d.drawLine(l.x + 1, l.y - CROSS_LEN, l.x + 1, l.y + CROSS_LEN); g2d.setColor(Color.WHITE); g2d.drawLine(l.x - CROSS_LEN, l.y, l.x + CROSS_LEN, l.y); g2d.drawLine(l.x, l.y - CROSS_LEN, l.x, l.y + CROSS_LEN); } void paintRulers(Graphics g2d) { int step = (int) (10 * _zoomRatio); if (step < 2) { step = 2; } int h = getHeight(), w = getWidth(); if (h % 2 == 1) { h--; } if (w % 2 == 1) { w--; } g2d.setColor(COLOR_BG_LINE); for (int x = w / 2; x >= 0; x -= step) { g2d.drawLine(x, 0, x, h); g2d.drawLine(w - x, 0, w - x, h); } for (int y = h / 2; y >= 0; y -= step) { g2d.drawLine(0, y, w, y); g2d.drawLine(0, h - y, w, h - y); } } Location convertViewToScreen(Point p) { Location ret = new Location(0, 0); if (_match != null) { ret.x = (int) (p.x / _zoomRatio + _viewX); ret.y = (int) (p.y / _zoomRatio + _viewY); } else { ret.x = (int) ((p.x - getWidth() / 2) / _zoomRatio); ret.y = (int) ((p.y - getHeight() / 2) / _zoomRatio); } return ret; } Point convertScreenToView(Location loc) { Point ret = new Point(); if (_match != null) { ret.x = (int) ((loc.x - _viewX) * _zoomRatio); ret.y = (int) ((loc.y - _viewY) * _zoomRatio); } else { ret.x = (int) (getWidth() / 2 + loc.x * _zoomRatio); ret.y = (int) (getHeight() / 2 + loc.y * _zoomRatio); } return ret; } public JComponent createControls() { JPanel pane = new JPanel(new GridBagLayout()); JLabel lblX = new JLabel(_I("lblTargetOffsetX")); JLabel lblY = new JLabel(_I("lblTargetOffsetY")); int x = _offset != null ? _offset.x : 0; int y = _offset != null ? _offset.y : 0; txtX = new JSpinner(new SpinnerNumberModel(x, -999, 999, 1)); txtY = new JSpinner(new SpinnerNumberModel(y, -999, 999, 1)); txtX.addChangeListener(this); txtY.addChangeListener(this); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.gridy = 0; pane.add(lblX, c); pane.add(txtX, c); pane.add(lblY, c); pane.add(txtY, c); pane.add(_msgApplied, c); return pane; } @Override public void stateChanged(javax.swing.event.ChangeEvent e) { int x = (Integer) txtX.getValue(); int y = (Integer) txtY.getValue(); setTarget(x, y); } public Location getTargetOffset() { return new Location(_offset); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PatternSimilaritySlider.java000077500000000000000000000027601315726130400265730ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import javax.swing.*; class PatternSimilaritySlider extends JSlider { final JPopupMenu pop = new JPopupMenu(); JMenuItem item = new JMenuItem(); private int curVal = -1; private JLabel lblVal = null; public PatternSimilaritySlider(int min, int max, int val, JLabel lbl) { super(min, max, val); curVal = val; lblVal = lbl; init(); } private void init() { showValue(lblVal, curVal); setPreferredSize(new Dimension(250, 60)); } @Override protected void paintComponent(Graphics g) { int w = getWidth(); final int margin = 13; final int y1 = 22, y2 = 33; int span = w - margin * 2; for (int i = 0; i < span; i++) { float score = (float) i / span; g.setColor(getScoreColor(score)); g.drawLine(margin + i, y1, margin + i, y2); } if (getValue() != curVal) { curVal = getValue(); showValue(lblVal, curVal); } super.paintComponent(g); } public void showValue(JLabel lbl, int val) { float sim = val > 99 ? 0.99f : (float) val / 100; String txt = String.format(" ( %.2f )", sim); lbl.setText(txt); lbl.repaint(); } static Color getScoreColor(double score) { // map hue to 0.5~1.0 Color c = new Color( Color.HSBtoRGB(0.5f + (float) score / 2, 1.0f, 1.0f)); // map alpha to 20~150 Color cMask = new Color( c.getRed(), c.getGreen(), c.getBlue(), 20 + (int) (score * 130)); return cMask; } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PatternWindow.java000077500000000000000000000233551315726130400245540ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Point; import java.awt.Window; import java.awt.event.*; import java.io.*; import javax.swing.*; import org.sikuli.script.Location; import org.sikuli.script.ScreenImage; import org.sikuli.script.ScreenUnion; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; public class PatternWindow extends JFrame { private static final String me = "PatternWindow: "; private EditorPatternButton _imgBtn; private PatternPaneScreenshot _screenshot; private PatternPaneTargetOffset _tarOffsetPane; private PatternPaneNaming paneNaming; private JTabbedPane tabPane; private JPanel paneTarget, panePreview; private JLabel[] msgApplied; private int tabSequence = 0; private static final int tabMax = 3; private ScreenImage _simg; private boolean dirty; private EditorPane currentPane; boolean isFileOverwritten = false; String fileRenameOld; String fileRenameNew; Dimension pDim; static String _I(String key, Object... args) { return SikuliIDEI18N._I(key, args); } public PatternWindow(EditorPatternButton imgBtn, boolean exact, float similarity, int numMatches) { init(imgBtn, exact, similarity, numMatches); } private void init(EditorPatternButton imgBtn, boolean exact, float similarity, int numMatches) { setTitle(_I("winPatternSettings")); setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); _imgBtn = imgBtn; takeScreenshot(); Container c = getContentPane(); c.setLayout(new BorderLayout()); GraphicsConfiguration gc = getGraphicsConfiguration(); int pOff = 50; Point pLoc = new Point(pOff, pOff); if (gc == null) { pDim = new Dimension(900, 700); } else { pDim = getGraphicsConfiguration().getBounds().getSize(); pDim.width = (int) ((pDim.width - 2 * pOff) * 0.95); pDim.height = (int) ((pDim.height - 2 * pOff) * 0.95); pLoc = getGraphicsConfiguration().getBounds().getLocation(); pLoc.translate(pOff, pOff); } setPreferredSize(pDim); tabPane = new JTabbedPane(); msgApplied = new JLabel[tabMax]; tabSequence = 0; JLabel aMsg = msgApplied[tabSequence] = new JLabel(); setMessageApplied(tabSequence, false); paneNaming = new PatternPaneNaming(_imgBtn, aMsg); tabPane.addTab(_I("tabNaming"), paneNaming); tabSequence++; msgApplied[tabSequence] = new JLabel(); setMessageApplied(tabSequence, false); panePreview = createPreviewPanel(); tabPane.addTab(_I("tabMatchingPreview"), panePreview); tabSequence++; msgApplied[tabSequence] = new JLabel(); setMessageApplied(tabSequence, false); paneTarget = createTargetPanel(); tabPane.addTab(_I("tabTargetOffset"), paneTarget); c.add(tabPane, BorderLayout.CENTER); c.add(createButtons(), BorderLayout.SOUTH); c.doLayout(); pack(); try { _screenshot.setParameters(_imgBtn.getFilename(), exact, similarity, numMatches); } catch (Exception e) { Debug.error(me + "Problem while setting up pattern pane\n%s", e.getMessage()); } setDirty(false); currentPane = SikuliIDE.getInstance().getCurrentCodePane(); setLocation(pLoc); setVisible(true); } void takeScreenshot() { SikuliIDE ide = SikuliIDE.getInstance(); ide.setVisible(false); try { Thread.sleep(500); } catch (Exception e) { } _simg = (new ScreenUnion()).getScreen().capture(); ide.setVisible(true); } private JPanel createPreviewPanel() { JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); _screenshot = new PatternPaneScreenshot(_simg, pDim, msgApplied[tabSequence]); createMarginBox(p, _screenshot); p.add(Box.createVerticalStrut(5)); p.add(_screenshot.createControls()); // p.add(Box.createVerticalStrut(5)); // p.add(msgApplied[tabSequence]); p.doLayout(); return p; } private JPanel createTargetPanel() { JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); _tarOffsetPane = new PatternPaneTargetOffset( _simg, _imgBtn.getFilename(), _imgBtn.getTargetOffset(), pDim, msgApplied[tabSequence]); createMarginBox(p, _tarOffsetPane); p.add(Box.createVerticalStrut(5)); p.add(_tarOffsetPane.createControls()); p.doLayout(); return p; } private JComponent createButtons() { JPanel pane = new JPanel(new GridBagLayout()); JButton btnOK = new JButton(_I("ok")); btnOK.addActionListener(new ActionOK(this)); JButton btnApply = new JButton(_I("apply")); btnApply.addActionListener(new ActionApply(this)); final JButton btnCancel = new JButton(_I("cancel")); btnCancel.addActionListener(new ActionCancel(this)); GridBagConstraints c = new GridBagConstraints(); c.gridy = 3; c.gridx = 0; c.insets = new Insets(5, 0, 10, 0); c.anchor = GridBagConstraints.LAST_LINE_END; pane.add(btnOK, c); c.gridx = 1; pane.add(btnApply, c); c.gridx = 2; pane.add(btnCancel, c); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { btnCancel.doClick(); } }); KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); this.getRootPane(). getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW). put(escapeKeyStroke, "ESCAPE"); this.getRootPane().getActionMap().put("ESCAPE", new AbstractAction() { public void actionPerformed(ActionEvent e) { btnCancel.doClick(); } }); return pane; } private void createMarginBox(Container c, Component comp) { c.add(Box.createVerticalStrut(10)); Box lrMargins = Box.createHorizontalBox(); lrMargins.add(Box.createHorizontalStrut(10)); lrMargins.add(comp); lrMargins.add(Box.createHorizontalStrut(10)); c.add(lrMargins); c.add(Box.createVerticalStrut(10)); } public void setMessageApplied(int i, boolean flag) { if (flag) { for (JLabel m : msgApplied) { m.setText(" (changed)"); } } else { msgApplied[i].setText(" ( )"); } } public void close() { _simg = null; _imgBtn.resetWindow(); } public JTabbedPane getTabbedPane() { return tabPane; } public void setTargetOffset(Location offset) { if (offset != null) { _tarOffsetPane.setTarget(offset.x, offset.y); } } private void actionPerformedUpdates(Window _parent) { boolean tempDirty = isDirty(); if (paneNaming.isDirty()) { String filename = paneNaming.getAbsolutePath(); if (filename.contains("%")) { Debug.error("%s\n%% in filename replaced with _", filename); filename = filename.replace("%", "_"); } String oldFilename = _imgBtn.getFilename(); if (FileManager.exists(filename)) { String name = FileManager.getName(filename); int ret = JOptionPane.showConfirmDialog( _parent, SikuliIDEI18N._I("msgFileExists", name), SikuliIDEI18N._I("dlgFileExists"), JOptionPane.WARNING_MESSAGE, JOptionPane.YES_NO_OPTION); if (ret != JOptionPane.YES_OPTION) { return; } if (isFileOverwritten) { if (!revertImageRename()) { return; } } isFileOverwritten = true; } try { FileManager.xcopy(oldFilename, filename); _imgBtn.setFilename(filename); fileRenameOld = oldFilename; fileRenameNew = filename; } catch (IOException ioe) { Debug.error("renaming failed: old: %s \nnew: %s\n%s", oldFilename, filename, ioe.getMessage()); isFileOverwritten = false; return; } paneNaming.updateFilename(); addDirty(true); } addDirty(_imgBtn.setParameters( _screenshot.isExact(), _screenshot.getSimilarity(), _screenshot.getNumMatches())); addDirty(_imgBtn.setTargetOffset(_tarOffsetPane.getTargetOffset())); if (isDirty() || tempDirty) { Debug.log(3, "Preview: update: " + _imgBtn.toString()); int i = _imgBtn.getWindow().getTabbedPane().getSelectedIndex(); _imgBtn.getWindow().setMessageApplied(i, true); _imgBtn.repaint(); } } private boolean revertImageRename() { try { FileManager.xcopy(fileRenameNew, fileRenameOld); _imgBtn.setFilename(fileRenameOld); } catch (IOException ioe) { Debug.error("revert renaming failed: new: %s \nold: %s\n%s", fileRenameNew, fileRenameOld, ioe.getMessage()); return false; } return true; } class ActionOK implements ActionListener { private Window _parent; public ActionOK(JFrame parent) { _parent = parent; } @Override public void actionPerformed(ActionEvent e) { actionPerformedUpdates(_parent); if (fileRenameOld != null) { currentPane.reparse(fileRenameOld, fileRenameNew, isFileOverwritten); } _imgBtn.getWindow().close(); _parent.dispose(); currentPane.setDirty(setDirty(false)); } } class ActionApply implements ActionListener { private Window _parent; public ActionApply(Window parent) { _parent = parent; } @Override public void actionPerformed(ActionEvent e) { actionPerformedUpdates(_parent); _imgBtn.getWindow().getTabbedPane().getSelectedComponent().transferFocus(); } } class ActionCancel implements ActionListener { private Window _parent; public ActionCancel(Window parent) { _parent = parent; } @Override public void actionPerformed(ActionEvent e) { if (isDirty()) { _imgBtn.resetParameters(); if (isFileOverwritten) { revertImageRename(); } } _imgBtn.getWindow().close(); _parent.dispose(); } } protected boolean isDirty() { return dirty; } private boolean setDirty(boolean flag) { boolean xDirty = dirty; dirty = flag; return xDirty; } protected void addDirty(boolean flag) { dirty |= flag; } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PreferencesWin.java000077500000000000000000000644371315726130400246740ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import org.sikuli.basics.PreferencesUser; import com.jgoodies.forms.factories.DefaultComponentFactory; import java.awt.*; import java.awt.event.*; import java.util.Arrays; import java.util.Comparator; import java.util.Locale; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.DefaultListCellRenderer; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSpinner; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.Element; import org.jdesktop.layout.*; import org.sikuli.basics.Debug; import org.sikuli.basics.Settings; import org.sikuli.script.Key; /* * Created by JFormDesigner on Mon Nov 16 10:13:52 EST 2009 */ public class PreferencesWin extends JFrame { PreferencesUser pref = PreferencesUser.getInstance(); private boolean isInitialized = false; int cap_hkey, cap_mod; int old_cap_hkey, old_cap_mod; Font _oldFont; String _oldFontName; int _oldFontSize; private double _delay; private int _old_cap_hkey, _old_cap_mod; private int _autoNamingMethod; private boolean _chkAutoUpdate; private boolean _chkExpandTab; private int _spnTabWidth; Locale _locale; EditorPane codePane; JFrame winPrefMore; boolean isDirty = false; // // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables private JTabbedPane _tabPane; private JTextField _txtHotkey; private JLabel _lblHotkey; private JLabel _lblDelay; private JSpinner spnDelay; private JLabel _lblDelaySecs; private JLabel _lblNaming; private JRadioButton _radTimestamp; private JRadioButton _radOCR; private JRadioButton _radOff; private JPanel _paneTextEditing; private JCheckBox chkExpandTab; private JLabel _lblTabWidth; private JComboBox _cmbFontName; private JLabel _lblFont; private JLabel _titleAppearance; private JLabel _titleIndentation; private JSpinner spnTabWidth; private JLabel _lblFontSize; private JSpinner _spnFontSize; private JCheckBox chkAutoUpdate; private JComboBox _cmbLang; private JLabel _lblUpdates; private JLabel _lblLanguage; private JButton _btnOk; private JButton _btnApply; private JButton _btnCancel; private JButton _btnMore; // JFormDesigner - End of variables declaration //GEN-END:variables // public PreferencesWin() { setTitle(SikuliIDE._I("winPreferences")); initComponents(); loadPrefs(); isInitialized = true; } private void initComponents() { // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents DefaultComponentFactory compFactory = DefaultComponentFactory.getInstance(); _tabPane = new JTabbedPane(); JPanel paneCapture = new JPanel(); _txtHotkey = new JTextField(); _lblHotkey = new JLabel(); _lblDelay = new JLabel(); spnDelay = new JSpinner(); _lblDelaySecs = new JLabel(); _lblNaming = new JLabel(); _radTimestamp = new JRadioButton(); _radOCR = new JRadioButton(); _radOff = new JRadioButton(); _paneTextEditing = new JPanel(); chkExpandTab = new JCheckBox(); _lblTabWidth = new JLabel(); _cmbFontName = new JComboBox(); _lblFont = new JLabel(); _titleAppearance = compFactory.createTitle(""); _titleIndentation = compFactory.createTitle(""); spnTabWidth = new JSpinner(); _lblFontSize = new JLabel(); _spnFontSize = new JSpinner(); JPanel paneGeneral = new JPanel(); chkAutoUpdate = new JCheckBox(); _cmbLang = new JComboBox(); _lblUpdates = new JLabel(); _lblLanguage = new JLabel(); JPanel paneOkCancel = new JPanel(); JPanel hSpacer1 = new JPanel(null); _btnOk = new JButton(); _btnApply = new JButton(); _btnCancel = new JButton(); _btnMore = new JButton(); //======== this ======== Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); //======== _tabPane ======== { _tabPane.setBorder(new EmptyBorder(10, 10, 0, 10)); //======== paneCapture ======== { //---- _txtHotkey ---- _txtHotkey.setHorizontalAlignment(SwingConstants.RIGHT); _txtHotkey.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { txtHotkeyFocusGained(e); } }); _txtHotkey.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { txtHotkeyKeyPressed(e); } }); //---- _lblHotkey ---- _lblHotkey.setLabelFor(_txtHotkey); //---- _lblDelay ---- _lblDelay.setLabelFor(spnDelay); //---- _spnDelay ---- spnDelay.setModel(new SpinnerNumberModel(1.0, 0.0, null, 0.1)); //---- _radTimestamp ---- _radTimestamp.setSelected(true); GroupLayout paneCaptureLayout = new GroupLayout(paneCapture); paneCapture.setLayout(paneCaptureLayout); paneCaptureLayout.setHorizontalGroup( paneCaptureLayout.createParallelGroup() .add(paneCaptureLayout.createSequentialGroup() .add(26, 26, 26) .add(paneCaptureLayout.createParallelGroup() .add(GroupLayout.TRAILING, _lblDelay) .add(GroupLayout.TRAILING, _lblHotkey) .add(GroupLayout.TRAILING, _lblNaming)) .addPreferredGap(LayoutStyle.RELATED) .add(paneCaptureLayout.createParallelGroup() .add(_radTimestamp) .add(_radOCR) .add(_radOff) .add(paneCaptureLayout.createSequentialGroup() .add(spnDelay, GroupLayout.DEFAULT_SIZE, 148, Short.MAX_VALUE) .addPreferredGap(LayoutStyle.RELATED) .add(_lblDelaySecs, GroupLayout.DEFAULT_SIZE, 161, Short.MAX_VALUE)) .add(_txtHotkey, GroupLayout.DEFAULT_SIZE, 315, Short.MAX_VALUE)) .add(69, 69, 69))); paneCaptureLayout.setVerticalGroup( paneCaptureLayout.createParallelGroup() .add(paneCaptureLayout.createSequentialGroup() .add(34, 34, 34) .add(paneCaptureLayout.createParallelGroup(GroupLayout.BASELINE) .add(_lblHotkey, GroupLayout.PREFERRED_SIZE, 22, GroupLayout.PREFERRED_SIZE) .add(_txtHotkey, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) .addPreferredGap(LayoutStyle.RELATED) .add(paneCaptureLayout.createParallelGroup() .add(_lblDelay, GroupLayout.PREFERRED_SIZE, 28, GroupLayout.PREFERRED_SIZE) .add(spnDelay, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .add(_lblDelaySecs, GroupLayout.PREFERRED_SIZE, 28, GroupLayout.PREFERRED_SIZE)) .addPreferredGap(LayoutStyle.RELATED) .add(paneCaptureLayout.createParallelGroup(GroupLayout.LEADING, false) .add(paneCaptureLayout.createSequentialGroup() .add(paneCaptureLayout.createParallelGroup(GroupLayout.BASELINE) .add(_lblNaming, GroupLayout.PREFERRED_SIZE, 22, GroupLayout.PREFERRED_SIZE) .add(_radTimestamp)) .add(18, 18, 18) .add(_radOff) .addPreferredGap(LayoutStyle.RELATED)) .add(GroupLayout.TRAILING, paneCaptureLayout.createSequentialGroup() .add(_radOCR) .add(21, 21, 21))) .add(80, 80, 80))); } _tabPane.addTab(SikuliIDEI18N._I("prefTabScreenCapturing"), paneCapture); //======== _paneTextEditing ======== { //---- _lblTabWidth ---- _lblTabWidth.setLabelFor(spnTabWidth); //---- _cmbFontName ---- _cmbFontName.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { fontNameItemStateChanged(e); } }); //---- _lblFont ---- _lblFont.setLabelFor(_cmbFontName); //---- _lblFontSize ---- _lblFontSize.setLabelFor(_spnFontSize); //---- _spnFontSize ---- _spnFontSize.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { fontSizeStateChanged(e); } }); GroupLayout _paneTextEditingLayout = new GroupLayout(_paneTextEditing); _paneTextEditing.setLayout(_paneTextEditingLayout); _paneTextEditingLayout.setHorizontalGroup( _paneTextEditingLayout.createParallelGroup() .add(GroupLayout.TRAILING, _paneTextEditingLayout.createSequentialGroup() .add(95, 95, 95) .add(_paneTextEditingLayout.createParallelGroup() .add(_titleIndentation, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .add(_paneTextEditingLayout.createSequentialGroup() .add(_titleAppearance, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.RELATED) .add(_paneTextEditingLayout.createParallelGroup() .add(_paneTextEditingLayout.createSequentialGroup() .add(29, 29, 29) .add(_paneTextEditingLayout.createParallelGroup() .add(GroupLayout.TRAILING, _lblTabWidth) .add(GroupLayout.TRAILING, _lblFont) .add(GroupLayout.TRAILING, _lblFontSize)) .addPreferredGap(LayoutStyle.RELATED) .add(_paneTextEditingLayout.createParallelGroup() .add(_cmbFontName, 0, 218, Short.MAX_VALUE) .add(_spnFontSize, GroupLayout.PREFERRED_SIZE, 67, GroupLayout.PREFERRED_SIZE) .add(spnTabWidth, GroupLayout.PREFERRED_SIZE, 52, GroupLayout.PREFERRED_SIZE)) .addPreferredGap(LayoutStyle.RELATED, 97, Short.MAX_VALUE)) .add(chkExpandTab, GroupLayout.DEFAULT_SIZE, 420, Short.MAX_VALUE)))) .addContainerGap())); _paneTextEditingLayout.setVerticalGroup( _paneTextEditingLayout.createParallelGroup() .add(_paneTextEditingLayout.createSequentialGroup() .add(21, 21, 21) .add(_titleIndentation, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .add(_paneTextEditingLayout.createParallelGroup() .add(_paneTextEditingLayout.createSequentialGroup() .add(81, 81, 81) .add(_titleAppearance, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) .add(_paneTextEditingLayout.createSequentialGroup() .addPreferredGap(LayoutStyle.RELATED) .add(chkExpandTab) .addPreferredGap(LayoutStyle.RELATED) .add(_paneTextEditingLayout.createParallelGroup() .add(_lblTabWidth, GroupLayout.PREFERRED_SIZE, 16, GroupLayout.PREFERRED_SIZE) .add(spnTabWidth, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) .add(40, 40, 40) .add(_paneTextEditingLayout.createParallelGroup(GroupLayout.BASELINE) .add(_lblFont) .add(_cmbFontName, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) .addPreferredGap(LayoutStyle.RELATED) .add(_paneTextEditingLayout.createParallelGroup(GroupLayout.TRAILING) .add(_lblFontSize, GroupLayout.PREFERRED_SIZE, 27, GroupLayout.PREFERRED_SIZE) .add(_spnFontSize, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)))) .addContainerGap(154, Short.MAX_VALUE))); _paneTextEditingLayout.linkSize(new Component[]{_lblTabWidth, spnTabWidth}, GroupLayout.VERTICAL); _paneTextEditingLayout.linkSize(new Component[]{_cmbFontName, _lblFont}, GroupLayout.VERTICAL); } _tabPane.addTab(SikuliIDEI18N._I("PreferencesWin.paneTextEditing.tab.title"), _paneTextEditing); //======== paneGeneral ======== { //---- _lblUpdates ---- _lblUpdates.setFont(_lblUpdates.getFont().deriveFont(_lblUpdates.getFont().getStyle() | Font.BOLD)); //---- _lblLanguage ---- _lblLanguage.setFont(_lblLanguage.getFont().deriveFont(_lblLanguage.getFont().getStyle() | Font.BOLD)); GroupLayout paneGeneralLayout = new GroupLayout(paneGeneral); paneGeneral.setLayout(paneGeneralLayout); paneGeneralLayout.setHorizontalGroup( paneGeneralLayout.createParallelGroup() .add(paneGeneralLayout.createSequentialGroup() .add(137, 137, 137) .add(paneGeneralLayout.createParallelGroup() .add(paneGeneralLayout.createSequentialGroup() .add(_lblLanguage) .add(185, 185, 185)) .add(paneGeneralLayout.createSequentialGroup() .add(38, 38, 38) .add(_cmbLang, GroupLayout.PREFERRED_SIZE, 215, GroupLayout.PREFERRED_SIZE)) .add(paneGeneralLayout.createSequentialGroup() .add(_lblUpdates) .add(318, 318, 318)) .add(GroupLayout.TRAILING, paneGeneralLayout.createSequentialGroup() .add(38, 38, 38) .add(chkAutoUpdate, GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE))) .addContainerGap())); paneGeneralLayout.setVerticalGroup( paneGeneralLayout.createParallelGroup() .add(paneGeneralLayout.createSequentialGroup() .add(26, 26, 26) .add(_lblUpdates) .addPreferredGap(LayoutStyle.RELATED) .add(chkAutoUpdate) .add(40, 40, 40) .add(_lblLanguage) .addPreferredGap(LayoutStyle.RELATED) .add(_cmbLang, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addContainerGap(196, Short.MAX_VALUE))); } _tabPane.addTab(SikuliIDEI18N._I("prefTabGeneralSettings"), paneGeneral); } contentPane.add(_tabPane, BorderLayout.CENTER); //======== paneOkCancel ======== { paneOkCancel.setBorder(new EmptyBorder(5, 5, 5, 5)); paneOkCancel.setLayout(new BoxLayout(paneOkCancel, BoxLayout.X_AXIS)); paneOkCancel.add(hSpacer1); //---- _btnMore ---- _btnMore.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { btnMoreActionPerformed(e); } }); paneOkCancel.add(_btnMore); //---- _btnOk ---- _btnOk.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { btnOkActionPerformed(e); } }); paneOkCancel.add(_btnOk); //---- _btnApply ---- _btnApply.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { btnApplyActionPerformed(e); } }); paneOkCancel.add(_btnApply); //---- _btnCancel ---- _btnCancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { btnCancelActionPerformed(e); } }); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { _btnCancel.doClick(); } }); paneOkCancel.add(_btnCancel); } contentPane.add(paneOkCancel, BorderLayout.SOUTH); setSize(600, 475); setLocationRelativeTo(getOwner()); //---- btngrpNaming ---- ButtonGroup btngrpNaming = new ButtonGroup(); btngrpNaming.add(_radTimestamp); btngrpNaming.add(_radOCR); btngrpNaming.add(_radOff); initComponentsI18n(); // JFormDesigner - End of component initialization //GEN-END:initComponents } private void initComponentsI18n() { // JFormDesigner - Component i18n initialization - DO NOT MODIFY //GEN-BEGIN:initI18n DefaultComponentFactory.setTextAndMnemonic(_titleAppearance, SikuliIDEI18N._I("PreferencesWin.titleAppearance.textWithMnemonic")); DefaultComponentFactory.setTextAndMnemonic(_titleIndentation, SikuliIDEI18N._I("PreferencesWin.titleIndentation.textWithMnemonic")); _lblHotkey.setText(SikuliIDEI18N._I("prefCaptureHotkey")); _lblDelay.setText(SikuliIDEI18N._I("prefCaptureDelay")); _lblDelaySecs.setText(SikuliIDEI18N._I("prefSeconds")); _lblNaming.setText(SikuliIDEI18N._I("prefAutoNaming")); _radTimestamp.setText(SikuliIDEI18N._I("prefTimestamp")); _radOCR.setText(SikuliIDEI18N._I("prefRecognizedText")); _radOff.setText(SikuliIDEI18N._I("prefManualInput")); _tabPane.setTitleAt(0, SikuliIDEI18N._I("prefTabScreenCapturing")); chkExpandTab.setText(SikuliIDEI18N._I("PreferencesWin.chkExpandTab.text")); _lblTabWidth.setText(SikuliIDEI18N._I("PreferencesWin.lblTabWidth.text")); _lblFont.setText(SikuliIDEI18N._I("PreferencesWin.lblFont.text")); _lblFontSize.setText(SikuliIDEI18N._I("PreferencesWin.lblFontSize.text")); _tabPane.setTitleAt(1, SikuliIDEI18N._I("PreferencesWin.paneTextEditing.tab.title")); chkAutoUpdate.setText(SikuliIDEI18N._I("prefGeneralAutoCheck")); _lblUpdates.setText(SikuliIDEI18N._I("PreferencesWin.lblUpdates.text")); _lblLanguage.setText(SikuliIDEI18N._I("PreferencesWin.lblLanguage.text")); _tabPane.setTitleAt(2, SikuliIDEI18N._I("prefTabGeneralSettings")); _btnMore.setText(SikuliIDEI18N._I("more")); _btnOk.setText(SikuliIDEI18N._I("ok")); _btnApply.setText(SikuliIDEI18N._I("apply")); _btnCancel.setText(SikuliIDEI18N._I("cancel")); // JFormDesigner - End of component i18n initialization //GEN-END:initI18n } private void loadPrefs() { SikuliIDE ide = SikuliIDE.getInstance(); _delay = pref.getCaptureDelay(); spnDelay.setValue(_delay); _old_cap_hkey = old_cap_hkey = cap_hkey = pref.getCaptureHotkey(); _old_cap_mod = old_cap_mod = cap_mod = pref.getCaptureHotkeyModifiers(); setTxtHotkey(cap_hkey, cap_mod); _autoNamingMethod = pref.getAutoNamingMethod(); switch (_autoNamingMethod) { case PreferencesUser.AUTO_NAMING_TIMESTAMP: _radTimestamp.setSelected(true); break; case PreferencesUser.AUTO_NAMING_OCR: _radOCR.setSelected(true); break; case PreferencesUser.AUTO_NAMING_OFF: _radOff.setSelected(true); break; default: Debug.error("Error in reading auto naming method preferences"); } _chkAutoUpdate = pref.getCheckUpdate(); chkAutoUpdate.setSelected(_chkAutoUpdate); _chkExpandTab = pref.getExpandTab(); chkExpandTab.setSelected(_chkExpandTab); _spnTabWidth = pref.getTabWidth(); spnTabWidth.setValue(_spnTabWidth); initFontPrefs(); initLangPrefs(); codePane = ide.getCurrentCodePane(); if (codePane != null) { _oldFont = codePane.getFont(); } else { _oldFontName = pref.getFontName(); _oldFontSize = pref.getFontSize(); } _locale = pref.getLocale(); } private void savePrefs() { SikuliIDE ide = SikuliIDE.getInstance(); pref.setCaptureDelay((Double) spnDelay.getValue()); pref.setCaptureHotkey(cap_hkey); pref.setCaptureHotkeyModifiers(cap_mod); pref.setAutoNamingMethod( _radTimestamp.isSelected() ? PreferencesUser.AUTO_NAMING_TIMESTAMP : _radOCR.isSelected() ? PreferencesUser.AUTO_NAMING_OCR : PreferencesUser.AUTO_NAMING_OFF); if (pref.getAutoNamingMethod() != PreferencesUser.AUTO_NAMING_TIMESTAMP) { pref.setPrefMoreTextOCR(true); } if (old_cap_hkey != cap_hkey || old_cap_mod != cap_mod) { ide.removeCaptureHotkey(); ide.installCaptureHotkey(); } pref.setCheckUpdate(chkAutoUpdate.isSelected()); pref.setExpandTab(chkExpandTab.isSelected()); pref.setTabWidth((Integer) spnTabWidth.getValue()); pref.setFontName((String) _cmbFontName.getSelectedItem()); pref.setFontSize((Integer) _spnFontSize.getValue()); Locale locale = (Locale) _cmbLang.getSelectedItem(); pref.setLocale(locale); SikuliIDEI18N.setLocale(locale); isDirty = true; } private void resetPrefs() { SikuliIDE ide = SikuliIDE.getInstance(); pref.setCaptureDelay(_delay); pref.setCaptureHotkey(_old_cap_hkey); pref.setCaptureHotkeyModifiers(_old_cap_mod); if (old_cap_hkey != _old_cap_hkey || old_cap_mod != _old_cap_mod) { ide.removeCaptureHotkey(); ide.installCaptureHotkey(); } pref.setAutoNamingMethod(_autoNamingMethod); pref.setCheckUpdate(_chkAutoUpdate); pref.setExpandTab(_chkExpandTab); pref.setTabWidth(_spnTabWidth); if (codePane == null) { pref.setFontName(_oldFontName); pref.setFontSize(_oldFontSize); } else { pref.setFontName(_oldFont.getFontName()); pref.setFontSize(_oldFont.getSize()); codePane.setFont(_oldFont); } pref.setLocale(_locale); SikuliIDEI18N.setLocale(_locale); } private void initFontPrefs() { String[] fontList = GraphicsEnvironment.getLocalGraphicsEnvironment() .getAvailableFontFamilyNames(); for (String font : fontList) { _cmbFontName.addItem(font); } _cmbFontName.setSelectedItem(pref.getFontName()); _spnFontSize.setValue(pref.getFontSize()); } private void initLangPrefs() { String[] SUPPORT_LOCALES = { "it", "es", "pt_BR", "ar", "fr", "ru", "bg", "he", "sv", "ca", "ja", "tr", "da", "ko", "uk", "de", "nl", "zh_CN", "en_US", "pl", "zh_TW", "ta_IN" }; Locale[] sortedLocales = new Locale[SUPPORT_LOCALES.length]; int count = 0; for (String locale_code : SUPPORT_LOCALES) { Locale l; if (locale_code.indexOf("_") >= 0) { String[] lang_country = locale_code.split("_"); l = new Locale(lang_country[0], lang_country[1]); } else { l = new Locale(locale_code); } sortedLocales[count++] = l; } Arrays.sort(sortedLocales, new Comparator() { @Override public int compare(Locale l1, Locale l2) { return l1.getDisplayLanguage().compareTo(l2.getDisplayLanguage()); } }); for (Locale l : sortedLocales) { _cmbLang.addItem(l); } _cmbLang.setRenderer(new LocaleListCellRenderer()); Locale curLocale = pref.getLocale(); _cmbLang.setSelectedItem(curLocale); if (!_cmbLang.getSelectedItem().equals(curLocale)) { if (curLocale.getVariant().length() > 0) { curLocale = new Locale(curLocale.getLanguage(), curLocale.getCountry()); _cmbLang.setSelectedItem(curLocale); } if (!_cmbLang.getSelectedItem().equals(curLocale)) { _cmbLang.setSelectedItem(new Locale(curLocale.getLanguage())); } } } private void btnMoreActionPerformed(ActionEvent e) { winPrefMore = new JFrame("Preferences: more Options ..."); Container mpwinCP = winPrefMore.getContentPane(); mpwinCP.setLayout(new BorderLayout()); mpwinCP.add(new PreferencesWindowMore(), BorderLayout.CENTER); winPrefMore.pack(); winPrefMore.setAlwaysOnTop(true); winPrefMore.setDefaultCloseOperation(DISPOSE_ON_CLOSE); if (Settings.isJava7()) { winPrefMore.setLocation(getLocation().x-70, getLocation().y); } else { winPrefMore.setLocation(getLocation().x+getWidth()+10, getLocation().y); } winPrefMore.setVisible(true); } private void btnOkActionPerformed(ActionEvent e) { savePrefs(); String warn = "Until some bugs have been fixed,\n" + "you should restart the IDE now!\n" + "(except for most options in [more options ...])\n" + "Otherwise you might notice strange behavior ;-)\n" + "--- but only if you have made any changes!\n\n" + "Use CANCEL next time, if nothing was changed!"; JOptionPane.showMessageDialog(this, warn, "--- Preferences have been saved ---", JOptionPane.WARNING_MESSAGE); if (winPrefMore != null) winPrefMore.dispose(); this.dispose(); } private void btnApplyActionPerformed(ActionEvent e) { savePrefs(); } private void btnCancelActionPerformed(ActionEvent e) { if (isDirty) { resetPrefs(); } if (winPrefMore != null) winPrefMore.dispose(); this.dispose(); } private void setTxtHotkey(int code, int mod) { cap_hkey = code; cap_mod = mod; _txtHotkey.setText(Key.convertKeyToText(code, mod)); } private void txtHotkeyFocusGained(FocusEvent e) { _txtHotkey.setEditable(true); } private void txtHotkeyKeyPressed(KeyEvent e) { int code = e.getKeyCode(); int mod = e.getModifiers(); Debug.log(2, "HotKey: " + code + " " + mod); setTxtHotkey(code, mod); _txtHotkey.setEditable(false); } private void updateFontPreview() { if (! isInitialized || codePane == null) { return; } SikuliIDE ide = SikuliIDE.getInstance(); Font font = new Font((String) _cmbFontName.getSelectedItem(), Font.PLAIN, (Integer) _spnFontSize.getValue()); try { Element root = codePane.getDocument().getDefaultRootElement(); codePane.jumpTo(root.getElementIndex(codePane.getCaretPosition())); } catch (Exception ex) { } codePane.setFont(font); isDirty = true; } private void fontNameItemStateChanged(ItemEvent e) { updateFontPreview(); } private void fontSizeStateChanged(ChangeEvent e) { updateFontPreview(); } } // class LocaleListCellRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) { Locale locale = (Locale) (value); return super.getListCellRendererComponent(list, locale.getDisplayName(locale), index, isSelected, hasFocus); } } // sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PreferencesWin.jfd000077500000000000000000000732421315726130400245100ustar00rootroot00000000000000 i18n.bundlePackage resources.i18n.IDE i18n.bundleName PreferenecesWin i18n.autoExternalize true i18n.keyPrefix PreferencesWin form/swing javax.swing.JFrame java.awt.BorderLayout $sizePolicy 1 javax.swing.JTabbedPane javax.swing.JTabbedPane border 10 10 0 10 JavaCodeGenerator.variableName tabPane tabPane javax.swing.JPanel org.jdesktop.layout.GroupLayout $horizontalGroup par l {seq {space :p:26:p, par l {comp lblDelay::t:p::p, comp lblHotkey::t:p::p, comp lblNaming::t:p::p}, space :::p, par l {comp radTimestamp::l:p::p, comp radOCR::l:p::p, comp radOff::l:p::p, seq l {comp spnDelay::::148:x:1, space :::p, comp lblDelaySecs::::161:x}, comp txtHotkey::l::315:x:1}, space :p:69:p}} $verticalGroup par l {seq l {space :p:34:p, par b {comp lblHotkey::b:p:22:p, comp txtHotkey::b:p::p}, space :::p, par l {comp lblDelay::l:p:28:p:1, comp spnDelay:::p::p:1, comp lblDelaySecs:::p:28:p:1}, space :::p, par l:::p {seq {par b {comp lblNaming::b:p:22:p, comp radTimestamp::b:p::p}, space s:::p, comp radOff:::p::p, space :::p}, seq t:::1 {comp radOCR:::p::p, space :p:21:p}}, space :p:80:p}} JavaCodeGenerator.variableLocal true paneCapture javax.swing.JTextField horizontalAlignment 4 txtHotkey java.awt.event.FocusListener focusGained txtHotkeyFocusGained true java.awt.event.KeyListener keyPressed txtHotkeyKeyPressed true javax.swing.JLabel text prefCaptureHotkey labelFor txtHotkey lblHotkey JavaCodeGenerator.variableLocal true JavaCodeGenerator.variableName lblHotkey javax.swing.JLabel text prefCaptureDelay labelFor spnDelay lblDelay JavaCodeGenerator.variableName lblDelay JavaCodeGenerator.variableLocal true javax.swing.JSpinner model 0.0 0.1 1.0 spnDelay javax.swing.JLabel text prefSeconds lblDelaySecs JavaCodeGenerator.variableName lblDelaySecs JavaCodeGenerator.variableLocal true javax.swing.JLabel text prefAutoNaming lblNaming JavaCodeGenerator.variableLocal true javax.swing.JRadioButton text prefTimestamp $buttonGroup btngrpNaming selected true radTimestamp javax.swing.JRadioButton text prefRecognizedText $buttonGroup btngrpNaming radOCR javax.swing.JRadioButton text prefManualInput $buttonGroup btngrpNaming radOff title prefTabScreenCapturing javax.swing.JPanel org.jdesktop.layout.GroupLayout $horizontalGroup par l {seq t {space :p:95:p, par l {comp titleIndentation::l:p::p, seq l {comp titleAppearance:::p::p, space :::p, par l {seq l {space :29:29:29, par l {comp lblTabWidth::t:p::p, comp lblFont::t:p::p, comp lblFontSize::t:p::p}, space :::p, par l {comp cmbFontName::l::218:x, comp spnFontSize::l:p:67:p, comp spnTabWidth::l:p:52:p}, space ::97:x}, comp chkExpandTab::l::420:x}}}, space :::p}} $verticalGroup par l {seq l {space :p:21:p, comp titleIndentation:::p::p, par l {seq l {space :p:81:p, comp titleAppearance:::p::p}, seq l {space :::p, comp chkExpandTab:::p::p, space :::p, par l {comp lblTabWidth:1::p:16:p, comp spnTabWidth:1::p::p}, space :p:40:p, par b {comp lblFont:2:b:p::p, comp cmbFontName:2:b:p::p}, space :::p, par t {comp lblFontSize:::p:27:p, comp spnFontSize:::p::p}}}, space ::154:x}} paneTextEditing javax.swing.JCheckBox text PreferencesWin.chkExpandTab.text chkExpandTab javax.swing.JLabel text PreferencesWin.lblTabWidth.text labelFor spnTabWidth lblTabWidth javax.swing.JComboBox cmbFontName java.awt.event.ItemListener itemStateChanged fontNameItemStateChanged true javax.swing.JLabel text PreferencesWin.lblFont.text labelFor cmbFontName lblFont com.jformdesigner.designer.wrapper.JGoodiesFormsTitle textWithMnemonic PreferencesWin.titleAppearance.textWithMnemonic titleAppearance com.jformdesigner.designer.wrapper.JGoodiesFormsTitle textWithMnemonic PreferencesWin.titleIndentation.textWithMnemonic titleIndentation javax.swing.JSpinner spnTabWidth javax.swing.JLabel text PreferencesWin.lblFontSize.text labelFor cmbFontName lblFontSize javax.swing.JSpinner spnFontSize javax.swing.event.ChangeListener stateChanged fontSizeStateChanged true title PreferencesWin.paneTextEditing.tab.title javax.swing.JPanel org.jdesktop.layout.GroupLayout $horizontalGroup par l {seq l {space :p:137:p, par l {seq l {comp lblLanguage:::p::p, space :p:185:p}, seq l {space :p:38:p, comp cmbLang:::p:215:p}, seq l {comp lblUpdates:::p::p, space :p:318:p}, seq t {space :p:38:p, comp chkAutoUpdate::::376:x}}, space :::p}} $verticalGroup par l {seq l {space :p:26:p, comp lblUpdates:::p::p, space :::p, comp chkAutoUpdate:::p::p, space :p:40:p, comp lblLanguage:::p::p, space :::p, comp cmbLang:::p::p, space ::196:x}} JavaCodeGenerator.variableLocal true paneGeneral javax.swing.JCheckBox text prefGeneralAutoCheck chkAutoUpdate JavaCodeGenerator.variableName chkAutoUpdate javax.swing.JComboBox cmbLang javax.swing.JLabel text PreferencesWin.lblUpdates.text font 1 0 false lblUpdates javax.swing.JLabel text PreferencesWin.lblLanguage.text font lblLanguage title prefTabGeneralSettings java.lang.String value Center javax.swing.JPanel javax.swing.BoxLayout border 5 5 5 5 JavaCodeGenerator.variableLocal true JavaCodeGenerator.variableName paneOkCancel paneOkCancel com.jformdesigner.designer.wrapper.HSpacer hSpacer1 JavaCodeGenerator.variableLocal true javax.swing.JButton text ok btnOk java.awt.event.ActionListener actionPerformed btnOkActionPerformed true javax.swing.JButton text cancel btnCancel java.awt.event.ActionListener actionPerformed btnCancelActionPerformed true javax.swing.JButton text apply btnApply java.awt.event.ActionListener actionPerformed btnOkActionPerformed true java.lang.String value South this location 5 5 size 600 475 javax.swing.ButtonGroup btngrpNaming location 0 360 sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PreferencesWindowMore.form000077500000000000000000000564161315726130400262510ustar00rootroot00000000000000
sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PreferencesWindowMore.java000077500000000000000000000567041315726130400262270ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import org.sikuli.basics.PreferencesUser; import javax.swing.JCheckBox; /** * * @author rhocke */ public class PreferencesWindowMore extends javax.swing.JPanel { /** * Creates new form NewPref */ private PreferencesUser prefs; private boolean DEBUG = true; private boolean openingWindow = false; public PreferencesWindowMore() { initComponents(); setStatus(); boolean debug = DEBUG; DEBUG = false; openingWindow = true; prefs = PreferencesUser.getInstance(); prefMoreHTML.setSelected(prefs.getAtSaveMakeHTML()); prefMoreClean.setSelected(prefs.getAtSaveCleanBundle()); prefMoreRunSave.setSelected(prefs.getPrefMoreRunSave()); //TODO: implement prefMoreHighlight prefMoreHighlight.setSelected(prefs.getPrefMoreHighlight()); prefMoreMessage.setSelected(prefs.getPrefMoreMessage() == PreferencesUser.HORIZONTAL); //TODO: command bar as menu prefMoreCommand.setSelected(prefs.getPrefMoreCommandBar()); prefMoreLogActions.setSelected(prefs.getPrefMoreLogActions()); prefMoreLogDebug.setSelected(prefs.getPrefMoreLogDebug()); prefMoreLogInfo.setSelected(prefs.getPrefMoreLogInfo()); prefMoreTextSearch.setSelected(prefs.getPrefMoreTextSearch()); prefMoreTextOCR.setSelected(prefs.getPrefMoreTextOCR()); prefMoreScripter.setSelected(prefs.getUserType() == PreferencesUser.SCRIPTER); prefMoreImageThumbs.setSelected(prefs.getPrefMoreImageThumbs()); prefMorePlainText.setSelected(prefs.getPrefMorePlainText()); DEBUG = debug; openingWindow = false; } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { jTextField1 = new javax.swing.JTextField(); jToggleButton1 = new javax.swing.JToggleButton(); buttonGroup1 = new javax.swing.ButtonGroup(); buttonGroup2 = new javax.swing.ButtonGroup(); jCheckBox4 = new javax.swing.JCheckBox(); jPanel1 = new javax.swing.JPanel(); jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); jCheckBox6 = new javax.swing.JCheckBox(); prefMoreLblSave = new javax.swing.JLabel(); prefMoreHTML = new javax.swing.JCheckBox(); prefMoreClean = new javax.swing.JCheckBox(); prefMoreLblRun = new javax.swing.JLabel(); prefMoreRunSave = new javax.swing.JCheckBox(); prefMoreHighlight = new javax.swing.JCheckBox(); jSeparator1 = new javax.swing.JSeparator(); jSeparator2 = new javax.swing.JSeparator(); jSeparator3 = new javax.swing.JSeparator(); jSeparator5 = new javax.swing.JSeparator(); prefMoreBtnOk = new javax.swing.JButton(); prefMoreLblLayout = new javax.swing.JLabel(); prefMoreMessage = new javax.swing.JCheckBox(); prefMoreCommand = new javax.swing.JCheckBox(); jSeparator6 = new javax.swing.JSeparator(); prefMoreLblLogsOld = new javax.swing.JLabel(); jSeparator7 = new javax.swing.JSeparator(); prefMoreLogActions = new javax.swing.JCheckBox(); prefMoreLogInfo = new javax.swing.JCheckBox(); prefMoreLogDebug = new javax.swing.JCheckBox(); jSeparator8 = new javax.swing.JSeparator(); prefMoreLblText = new javax.swing.JLabel(); prefMoreTextSearch = new javax.swing.JCheckBox(); prefMoreTextOCR = new javax.swing.JCheckBox(); jLabel1 = new javax.swing.JLabel(); prefMoreScripter = new javax.swing.JCheckBox(); jSeparator9 = new javax.swing.JSeparator(); prefMoreLblStatus = new javax.swing.JLabel(); prefMoreLblTitle1 = new javax.swing.JLabel(); prefMoreImageThumbs = new javax.swing.JCheckBox(); prefMorePlainText = new javax.swing.JCheckBox(); jSeparator10 = new javax.swing.JSeparator(); jTextField1.setText("jTextField1"); jToggleButton1.setText("jToggleButton1"); jCheckBox4.setText("jCheckBox4"); org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(0, 100, Short.MAX_VALUE) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(0, 100, Short.MAX_VALUE) ); jButton1.setText("jButton1"); jButton2.setText("jButton2"); jCheckBox6.setText("jCheckBox6"); prefMoreLblSave.setText("Options on Save"); prefMoreHTML.setText("create HTML"); prefMoreHTML.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreHTMLStateChanged(evt); } }); prefMoreClean.setText("delete not used images"); prefMoreClean.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreCleanStateChanged(evt); } }); prefMoreLblRun.setText("Options on Run"); prefMoreRunSave.setText("autosave all"); prefMoreRunSave.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreRunSaveStateChanged(evt); } }); prefMoreHighlight.setText("always highlight"); prefMoreHighlight.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreHighlightStateChanged(evt); } }); prefMoreBtnOk.setText("Save"); prefMoreBtnOk.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { prefMoreBtnOkActionPerformed(evt); } }); prefMoreLblLayout.setText("IDE Layout"); prefMoreMessage.setText("message area at bottom *"); prefMoreMessage.setToolTipText("OFF --- message area on right sight, ON --- bottom (restart needed)"); // NOI18N prefMoreMessage.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreMessageStateChanged(evt); } }); prefMoreCommand.setText("CommandBar (old style) *"); prefMoreCommand.setToolTipText("OFF --- no command bar - commands in Tools menu instaed, ON --- old style (restart needed)"); prefMoreCommand.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreCommandStateChanged(evt); } }); prefMoreLblLogsOld.setText("Messages to show"); prefMoreLogActions.setText("Actions"); prefMoreLogActions.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreLogActionsStateChanged(evt); } }); prefMoreLogInfo.setText("Info"); prefMoreLogInfo.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreLogInfoStateChanged(evt); } }); prefMoreLogDebug.setText("Debug"); prefMoreLogDebug.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreLogDebugStateChanged(evt); } }); prefMoreLblText.setText("TextSearch and OCR"); prefMoreLblText.setToolTipText("... only if you know what you are doing ;-)"); // NOI18N prefMoreTextSearch.setText("allow searching for text"); prefMoreTextSearch.setToolTipText("... only if you know what you are doing ;-)"); // NOI18N prefMoreTextSearch.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreTextSearchStateChanged(evt); } }); prefMoreTextOCR.setText("allow OCR"); prefMoreTextOCR.setToolTipText("... only if you know what you are doing ;-)"); // NOI18N prefMoreTextOCR.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreTextOCRStateChanged(evt); } }); prefMoreScripter.setText("Activate the new layout (X-1.0) * (no CommandBar, MessageArea on right side)"); prefMoreScripter.setToolTipText("... no command bar - in Tools menu instead, message area on right side, some more options ..."); // NOI18N prefMoreScripter.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreScripterStateChanged(evt); } }); prefMoreLblTitle1.setText("* these prefs need a restart of the IDE - others are active after save (no restart needed)"); prefMoreImageThumbs.setText("ImageThumbs (on) / ImageLabels (off) *"); prefMoreImageThumbs.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMoreThumbsStateChanged(evt); } }); prefMorePlainText.setText("show script as plain text"); prefMorePlainText.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { prefMorePlainStateChanged(evt); } }); org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() .add(6, 6, 6) .add(jSeparator5, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 564, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(layout.createSequentialGroup() .add(26, 26, 26) .add(prefMoreLblTitle1)) .add(layout.createSequentialGroup() .add(6, 6, 6) .add(jSeparator1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 564, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(layout.createSequentialGroup() .add(32, 32, 32) .add(prefMoreLblSave) .add(73, 73, 73) .add(prefMoreHTML) .add(75, 75, 75) .add(prefMoreClean)) .add(layout.createSequentialGroup() .add(6, 6, 6) .add(jSeparator2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 564, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(layout.createSequentialGroup() .add(32, 32, 32) .add(prefMoreLblRun) .add(77, 77, 77) .add(prefMoreRunSave) .add(78, 78, 78) .add(prefMoreHighlight)) .add(jSeparator3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 570, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(layout.createSequentialGroup() .add(32, 32, 32) .add(prefMoreLblLayout) .add(40, 40, 40) .add(prefMoreMessage) .add(26, 26, 26) .add(prefMoreCommand)) .add(layout.createSequentialGroup() .add(6, 6, 6) .add(jSeparator6, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 564, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(layout.createSequentialGroup() .add(32, 32, 32) .add(prefMoreLblLogsOld) .add(63, 63, 63) .add(prefMoreLogActions) .add(50, 50, 50) .add(prefMoreLogInfo) .add(57, 57, 57) .add(prefMoreLogDebug)) .add(layout.createSequentialGroup() .add(6, 6, 6) .add(jSeparator7, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 564, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(layout.createSequentialGroup() .add(33, 33, 33) .add(prefMoreLblText) .add(48, 48, 48) .add(prefMoreTextSearch) .add(61, 61, 61) .add(prefMoreTextOCR)) .add(layout.createSequentialGroup() .add(6, 6, 6) .add(jSeparator8, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 564, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(layout.createSequentialGroup() .add(32, 32, 32) .add(prefMoreScripter)) .add(layout.createSequentialGroup() .add(6, 6, 6) .add(jSeparator9, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 564, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(layout.createSequentialGroup() .add(32, 32, 32) .add(prefMoreImageThumbs) .add(47, 47, 47) .add(prefMorePlainText)) .add(layout.createSequentialGroup() .add(6, 6, 6) .add(jSeparator10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 564, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(layout.createSequentialGroup() .add(495, 495, 495) .add(prefMoreBtnOk)) .add(layout.createSequentialGroup() .add(59, 59, 59) .add(prefMoreLblStatus)) ); layout.setVerticalGroup( layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() .add(jSeparator5, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(6, 6, 6) .add(prefMoreLblTitle1) .add(6, 6, 6) .add(jSeparator1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(6, 6, 6) .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() .add(4, 4, 4) .add(prefMoreLblSave)) .add(prefMoreHTML) .add(prefMoreClean)) .add(4, 4, 4) .add(jSeparator2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(4, 4, 4) .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() .add(4, 4, 4) .add(prefMoreLblRun)) .add(prefMoreRunSave) .add(prefMoreHighlight)) .add(5, 5, 5) .add(jSeparator3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() .add(4, 4, 4) .add(prefMoreLblLayout)) .add(prefMoreMessage) .add(prefMoreCommand)) .add(6, 6, 6) .add(jSeparator6, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(6, 6, 6) .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() .add(4, 4, 4) .add(prefMoreLblLogsOld)) .add(prefMoreLogActions) .add(prefMoreLogInfo) .add(prefMoreLogDebug)) .add(6, 6, 6) .add(jSeparator7, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(6, 6, 6) .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() .add(4, 4, 4) .add(prefMoreLblText)) .add(prefMoreTextSearch) .add(prefMoreTextOCR)) .add(6, 6, 6) .add(jSeparator8, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(6, 6, 6) .add(prefMoreScripter) .add(6, 6, 6) .add(jSeparator9, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(6, 6, 6) .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(prefMoreImageThumbs) .add(prefMorePlainText)) .add(6, 6, 6) .add(jSeparator10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(prefMoreBtnOk) .add(0, 0, 0) .add(prefMoreLblStatus)) ); }// //GEN-END:initComponents private void setStatus() { setStatus("..."); } private void setStatus(String text) { setStatus(text, false); } private void setStatus(String text, boolean force) { if (DEBUG || force) { prefMoreLblStatus.setText(text); } } private boolean isSelected(JCheckBox cb) { if (cb.getSelectedObjects() != null) { setStatus("ON --- "+cb.getText()); return true; } else { setStatus("OFF --- "+cb.getText()); return false; } } private void savePrefs(String msg) { prefs.setAtSaveMakeHTML(isSelected(prefMoreHTML)); prefs.setAtSaveCleanBundle(isSelected(prefMoreClean)); prefs.setPrefMoreRunSave(isSelected(prefMoreRunSave)); prefs.setPrefMoreHighlight(isSelected(prefMoreHighlight)); prefs.setPrefMoreMessage(isSelected(prefMoreMessage) ? PreferencesUser.HORIZONTAL : PreferencesUser.VERTICAL); prefs.setPrefMoreCommandBar(isSelected(prefMoreCommand)); prefs.setPrefMoreLogActions(isSelected(prefMoreLogActions)); prefs.setPrefMoreLogInfo(isSelected(prefMoreLogInfo)); prefs.setPrefMoreLogDebug(isSelected(prefMoreLogDebug)); prefs.setPrefMoreTextSearch(isSelected(prefMoreTextSearch)); prefs.setPrefMoreTextOCR(isSelected(prefMoreTextOCR)); prefs.setPrefMoreImageThumbs(isSelected(prefMoreImageThumbs)); prefs.setPrefMorePlainText(isSelected(prefMorePlainText)); prefs.setUserType(isSelected(prefMoreScripter)?PreferencesUser.SCRIPTER:PreferencesUser.SIKULI_USER); setStatus(msg, true); } private void prefMoreBtnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prefMoreBtnOkActionPerformed savePrefs("Settings saved"); }//GEN-LAST:event_prefMoreBtnOkActionPerformed private void prefMoreHighlightStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreHighlightStateChanged isSelected(prefMoreHighlight); }//GEN-LAST:event_prefMoreHighlightStateChanged private void prefMoreCleanStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreCleanStateChanged isSelected(prefMoreClean); }//GEN-LAST:event_prefMoreCleanStateChanged private void prefMoreHTMLStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreHTMLStateChanged isSelected(prefMoreHTML); }//GEN-LAST:event_prefMoreHTMLStateChanged private void prefMoreRunSaveStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreRunSaveStateChanged isSelected(prefMoreRunSave); }//GEN-LAST:event_prefMoreRunSaveStateChanged private void prefMoreScripterStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreScripterStateChanged if (isSelected(prefMoreScripter)) { if (! openingWindow) { prefMoreMessage.setSelected(false); prefMoreCommand.setSelected(false); prefMoreHTML.setSelected(false); prefMoreRunSave.setSelected(true); prefMoreLogActions.setSelected(false); prefMoreLogDebug.setSelected(false); prefMoreLogInfo.setSelected(false); prefMoreImageThumbs.setSelected(false); prefMorePlainText.setSelected(false); savePrefs("Switched to new Layout - Restart IDE!"); } } else { prefMoreImageThumbs.setSelected(true); prefMorePlainText.setSelected(false); prefMoreMessage.setSelected(true); prefMoreCommand.setSelected(true); prefMoreLogActions.setSelected(true); prefMoreLogDebug.setSelected(true); prefMoreLogInfo.setSelected(true); savePrefs("Switched to old Layout - Restart IDE!"); } }//GEN-LAST:event_prefMoreScripterStateChanged private void prefMoreLogActionsStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreLogActionsStateChanged isSelected(prefMoreLogActions); }//GEN-LAST:event_prefMoreLogActionsStateChanged private void prefMoreLogDebugStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreLogDebugStateChanged isSelected(prefMoreLogDebug); }//GEN-LAST:event_prefMoreLogDebugStateChanged private void prefMoreLogInfoStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreLogInfoStateChanged isSelected(prefMoreLogInfo); }//GEN-LAST:event_prefMoreLogInfoStateChanged private void prefMoreMessageStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreMessageStateChanged isSelected(prefMoreMessage); }//GEN-LAST:event_prefMoreMessageStateChanged private void prefMoreCommandStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreCommandStateChanged isSelected(prefMoreCommand); }//GEN-LAST:event_prefMoreCommandStateChanged private void prefMoreTextSearchStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreTextSearchStateChanged isSelected(prefMoreTextSearch); }//GEN-LAST:event_prefMoreTextSearchStateChanged private void prefMoreTextOCRStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreTextOCRStateChanged isSelected(prefMoreTextOCR); }//GEN-LAST:event_prefMoreTextOCRStateChanged private void prefMoreThumbsStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMoreThumbsStateChanged isSelected(prefMoreImageThumbs); }//GEN-LAST:event_prefMoreThumbsStateChanged private void prefMorePlainStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_prefMorePlainStateChanged isSelected(prefMorePlainText); }//GEN-LAST:event_prefMorePlainStateChanged // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.ButtonGroup buttonGroup1; private javax.swing.ButtonGroup buttonGroup2; private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; private javax.swing.JCheckBox jCheckBox4; private javax.swing.JCheckBox jCheckBox6; private javax.swing.JLabel jLabel1; private javax.swing.JPanel jPanel1; private javax.swing.JSeparator jSeparator1; private javax.swing.JSeparator jSeparator10; private javax.swing.JSeparator jSeparator2; private javax.swing.JSeparator jSeparator3; private javax.swing.JSeparator jSeparator5; private javax.swing.JSeparator jSeparator6; private javax.swing.JSeparator jSeparator7; private javax.swing.JSeparator jSeparator8; private javax.swing.JSeparator jSeparator9; private javax.swing.JTextField jTextField1; private javax.swing.JToggleButton jToggleButton1; private javax.swing.JButton prefMoreBtnOk; private javax.swing.JCheckBox prefMoreClean; private javax.swing.JCheckBox prefMoreCommand; private javax.swing.JCheckBox prefMoreHTML; private javax.swing.JCheckBox prefMoreHighlight; private javax.swing.JCheckBox prefMoreImageThumbs; private javax.swing.JLabel prefMoreLblLayout; private javax.swing.JLabel prefMoreLblLogsOld; private javax.swing.JLabel prefMoreLblRun; private javax.swing.JLabel prefMoreLblSave; private javax.swing.JLabel prefMoreLblStatus; private javax.swing.JLabel prefMoreLblText; private javax.swing.JLabel prefMoreLblTitle1; private javax.swing.JCheckBox prefMoreLogActions; private javax.swing.JCheckBox prefMoreLogDebug; private javax.swing.JCheckBox prefMoreLogInfo; private javax.swing.JCheckBox prefMoreMessage; private javax.swing.JCheckBox prefMorePlainText; private javax.swing.JCheckBox prefMoreRunSave; private javax.swing.JCheckBox prefMoreScripter; private javax.swing.JCheckBox prefMoreTextOCR; private javax.swing.JCheckBox prefMoreTextSearch; // End of variables declaration//GEN-END:variables } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/PreviewWindow.java000066400000000000000000000026571315726130400245570ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.sikuli.ide; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import org.sikuli.util.OverlayTransparentWindow; /** * * @author rhocke */ public class PreviewWindow extends OverlayTransparentWindow implements MouseListener { public PreviewWindow() { } @Override public void mouseClicked(MouseEvent e) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mousePressed(MouseEvent e) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mouseReleased(MouseEvent e) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mouseEntered(MouseEvent e) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mouseExited(MouseEvent e) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/SikuliEditorKit.java000077500000000000000000000436351315726130400250310ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import org.sikuli.basics.PreferencesUser; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.Map; import javax.swing.*; import javax.swing.text.*; import org.sikuli.basics.Debug; import org.sikuli.idesupport.IIndentationLogic; public class SikuliEditorKit extends StyledEditorKit { private static final String me = "EditorKit: "; private ViewFactory _viewFactory; private EditorPane pane; public static final String deIndentAction = "SKL.DeindentAction"; private static final TextAction[] defaultActions = { new InsertTabAction(), new DeindentAction(), new InsertBreakAction(), new NextVisualPositionAction(forwardAction, false, SwingConstants.EAST), new NextVisualPositionAction(backwardAction, false, SwingConstants.WEST), new NextVisualPositionAction(selectionForwardAction, true, SwingConstants.EAST), new NextVisualPositionAction(selectionBackwardAction, true, SwingConstants.WEST), new NextVisualPositionAction(upAction, false, SwingConstants.NORTH), new NextVisualPositionAction(downAction, false, SwingConstants.SOUTH), new NextVisualPositionAction(selectionUpAction, true, SwingConstants.NORTH), new NextVisualPositionAction(selectionDownAction, true, SwingConstants.SOUTH),}; public SikuliEditorKit() { pane = SikuliIDE.getInstance().getCurrentCodePane(); _viewFactory = new EditorViewFactory(); ((EditorViewFactory) _viewFactory).setContentType(pane.getSikuliContentType()); } public static class InsertTabAction extends TextAction { private IIndentationLogic indentationLogic; public InsertTabAction() { super(insertTabAction); } public InsertTabAction(String name) { super(name); } @Override public void actionPerformed(ActionEvent e) { Debug.log(5, "InsertTabAction " + e); JTextComponent text = (JTextComponent) e.getSource(); actionPerformed(text); } public void actionPerformed(JTextComponent text) { boolean indentError = false; Document doc = text.getDocument(); Element map = doc.getDefaultRootElement(); String tabWhitespace = PreferencesUser.getInstance().getTabWhitespace(); Caret c = text.getCaret(); int dot = c.getDot(); int mark = c.getMark(); int dotLine = map.getElementIndex(dot); int markLine = map.getElementIndex(mark); if (dotLine != markLine) { int first = Math.min(dotLine, markLine); int last = Math.max(dotLine, markLine); Element elem; int start; try { for (int i = first; i < last; i++) { elem = map.getElement(i); start = elem.getStartOffset(); doc.insertString(start, tabWhitespace, null); } elem = map.getElement(last); start = elem.getStartOffset(); if (Math.max(c.getDot(), c.getMark()) != start) { doc.insertString(start, tabWhitespace, null); } } catch (BadLocationException ble) { Debug.error(me + "Problem while indenting line\n%s", ble.getMessage()); UIManager.getLookAndFeel().provideErrorFeedback(text); } } else { text.replaceSelection(tabWhitespace); } } } public static class DeindentAction extends TextAction { //TODO dedent not working consistently on last line (no last empty line) private IIndentationLogic indentationLogic; private Segment segLine; public DeindentAction() { this(deIndentAction); } public DeindentAction(String name) { super(name); segLine = new Segment(); } @Override public void actionPerformed(ActionEvent e) { Debug.log(5, "DedentAction " + e); JTextComponent text = (JTextComponent) e.getSource(); actionPerformed(text); } public void actionPerformed(JTextComponent text) { indentationLogic = ((EditorPane) text).getIndentationLogic(); if (indentationLogic == null) { return; } StyledDocument doc = (StyledDocument) text.getDocument(); Element map = doc.getDefaultRootElement(); Caret c = text.getCaret(); int dot = c.getDot(); int mark = c.getMark(); int line1 = map.getElementIndex(dot); if (dot != mark) { int line2 = map.getElementIndex(mark); int begin = Math.min(line1, line2); int end = Math.max(line1, line2); Element elem; try { for (line1 = begin; line1 < end; line1++) { elem = map.getElement(line1); handleDecreaseIndent(line1, elem, doc); } elem = map.getElement(end); int start = elem.getStartOffset(); if (Math.max(c.getDot(), c.getMark()) != start) { handleDecreaseIndent(end, elem, doc); } } catch (BadLocationException ble) { Debug.error(me + "Problem while de-indenting line\n%s", ble.getMessage()); UIManager.getLookAndFeel().provideErrorFeedback(text); } } else { Element elem = map.getElement(line1); try { handleDecreaseIndent(line1, elem, doc); } catch (BadLocationException ble) { Debug.error(me + "Problem while de-indenting line\n%s", ble.getMessage()); UIManager.getLookAndFeel().provideErrorFeedback(text); } } } private void handleDecreaseIndent(int line, Element elem, StyledDocument doc) throws BadLocationException { int start = elem.getStartOffset(); int end = elem.getEndOffset() - 1; doc.getText(start, end - start, segLine); int i = segLine.offset; end = i + segLine.count; if (end > i) { String leadingWS = indentationLogic.getLeadingWhitespace(doc, start, end - start); int toRemove = indentationLogic.checkDedent(leadingWS, line + 1); doc.remove(start, toRemove); } } } public static class InsertBreakAction extends TextAction { private IIndentationLogic indentationLogic; public InsertBreakAction() { super(insertBreakAction); } public InsertBreakAction(String name) { super(name); } @Override public void actionPerformed(ActionEvent e) { Debug.log(5, "InsertBreakAction " + e); JTextComponent text = (JTextComponent) e.getSource(); insertBreak(text); } public void insertBreak(JTextComponent text) { boolean noSelection = text.getSelectionStart() == text.getSelectionEnd(); if (noSelection) { insertNewlineWithAutoIndent(text); } else { text.replaceSelection("\n"); //TODO insertNewlineWithAutoIndent } } private void insertNewlineWithAutoIndent(JTextComponent text) { indentationLogic = ((EditorPane) text).getIndentationLogic(); if (indentationLogic == null) { text.replaceSelection("\n"); return; } try { int caretPos = text.getCaretPosition(); StyledDocument doc = (StyledDocument) text.getDocument(); Element map = doc.getDefaultRootElement(); int lineNum = map.getElementIndex(caretPos); Element line = map.getElement(lineNum); int start = line.getStartOffset(); int end = line.getEndOffset() - 1; int len = end - start; String s = doc.getText(start, len); StringBuilder sb = new StringBuilder("\n"); String leadingWS = indentationLogic.getLeadingWhitespace(doc, start, caretPos - start); sb.append(leadingWS); //TODO better control over automatic indentation indentationLogic.checkIndent(leadingWS, lineNum + 1); // If there is only whitespace between the caret and // the EOL, pressing Enter auto-indents the new line to // the same place as the previous line. int nonWhitespacePos = indentationLogic.atEndOfLine(doc, caretPos, start, s, len); if (nonWhitespacePos == -1) { if (leadingWS.length() == len) { // If the line was nothing but whitespace, select it // so its contents get removed. text.setSelectionStart(start); } else { // Select the whitespace between the caret and the EOL // to remove it text.setSelectionStart(caretPos); } text.setSelectionEnd(end); text.replaceSelection(sb.toString()); // auto-indentation for python statements like if, while, for, try, // except, def, class and auto-deindentation for break, continue, // pass, return analyseDocument(doc, lineNum, indentationLogic); // auto-completion: add colon if it is obvious if (indentationLogic.shouldAddColon()) { doc.insertString(caretPos, ":", null); indentationLogic.setLastLineEndsWithColon(); } int lastLineChange = indentationLogic.shouldChangeLastLineIndentation(); int nextLineChange = indentationLogic.shouldChangeNextLineIndentation(); if (lastLineChange != 0) { Debug.log(5, "change line %d indentation by %d columns", lineNum + 1, lastLineChange); changeIndentation((DefaultStyledDocument) doc, lineNum, lastLineChange); // nextLineChange was determined based on indentation of last line before // the change nextLineChange += lastLineChange; } if (nextLineChange != 0) { Debug.log(5, "change line %d indentation by %d columns", lineNum + 2, nextLineChange); changeIndentation((DefaultStyledDocument) doc, lineNum + 1, nextLineChange); } } // If there is non-whitespace between the caret and the // EOL, pressing Enter takes that text to the next line // and auto-indents it to the same place as the last // line. Additional auto-indentation or dedentation for // specific python statements is only done for the next line. else { text.setCaretPosition(nonWhitespacePos); doc.insertString(nonWhitespacePos, sb.toString(), null); analyseDocument(doc, lineNum, indentationLogic); int nextLineChange = indentationLogic.shouldChangeNextLineIndentation(); if (nextLineChange != 0) { Debug.log(5, "change line %d indentation by %d columns", lineNum + 2, nextLineChange); changeIndentation((DefaultStyledDocument) doc, lineNum + 1, nextLineChange); } } } catch (BadLocationException ble) { text.replaceSelection("\n"); Debug.error(me + "Problem while inserting new line with auto-indent\n%s", ble.getMessage()); } } private void analyseDocument(Document document, int lineNum, IIndentationLogic indentationLogic) throws BadLocationException { Element map = document.getDefaultRootElement(); int endPos = map.getElement(lineNum).getEndOffset(); indentationLogic.reset(); indentationLogic.addText(document.getText(0, endPos)); } /** * Change the indentation of a line. Any existing leading whitespace is * replaced by the appropriate number of tab characters (padded with blank * characters if necessary) if tab expansion in the user preferences is * true, or the appropriate number of blank characters if tab expansion is * false. * * @param linenum the line number (0-based) * @param columns the number of columns by which to increase the indentation * (if columns is greater than 0) or decrease the indentation (if columns is * less than 0) * @throws BadLocationException if the specified line does not exist */ // TODO: make this a method of SikuliDocument, no need to pass document as argument private void changeIndentation(DefaultStyledDocument doc, int linenum, int columns) throws BadLocationException { PreferencesUser pref = PreferencesUser.getInstance(); boolean expandTab = pref.getExpandTab(); int tabWidth = pref.getTabWidth(); if (linenum < 0) { throw new BadLocationException("Negative line", -1); } Element map = doc.getDefaultRootElement(); if (linenum >= map.getElementCount()) { throw new BadLocationException("No such line", doc.getLength() + 1); } if (columns == 0) { return; } Element lineElem = map.getElement(linenum); int lineStart = lineElem.getStartOffset(); int lineLength = lineElem.getEndOffset() - lineStart; String line = doc.getText(lineStart, lineLength); // determine current indentation and number of whitespace characters int wsChars; int indentation = 0; for (wsChars = 0; wsChars < line.length(); wsChars++) { char c = line.charAt(wsChars); if (c == ' ') { indentation++; } else if (c == '\t') { indentation += tabWidth; } else { break; } } int newIndentation = indentation + columns; if (newIndentation <= 0) { doc.remove(lineStart, wsChars); return; } // build whitespace string for new indentation StringBuilder newWs = new StringBuilder(newIndentation / tabWidth + tabWidth - 1); int ind = 0; if (!expandTab) { for (; ind + tabWidth <= newIndentation; ind += tabWidth) { newWs.append('\t'); } } for (; ind < newIndentation; ind++) { newWs.append(' '); } doc.replace(lineStart, wsChars, newWs.toString(), null); } } private static class NextVisualPositionAction extends TextAction { private boolean select; private int direction; NextVisualPositionAction(String nm, boolean select, int dir) { //TODO forward selection space+image - space not selected alone //TODO up/down might step left or right super(nm); this.select = select; this.direction = dir; } private static int getNSVisualPosition(EditorPane txt, int pos, int direction) { Element root = txt.getDocument().getDefaultRootElement(); int numLines = root.getElementIndex(txt.getDocument().getLength() - 1) + 1; int line = root.getElementIndex(pos) + 1; int tarLine = direction == SwingConstants.NORTH ? line - 1 : line + 1; try { if (tarLine <= 0) { return 0; } if (tarLine > numLines) { return txt.getDocument().getLength(); } Rectangle curRect = txt.modelToView(pos); Rectangle tarEndRect; if (tarLine < numLines) { tarEndRect = txt.modelToView(txt.getLineStartOffset(tarLine) - 1); } else { tarEndRect = txt.modelToView(txt.getDocument().getLength() - 1); } Debug.log(9, "curRect: " + curRect + ", tarEnd: " + tarEndRect); if (curRect.x > tarEndRect.x) { pos = txt.viewToModel(new Point(tarEndRect.x, tarEndRect.y)); } else { pos = txt.viewToModel(new Point(curRect.x, tarEndRect.y)); } } catch (BadLocationException e) { Debug.error(me + "Problem getting next visual position\n%s", e.getMessage()); } return pos; } @Override public void actionPerformed(ActionEvent e) { JTextComponent textArea = (JTextComponent) e.getSource(); Caret caret = textArea.getCaret(); int dot = caret.getDot(); /* * Move to the beginning/end of selection on a "non-shifted" * left- or right-keypress. We shouldn't have to worry about * navigation filters as, if one is being used, it let us get * to that position before. */ if (!select) { switch (direction) { case SwingConstants.EAST: int mark = caret.getMark(); if (dot != mark) { caret.setDot(Math.max(dot, mark)); return; } break; case SwingConstants.WEST: mark = caret.getMark(); if (dot != mark) { caret.setDot(Math.min(dot, mark)); return; } break; default: } } Position.Bias[] bias = new Position.Bias[1]; Point magicPosition = caret.getMagicCaretPosition(); try { if (magicPosition == null && (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH)) { Rectangle r = textArea.modelToView(dot); magicPosition = new Point(r.x, r.y); } NavigationFilter filter = textArea.getNavigationFilter(); if (filter != null) { dot = filter.getNextVisualPositionFrom(textArea, dot, Position.Bias.Forward, direction, bias); } else { if (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH) { dot = getNSVisualPosition((EditorPane) textArea, dot, direction); } else { dot = textArea.getUI().getNextVisualPositionFrom( textArea, dot, Position.Bias.Forward, direction, bias); } } if (select) { caret.moveDot(dot); } else { caret.setDot(dot); } if (magicPosition != null && (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH)) { caret.setMagicCaretPosition(magicPosition); } } catch (BadLocationException ble) { Debug.error(me + "Problem while trying to move caret\n%s", ble.getMessage()); } } } // @Override public Action[] getActions() { return TextAction.augmentList(super.getActions(), defaultActions); } @Override public ViewFactory getViewFactory() { return _viewFactory; } @Override public String getContentType() { return pane.getSikuliContentType(); } @Override public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException { Debug.log(3, "SikuliEditorKit.read"); super.read(in, doc, pos); } @Override public void write(Writer out, Document doc, int pos, int len) throws IOException, BadLocationException { write(out, doc, pos, len, null); } public void write(Writer out, Document doc, int pos, int len, Map copiedImgs) throws IOException, BadLocationException { Debug.log(9, "SikuliEditorKit.write %d %d", pos, len); DefaultStyledDocument sdoc = (DefaultStyledDocument) doc; int i = pos; String absPath; while (i < pos + len) { Element e = sdoc.getCharacterElement(i); int start = e.getStartOffset(), end = e.getEndOffset(); if (e.getName().equals(StyleConstants.ComponentElementName)) { // A image argument to be filled AttributeSet attr = e.getAttributes(); Component com = StyleConstants.getComponent(attr); out.write(com.toString()); if (copiedImgs != null && (com instanceof EditorPatternButton || com instanceof EditorPatternLabel)) { if (com instanceof EditorPatternButton) { absPath = ((EditorPatternButton) com).getFilename(); } else { absPath = ((EditorPatternLabel) com).getFile(); } String fname = (new File(absPath)).getName(); copiedImgs.put(fname, absPath); Debug.log(3, "save image for copy&paste: " + fname + " -> " + absPath); } } else { if (start < pos) { start = pos; } if (end > pos + len) { end = pos + len; } out.write(doc.getText(start, end - start)); } i = end; } out.close(); } // } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/SikuliIDE.java000066400000000000000000003004571315726130400235270ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import com.explodingpixels.macwidgets.MacUtils; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.event.AWTEventListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.ToolTipManager; import javax.swing.UIManager; import javax.swing.WindowConstants; import javax.swing.event.ChangeListener; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultEditorKit; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import org.apache.commons.cli.CommandLine; import org.jdesktop.swingx.JXCollapsiblePane; import org.jdesktop.swingx.JXSearchField; import org.jdesktop.swingx.JXTaskPane; import org.jdesktop.swingx.JXTaskPaneContainer; import org.sikuli.android.ADBClient; import org.sikuli.android.ADBScreen; import org.sikuli.android.ADBTest; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.basics.HotkeyEvent; import org.sikuli.basics.HotkeyListener; import org.sikuli.basics.HotkeyManager; import org.sikuli.basics.PreferencesUser; import org.sikuli.basics.Settings; import org.sikuli.idesupport.IDESplash; import org.sikuli.idesupport.IDESupport; import org.sikuli.idesupport.IIDESupport; import org.sikuli.script.*; import org.sikuli.script.Sikulix; import org.sikuli.scriptrunner.IScriptRunner; import org.sikuli.scriptrunner.ScriptingSupport; import org.sikuli.util.CommandArgs; import org.sikuli.util.CommandArgsEnum; import org.sikuli.util.EventObserver; import org.sikuli.util.EventSubject; import org.sikuli.util.OverlayCapturePrompt; import org.sikuli.util.SikulixFileChooser; public class SikuliIDE extends JFrame implements InvocationHandler { private static String me = "IDE: "; private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } final static boolean ENABLE_UNIFIED_TOOLBAR = true; final static Color COLOR_SEARCH_FAILED = Color.red; final static Color COLOR_SEARCH_NORMAL = Color.black; final static int WARNING_CANCEL = 2; final static int WARNING_ACCEPTED = 1; final static int WARNING_DO_NOTHING = 0; final static int IS_SAVE_ALL = 3; private Dimension _windowSize = null; private Point _windowLocation = null; private CloseableTabbedPane tabPane; private EditorLineNumberView lineNumberColumn; private JSplitPane _mainSplitPane; private JTabbedPane msgPane; private boolean msgPaneCollapsed = false; private EditorConsolePane _console; private JXCollapsiblePane _cmdList; private SikuliIDEStatusBar _status = null; private ButtonCapture _btnCapture; private ButtonRun _btnRun = null, _btnRunViz = null; private boolean ideIsRunningScript = false; private JXSearchField _searchField; private JMenuBar _menuBar = new JMenuBar(); private JMenu _fileMenu = new JMenu(_I("menuFile")); private JMenu _editMenu = new JMenu(_I("menuEdit")); private UndoAction _undoAction = new UndoAction(); private RedoAction _redoAction = new RedoAction(); private FindAction _findHelper; private JMenu _runMenu = new JMenu(_I("menuRun")); private JMenu _viewMenu = new JMenu(_I("menuView")); private JMenu _toolMenu = new JMenu(_I("menuTool")); private JMenu _helpMenu = new JMenu(_I("menuHelp")); private JXCollapsiblePane _sidePane; private JCheckBoxMenuItem _chkShowUnitTest; private JMenuItem chkShowCmdList = null; private JCheckBoxMenuItem chkShowThumbs; //private UnitTestRunner _testRunner; private static CommandLine cmdLine; private static String cmdValue; private static String[] loadScripts = null; private static SikuliIDE sikulixIDE = null; private boolean _inited = false; private int restoredScripts = 0; private int alreadyOpenedTab = -1; private PreferencesUser prefs; private boolean ACCESSING_AS_FOLDER = false; private static long start; private boolean showAbout = true; private boolean showPrefs = true; private boolean showQuit = true; IDESplash ideSplash = null; boolean idePause = false; int waitBeforeVisible = 0; private synchronized boolean setPause(Boolean state) { if (state != null) { idePause = state; } return idePause; } private boolean getPause() { return setPause(null); } private void waitPause() { if (getPause()) { ideSplash.setVisible(false); Sikulix.popup("No options yet!\nClick OK to continue!", String.format("%s-%s", runTime.getVersionShort(), runTime.sxBuildStamp)); ideSplash.showAction(" "); ideSplash.setVisible(true); waitBeforeVisible = 2; } } public static void showIDE() { Debug.log(3, "showIDE"); sikulixIDE.setVisible(true); } public static void hideIDE() { Debug.log(3, "hideIDE"); sikulixIDE.setVisible(false); RunTime.pause(0.5f); } public static void showAgain() { sikulixIDE.setVisible(true); EditorPane codePane = sikulixIDE.getCurrentCodePane(); codePane.requestFocus(); } private SikuliIDE() { super("SikuliX-IDE"); } public static synchronized SikuliIDE getInstance() { if (sikulixIDE == null) { sikulixIDE = new SikuliIDE(); } return sikulixIDE; } public static String _I(String key, Object... args) { try { return SikuliIDEI18N._I(key, args); } catch (Exception e) { Debug.log(3, "[I18N] " + key); return key; } } public static RunTime runTime; public static void run(String[] args) { start = (new Date()).getTime(); runTime = RunTime.get(RunTime.Type.IDE, args); CommandArgs cmdArgs = new CommandArgs("IDE"); cmdLine = cmdArgs.getCommandLine(CommandArgs.scanArgs(args)); if (cmdLine == null) { Debug.error("Did not find any valid option on command line!"); System.exit(1); } runTime.setArgs(cmdArgs.getUserArgs(), cmdArgs.getSikuliArgs()); if (RunTime.shouldRunServer) { RunServer.run(null); System.exit(0); } if (cmdLine.hasOption("h")) { cmdArgs.printHelp(); System.exit(0); } if (cmdLine.hasOption(CommandArgsEnum.RUN.shortname()) || cmdLine.hasOption(CommandArgsEnum.TEST.shortname()) || cmdLine.hasOption(CommandArgsEnum.INTERACTIVE.shortname())) { log(lvl, "Switching to ScriptRunner with option -r, -t or -i"); ScriptingSupport.runscript(args); } getInstance(); log(3, "running with Locale: %s", SikuliIDEI18N.getLocaleShow()); sikulixIDE.initNativeSupport(); sikulixIDE.ideSplash = new IDESplash(runTime); if (cmdLine.hasOption(CommandArgsEnum.DEBUG.shortname())) { cmdValue = cmdLine.getOptionValue(CommandArgsEnum.DEBUG.longname()); if (cmdValue != null) { Debug.on(cmdValue); } } if (cmdLine.hasOption("c")) { System.setProperty("sikuli.console", "false"); } if (cmdLine.hasOption(CommandArgsEnum.LOGFILE.shortname())) { cmdValue = cmdLine.getOptionValue(CommandArgsEnum.LOGFILE.longname()); if (!Debug.setLogFile(cmdValue == null ? "" : cmdValue)) { System.exit(1); } } if (cmdLine.hasOption(CommandArgsEnum.USERLOGFILE.shortname())) { cmdValue = cmdLine.getOptionValue(CommandArgsEnum.USERLOGFILE.longname()); if (!Debug.setUserLogFile(cmdValue == null ? "" : cmdValue)) { System.exit(1); } } if (cmdLine.hasOption(CommandArgsEnum.LOAD.shortname())) { loadScripts = cmdLine.getOptionValues(CommandArgsEnum.LOAD.longname()); log(lvl, "requested to load: %s", loadScripts); } //TODO how to differentiate open and run for doubleclick/drop scripts if (macOpenFiles != null) { for (File f : macOpenFiles) { if (f.getName().endsWith(".sikuli") || f.getName().endsWith(".skl")) { ScriptingSupport.runscript(new String[]{"-r", f.getAbsolutePath()}); } } } runTime.printArgs(); try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); //TODO UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e) { log(-1, "Problem loading UIManager!\nError: %s", e.getMessage()); } sikulixIDE.initHotkeys(); sikulixIDE.ideSplash.showAction("Interrupt with " + HotkeyManager.getInstance().getHotKeyText("Abort")); sikulixIDE.ideSplash.showStep("Init ScriptingSupport"); ScriptingSupport.init(); IDESupport.initIDESupport(); sikulixIDE.initSikuliIDE(args); } private void initSikuliIDE(String[] args) { sikulixIDE.ideSplash.showStep("Reading Preferences"); prefs = PreferencesUser.getInstance(); //prefs.exportPrefs(new File(runTime.fUserDir, "SikulixIDEprefs.txt").getAbsolutePath()); if (prefs.getUserType() < 0) { prefs.setUserType(PreferencesUser.NEWBEE); prefs.setIdeSession(""); prefs.setDefaults(prefs.getUserType()); } _windowSize = prefs.getIdeSize(); _windowLocation = prefs.getIdeLocation(); Rectangle monitor = runTime.hasPoint(_windowLocation); if (monitor == null) { log(-1, "Remembered window not valid. Going to primary screen"); monitor = runTime.getMonitor(-1); _windowSize.width = 0; } if (_windowSize.width == 0) { _windowSize = new Dimension(1024, 700); _windowLocation = new Point(100, 50); } Rectangle win = monitor.intersection(new Rectangle(_windowLocation, _windowSize)); setSize(win.getSize()); setLocation(_windowLocation); sikulixIDE.ideSplash.showStep("Init Window"); Debug.log(3, "IDE: Adding components to window"); initMenuBars(this); final Container c = getContentPane(); c.setLayout(new BorderLayout()); Debug.log(3, "IDE: creating tabbed editor"); initTabPane(); Debug.log(3, "IDE: creating message area"); initMsgPane(prefs.getPrefMoreMessage() == PreferencesUser.HORIZONTAL); // RaiMan not used initSidePane(); // IDE UnitTest Debug.log(3, "IDE: creating combined work window"); JPanel codeAndUnitPane = new JPanel(new BorderLayout(10, 10)); codeAndUnitPane.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 0)); codeAndUnitPane.add(tabPane, BorderLayout.CENTER); // RaiMan not used codeAndUnitPane.add(_sidePane, BorderLayout.EAST); if (prefs.getPrefMoreMessage() == PreferencesUser.VERTICAL) { _mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, codeAndUnitPane, msgPane); } else { _mainSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, codeAndUnitPane, msgPane); } _mainSplitPane.setResizeWeight(0.6); _mainSplitPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); Debug.log(3, "IDE: Putting all together"); JPanel editPane = new JPanel(new BorderLayout(0, 0)); JComponent cp = createCommandPane(); if (PreferencesUser.getInstance().getPrefMoreCommandBar()) { editPane.add(cp, BorderLayout.WEST); } editPane.add(_mainSplitPane, BorderLayout.CENTER); c.add(editPane, BorderLayout.CENTER); JToolBar tb = initToolbar(); c.add(tb, BorderLayout.NORTH); // the buttons c.add(initStatusbar(), BorderLayout.SOUTH); c.doLayout(); initShortcutKeys(); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); initWindowListener(); initTooltip(); sikulixIDE.ideSplash.showStep("Check for Updates"); autoCheckUpdate(); try { getCurrentCodePane().requestFocus(); } catch (Exception e) { } waitPause(); sikulixIDE.ideSplash.showStep("Restore last Session"); restoreSession(0); if (tabPane.getTabCount() == 0) { (new FileAction()).doNew(null); } tabPane.setSelectedIndex(0); Debug.info("IDE startup: %4.1f seconds", (new Date().getTime() - start) / 1000.0); if (waitBeforeVisible > 0) { try { Thread.sleep(1000 * waitBeforeVisible); } catch (InterruptedException ex) { } } sikulixIDE.ideSplash.setVisible(false); sikulixIDE.ideSplash.dispose(); sikulixIDE.ideSplash = null; setVisible(true); _mainSplitPane.setDividerLocation(0.6); _inited = true; } private void initNativeSupport() { if (!Settings.isMac()) { return; } log(lvl, "initNativeSupport: starting"); if (System.getProperty("sikulix.asapp") != null) { Settings.isMacApp = true; } try { // com.apple.eawt.QuitResponse Class sysclass = URLClassLoader.class; Class comAppleEawtApplication = sysclass.forName("com.apple.eawt.Application"); Method mGetApplication = comAppleEawtApplication.getDeclaredMethod("getApplication", null); Object instApplication = mGetApplication.invoke(null, null); Class clAboutHandler = sysclass.forName("com.apple.eawt.AboutHandler"); Class clPreferencesHandler = sysclass.forName("com.apple.eawt.PreferencesHandler"); Class clQuitHandler = sysclass.forName("com.apple.eawt.QuitHandler"); Class clOpenHandler = sysclass.forName("com.apple.eawt.OpenFilesHandler"); Object appHandler = Proxy.newProxyInstance( comAppleEawtApplication.getClassLoader(), new Class[]{clAboutHandler, clPreferencesHandler, clQuitHandler, clOpenHandler}, this); Method m = comAppleEawtApplication.getMethod("setAboutHandler", new Class[]{clAboutHandler}); m.invoke(instApplication, new Object[]{appHandler}); showAbout = false; m = comAppleEawtApplication.getMethod("setPreferencesHandler", new Class[]{clPreferencesHandler}); m.invoke(instApplication, new Object[]{appHandler}); showPrefs = false; m = comAppleEawtApplication.getMethod("setQuitHandler", new Class[]{clQuitHandler}); m.invoke(instApplication, new Object[]{appHandler}); showQuit = false; m = comAppleEawtApplication.getMethod("setOpenFileHandler", new Class[]{clOpenHandler}); m.invoke(instApplication, new Object[]{appHandler}); } catch (Exception ex) { String em = String.format("initNativeSupport: Mac: error:\n%s", ex.getMessage()); log(-1, em); Sikulix.popError(em, "IDE has problems ..."); System.exit(1); } log(lvl, "initNativeSupport: success"); } private static List macOpenFiles = null; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String mName = method.getName(); if ("handleAbout".equals(mName)) { sikulixIDE.doAbout(); } else if ("handlePreferences".equals(mName)) { sikulixIDE.showPreferencesWindow(); } else if ("openFiles".equals(mName)) { log(lvl, "nativeSupport: should open files"); try { Method mOpenFiles = args[0].getClass().getMethod("getFiles", new Class[]{}); macOpenFiles = (List) mOpenFiles.invoke(args[0], new Class[]{}); for (File f : macOpenFiles) { log(lvl, "nativeSupport: openFiles: %s", macOpenFiles); } } catch (Exception ex) { log(lvl, "NativeSupport: Quit: error: %s", ex.getMessage()); System.exit(1); } } else if ("handleQuitRequestWith".equals(mName)) { try { Class sysclass = URLClassLoader.class; Class comAppleEawtQuitResponse = sysclass.forName("com.apple.eawt.QuitResponse"); Method mCancelQuit = comAppleEawtQuitResponse.getMethod("cancelQuit", null); Method mPerformQuit = comAppleEawtQuitResponse.getMethod("performQuit", null); Object resp = args[1]; if (!sikulixIDE.quit()) { mCancelQuit.invoke(resp, null); } else { mPerformQuit.invoke(resp, null); } } catch (Exception ex) { log(lvl, "NativeSupport: Quit: error: %s", ex.getMessage()); System.exit(1); } } return new Object(); } @Override public void setTitle(String title) { super.setTitle(runTime.SikuliVersionIDE + " - " + title); } public static ImageIcon getIconResource(String name) { URL url = SikuliIDE.class.getResource(name); if (url == null) { Debug.error("Warning: could not load \"" + name + "\" icon"); return null; } return new ImageIcon(url); } // private boolean saveSession(int action, boolean quitting) { int nTab = tabPane.getTabCount(); StringBuilder sbuf = new StringBuilder(); for (int tabIndex = 0; tabIndex < nTab; tabIndex++) { try { EditorPane codePane = getPaneAtIndex(tabIndex); if (action == WARNING_DO_NOTHING) { if (quitting) { codePane.setDirty(false); } if (codePane.getCurrentFilename() == null) { continue; } } else if (codePane.isDirty()) { if (!(new FileAction()).doSaveIntern(tabIndex)) { if (quitting) { codePane.setDirty(false); } continue; } } if (action == IS_SAVE_ALL) { continue; } File f = codePane.getCurrentFile(); if (f != null) { String bundlePath = codePane.getSrcBundle(); Debug.log(5, "save session: " + bundlePath); if (tabIndex != 0) { sbuf.append(";"); } sbuf.append(bundlePath); } } catch (Exception e) { log(-1, "Problem while trying to save all changed-not-saved scripts!\nError: %s", e.getMessage()); return false; } } PreferencesUser.getInstance().setIdeSession(sbuf.toString()); return true; } private void restoreSession(int tabIndex) { String session_str = prefs.getIdeSession(); if (session_str == null && loadScripts == null && macOpenFiles == null) { return; } List filesToLoad = new ArrayList(); if (macOpenFiles != null) { for (File f : macOpenFiles) { filesToLoad.add(f); restoreScriptFromSession(f); } } if (session_str != null) { String[] filenames = session_str.split(";"); for (int i = 0; i < filenames.length; i++) { if (filenames[i].isEmpty()) { continue; } File f = new File(filenames[i]); if (f.exists() && !filesToLoad.contains(f)) { Debug.log(3, "restore session: %s", f); filesToLoad.add(f); restoreScriptFromSession(f); } } } if (loadScripts != null) { for (int i = 0; i < loadScripts.length; i++) { if (loadScripts[i].isEmpty()) { continue; } File f = new File(loadScripts[i]); if (f.exists() && !filesToLoad.contains(f)) { Debug.log(3, "preload script: %s", f); filesToLoad.add(f); restoreScriptFromSession(f); } } } } private boolean restoreScriptFromSession(File file) { EditorPane ep = (new FileAction()).doNew(null, -1); ep.loadFile(file.getAbsolutePath()); if (ep.hasEditingFile()) { setCurrentFileTabTitle(file.getAbsolutePath()); return true; } log(-1, "restoreScriptFromSession: Can't load: %s", file); // (new FileAction()).doCloseTab(null); return false; } // // public static IIDESupport getIDESupport(String ending) { return IDESupport.ideSupporter.get(ending); } public JMenu getFileMenu() { return _fileMenu; } public JMenu getRunMenu() { return _runMenu; } public CloseableTabbedPane getTabPane() { return tabPane; } public EditorPane getCurrentCodePane() { if (tabPane.getSelectedIndex() == -1) { return null; } JScrollPane scrPane = (JScrollPane) tabPane.getSelectedComponent(); EditorPane pane = (EditorPane) scrPane.getViewport().getView(); return pane; } public EditorPane getPaneAtIndex(int index) { JScrollPane scrPane = (JScrollPane) tabPane.getComponentAt(index); EditorPane codePane = (EditorPane) scrPane.getViewport().getView(); return codePane; } public void setCurrentFileTabTitle(String fname) { int tabIndex = tabPane.getSelectedIndex(); setFileTabTitle(fname, tabIndex); } public String getCurrentFileTabTitle() { String fname = tabPane.getTitleAt(tabPane.getSelectedIndex()); if (fname.startsWith("*")) { return fname.substring(1); } else { return fname; } } public void setCurrentFileTabTitleDirty(boolean isDirty) { int i = tabPane.getSelectedIndex(); String title = tabPane.getTitleAt(i); if (!isDirty && title.startsWith("*")) { title = title.substring(1); tabPane.setTitleAt(i, title); } else if (isDirty && !title.startsWith("*")) { title = "*" + title; tabPane.setTitleAt(i, title); } } public void setFileTabTitle(String fName, int tabIndex) { String sName = new File(fName).getName(); int i = sName.lastIndexOf("."); if (i > 0) { tabPane.setTitleAt(tabIndex, sName.substring(0, i)); } else { tabPane.setTitleAt(tabIndex, sName); } this.setTitle(new File(fName).getAbsolutePath()); } public ArrayList getOpenedFilenames() { int nTab = tabPane.getTabCount(); File file = null; String filePath; ArrayList filenames = new ArrayList(0); if (nTab > 0) { for (int i = 0; i < nTab; i++) { EditorPane codePane = getPaneAtIndex(i); file = codePane.getCurrentFile(false); if (file != null) { filePath = FileManager.slashify(file.getAbsolutePath(), false); filePath = filePath.substring(0, filePath.lastIndexOf("/")); filenames.add(filePath); } else { filenames.add(""); } } } return filenames; } public int isAlreadyOpen(String filename) { int aot = getOpenedFilenames().indexOf(filename); if (aot > -1 && aot < (tabPane.getTabCount() - 1)) { alreadyOpenedTab = aot; return aot; } return -1; } private void autoCheckUpdate() { PreferencesUser pref = PreferencesUser.getInstance(); if (!pref.getCheckUpdate()) { return; } long last_check = pref.getCheckUpdateTime(); long now = (new Date()).getTime(); if (now - last_check > 1000 * 604800) { Debug.log(3, "autocheck update"); (new HelpAction()).checkUpdate(true); } pref.setCheckUpdateTime(); } public synchronized boolean isRunningScript() { return ideIsRunningScript; } public synchronized void setIsRunningScript(boolean state) { ideIsRunningScript = state; } protected boolean doBeforeRun() { int action; if (checkDirtyPanes()) { if (prefs.getPrefMoreRunSave()) { action = WARNING_ACCEPTED; } else { action = askForSaveAll("Run"); if (action < 0) { return false; } } saveSession(action, false); } Settings.ActionLogs = prefs.getPrefMoreLogActions(); Settings.DebugLogs = prefs.getPrefMoreLogDebug(); Settings.InfoLogs = prefs.getPrefMoreLogInfo(); Settings.Highlight = prefs.getPrefMoreHighlight(); Settings.OcrTextSearch = prefs.getPrefMoreTextSearch(); Settings.OcrTextRead = prefs.getPrefMoreTextOCR(); runTime.resetProject(); return true; } protected boolean doBeforeQuit() { if (checkDirtyPanes()) { int action = askForSaveAll("Quit"); if (action < 0) { return false; } return saveSession(action, true); } return saveSession(WARNING_DO_NOTHING, true); } private int askForSaveAll(String typ) { //TODO I18N String warn = "Some scripts are not saved yet!"; String title = SikuliIDEI18N._I("dlgAskCloseTab"); String[] options = new String[3]; options[WARNING_DO_NOTHING] = typ + " immediately"; options[WARNING_ACCEPTED] = "Save all and " + typ; options[WARNING_CANCEL] = SikuliIDEI18N._I("cancel"); int ret = JOptionPane.showOptionDialog(this, warn, title, 0, JOptionPane.WARNING_MESSAGE, null, options, options[2]); if (ret == WARNING_CANCEL || ret == JOptionPane.CLOSED_OPTION) { return -1; } return ret; } public void doAbout() { //TODO full featured About String info = "You are running " + runTime.SikuliVersionIDE + "\n\nNeed help? -> start with Help Menu\n\n" + "*** Have fun ;-)\n\n" + "Tsung-Hsiang Chang aka vgod\n" + "Tom Yeh\n" + "Raimund Hocke aka RaiMan\n\n" + "\n\nBuild: " + runTime.SikuliVersionBuild; JOptionPane.showMessageDialog(this, info, "Sikuli About", JOptionPane.PLAIN_MESSAGE); } private static String[] collectOptions(String type, String[] args) { List resArgs = new ArrayList(); if (args != null) { resArgs.addAll(Arrays.asList(args)); } String msg = "----------------------- You might set some options -----------------------"; msg += "\n\n"; msg += "-r name --- Run script name: foo[.sikuli] or foo.skl (no IDE window)"; msg += "\n"; msg += "-u [file] --- Write user log messages to file (default: /UserLog.txt )"; msg += "\n"; msg += "-f [file] --- Write Sikuli log messages to file (default: /SikuliLog.txt)"; msg += "\n"; msg += "-d n --- Set a higher level n for Sikuli's debug messages (default: 0)"; msg += "\n"; msg += "-- …more… All space delimited entries after -- go to sys.argv"; msg += "\n \"\" makes one parameter (may contain intermediate blanks)"; msg += "\n\n"; msg += "-------------------------------------------------------------------------"; msg += "\n"; msg += "-d Special debugging option in case of mysterious errors:"; msg += "\n"; msg += " Debug level is set to 3 and debug output goes to /SikuliLog.txt"; msg += "\n"; msg += " Content might be used to ask questions or report bugs"; msg += "\n"; msg += "-------------------------------------------------------------------------"; msg += "\n"; msg += " Just click OK to start IDE with no options - defaults will be used"; String ret = JOptionPane.showInputDialog(null, msg, "SikuliX: collect runtime options", JOptionPane.QUESTION_MESSAGE); if (ret == null) { return null; } log(3, "collectOptions: returned [" + ret + "]"); if (!ret.isEmpty()) { System.setProperty("sikuli.SIKULI_COMMAND", ret); resArgs.addAll(Arrays.asList(ret.split(" +"))); } return resArgs.toArray(new String[0]); } // // public boolean isInited() { return _inited; } // private JMenuItem createMenuItem(JMenuItem item, KeyStroke shortcut, ActionListener listener) { if (shortcut != null) { item.setAccelerator(shortcut); } item.addActionListener(listener); return item; } private JMenuItem createMenuItem(String name, KeyStroke shortcut, ActionListener listener) { JMenuItem item = new JMenuItem(name); return createMenuItem(item, shortcut, listener); } class MenuAction implements ActionListener { protected Method actMethod = null; protected String action; public MenuAction() { } public MenuAction(String item) throws NoSuchMethodException { Class[] paramsWithEvent = new Class[1]; try { paramsWithEvent[0] = Class.forName("java.awt.event.ActionEvent"); actMethod = this.getClass().getMethod(item, paramsWithEvent); action = item; } catch (ClassNotFoundException cnfe) { log(-1, "Can't find menu action: " + cnfe); } } @Override public void actionPerformed(ActionEvent e) { if (actMethod != null) { try { Debug.log(3, "MenuAction." + action); Object[] params = new Object[1]; params[0] = e; actMethod.invoke(this, params); } catch (Exception ex) { log(-1, "Problem when trying to invoke menu action %s\nError: %s", action, ex.getMessage()); } } } } private static JMenu recentMenu = null; private static Map recentProjects = new HashMap(); private static java.util.List recentProjectsMenu = new ArrayList(); private static int recentMax = 10; private static int recentMaxMax = recentMax + 10; // private void initFileMenu() throws NoSuchMethodException { JMenuItem jmi; int scMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); _fileMenu.setMnemonic(java.awt.event.KeyEvent.VK_F); if (showAbout) { _fileMenu.add(createMenuItem("About SikuliX", KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, scMask), new FileAction(FileAction.ABOUT))); _fileMenu.addSeparator(); } _fileMenu.add(createMenuItem(_I("menuFileNew"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_N, scMask), new FileAction(FileAction.NEW))); jmi = _fileMenu.add(createMenuItem(_I("menuFileOpen"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, scMask), new FileAction(FileAction.OPEN))); jmi.setName("OPEN"); recentMenu = new JMenu(_I("menuRecent")); if (Settings.experimental) { _fileMenu.add(recentMenu); } if (Settings.isMac() && !Settings.handlesMacBundles) { _fileMenu.add(createMenuItem("Open folder.sikuli ...", null, // KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, scMask), new FileAction(FileAction.OPEN_FOLDER))); } jmi = _fileMenu.add(createMenuItem(_I("menuFileSave"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, scMask), new FileAction(FileAction.SAVE))); jmi.setName("SAVE"); jmi = _fileMenu.add(createMenuItem(_I("menuFileSaveAs"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, InputEvent.SHIFT_MASK | scMask), new FileAction(FileAction.SAVE_AS))); jmi.setName("SAVE_AS"); if (Settings.isMac() && !Settings.handlesMacBundles) { _fileMenu.add(createMenuItem(_I("Save as folder.sikuli ..."), // KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, // InputEvent.SHIFT_MASK | scMask), null, new FileAction(FileAction.SAVE_AS_FOLDER))); } //TODO _fileMenu.add(createMenuItem(_I("menuFileSaveAll"), _fileMenu.add(createMenuItem("Save all", KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, InputEvent.CTRL_MASK | scMask), new FileAction(FileAction.SAVE_ALL))); _fileMenu.add(createMenuItem(_I("menuFileExport"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_E, InputEvent.SHIFT_MASK | scMask), new FileAction(FileAction.EXPORT))); jmi = _fileMenu.add(createMenuItem(_I("menuFileCloseTab"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_W, scMask), new FileAction(FileAction.CLOSE_TAB))); jmi.setName("CLOSE_TAB"); if (showPrefs) { _fileMenu.addSeparator(); _fileMenu.add(createMenuItem(_I("menuFilePreferences"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_P, scMask), new FileAction(FileAction.PREFERENCES))); } if (showQuit) { _fileMenu.addSeparator(); _fileMenu.add(createMenuItem(_I("menuFileQuit"), null, new FileAction(FileAction.QUIT))); } } public FileAction getFileAction(int tabIndex) { return new FileAction(tabIndex); } class FileAction extends MenuAction { static final String ABOUT = "doAbout"; static final String NEW = "doNew"; static final String INSERT = "doInsert"; static final String OPEN = "doLoad"; static final String RECENT = "doRecent"; static final String OPEN_FOLDER = "doLoadFolder"; static final String SAVE = "doSave"; static final String SAVE_AS = "doSaveAs"; static final String SAVE_AS_FOLDER = "doSaveAsFolder"; static final String SAVE_ALL = "doSaveAll"; static final String EXPORT = "doExport"; static final String CLOSE_TAB = "doCloseTab"; static final String PREFERENCES = "doPreferences"; static final String QUIT = "doQuit"; static final String ENTRY = "doRecent"; private int targetTab = -1; public FileAction() { super(); } public FileAction(int tabIndex) { super(); targetTab = tabIndex; } public FileAction(String item) throws NoSuchMethodException { super(item); } public void doAbout(ActionEvent ae) { sikulixIDE.doAbout(); } public void doQuit(ActionEvent ae) { log(lvl, "doQuit requested"); if (!doBeforeQuit()) { return; } while (true) { EditorPane codePane = sikulixIDE.getCurrentCodePane(); if (codePane == null) { break; } if (!sikulixIDE.closeCurrentTab()) { return; } } Sikulix.cleanUp(0); System.exit(0); } public void doPreferences(ActionEvent ae) { sikulixIDE.showPreferencesWindow(); } public void doNew(ActionEvent ae) { EditorPane ep = doNew(ae, -1); ep.getSrcBundle(); ep.initBeforeLoad(null); } public EditorPane doNew(ActionEvent ae, int tabIndex) { log(lvl, "doNew: create new tab at: %d", tabIndex); EditorPane codePane = new EditorPane(sikulixIDE); JScrollPane scrPane = new JScrollPane(codePane); lineNumberColumn = new EditorLineNumberView(codePane); scrPane.setRowHeaderView(lineNumberColumn); if (ae == null) { if (tabIndex < 0 || tabIndex >= tabPane.getTabCount()) { tabPane.addTab(_I("tabUntitled"), scrPane); } else { tabPane.addTab(_I("tabUntitled"), scrPane, tabIndex); } tabPane.setSelectedIndex(tabIndex < 0 ? tabPane.getTabCount() - 1 : tabIndex); } else { tabPane.addTab(_I("tabUntitled"), scrPane, 0); tabPane.setSelectedIndex(0); } // codePane.getSrcBundle(); codePane.requestFocus(); return codePane; } public void doInsert(ActionEvent ae) { doLoad(null); } public void doLoad(ActionEvent ae) { boolean accessingAsFile = false; if (Settings.isMac()) { accessingAsFile = !ACCESSING_AS_FOLDER; ACCESSING_AS_FOLDER = false; } alreadyOpenedTab = tabPane.getSelectedIndex(); String fname = tabPane.getLastClosed(); try { EditorPane codePane = doNew(null, targetTab); if (ae != null || fname == null) { codePane.isSourceBundleTemp(); fname = codePane.loadFile(accessingAsFile); } else { codePane.loadFile(fname); if (codePane.hasEditingFile()) { setCurrentFileTabTitle(fname); } else { fname = null; } } if (fname != null) { sikulixIDE.setCurrentFileTabTitle(fname); } else { if (ae != null) { doCloseTab(null); } tabPane.setSelectedIndex(alreadyOpenedTab); } doRecentAdd(getCurrentCodePane()); } catch (IOException eio) { log(-1, "Problem when trying to load %s\nError: %s", fname, eio.getMessage()); } } private void doRecentAdd(EditorPane codePane) { String fPath = new File(codePane.getSrcBundle()).getAbsolutePath(); if (Settings.experimental) { log(3, "doRecentAdd: %s", fPath); String fName = new File(fPath).getName(); if (recentProjectsMenu.contains(fName)) { recentProjectsMenu.remove(fName); } else { recentProjects.put(fName, fPath); if (recentProjectsMenu.size() == recentMaxMax) { String fObsolete = recentProjectsMenu.remove(recentMax - 1); recentProjects.remove(fObsolete); } } recentProjectsMenu.add(0, fName); recentMenu.removeAll(); for (String entry : recentProjectsMenu.subList(1, recentProjectsMenu.size())) { if (isAlreadyOpen(recentProjects.get(entry)) > -1) { continue; } try { recentMenu.add(createMenuItem(entry, null, new FileAction(FileAction.ENTRY))); } catch (NoSuchMethodException ex) { } } } } public void doRecent(ActionEvent ae) { log(3, "doRecent: menuOpenRecent: %s", ae.getActionCommand()); } public void doLoadFolder(ActionEvent ae) { Debug.log(3, "IDE: doLoadFolder requested"); ACCESSING_AS_FOLDER = true; doLoad(ae); } public void doSave(ActionEvent ae) { String fname = null; try { EditorPane codePane = getCurrentCodePane(); fname = codePane.saveFile(); if (fname != null) { fname = codePane.getSrcBundle(); setCurrentFileTabTitle(fname); tabPane.setLastClosed(fname); } } catch (Exception ex) { if (ex instanceof IOException) { log(-1, "Problem when trying to save %s\nError: %s", fname, ex.getMessage()); } else { log(-1, "A non-IOException-problem when trying to save %s\nError: %s", fname, ex.getMessage()); } } } public boolean doSaveIntern(int tabIndex) { int currentTab = tabPane.getSelectedIndex(); tabPane.setSelectedIndex(tabIndex); boolean retval = true; EditorPane codePane = getPaneAtIndex(tabIndex); String fname = null; try { fname = codePane.saveFile(); if (fname != null) { setFileTabTitle(fname, tabIndex); } else { retval = false; } } catch (Exception ex) { log(-1, "Problem when trying to save %s\nError: %s", fname, ex.getMessage()); retval = false; } tabPane.setSelectedIndex(currentTab); return retval; } public void doSaveAs(ActionEvent ae) { boolean accessingAsFile = false; if (Settings.isMac()) { accessingAsFile = !ACCESSING_AS_FOLDER; ACCESSING_AS_FOLDER = false; } String fname = null; EditorPane codePane = getCurrentCodePane(); String orgName = codePane.getCurrentShortFilename(); log(lvl, "doSaveAs requested: %s", orgName); try { fname = codePane.saveAsFile(accessingAsFile); if (fname != null) { setCurrentFileTabTitle(fname); } else { log(-1, "doSaveAs: %s not completed", orgName); } } catch (Exception ex) { log(-1, "doSaveAs: %s Error: %s", orgName, ex.getMessage()); } } public void doSaveAsFolder(ActionEvent ae) { log(lvl, "doSaveAsFolder requested"); ACCESSING_AS_FOLDER = true; doSaveAs(ae); } public void doSaveAll(ActionEvent ae) { log(lvl, "doSaveAll requested"); if (!checkDirtyPanes()) { return; } saveSession(IS_SAVE_ALL, false); } public void doExport(ActionEvent ae) { EditorPane codePane = getCurrentCodePane(); String orgName = codePane.getCurrentShortFilename(); log(lvl, "doExport requested: %s", orgName); String fname = null; try { fname = codePane.exportAsZip(); } catch (Exception ex) { log(-1, "Problem when trying to save %s\nError: %s", fname, ex.getMessage()); } } public void doCloseTab(ActionEvent ae) { if (ae == null) { tabPane.remove(tabPane.getSelectedIndex()); return; } EditorPane codePane = getCurrentCodePane(); String orgName = codePane.getCurrentShortFilename(); log(lvl, "doCloseTab requested: %s", orgName); try { if (codePane.close()) { tabPane.remove(tabPane.getSelectedIndex()); } } catch (Exception ex) { Debug.info("Can't close this tab: %s", ex.getMessage()); } codePane = getCurrentCodePane(); if (codePane != null) { codePane.requestFocus(); } else if (ae != null) { (new FileAction()).doNew(null); } } } public void showPreferencesWindow() { PreferencesWin pwin = new PreferencesWin(); pwin.setAlwaysOnTop(true); pwin.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); if (!Settings.isJava7()) { pwin.setLocation(getLocation()); } pwin.setVisible(true); } public boolean closeCurrentTab() { EditorPane pane = getCurrentCodePane(); (new FileAction()).doCloseTab(null); if (pane == getCurrentCodePane()) { return false; } return true; } protected boolean quit() { (new FileAction()).doQuit(null); if (getCurrentCodePane() == null) { return true; } else { return false; } } protected boolean checkDirtyPanes() { for (int i = 0; i < tabPane.getTabCount(); i++) { try { EditorPane codePane = getPaneAtIndex(i); if (codePane.isDirty()) { //RaiMan not used: getRootPane().putClientProperty("Window.documentModified", true); return true; } } catch (Exception e) { Debug.error("checkDirtyPanes: " + e.getMessage()); } } //RaiMan not used: getRootPane().putClientProperty("Window.documentModified", false); return false; } // // private void initEditMenu() throws NoSuchMethodException { int scMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); _editMenu.setMnemonic(java.awt.event.KeyEvent.VK_E); JMenuItem undoItem = _editMenu.add(_undoAction); undoItem.setAccelerator( KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, scMask)); JMenuItem redoItem = _editMenu.add(_redoAction); redoItem.setAccelerator( KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, scMask | InputEvent.SHIFT_MASK)); _editMenu.addSeparator(); _editMenu.add(createMenuItem(_I("menuEditCut"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, scMask), new EditAction(EditAction.CUT))); _editMenu.add(createMenuItem(_I("menuEditCopy"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, scMask), new EditAction(EditAction.COPY))); _editMenu.add(createMenuItem(_I("menuEditPaste"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_V, scMask), new EditAction(EditAction.PASTE))); _editMenu.add(createMenuItem(_I("menuEditSelectAll"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, scMask), new EditAction(EditAction.SELECT_ALL))); _editMenu.addSeparator(); JMenu findMenu = new JMenu(_I("menuFind")); _findHelper = new FindAction(); findMenu.setMnemonic(KeyEvent.VK_F); findMenu.add(createMenuItem(_I("menuFindFind"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F, scMask), new FindAction(FindAction.FIND))); findMenu.add(createMenuItem(_I("menuFindFindNext"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_G, scMask), new FindAction(FindAction.FIND_NEXT))); findMenu.add(createMenuItem(_I("menuFindFindPrev"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_G, scMask | InputEvent.SHIFT_MASK), new FindAction(FindAction.FIND_PREV))); _editMenu.add(findMenu); _editMenu.addSeparator(); _editMenu.add(createMenuItem(_I("menuEditIndent"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_TAB, 0), new EditAction(EditAction.INDENT))); _editMenu.add(createMenuItem(_I("menuEditUnIndent"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_TAB, InputEvent.SHIFT_MASK), new EditAction(EditAction.UNINDENT))); } class EditAction extends MenuAction { static final String CUT = "doCut"; static final String COPY = "doCopy"; static final String PASTE = "doPaste"; static final String SELECT_ALL = "doSelectAll"; static final String INDENT = "doIndent"; static final String UNINDENT = "doUnindent"; public EditAction() { super(); } public EditAction(String item) throws NoSuchMethodException { super(item); } private void performEditorAction(String action, ActionEvent ae) { EditorPane pane = getCurrentCodePane(); pane.getActionMap().get(action).actionPerformed(ae); } public void doCut(ActionEvent ae) { //TODO delete current line if no selection performEditorAction(DefaultEditorKit.cutAction, ae); } public void doCopy(ActionEvent ae) { //TODO copy current line if no selection performEditorAction(DefaultEditorKit.copyAction, ae); } public void doPaste(ActionEvent ae) { performEditorAction(DefaultEditorKit.pasteAction, ae); } public void doSelectAll(ActionEvent ae) { performEditorAction(DefaultEditorKit.selectAllAction, ae); } public void doIndent(ActionEvent ae) { EditorPane pane = getCurrentCodePane(); (new SikuliEditorKit.InsertTabAction()).actionPerformed(pane); } public void doUnindent(ActionEvent ae) { EditorPane pane = getCurrentCodePane(); (new SikuliEditorKit.DeindentAction()).actionPerformed(pane); } } class OpenRecent extends MenuAction { public OpenRecent() { super(); } public void openRecent(ActionEvent ae) { log(lvl, "openRecent: %s", ae.getActionCommand()); } } class FindAction extends MenuAction { static final String FIND = "doFind"; static final String FIND_NEXT = "doFindNext"; static final String FIND_PREV = "doFindPrev"; public FindAction() { super(); } public FindAction(String item) throws NoSuchMethodException { super(item); } public void doFind(ActionEvent ae) { _searchField.selectAll(); _searchField.requestFocus(); } public void doFindNext(ActionEvent ae) { findNext(_searchField.getText()); } public void doFindPrev(ActionEvent ae) { findPrev(_searchField.getText()); } private boolean _find(String str, int begin, boolean forward) { if (str == "!") { return false; } EditorPane codePane = getCurrentCodePane(); int pos = codePane.search(str, begin, forward); Debug.log(7, "find \"" + str + "\" at " + begin + ", found: " + pos); if (pos < 0) { return false; } return true; } public boolean findStr(String str) { if (getCurrentCodePane() != null) { return _find(str, getCurrentCodePane().getCaretPosition(), true); } return false; } public boolean findPrev(String str) { if (getCurrentCodePane() != null) { return _find(str, getCurrentCodePane().getCaretPosition(), false); } return false; } public boolean findNext(String str) { if (getCurrentCodePane() != null) { return _find(str, getCurrentCodePane().getCaretPosition() + str.length(), true); } return false; } public void setFailed(boolean failed) { Debug.log(7, "search failed: " + failed); _searchField.setBackground(Color.white); if (failed) { _searchField.setForeground(COLOR_SEARCH_FAILED); } else { _searchField.setForeground(COLOR_SEARCH_NORMAL); } } } class UndoAction extends AbstractAction { public UndoAction() { super(_I("menuEditUndo")); setEnabled(false); } public void updateUndoState() { if (getCurrentCodePane() != null && getCurrentCodePane().getUndoManager().canUndo()) { setEnabled(true); } else { setEnabled(false); } } @Override public void actionPerformed(ActionEvent e) { EditorUndoManager undo = getCurrentCodePane().getUndoManager(); try { undo.undo(); } catch (CannotUndoException ex) { } updateUndoState(); _redoAction.updateRedoState(); } } class RedoAction extends AbstractAction { public RedoAction() { super(_I("menuEditRedo")); setEnabled(false); } @Override public void actionPerformed(ActionEvent e) { EditorUndoManager undo = getCurrentCodePane().getUndoManager(); try { undo.redo(); } catch (CannotRedoException ex) { } updateRedoState(); _undoAction.updateUndoState(); } protected void updateRedoState() { if (getCurrentCodePane() != null && getCurrentCodePane().getUndoManager().canRedo()) { setEnabled(true); } else { setEnabled(false); } } } public void updateUndoRedoStates() { _undoAction.updateUndoState(); _redoAction.updateRedoState(); } // // private void initRunMenu() throws NoSuchMethodException { JMenuItem item; int scMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); _runMenu.setMnemonic(java.awt.event.KeyEvent.VK_R); item = _runMenu.add(createMenuItem(_I("menuRunRun"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_R, scMask), new RunAction(RunAction.RUN))); item.setName("RUN"); item = _runMenu.add(createMenuItem(_I("menuRunRunAndShowActions"), KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_R, InputEvent.ALT_MASK | scMask), new RunAction(RunAction.RUN_SHOW_ACTIONS))); item.setName("RUN_SLOWLY"); PreferencesUser pref = PreferencesUser.getInstance(); item = createMenuItem(_I("menuRunStop"), KeyStroke.getKeyStroke( pref.getStopHotkey(), pref.getStopHotkeyModifiers()), new RunAction(RunAction.RUN_SHOW_ACTIONS)); item.setEnabled(false); _runMenu.add(item); } class RunAction extends MenuAction { static final String RUN = "run"; static final String RUN_SHOW_ACTIONS = "runShowActions"; public RunAction() { super(); } public RunAction(String item) throws NoSuchMethodException { super(item); } public void run(ActionEvent ae) { doRun(_btnRun); } public void runShowActions(ActionEvent ae) { doRun(_btnRunViz); } private void doRun(ButtonRun btn) { btn.runCurrentScript(); } } // // private void initViewMenu() throws NoSuchMethodException { int scMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); _viewMenu.setMnemonic(java.awt.event.KeyEvent.VK_V); if (prefs.getPrefMoreCommandBar()) { chkShowCmdList = new JCheckBoxMenuItem(_I("menuViewCommandList"), true); _viewMenu.add(createMenuItem(chkShowCmdList, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_L, scMask), new ViewAction(ViewAction.CMD_LIST))); } chkShowThumbs = new JCheckBoxMenuItem(_I("menuViewShowThumbs"), false); _viewMenu.add(createMenuItem(chkShowThumbs, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_T, scMask), new ViewAction(ViewAction.SHOW_THUMBS))); //TODO Message Area clear //TODO Message Area LineBreak } class ViewAction extends MenuAction { static final String UNIT_TEST = "toggleUnitTest"; static final String CMD_LIST = "toggleCmdList"; static final String SHOW_THUMBS = "toggleShowThumbs"; public ViewAction() { super(); } public ViewAction(String item) throws NoSuchMethodException { super(item); } public void toggleCmdList(ActionEvent ae) { _cmdList.setCollapsed(!_cmdList.isCollapsed()); } public void toggleShowThumbs(ActionEvent ae) { getCurrentCodePane().showThumbs = chkShowThumbs.getState(); getCurrentCodePane().saveCaretPosition(); if (!getCurrentCodePane().reparse()) { chkShowThumbs.setState(!chkShowThumbs.getState()); getCurrentCodePane().showThumbs = chkShowThumbs.getState(); } } public void toggleUnitTest(ActionEvent ae) { if (_chkShowUnitTest.getState()) { _sidePane.setCollapsed(false); } else { _sidePane.setCollapsed(true); } } } // // private void initToolMenu() throws NoSuchMethodException { int scMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); _toolMenu.setMnemonic(java.awt.event.KeyEvent.VK_T); _toolMenu.add(createMenuItem(_I("menuToolExtensions"), null, new ToolAction(ToolAction.EXTENSIONS))); _toolMenu.add(createMenuItem(_I("menuToolAndroid"), null, new ToolAction(ToolAction.ANDROID))); } class ToolAction extends MenuAction { static final String EXTENSIONS = "extensions"; static final String ANDROID = "android"; ToolAction() { super(); } public ToolAction(String item) throws NoSuchMethodException { super(item); } public void extensions(ActionEvent ae) { showExtensionsFrame(); } public void android(ActionEvent ae) { androidSupport(); } } private void showExtensionsFrame() { // String warn = "You might proceed, if you\n" // + "- have some programming skills\n" // + "- read the docs about extensions\n" // + "- know what you are doing\n\n" // + "Otherwise you should press Cancel!"; String warn = "Not available yet - click what you like ;-)"; String title = "Need your attention!"; String[] options = new String[3]; options[WARNING_DO_NOTHING] = "OK"; options[WARNING_ACCEPTED] = "Be quiet!"; options[WARNING_CANCEL] = "Cancel"; int ret = JOptionPane.showOptionDialog(this, warn, title, 0, JOptionPane.WARNING_MESSAGE, null, options, options[2]); if (ret == WARNING_CANCEL || ret == JOptionPane.CLOSED_OPTION) { return; } if (ret == WARNING_ACCEPTED) { //TODO set prefs to be quiet on extensions warning } ; ExtensionManagerFrame extmg = ExtensionManagerFrame.getInstance(); if (extmg != null) { extmg.setVisible(true); } } private static IScreen defaultScreen = null; public static IScreen getDefaultScreen() { return defaultScreen; } private void androidSupport() { final ADBScreen aScr = new ADBScreen(); String title = "Android Support - !!EXPERIMENTAL!!"; if (aScr.isValid()) { String warn = "Device found: " + aScr.getDeviceDescription() + "\n\n" + "click Check: a short test is run with the device\n" + "click Default: set device as default screen for capture\n" + "click Cancel: nothing is done (default screen is reset)\n" + "\nBE PREPARED: Feature is experimental - no guarantee ;-)"; String[] options = new String[3]; options[WARNING_DO_NOTHING] = "Check"; options[WARNING_ACCEPTED] = "Default"; options[WARNING_CANCEL] = "Cancel"; int ret = JOptionPane.showOptionDialog(this, warn, title, 0, JOptionPane.WARNING_MESSAGE, null, options, options[2]); if (ret == WARNING_CANCEL || ret == JOptionPane.CLOSED_OPTION) { defaultScreen = null; return; } if (ret == WARNING_DO_NOTHING) { SikuliIDE.hideIDE(); Thread test = new Thread() { @Override public void run() { androidSupportTest(aScr); } }; test.start(); } else if (ret == WARNING_ACCEPTED) { defaultScreen = aScr; return; } } else if (!ADBClient.isAdbAvailable) { Sikulix.popError("Package adb seems not to be available.\nIt must be installed for Android support.", title); } else { Sikulix.popError("No android device attached", title); } } private void androidSupportTest(ADBScreen aScr) { ADBTest.ideTest(aScr); SikuliIDE.showIDE(); } // // private void initHelpMenu() throws NoSuchMethodException { int scMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); _helpMenu.setMnemonic(java.awt.event.KeyEvent.VK_H); _helpMenu.add(createMenuItem(_I("menuHelpQuickStart"), null, new HelpAction(HelpAction.QUICK_START))); _helpMenu.addSeparator(); _helpMenu.add(createMenuItem(_I("menuHelpGuide"), null, new HelpAction(HelpAction.OPEN_DOC))); _helpMenu.add(createMenuItem(_I("menuHelpDocumentations"), null, new HelpAction(HelpAction.OPEN_GUIDE))); _helpMenu.add(createMenuItem(_I("menuHelpFAQ"), null, new HelpAction(HelpAction.OPEN_FAQ))); _helpMenu.add(createMenuItem(_I("menuHelpAsk"), null, new HelpAction(HelpAction.OPEN_ASK))); _helpMenu.add(createMenuItem(_I("menuHelpBugReport"), null, new HelpAction(HelpAction.OPEN_BUG_REPORT))); // _helpMenu.add(createMenuItem(_I("menuHelpTranslation"), // null, new HelpAction(HelpAction.OPEN_TRANSLATION))); _helpMenu.addSeparator(); _helpMenu.add(createMenuItem(_I("menuHelpHomepage"), null, new HelpAction(HelpAction.OPEN_HOMEPAGE))); _helpMenu.addSeparator(); _helpMenu.add(createMenuItem(_I("menuHelpCheckUpdate"), null, new HelpAction(HelpAction.CHECK_UPDATE))); } class HelpAction extends MenuAction { static final String CHECK_UPDATE = "doCheckUpdate"; static final String QUICK_START = "openQuickStart"; static final String OPEN_DOC = "openDoc"; static final String OPEN_GUIDE = "openTutor"; static final String OPEN_FAQ = "openFAQ"; static final String OPEN_ASK = "openAsk"; static final String OPEN_BUG_REPORT = "openBugReport"; static final String OPEN_TRANSLATION = "openTranslation"; static final String OPEN_HOMEPAGE = "openHomepage"; public HelpAction() { super(); } public HelpAction(String item) throws NoSuchMethodException { super(item); } public void openQuickStart(ActionEvent ae) { FileManager.openURL("http://sikulix.com/quickstart/"); } public void openDoc(ActionEvent ae) { FileManager.openURL("http://sikulix-2014.readthedocs.org/en/latest/index.html"); } public void openTutor(ActionEvent ae) { FileManager.openURL("http://www.sikuli.org/videos.html"); } public void openFAQ(ActionEvent ae) { FileManager.openURL("https://answers.launchpad.net/sikuli/+faqs"); } public void openAsk(ActionEvent ae) { String title = "SikuliX - Ask a question"; String msg = "If you want to ask a question about SikuliX\n%s\n" + "\nplease do the following:" + "\n- after having clicked yes" + "\n the page on Launchpad should open in your browser." + "\n- You should first check using Launchpad's search funktion," + "\n wether similar questions have already been asked." + "\n- If you decide to ask a new question," + "\n try to enter a short but speaking title" + "\n- In a new questions's text field first paste using ctrl/cmd-v" + "\n which should enter the SikuliX version/system/java info" + "\n that was internally stored in the clipboard before" + "\n\nIf you do not want to ask a question now: click No"; askBugOrAnswer(msg, title, "https://answers.launchpad.net/sikuli"); } public void openBugReport(ActionEvent ae) { String title = "SikuliX - Report a bug"; String msg = "If you want to report a bug for SikuliX\n%s\n" + "\nplease do the following:" + "\n- after having clicked yes" + "\n the page on Launchpad should open in your browser" + "\n- fill in a short but speaking bug title and create the bug" + "\n- in the bug's text field first paste using ctrl/cmd-v" + "\n which should enter the SikuliX version/system/java info" + "\n that was internally stored in the clipboard before" + "\n\nIf you do not want to report a bug now: click No"; askBugOrAnswer(msg, title, "https://bugs.launchpad.net/sikuli/+filebug"); } private void askBugOrAnswer(String msg, String title, String url) { String si = runTime.getSystemInfo(); System.out.println(si); msg = String.format(msg, si); if (Sikulix.popAsk(msg, title)) { Clipboard clb = Toolkit.getDefaultToolkit().getSystemClipboard(); StringSelection sic = new StringSelection(si.toString()); clb.setContents(sic, sic); FileManager.openURL(url); } } public void openTranslation(ActionEvent ae) { FileManager.openURL("https://translations.launchpad.net/sikuli/sikuli-x/+translations"); } public void openHomepage(ActionEvent ae) { FileManager.openURL("http://sikulix.com"); } public void doCheckUpdate(ActionEvent ae) { if (!checkUpdate(false)) { JOptionPane.showMessageDialog(null, _I("msgNoUpdate"), runTime.SikuliVersionIDE, JOptionPane.INFORMATION_MESSAGE); } } public boolean checkUpdate(boolean isAutoCheck) { JFrame f = null; String ver = ""; String details; AutoUpdater au = new AutoUpdater(); if (!isAutoCheck) { //TODO replace this hack: wait update check f = au.showUpdateFrame("Checking for new version ... please wait!", "Checking for new version ... please wait! Checking for new version ... please wait!", -1); } PreferencesUser pref = PreferencesUser.getInstance(); Debug.log(3, "being asked to check update"); int whatUpdate = au.checkUpdate(); if (f != null) { f.dispose(); } if (whatUpdate >= AutoUpdater.SOMEBETA) { //TODO add Prefs wantBeta check whatUpdate -= AutoUpdater.SOMEBETA; } if (whatUpdate > 0) { if (whatUpdate == AutoUpdater.BETA) { ver = au.getBeta(); details = au.getBetaDetails(); } else { ver = au.getVersion(); details = au.getDetails(); } if (isAutoCheck && pref.getLastSeenUpdate().equals(ver)) { return false; } au.showUpdateFrame(_I("dlgUpdateAvailable", ver), details, whatUpdate); PreferencesUser.getInstance().setLastSeenUpdate(ver); return true; } return false; } } private void initMenuBars(JFrame frame) { try { initFileMenu(); initEditMenu(); initRunMenu(); initViewMenu(); initToolMenu(); initHelpMenu(); } catch (NoSuchMethodException e) { log(-1, "Problem when initializing menues\nError: %s", e.getMessage()); } _menuBar.add(_fileMenu); _menuBar.add(_editMenu); _menuBar.add(_runMenu); _menuBar.add(_viewMenu); _menuBar.add(_toolMenu); _menuBar.add(_helpMenu); frame.setJMenuBar(_menuBar); } // private String[] getCommandCategories() { String[] CommandCategories = { _I("cmdListFind"), _I("cmdListMouse"), _I("cmdListKeyboard"), _I("cmdListObserver") }; return CommandCategories; } private String[][] getCommandsOnToolbar() { String[][] CommandsOnToolbar = { {"find"}, {"PATTERN"}, {_I("cmdFind")}, {"findAll"}, {"PATTERN"}, {_I("cmdFindAll")}, {"wait"}, {"PATTERN", "[timeout]"}, {_I("cmdWait")}, {"waitVanish"}, {"PATTERN", "[timeout]"}, {_I("cmdWaitVanish")}, {"exists"}, {"PATTERN", "[timeout]"}, {_I("cmdExists")}, {"----"}, {}, {}, {"click"}, {"PATTERN", "[modifiers]"}, {_I("cmdClick")}, {"doubleClick"}, {"PATTERN", "[modifiers]"}, {_I("cmdDoubleClick")}, {"rightClick"}, {"PATTERN", "[modifiers]"}, {_I("cmdRightClick")}, {"hover"}, {"PATTERN"}, {_I("cmdHover")}, {"dragDrop"}, {"PATTERN", "PATTERN", "[modifiers]"}, {_I("cmdDragDrop")}, /* RaiMan not used * {"drag"}, {"PATTERN"}, * {"dropAt"}, {"PATTERN", "[delay]"}, * RaiMan not used */ {"----"}, {}, {}, {"type"}, {"_text", "[modifiers]"}, {_I("cmdType")}, {"type"}, {"PATTERN", "_text", "[modifiers]"}, {_I("cmdType2")}, {"paste"}, {"_text", "[modifiers]"}, {_I("cmdPaste")}, {"paste"}, {"PATTERN", "_text", "[modifiers]"}, {_I("cmdPaste2")}, {"----"}, {}, {}, {"onAppear"}, {"PATTERN", "_hnd"}, {_I("cmdOnAppear")}, {"onVanish"}, {"PATTERN", "_hnd"}, {_I("cmdOnVanish")}, {"onChange"}, {"_hnd"}, {_I("cmdOnChange")}, {"observe"}, {"[time]", "[background]"}, {_I("cmdObserve")},}; return CommandsOnToolbar; } private JComponent createCommandPane() { JXTaskPaneContainer con = new JXTaskPaneContainer(); PreferencesUser pref = PreferencesUser.getInstance(); JCheckBox chkAutoCapture = new JCheckBox(_I("cmdListAutoCapture"), pref.getAutoCaptureForCmdButtons()); chkAutoCapture.addChangeListener(new ChangeListener() { @Override public void stateChanged(javax.swing.event.ChangeEvent e) { boolean flag = ((JCheckBox) e.getSource()).isSelected(); PreferencesUser pref = PreferencesUser.getInstance(); pref.setAutoCaptureForCmdButtons(flag); } }); JXTaskPane setPane = new JXTaskPane(); setPane.setTitle(_I("cmdListSettings")); setPane.add(chkAutoCapture); setPane.setCollapsed(true); con.add(setPane); int cat = 0; JXTaskPane taskPane = new JXTaskPane(); taskPane.setTitle(getCommandCategories()[cat++]); con.add(taskPane); String[][] CommandsOnToolbar = getCommandsOnToolbar(); boolean collapsed; for (int i = 0; i < CommandsOnToolbar.length; i++) { String cmd = CommandsOnToolbar[i++][0]; String[] params = CommandsOnToolbar[i++]; String[] desc = CommandsOnToolbar[i]; //TODO: more elegeant way, to handle special cases if (cmd.equals("----")) { if (cat == 2) { collapsed = true; } else { collapsed = false; } if (cat == 3) { if (prefs.getUserType() == PreferencesUser.NEWBEE) { break; } else { collapsed = true; } } taskPane = new JXTaskPane(); taskPane.setTitle(getCommandCategories()[cat++]); con.add(taskPane); taskPane.setCollapsed(collapsed); } else { taskPane.add(new ButtonGenCommand(cmd, desc[0], params)); } } Dimension conDim = con.getSize(); con.setPreferredSize(new Dimension(250, 1000)); _cmdList = new JXCollapsiblePane(JXCollapsiblePane.Direction.LEFT); _cmdList.setMinimumSize(new Dimension(0, 0)); _cmdList.add(new JScrollPane(con)); _cmdList.setCollapsed(false); return _cmdList; } // private JToolBar initCmdToolbar() { JToolBar toolbar = new JToolBar(JToolBar.VERTICAL); toolbar.add(createCommandPane()); return toolbar; } // // // private JToolBar initToolbar() { if (ENABLE_UNIFIED_TOOLBAR) { MacUtils.makeWindowLeopardStyle(this.getRootPane()); } JToolBar toolbar = new JToolBar(); JButton btnInsertImage = new ButtonInsertImage(); _btnCapture = new ButtonCapture(); JButton btnSubregion = new ButtonSubregion().init(); JButton btnLocation = new ButtonLocation().init(); JButton btnOffset = new ButtonOffset().init(); JButton btnShow = new ButtonShow().init(); JButton btnShowIn = new ButtonShowIn().init(); toolbar.add(_btnCapture); toolbar.add(btnInsertImage); toolbar.add(btnSubregion); toolbar.add(btnLocation); toolbar.add(btnOffset); toolbar.add(btnShow); toolbar.add(btnShowIn); toolbar.add(Box.createHorizontalGlue()); _btnRun = new ButtonRun(); toolbar.add(_btnRun); _btnRunViz = new ButtonRunViz(); toolbar.add(_btnRunViz); toolbar.add(Box.createHorizontalGlue()); //TODO get it working for OSX 10.10 // if (!Settings.isMac10()) { JComponent jcSearchField = createSearchField(); toolbar.add(jcSearchField); // } toolbar.add(Box.createRigidArea(new Dimension(7, 0))); toolbar.setFloatable(false); //toolbar.setMargin(new Insets(0, 0, 0, 5)); return toolbar; } class ButtonInsertImage extends ButtonOnToolbar implements ActionListener { public ButtonInsertImage() { super(); URL imageURL = SikuliIDE.class.getResource("/icons/insert-image-icon.png"); setIcon(new ImageIcon(imageURL)); setText(SikuliIDE._I("btnInsertImageLabel")); //setMaximumSize(new Dimension(26,26)); setToolTipText(SikuliIDE._I("btnInsertImageHint")); addActionListener(this); } @Override public void actionPerformed(ActionEvent ae) { EditorPane codePane = getCurrentCodePane(); File file = new SikulixFileChooser(sikulixIDE).loadImage(); if (file == null) { return; } String path = FileManager.slashify(file.getAbsolutePath(), false); Debug.info("load image: " + path); EditorPatternButton icon; String img = codePane.copyFileToBundle(path).getAbsolutePath(); if (prefs.getDefaultThumbHeight() > 0) { icon = new EditorPatternButton(codePane, img); codePane.insertComponent(icon); } else { codePane.insertString("\"" + (new File(img)).getName() + "\""); } } } class ButtonSubregion extends ButtonOnToolbar implements ActionListener, EventObserver { String promptText; String buttonText; String iconFile; String buttonHint; public ButtonSubregion() { super(); promptText = SikuliIDE._I("msgCapturePrompt"); buttonText = "Region"; // SikuliIDE._I("btnRegionLabel"); iconFile = "/icons/region-icon.png"; buttonHint = SikuliIDE._I("btnRegionHint"); } public ButtonSubregion init() { URL imageURL = SikuliIDE.class.getResource(iconFile); setIcon(new ImageIcon(imageURL)); setText(buttonText); //setMaximumSize(new Dimension(26,26)); setToolTipText(buttonHint); addActionListener(this); return this; } @Override public void actionPerformed(ActionEvent ae) { if (shouldRun()) { sikulixIDE.setVisible(false); RunTime.pause(0.5f); Screen.doPrompt(promptText, this); } else { nothingTodo(); } } public void nothingTodo() { } public boolean shouldRun() { Debug.log(3, "TRACE: ButtonSubRegion triggered"); return true; } @Override public void update(EventSubject es) { OverlayCapturePrompt ocp = (OverlayCapturePrompt) es; ScreenImage simg = ocp.getSelection(); Screen.closePrompt(); Screen.resetPrompt(ocp); captureComplete(simg); updateAfter(); } public void updateAfter() { SikuliIDE.showAgain(); } public void captureComplete(ScreenImage simg) { int x, y, w, h; EditorPane codePane = getCurrentCodePane(); if (simg != null) { Rectangle roi = simg.getROI(); x = (int) roi.getX(); y = (int) roi.getY(); w = (int) roi.getWidth(); h = (int) roi.getHeight(); sikulixIDE.setVisible(false); if (codePane.showThumbs) { if (prefs.getPrefMoreImageThumbs()) { codePane.insertComponent(new EditorRegionButton(codePane, x, y, w, h)); } else { codePane.insertComponent(new EditorRegionLabel(codePane, new EditorRegionButton(codePane, x, y, w, h).toString())); } } else { codePane.insertString(codePane.getRegionString(x, y, w, h)); } } } } class ButtonLocation extends ButtonSubregion { public ButtonLocation() { super(); promptText = "Select a Location"; buttonText = "Location"; iconFile = "/icons/region-icon.png"; buttonHint = "Select location as center of selection"; } @Override public void captureComplete(ScreenImage simg) { int x, y, w, h; if (simg != null) { Rectangle roi = simg.getROI(); x = (int) (roi.getX() + roi.getWidth() / 2); y = (int) (roi.getY() + roi.getHeight() / 2); getCurrentCodePane().insertString(String.format("Location(%d, %d)", x, y)); } } } class ButtonOffset extends ButtonSubregion { public ButtonOffset() { super(); promptText = "Select an Offset"; buttonText = "Offset"; iconFile = "/icons/region-icon.png"; buttonHint = "Select offset as topLeft to buttomRight of selection"; } @Override public void captureComplete(ScreenImage simg) { int x, y, ox, oy; if (simg != null) { Rectangle roi = simg.getROI(); x = (int) roi.getX(); y = (int) roi.getY(); ox = (int) roi.getWidth(); oy = (int) roi.getHeight(); getCurrentCodePane().insertString(String.format("Region(%d, %d, %d, %d).asOffset()", x, y, ox, oy)); } } } class ButtonShow extends ButtonOnToolbar implements ActionListener { String buttonText; String iconFile; String buttonHint; public ButtonShow() { super(); buttonText = "Show"; iconFile = "/icons/region-icon.png"; buttonHint = "Show the item at the cursor"; } public ButtonShow init() { URL imageURL = SikuliIDE.class.getResource(iconFile); setIcon(new ImageIcon(imageURL)); setText(buttonText); //setMaximumSize(new Dimension(26,26)); setToolTipText(buttonHint); addActionListener(this); return this; } @Override public void actionPerformed(ActionEvent e) { String line = ""; EditorPane codePane = getCurrentCodePane(); line = codePane.getLineTextAtCaret(); String item = codePane.parseLineText(line); if (!item.isEmpty()) { String eval = ""; item = item.replaceAll("\"", "\\\""); if (item.startsWith("Region")) { if (item.contains(".asOffset()")) { item = item.replace(".asOffset()", ""); } eval = "Region.create" + item.substring(6) + ".highlight(2);"; } else if (item.startsWith("Location")) { eval = "new " + item + ".grow(10).highlight(2);"; } else if (item.startsWith("Pattern")) { eval = "m = Screen.all().exists(new " + item + ", 0); if (m != null) m.highlight(2); else print(m);"; } else if (item.startsWith("\"")) { eval = "m = Screen.all().exists(" + item + ", 0); if (m != null) m.highlight(2); else print(m);"; } if (!eval.isEmpty()) { Runner.runjsEval(eval); return; } } Sikulix.popup("Nothing to show"); } } class ButtonShowIn extends ButtonSubregion { String item = ""; public ButtonShowIn() { super(); buttonText = "Show in"; iconFile = "/icons/region-icon.png"; buttonHint = "Show the item at the cursor in the selected region"; } @Override public boolean shouldRun() { Debug.log(3, "TRACE: ButtonShowIn triggered"); EditorPane codePane = getCurrentCodePane(); String line = codePane.getLineTextAtCaret(); item = codePane.parseLineText(line); item = item.replaceAll("\"", "\\\""); if (item.startsWith("Pattern")) { item = "m = null; r = #region#; " + "if (r != null) m = r.exists(new " + item + ", 0); " + "if (m != null) m.highlight(2); else print(m);"; } else if (item.startsWith("\"")) { item = "m = null; r = #region#; " + "if (r != null) m = r.exists(" + item + ", 0); " + "if (m != null) m.highlight(2); else print(m);"; } return !item.isEmpty(); } @Override public void nothingTodo() { Sikulix.popup("Nothing to show"); } @Override public void captureComplete(ScreenImage simg) { if (simg != null) { Region reg = new Region(simg.getROI()); String itemReg = String.format("new Region(%d, %d, %d, %d)", reg.x, reg.y, reg.w, reg.h); item = item.replace("#region#", itemReg); Runner.runjsEval(item); } else { SikuliIDE.showAgain(); nothingTodo(); } } @Override public void updateAfter() { } } class ButtonRun extends ButtonOnToolbar implements ActionListener { private Thread _runningThread = null; public ButtonRun() { super(); URL imageURL = SikuliIDE.class.getResource("/icons/run_big_green.png"); setIcon(new ImageIcon(imageURL)); initTooltip(); addActionListener(this); setText(_I("btnRunLabel")); //setMaximumSize(new Dimension(45,45)); } @Override public void actionPerformed(ActionEvent ae) { runCurrentScript(); } public void runCurrentScript() { if (System.out.checkError()) { Sikulix.popError("System.out is broken (console output)!" + "\nYou will not see any messages anymore!" + "\nSave your work and restart the IDE!" + "\nYou may ignore this on your own risk!", "Fatal Error"); } SikuliIDE.getStatusbar().setMessage("... PLEASE WAIT ... checking IDE state before running script"); if (ideIsRunningScript || sikulixIDE.getCurrentCodePane().getDocument().getLength() == 0 || !sikulixIDE.doBeforeRun()) { return; } SikuliIDE.getStatusbar().resetMessage(); sikulixIDE.setVisible(false); RunTime.pause(0.1f); sikulixIDE.setIsRunningScript(true); final IScriptRunner[] srunners = new IScriptRunner[]{null}; EditorPane codePane = getCurrentCodePane(); String cType = codePane.getContentType(); File scriptFile = null; if (codePane.isDirty()) { scriptFile = FileManager.createTempFile(Runner.typeEndings.get(cType)); if (scriptFile != null) { try { codePane.write(new BufferedWriter(new OutputStreamWriter( new FileOutputStream(scriptFile), "UTF8"))); } catch (Exception ex) { scriptFile = null; } } if (scriptFile == null) { log(-1, "runCurrentScript: temp file for running not available"); return; } } else { scriptFile = codePane.getCurrentFile(); } _console.clear(); resetErrorMark(); String parent = null; File path = new File(getCurrentBundlePath()); if (path != null && !codePane.isSourceBundleTemp()) { parent = path.getParent(); } IScriptRunner srunner = ScriptingSupport.getRunner(null, cType); if (srunner == null) { log(-1, "runCurrentScript: Could not load a script runner for: %s", cType); return; } addScriptCode(srunner); srunners[0] = srunner; ImagePath.reset(path.getAbsolutePath()); String tabtitle = tabPane.getTitleAt(tabPane.getSelectedIndex()); if (tabtitle.startsWith("*")) { tabtitle = tabtitle.substring(1); } final SubRun doRun = new SubRun(srunners, scriptFile, path, parent, tabtitle); _runningThread = new Thread(doRun); _runningThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { if (System.out.checkError()) { Sikulix.popError("System.out is broken (console output)!" + "\nYou will not see any messages anymore!" + "\nSave your work and restart the IDE!", "Fatal Error"); } log(lvl, "Scriptrun: cleanup in handler for uncaughtException: %s", e.toString()); doRun.hasFinished(true); doRun.afterRun(); } }); _runningThread.start(); } private class SubRun implements Runnable { private boolean finished = false; private int ret = 0; private IScriptRunner[] srunners = null; private File scriptFile = null; private File path = null; private String tabtitle = ""; private String parent; public SubRun(IScriptRunner[] srunners, File scriptFile, File path, String parent, String tabtitle) { this.srunners = srunners; this.scriptFile = scriptFile; this.path = path; this.tabtitle = tabtitle; this.parent = parent; } @Override public void run() { try { ret = srunners[0].runScript(scriptFile, path, runTime.getArgs(), new String[]{parent, tabtitle}); } catch (Exception ex) { log(-1, "(%s).runScript: Exception: %s", srunners[0], ex); } hasFinished(true); afterRun(); } public int getRet() { return ret; } public boolean hasFinished() { return hasFinished(false); } public synchronized boolean hasFinished(boolean state) { if (state) { finished = true; } return finished; } public void afterRun() { addErrorMark(ret); srunners[0].close(); srunners[0] = null; if (Image.getIDEshouldReload()) { EditorPane pane = sikulixIDE.getCurrentCodePane(); int line = pane.getLineNumberAtCaret(pane.getCaretPosition()); sikulixIDE.getCurrentCodePane().reparse(); sikulixIDE.getCurrentCodePane().jumpTo(line); } sikulixIDE.setIsRunningScript(false); sikulixIDE.setVisible(true); Sikulix.cleanUp(0); _runningThread = null; } } protected void addScriptCode(IScriptRunner srunner) { srunner.execBefore(null); srunner.execBefore(new String[]{"Settings.setShowActions(Settings.FALSE)"}); } public boolean isRunning() { return _runningThread != null; } public boolean stopRunScript() { if (_runningThread != null) { _runningThread.interrupt(); _runningThread.stop(); return true; } return false; } private void initTooltip() { PreferencesUser pref = PreferencesUser.getInstance(); String strHotkey = Key.convertKeyToText( pref.getStopHotkey(), pref.getStopHotkeyModifiers()); String stopHint = _I("btnRunStopHint", strHotkey); setToolTipText(_I("btnRun", stopHint)); } public void addErrorMark(int line) { if (line < 0) { line *= -1; } else { return; } JScrollPane scrPane = (JScrollPane) tabPane.getSelectedComponent(); EditorLineNumberView lnview = (EditorLineNumberView) (scrPane.getRowHeader().getView()); lnview.addErrorMark(line); EditorPane codePane = SikuliIDE.this.getCurrentCodePane(); codePane.jumpTo(line); codePane.requestFocus(); } public void resetErrorMark() { JScrollPane scrPane = (JScrollPane) tabPane.getSelectedComponent(); EditorLineNumberView lnview = (EditorLineNumberView) (scrPane.getRowHeader().getView()); lnview.resetErrorMark(); } } class ButtonRunViz extends ButtonRun { public ButtonRunViz() { super(); URL imageURL = SikuliIDE.class.getResource("/icons/run_big_yl.png"); setIcon(new ImageIcon(imageURL)); setToolTipText(_I("menuRunRunAndShowActions")); setText(_I("btnRunSlowMotionLabel")); } @Override protected void addScriptCode(IScriptRunner srunner) { srunner.execBefore(null); srunner.execBefore(new String[]{"Settings.setShowActions(Settings.TRUE)"}); } } protected String getCurrentBundlePath() { EditorPane pane = getCurrentCodePane(); return pane.getBundlePath(); } private JComponent createSearchField() { _searchField = new JXSearchField("Find"); _searchField.setUseNativeSearchFieldIfPossible(true); //_searchField.setLayoutStyle(JXSearchField.LayoutStyle.MAC); _searchField.setMinimumSize(new Dimension(220, 30)); _searchField.setPreferredSize(new Dimension(220, 30)); _searchField.setMaximumSize(new Dimension(380, 30)); _searchField.setMargin(new Insets(0, 3, 0, 3)); _searchField.setToolTipText("Search is case sensitive - " + "start with ! to make search not case sensitive"); _searchField.setCancelAction(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { getCurrentCodePane().requestFocus(); _findHelper.setFailed(false); } }); _searchField.setFindAction(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { //FIXME: On Linux the found selection disappears somehow if (!Settings.isLinux()) //HACK { _searchField.selectAll(); } boolean ret = _findHelper.findNext(_searchField.getText()); _findHelper.setFailed(!ret); } }); _searchField.addKeyListener(new KeyAdapter() { @Override public void keyReleased(java.awt.event.KeyEvent ke) { boolean ret; if (ke.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) { //FIXME: On Linux the found selection disappears somehow if (!Settings.isLinux()) //HACK { _searchField.selectAll(); } ret = _findHelper.findNext(_searchField.getText()); } else { ret = _findHelper.findStr(_searchField.getText()); } _findHelper.setFailed(!ret); } }); return _searchField; } // private void initTabPane() { tabPane = new CloseableTabbedPane(); tabPane.setUI(new AquaCloseableTabbedPaneUI()); tabPane.addCloseableTabbedPaneListener( new CloseableTabbedPaneListener() { @Override public boolean closeTab(int i) { EditorPane codePane; try { codePane = getPaneAtIndex(i); tabPane.setLastClosed(codePane.getSrcBundle()); Debug.log(4, "close tab " + i + " n:" + tabPane.getComponentCount()); boolean ret = codePane.close(); Debug.log(4, "after close tab n:" + tabPane.getComponentCount()); if (ret && tabPane.getTabCount() < 2) { (new FileAction()).doNew(null); } return ret; } catch (Exception e) { log(-1, "Problem closing tab %d\nError: %s", i, e.getMessage()); return false; } } }); tabPane.addChangeListener(new ChangeListener() { @Override public void stateChanged(javax.swing.event.ChangeEvent e) { EditorPane codePane = null; JTabbedPane tab = (JTabbedPane) e.getSource(); int i = tab.getSelectedIndex(); if (i >= 0) { codePane = getPaneAtIndex(i); String fname = codePane.getCurrentSrcDir(); if (fname == null) { SikuliIDE.this.setTitle(tab.getTitleAt(i)); } else { ImagePath.setBundlePath(fname); SikuliIDE.this.setTitle(fname); } SikuliIDE.this.chkShowThumbs.setState(SikuliIDE.this.getCurrentCodePane().showThumbs); } updateUndoRedoStates(); if (codePane != null) { SikuliIDE.getStatusbar().setCurrentContentType( SikuliIDE.this.getCurrentCodePane().getSikuliContentType()); } } }); } private void initMsgPane(boolean atBottom) { msgPane = new JTabbedPane(); _console = new EditorConsolePane(); msgPane.addTab(_I("paneMessage"), null, _console, "DoubleClick to hide/unhide"); if (Settings.isWindows() || Settings.isLinux()) { msgPane.setBorder(BorderFactory.createEmptyBorder(5, 8, 5, 8)); } msgPane.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent me) { if (me.getClickCount() < 2) { return; } if (msgPaneCollapsed) { _mainSplitPane.setDividerLocation(_mainSplitPane.getLastDividerLocation()); msgPaneCollapsed = false; } else { int pos = _mainSplitPane.getWidth() - 35; if (prefs.getPrefMoreMessage() == PreferencesUser.HORIZONTAL) { pos = _mainSplitPane.getHeight() - 35; } _mainSplitPane.setDividerLocation(pos); msgPaneCollapsed = true; } } // @Override public void mousePressed(MouseEvent me) { } @Override public void mouseReleased(MouseEvent me) { } @Override public void mouseEntered(MouseEvent me) { } @Override public void mouseExited(MouseEvent me) { } // }); } public Container getMsgPane() { return msgPane; } private SikuliIDEStatusBar initStatusbar() { _status = new SikuliIDEStatusBar(); return _status; } public static SikuliIDEStatusBar getStatusbar() { if (sikulixIDE == null) { return null; } else { return sikulixIDE._status; } } private void initWindowListener() { setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { SikuliIDE.this.quit(); } }); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { PreferencesUser.getInstance().setIdeSize(SikuliIDE.this.getSize()); } @Override public void componentMoved(ComponentEvent e) { PreferencesUser.getInstance().setIdeLocation(SikuliIDE.this.getLocation()); } }); } private void initTooltip() { ToolTipManager tm = ToolTipManager.sharedInstance(); tm.setDismissDelay(30000); } // private void nextTab() { int i = tabPane.getSelectedIndex(); int next = (i + 1) % tabPane.getTabCount(); tabPane.setSelectedIndex(next); } private void prevTab() { int i = tabPane.getSelectedIndex(); int prev = (i - 1 + tabPane.getTabCount()) % tabPane.getTabCount(); tabPane.setSelectedIndex(prev); } private void initShortcutKeys() { final int scMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { private boolean isKeyNextTab(java.awt.event.KeyEvent ke) { if (ke.getKeyCode() == java.awt.event.KeyEvent.VK_TAB && ke.getModifiers() == InputEvent.CTRL_MASK) { return true; } if (ke.getKeyCode() == java.awt.event.KeyEvent.VK_CLOSE_BRACKET && ke.getModifiers() == (InputEvent.META_MASK | InputEvent.SHIFT_MASK)) { return true; } return false; } private boolean isKeyPrevTab(java.awt.event.KeyEvent ke) { if (ke.getKeyCode() == java.awt.event.KeyEvent.VK_TAB && ke.getModifiers() == (InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)) { return true; } if (ke.getKeyCode() == java.awt.event.KeyEvent.VK_OPEN_BRACKET && ke.getModifiers() == (InputEvent.META_MASK | InputEvent.SHIFT_MASK)) { return true; } return false; } public void eventDispatched(AWTEvent e) { java.awt.event.KeyEvent ke = (java.awt.event.KeyEvent) e; //Debug.log(ke.toString()); if (ke.getID() == java.awt.event.KeyEvent.KEY_PRESSED) { if (isKeyNextTab(ke)) { nextTab(); } else if (isKeyPrevTab(ke)) { prevTab(); } } } }, AWTEvent.KEY_EVENT_MASK); } public void removeCaptureHotkey() { HotkeyManager.getInstance().removeHotkey("Capture"); } public void installCaptureHotkey() { HotkeyManager.getInstance().addHotkey("Capture", new HotkeyListener() { @Override public void hotkeyPressed(HotkeyEvent e) { if (!isRunningScript()) { onQuickCapture(); } } }); } public void onQuickCapture() { onQuickCapture(null); } public void onQuickCapture(String arg) { if (isInited()) { Debug.log(3, "QuickCapture"); _btnCapture.capture(0); } } public void removeStopHotkey() { HotkeyManager.getInstance().removeHotkey("Abort"); } public void installStopHotkey() { HotkeyManager.getInstance().addHotkey("Abort", new HotkeyListener() { @Override public void hotkeyPressed(HotkeyEvent e) { onStopRunning(); } }); } public void onStopRunning() { Debug.log(3, "AbortKey was pressed"); boolean shouldCleanUp = true; if (_btnRun != null && _btnRun.isRunning()) { shouldCleanUp &= _btnRun.stopRunScript(); } if (_btnRunViz != null && _btnRunViz.isRunning()) { shouldCleanUp &= _btnRunViz.stopRunScript(); } if (_btnRun == null && _btnRunViz == null) { ideSplash.showAction("... accepted - please wait ..."); setPause(true); return; } if (shouldCleanUp) { org.sikuli.script.Sikulix.cleanUp(-1); this.setVisible(true); } else { Debug.log(3, "AbortKey was pressed, but nothing to stop here ;-)"); } } private void initHotkeys() { installCaptureHotkey(); installStopHotkey(); } // // /* private void initSidePane() { initUnitPane(); _sidePane = new JXCollapsiblePane(JXCollapsiblePane.Direction.RIGHT); _sidePane.setMinimumSize(new Dimension(0, 0)); CloseableTabbedPane tabPane = new CloseableTabbedPane(); _sidePane.getContentPane().add(tabPane); tabPane.setMinimumSize(new Dimension(0, 0)); tabPane.addTab(_I("tabUnitTest"), _unitPane); tabPane.addCloseableTabbedPaneListener(new CloseableTabbedPaneListener() { @Override public boolean closeTab(int tabIndexToClose) { _sidePane.setCollapsed(true); _chkShowUnitTest.setState(false); return false; } }); _sidePane.setCollapsed(true); } private void initUnitPane() { _testRunner = new UnitTestRunner(); _unitPane = _testRunner.getPanel(); _chkShowUnitTest.setState(false); addAuxTab(_I("paneTestTrace"), _testRunner.getTracePane()); } */ public void addAuxTab(String tabName, JComponent com) { msgPane.addTab(tabName, com); } public void jumpTo(String funcName) throws BadLocationException { EditorPane pane = getCurrentCodePane(); pane.jumpTo(funcName); pane.grabFocus(); } public void jumpTo(int lineNo) throws BadLocationException { EditorPane pane = getCurrentCodePane(); pane.jumpTo(lineNo); pane.grabFocus(); } // } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/SikuliIDEI18N.java000077500000000000000000000033221315726130400241210ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import org.sikuli.basics.PreferencesUser; import java.text.MessageFormat; import java.util.*; import org.sikuli.basics.Debug; public class SikuliIDEI18N { static ResourceBundle i18nRB = null; static ResourceBundle i18nRB_en = null; static Locale curLocale = null; static { Locale locale_en = new Locale("en","US"); i18nRB_en = ResourceBundle.getBundle("i18n/IDE",locale_en); Locale locale = PreferencesUser.getInstance().getLocale(); curLocale = locale; if(!setLocale(locale)){ locale = locale_en; PreferencesUser.getInstance().setLocale(locale); } } public static boolean setLocale(Locale locale){ try{ i18nRB = ResourceBundle.getBundle("i18n/IDE",locale); } catch(MissingResourceException e){ Debug.error("SikuliIDEI18N: no locale for " + locale); return false; } return true; } public static String getLocaleShow() { String ret = curLocale.toString(); if (i18nRB == null) ret += " (using en_US)"; return ret; } public static String _I(String key, Object... args){ String ret; if(i18nRB==null) ret = i18nRB_en.getString(key); else{ try { ret = i18nRB.getString(key); } catch (MissingResourceException e) { ret = i18nRB_en.getString(key); } } if(args.length>0){ MessageFormat formatter = new MessageFormat(""); formatter.setLocale(curLocale); formatter.applyPattern(ret); ret = formatter.format(args); } return ret; } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/SikuliIDEPopUpMenu.java000066400000000000000000000317161315726130400253370ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.script.Image; import org.sikuli.script.ImagePath; import org.sikuli.script.Runner; import org.sikuli.script.Sikulix; import org.sikuli.scriptrunner.ScriptingSupport; public class SikuliIDEPopUpMenu extends JPopupMenu { private static String me = "SikuliIDEPopUpMenu: "; private static int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } private String popType; private boolean validMenu = true; public static final String POP_TAB = "POP_TAB"; private CloseableTabbedPane refTab; public static final String POP_IMAGE = "POP_IMAGE"; private EditorPane refEditorPane = null; public static final String POP_LINE = "POP_LINE"; private EditorLineNumberView refLineNumberView = null; private static String[] selOptionsType = null; private MouseEvent mouseTrigger; private int menuCount = 0; private Map menus = new HashMap(); /** * Get the value of isValidMenu * * @return the value of isValidMenu */ public boolean isValidMenu() { return validMenu; } public SikuliIDEPopUpMenu(String pType, Object ref) { popType = pType; init(ref); } private void init(Object ref) { if (popType.equals(POP_TAB)) { refTab = (CloseableTabbedPane) ref; popTabMenu(); } else if (popType.equals(POP_IMAGE)) { refEditorPane = (EditorPane) ref; popImageMenu(); } else if (popType.equals(POP_LINE)) { refLineNumberView = (EditorLineNumberView) ref; popLineMenu(); } else { validMenu = false; } if (!validMenu) { return; } } public void doShow(CloseableTabbedPane comp, MouseEvent me) { mouseTrigger = me; show(comp, me.getX(), me.getY()); } private void fireIDEFileMenu(String name) throws NoSuchMethodException { fireIDEMenu(SikuliIDE.getInstance().getFileMenu(), name); } private void fireIDERunMenu(String name) throws NoSuchMethodException { fireIDEMenu(SikuliIDE.getInstance().getRunMenu(), name); } private void fireIDEMenu(JMenu menu, String name) throws NoSuchMethodException { JMenuItem jmi; String jmiName = null; for (int i = 0; i < menu.getItemCount(); i++) { jmi = menu.getItem(i); if (jmi == null || jmi.getName() == null) { continue; } jmiName = jmi.getName(); if (jmiName.equals(name)) { jmi.doClick(); } } if (jmiName == null) { log(-1, "IDEFileMenu not found: " + name); } } private void fireInsertTabAndLoad(int tabIndex) { SikuliIDE.FileAction insertNewTab = SikuliIDE.getInstance().getFileAction(tabIndex); insertNewTab.doInsert(null); } private JMenuItem createMenuItem(JMenuItem item, ActionListener listener) { item.addActionListener(listener); return item; } private JMenuItem createMenuItem(String name, ActionListener listener) { return createMenuItem(new JMenuItem(name), listener); } private void createMenuSeperator() { menuCount++; addSeparator(); } private void setMenuText(int index, String text) { ((JMenuItem) getComponent(index)).setText(text); } private String getMenuText(int index) { return ((JMenuItem) getComponent(index)).getText(); } private void setMenuEnabled(int index, boolean enabled) { ((JMenuItem) getComponent(index)).setEnabled(enabled); } class MenuAction implements ActionListener { protected Method actMethod = null; protected String action; protected int menuPos; public MenuAction() { } public MenuAction(String item) throws NoSuchMethodException { Class[] paramsWithEvent = new Class[1]; try { paramsWithEvent[0] = Class.forName("java.awt.event.ActionEvent"); actMethod = this.getClass().getMethod(item, paramsWithEvent); action = item; menuPos = menuCount++; menus.put(item, menuPos); } catch (ClassNotFoundException cnfe) { log(-1, "Can't find menu action: %s\n" + cnfe, item); } } @Override public void actionPerformed(ActionEvent e) { if (actMethod != null) { try { log(lvl, "PopMenuAction." + action); Object[] params = new Object[1]; params[0] = e; actMethod.invoke(this, params); } catch (Exception ex) { log(-1, "Problem when trying to invoke menu action %s\nError: %s", action, ex.getMessage()); } } } } private void popTabMenu() { try { add(createMenuItem("Set Type", new PopTabAction(PopTabAction.SET_TYPE))); createMenuSeperator(); add(createMenuItem("Move Tab", new PopTabAction(PopTabAction.MOVE_TAB))); add(createMenuItem("Duplicate", new PopTabAction(PopTabAction.DUPLICATE))); add(createMenuItem("Open", new PopTabAction(PopTabAction.OPEN))); add(createMenuItem("Open left", new PopTabAction(PopTabAction.OPENL))); createMenuSeperator(); add(createMenuItem("Save", new PopTabAction(PopTabAction.SAVE))); add(createMenuItem("SaveAs", new PopTabAction(PopTabAction.SAVE_AS))); createMenuSeperator(); add(createMenuItem("Run", new PopTabAction(PopTabAction.RUN))); add(createMenuItem("Run Slowly", new PopTabAction(PopTabAction.RUN_SLOW))); createMenuSeperator(); add(createMenuItem("Reset", new PopTabAction(PopTabAction.RESET))); } catch (NoSuchMethodException ex) { validMenu = false; } } class PopTabAction extends MenuAction { static final String SET_TYPE = "doSetType"; static final String MOVE_TAB = "doMoveTab"; static final String DUPLICATE = "doDuplicate"; static final String OPEN = "doOpen"; static final String OPENL = "doOpenLeft"; static final String SAVE = "doSave"; static final String SAVE_AS = "doSaveAs"; static final String RUN = "doRun"; static final String RUN_SLOW = "doRunSlow"; static final String RESET = "doReset"; public PopTabAction() { super(); } public PopTabAction(String item) throws NoSuchMethodException { super(item); } public void doSetType(ActionEvent ae) { //TODO use a popUpSelect for more language options Debug.log(3, "doSetType: selected"); String error = ""; EditorPane cp = SikuliIDE.getInstance().getCurrentCodePane(); if (selOptionsType == null) { Set types = Runner.typeEndings.keySet(); selOptionsType = new String[types.size()]; int i = 0; for (String e : types) { if (e.contains("plain")) { continue; } selOptionsType[i++] = e.replaceFirst(".*?\\/", ""); } } String currentType = cp.getSikuliContentType(); String targetType = Sikulix.popSelect("Select the Scripting Language ...", selOptionsType, currentType.replaceFirst(".*?\\/", "")); if (targetType == null) { targetType = currentType; } else { targetType = "text/" + targetType; } if (currentType.equals(targetType)) { SikuliIDE.getStatusbar().setCurrentContentType(currentType); return; } String targetEnding = Runner.typeEndings.get(targetType); if (cp.getText().length() > 0) { // if (!cp.reparseCheckContent()) { if (!Sikulix.popAsk(String.format( "Switch to %s requested, but tab is not empty!\n" + "Click YES, to discard content and switch\n" + "Click NO to cancel this action and keep content.", targetType))) { error = ": with errors"; } } if (error.isEmpty()) { cp.reInit(targetEnding); // cp.setText(String.format(Settings.TypeCommentDefault, cp.getSikuliContentType())); cp.setText(""); error = ": (" + targetType + ")"; } String msg = "doSetType: completed" + error ; SikuliIDE.getStatusbar().setMessage(msg); SikuliIDE.getStatusbar().setCurrentContentType(targetType); Debug.log(3, msg); } public void doMoveTab(ActionEvent ae) throws NoSuchMethodException { if (ae.getActionCommand().contains("Insert")) { log(lvl, "doMoveTab: entered at insert"); doLoad(refTab.getSelectedIndex()+1); resetMenuAfterMoveTab(); return; } log(lvl, "doMoveTab: entered at move"); refTab.resetLastClosed(); if (SikuliIDE.getInstance().getCurrentCodePane().isSourceBundleTemp()) { log(-1, "Untitled tab cannot be moved"); return; } // fireIDEFileMenu("SAVE"); boolean success = refTab.fireCloseTab(mouseTrigger, refTab.getSelectedIndex()); if (success && refTab.getLastClosed() != null) { refTab.isLastClosedByMove = true; setMenuText(menus.get(MOVE_TAB), "Insert Right"); setMenuText(menus.get(OPENL), "Insert Left"); log(lvl, "doMoveTab: preparation success"); } else { log(-1, "doMoveTab: preperation aborted"); } } private void checkAndResetMoveTab() throws NoSuchMethodException { if (refTab.isLastClosedByMove) { log (-1, "doMoveTab: is prepared and will be aborted"); int currentTab = refTab.getSelectedIndex(); doLoad(refTab.getSelectedIndex()+1); refTab.setSelectedIndex(currentTab); } resetMenuAfterMoveTab(); } private void resetMenuAfterMoveTab() { setMenuText(menus.get(MOVE_TAB), "Move Tab"); setMenuText(menus.get(OPENL), "Open left"); refTab.resetLastClosed(); } public void doDuplicate(ActionEvent ae) throws NoSuchMethodException { log(lvl, "doDuplicate: entered"); EditorPane ep = SikuliIDE.getInstance().getCurrentCodePane(); checkAndResetMoveTab(); fireIDEFileMenu("SAVE"); if (ep.isSourceBundleTemp()) { log(-1, "Untitled tab cannot be duplicated"); return; } String bundleOld = ep.getBundlePath(); fireIDEFileMenu("SAVE_AS"); if (FileManager.pathEquals(bundleOld, ep.getBundlePath())) { log(-1,"duplicate must use different project name"); return; } setMenuText(menus.get(OPENL), "Insert left"); doOpenLeft(null); } private boolean doLoad(int tabIndex) { boolean success = true; fireInsertTabAndLoad(tabIndex); return success; } public void doOpen(ActionEvent ae) throws NoSuchMethodException { log(lvl, "doOpen: entered"); checkAndResetMoveTab(); doLoad(refTab.getSelectedIndex()+1); } public void doOpenLeft(ActionEvent ae) throws NoSuchMethodException { if (getMenuText(5).contains("Insert")) { log(lvl, "doOpenLeft: entered at insert left"); doLoad(refTab.getSelectedIndex()); resetMenuAfterMoveTab(); return; } log(lvl, "doOpenLeft: entered"); doLoad(refTab.getSelectedIndex()); } public void doSave(ActionEvent ae) throws NoSuchMethodException { log(lvl, "doSave: entered"); fireIDEFileMenu("SAVE"); } public void doSaveAs(ActionEvent ae) throws NoSuchMethodException { log(lvl, "doSaveAs: entered"); fireIDEFileMenu("SAVE_AS"); } public void doRun(ActionEvent ae) throws NoSuchMethodException { log(lvl, "doRun: entered"); fireIDERunMenu("RUN"); } public void doRunSlow(ActionEvent ae) throws NoSuchMethodException { log(lvl, "doRunSlow: entered"); fireIDERunMenu("RUN_SLOWLY"); } public void doReset(ActionEvent ae) throws NoSuchMethodException { log(lvl, "Reset: entered"); checkAndResetMoveTab(); Image.dump(lvl); ImagePath.reset(); Image.dump(lvl); SikuliIDE.getInstance().getCurrentCodePane().reparse(); } } private void popImageMenu() { try { add(createMenuItem("Preview", new PopImageAction(PopImageAction.PREVIEW))); } catch (NoSuchMethodException ex) { validMenu = false; } } class PopImageAction extends MenuAction { static final String PREVIEW = "doPreview"; public PopImageAction() { super(); } public PopImageAction(String item) throws NoSuchMethodException { super(item); } public void doPreview(ActionEvent ae) { log(lvl, "doPreview:"); } } private void popLineMenu() { try { add(createMenuItem("Action", new PopLineAction(PopLineAction.ACTION))); } catch (NoSuchMethodException ex) { validMenu = false; } } class PopLineAction extends MenuAction { static final String ACTION = "doAction"; public PopLineAction() { super(); } public PopLineAction(String item) throws NoSuchMethodException { super(item); } public void doAction(ActionEvent ae) { log(lvl, "doAction:"); } } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/SikuliIDEStatusBar.java000066400000000000000000000045531315726130400253560ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import javax.swing.*; import com.explodingpixels.macwidgets.plaf.EmphasizedLabelUI; import java.util.Date; import org.sikuli.basics.Settings; class SikuliIDEStatusBar extends JPanel { private JLabel _lblMsg; private JLabel _lblCaretPos; private String currentContentType = "???"; private int currentRow; private int currentCol; private long starting; public SikuliIDEStatusBar() { setLayout(new BorderLayout()); setPreferredSize(new Dimension(10, 20)); JPanel rightPanel = new JPanel(new BorderLayout()); rightPanel.setOpaque(false); _lblMsg = new JLabel(); _lblMsg.setPreferredSize(new Dimension(400, 20)); _lblMsg.setUI(new EmphasizedLabelUI()); _lblMsg.setFont(new Font("Monaco", Font.TRUETYPE_FONT, 11)); _lblCaretPos = new JLabel(); _lblCaretPos.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 15)); _lblCaretPos.setUI(new EmphasizedLabelUI()); _lblCaretPos.setFont(UIManager.getFont("Button.font").deriveFont(11.0f)); setCaretPosition(1, 1); resetMessage(); add(_lblMsg, BorderLayout.WEST); add(_lblCaretPos, BorderLayout.LINE_END); // add(rightPanel, BorderLayout.EAST); } public void setCurrentContentType(String ct) { if (ct == null) { return; } currentContentType = ct.replaceFirst(".*?\\/", ""); setCaretPosition(-1, 0); } public void setCaretPosition(int row, int col) { if (row > -1) { currentRow = row; currentCol = col; } _lblCaretPos.setText(String.format("(%s) | R: %d | C: %d", currentContentType, currentRow, currentCol)); if (starting > 0 && new Date().getTime() - starting > 3000) { resetMessage(); } } public void setMessage(String text) { _lblMsg.setText(" " + text); repaint(); starting = new Date().getTime(); } public void resetMessage() { setMessage(SikuliIDE.runTime.SikuliVersionIDE + " (" + SikuliIDE.runTime.SikuliVersionBuild + ")"); starting = 0; } // @Override // protected void paintComponent(Graphics g) { // super.paintComponent(g); // int y = 0; // g.setColor(new Color(156, 154, 140)); // g.drawLine(0, y, getWidth(), y); // y++; // g.setColor(new Color(196, 194, 183)); // g.drawLine(0, y, getWidth(), y); // } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/ide/Sikulix.java000066400000000000000000000015431315726130400233670ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.security.CodeSource; import javax.swing.JOptionPane; import org.sikuli.basics.Settings; import org.sikuli.script.Screen; public class Sikulix { public static void main(String[] args) { String jarName = ""; CodeSource codeSrc = SikuliIDE.class.getProtectionDomain().getCodeSource(); if (codeSrc != null && codeSrc.getLocation() != null) { jarName = codeSrc.getLocation().getPath(); } if (jarName.contains("sikulixsetupIDE")) { JOptionPane.showMessageDialog(null, "Not useable!\nRun setup first!", "sikulixsetupIDE", JOptionPane.ERROR_MESSAGE); System.exit(0); } // Screen.ignorePrimaryAtCapture = true; // Settings.TraceLogs = true; SikuliIDE.run(args); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/idesupport/000077500000000000000000000000001315726130400225265ustar00rootroot00000000000000sikulix-1.1.1/IDE/src/main/java/org/sikuli/idesupport/IDESplash.java000066400000000000000000000034041315726130400251460ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.idesupport; import java.awt.Color; import static java.awt.Component.CENTER_ALIGNMENT; import java.awt.Container; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import org.sikuli.script.RunTime; public class IDESplash extends JFrame { RunTime runTime = null; JLabel action; JLabel step; public IDESplash(RunTime rt) { init(rt); } void init(RunTime rt) { runTime = rt; setResizable(false); setUndecorated(true); Container pane = getContentPane(); ((JComponent) pane).setBorder(BorderFactory.createLineBorder(Color.lightGray, 5)); pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); pane.add(new JLabel(" ")); JLabel title = new JLabel("SikuliX-IDE is starting"); title.setAlignmentX(CENTER_ALIGNMENT); pane.add(title); pane.add(new JLabel(" ")); action = new JLabel(" "); action.setAlignmentX(CENTER_ALIGNMENT); pane.add(action); pane.add(new JLabel(" ")); step = new JLabel("... starting"); step.setAlignmentX(CENTER_ALIGNMENT); pane.add(step); pane.add(new JLabel(" ")); JLabel version = new JLabel(String.format("%s-%s", rt.getVersionShort(), rt.sxBuildStamp)); version.setAlignmentX(CENTER_ALIGNMENT); pane.add(version); pane.add(new JLabel(" ")); pack(); setSize(200, 155); setLocationRelativeTo(null); setAlwaysOnTop(true); setVisible(true); } public void showAction(String actionText) { action.setText(actionText); repaint(); } public void showStep(String stepTitle) { step.setText(stepTitle); repaint(); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/idesupport/IDESupport.java000066400000000000000000000013641315726130400253730ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.idesupport; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.ServiceLoader; public class IDESupport { public static Map ideSupporter = new HashMap(); public static void initIDESupport() { ServiceLoader sloader = ServiceLoader.load(IIDESupport.class); Iterator supIterator = sloader.iterator(); while (supIterator.hasNext()) { IIDESupport current = supIterator.next(); try { for (String ending : current.getEndings()) { ideSupporter.put(ending, current); } } catch (Exception ex) { } } } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/idesupport/IIDESupport.java000066400000000000000000000004471315726130400255050ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.idesupport; import org.sikuli.idesupport.IIndentationLogic; public interface IIDESupport { public String[] getEndings(); public IIndentationLogic getIndentationLogic(); } sikulix-1.1.1/IDE/src/main/java/org/sikuli/idesupport/IIndentationLogic.java000066400000000000000000000021341315726130400267340ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package org.sikuli.idesupport; import javax.swing.text.BadLocationException; import javax.swing.text.StyledDocument; /** * * @author rhocke */ public interface IIndentationLogic { public void setTabWidth(int tabwidth); public int checkDedent(String leadingWhitespace, int line); public void checkIndent(String leadingWhitespace, int line); public boolean shouldAddColon(); public void setLastLineEndsWithColon(); public int shouldChangeLastLineIndentation(); public int shouldChangeNextLineIndentation(); public void reset(); public void addText(String text); public String getLeadingWhitespace(String text) ; public String getLeadingWhitespace(StyledDocument doc, int head, int len) throws BadLocationException; public int atEndOfLine(StyledDocument doc, int cpos, int start, String s, int sLen); } sikulix-1.1.1/IDE/src/main/java/org/sikuli/idesupport/JRubyIDESupport.java000066400000000000000000000010101315726130400263330ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.idesupport; /** * all methods from/for IDE, that are JRuby specific */ public class JRubyIDESupport implements IIDESupport { @Override public String[] getEndings() { return new String [] {"rb"}; } @Override public IIndentationLogic getIndentationLogic() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/idesupport/JythonIDESupport.java000066400000000000000000000006511315726130400265650ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.idesupport; /** * all methods from/for IDE, that are Python specific */ public class JythonIDESupport implements IIDESupport { @Override public String[] getEndings() { return new String [] {"py"}; } @Override public IIndentationLogic getIndentationLogic() { return new PythonIndentation(); } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/idesupport/PythonIndentation.java000066400000000000000000000421221315726130400270500ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.idesupport; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.text.BadLocationException; import javax.swing.text.Element; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; import org.sikuli.basics.PreferencesUser; import org.sikuli.basics.Debug; /** * Implements the logic for giving hints about the (correct) indentation of new * lines of Python code entered by a user. Auto indentation uses these hints to * automatically adjust the the indentation of the next line to be entered, * and/or the last line entered. The typical usage is: *
    *
  1. Call {@link #reset()} to reset the object's state. *
  2. Feed each line of python text entered by the user (including the * terminating end-of-line sequence) to the object by calling * {@link #addText(String)}. *
  3. Call {@link #shouldChangeLastLineIndentation()} and * {@link #shouldChangeNextLineIndentation()} to get hints about the indentation * of the last line entered, and the next line to be entered. *
* Note: the proposed indentation change for the next line may depend on the * current indentation of the last line entered. If you change the indentation * of the last line entered, you may also have to adjust the indentation of the * next line by the same amount. *

* This implementation determines the logical line structure of a Python * document from the beginning of the document up to the line for which * indentation hints are requested. The indentation of a line is based on the * current indentation of the line and the indentation of the logical Python * line that contains the line. See {@link PythonState} and line * structure in the Python * language reference for information about logical lines. *

* This implementation provides indentation hints for the following contexts: *

    *
  • compound statements such as {@code if/elif/else}, {@code for}, {@code * while}, {@code try/except/finally}, function and class definitions *
  • statements after which indentation is normally decreased: {@code break}, * {@code continue}, {@code pass}, {@code raise}, {@code return} *
  • expressions in parentheses, square brackets and curly braces that extend * over multiple lines (implicit line joining) *
  • explicit line joining (backslash-escaped by end-of-line) *
  • long strings *
*/ public class PythonIndentation implements IIndentationLogic { public static final int PARENTHESIS_INDENTATION_TABSTOPS = 2; public static final int NESTED_PARENTHESIS_INDENTATION_TABSTOPS = 1; public static final int LONG_STRING_INDENTATION_COLUMNS = 3; public static final int EXPLICIT_LINE_JOINING_INDENTATION_TABSTOPS = 2; private static final Pattern ENDS_WITH_COLON = Pattern.compile( "^[^#]*:\\s*(?:#.*)?$", Pattern.DOTALL); private static final Pattern UNINDENT_NEXT_LINE_STATEMENT = Pattern .compile("^\\s*(?:break|continue|pass|raise|return)\\b"); private static final Pattern UNINDENT_LAST_LINE_STATEMENT = Pattern .compile("^\\s*(?:elif|else|except|finally)\\b"); // this is matched with a logical line structure private static final Pattern COMPOUND_HEADER_STATEMENT_WITH_ARG = Pattern .compile("^\\s*(?:if|elif|for|while|with|except|def|class)\\b.*[\\w'\")\\]}](?:\\s*:)?\\s*$"); private static final Pattern COMPOUND_HEADER_STATEMENT_WITHOUT_ARG = Pattern .compile("^\\s*(?:else|try|except|finally)\\b(?:\\s*:)?\\s*$"); private Matcher endsWithColonMatcher = ENDS_WITH_COLON.matcher(""); private Matcher unindentNextLineStatementMatcher = UNINDENT_NEXT_LINE_STATEMENT .matcher(""); private Matcher unindentLastLineStatementMatcher = UNINDENT_LAST_LINE_STATEMENT .matcher(""); private Matcher compoundHeaderStatementWithArgMatcher = COMPOUND_HEADER_STATEMENT_WITH_ARG .matcher(""); private Matcher compoundHeaderStatementWithoutArgMatcher = COMPOUND_HEADER_STATEMENT_WITHOUT_ARG .matcher(""); private PythonState pythonState; private boolean wasColonAdded; public PythonIndentation(){ pythonState = new PythonState(); wasColonAdded = false; } @Override public void checkIndent(String leadingWhitespace, int line) { if(leadingWhitespace.contains("\t") && leadingWhitespace.contains(" ")) { } int lws = leadingWhitespace.length(); int tws = PreferencesUser.getInstance().getTabWhitespace().length(); //TODo obsolete, when indentation is checked at load time if(tws > 1 && leadingWhitespace.contains("\t") && leadingWhitespace.contains(" ")) { Debug.error("PythonIndentation: indent has mixed tab and space in line " + line); } if (tws == 1 || (lws % tws) == 0) { return; } Debug.error("PythonIndentation: indent not consistent with tab settings in line " + line); } @Override public int checkDedent(String leadingWhitespace, int line) { int lws = leadingWhitespace.length(); int tws = PreferencesUser.getInstance().getTabWhitespace().length(); if (lws < tws) { return lws; } checkIndent(leadingWhitespace, line); return tws; } /** * Checks if the last logical line (logically) ends with a colon. A logical * line logically ends with a colon if it contains a colon that is not inside * a comment and the colon is only followed by whitespace or by a comment. *

* If {@link #setLastLineEndsWithColon()} was called after the last chunk of * text was fed to this object, this method returns true. *

* This method is not thread safe! * * @return true if the last logical line logically ends with a colon */ public boolean endsLastLogicalLineWithColon(){ // not thread safe! if( wasColonAdded ) { return true; } return endsWithColonMatcher.reset( pythonState.getLastLogicalLineStructure()).matches(); } /** * Checks if the last logical line (logically) contains a colon. A logical * line logically contains a colon if it contains a colon that is not inside * a parenthesized expression, a string or a comment. * * @return true if the last logical line logically contains a colon */ public boolean hasLastLogicalLineColon(){ if( wasColonAdded ) { return true; } return pythonState.getLastLogicalLineStructure().contains(":"); } /** * Checks if the last logical line begins with a python statement that * usually terminates an indented block ({@code break}, {@code continue}, * {@code pass}, {@code raise}, {@code return}). *

* This method returns true only if {@code break}/{@code continue}/ {@code * pass}/{@code raise}/{@code return} is the first statement in the logical * line. It does not recognize things like {@code print x; return}. *

* This method is not thread safe! * * @return true if the last logical line begins with a statement that usually * terminates an indented block */ public boolean isLastLogicalLineUnindentNextLineStatement(){ // not thread safe! return unindentNextLineStatementMatcher.reset( pythonState.getLastLogicalLine()).find(); } /** * Checks if the last physical line begins with a python statement that must * have a lower indentation level than the preceding block ({@code else}, * {@code elif}, {@code except}, {@code finally}). *

* This method is not thread safe! * * @return true if the last physical line begins with a statement that must * have a lower indentation level than the preceding block */ public boolean isUnindentLastLineStatement(){ // not thread safe! return unindentLastLineStatementMatcher.reset( pythonState.getLastPhysicalLine()).find(); } /** * Checks if the last logical line begins with a python statement that starts * a clause of a compound statement ({@code if}, {@code else}, {@code elif}, * {@code for}, {@code while}, {@code with}, {@code try}, {@code except}, * {@code finally}, {@code def}, {@code class}). *

* This method is not thread safe! * * @return true if the logical line begins with a statement that starts a * clause of a compound statement */ public boolean isLastLogicalLineCompoundHeaderStatement(){ // not thread safe! String structure = pythonState.getLastLogicalLineStructure(); return compoundHeaderStatementWithArgMatcher.reset(structure).find() || compoundHeaderStatementWithoutArgMatcher.reset(structure).find(); } /** * Sets the number of whitespace columns that equals a single tab used by * this object to calculate the indentation of lines. * * @param tabwidth * the number of whitespace columns that equals a single tab */ @Override public void setTabWidth(int tabwidth){ pythonState.setTabSize(tabwidth); } /** * Returns the number of whitespace columns that equals a single tab used by * this object to calculate the indentation of lines. * * @return the number of whitespace columns that equals a single tab */ public int getTabWidth(){ return pythonState.getTabSize(); } /** * Resets the state of this object. The new state will be as if no text had * been fed to this object. */ @Override public void reset(){ pythonState.reset(); } /** * Feeds a chunk of text (i.e. code) to this object. The text can be a single * line or multiple lines or even incomplete lines. You can feed an entire * document at once, or line by line. Any new text will be (virtually) * appended to text added earlier. * * @param text * a chunk of code */ @Override public void addText(String text){ wasColonAdded = false; pythonState.update(text); } /** * Returns the line number of the last line fed to this object. * * @return the line number of the last line (0-based) */ public int getLastLineNumber(){ return pythonState.getPhysicalLineNumber(); } /** * Tells this object to assume that the last logical line ends with a colon. * Auto-completion that adds a colon at the end of a line after the line was * fed to this object must call this method to notify this object that a * colon was added. This affects the hints for line indentation. */ @Override public void setLastLineEndsWithColon(){ wasColonAdded = true; } /** * Returns a hint about how the indentation of the last line fed to this * object should be changed. A negative value means decrease indentation * while a positive value means increase indentation by the returned value. * * @return the number of columns by which the indentation should be changed */ @Override public int shouldChangeLastLineIndentation(){ // only change indentation of the first physical line of a logical line if( pythonState.getPhysicalLineNumber() > pythonState .getLogicalLinePhysicalStartLineNumber() ) { return 0; } // if this is not the first logical line and the indentation level is // already less than the previous logical line, do not unindent further if( pythonState.getLogicalLineNumber() > 0 && pythonState.getLastLogicalLineIndentation() < pythonState .getPrevLogicalLineIndentation() ) { return 0; } int change; if( isUnindentLastLineStatement() ){ change = -pythonState.getTabSize(); }else{ change = 0; } // avoid negative indentation int physicalIndentation = pythonState.getLastPhysicalLineIndentation(); if( physicalIndentation + change < 0 ){ change = -physicalIndentation; } return change; } /** * Returns a hint about how the indentation of the next line (the line * following the last line fed to this object) should be changed. A negative * value means decrease indentation while a positive value means increase * indentation by the returned value. * * @return the number of columns by which the indentation should be changed */ @Override public int shouldChangeNextLineIndentation(){ if( !pythonState.isPhysicalLineComplete() ) { return 0; } int logicalIndentation = pythonState.getLastLogicalLineIndentation(); int physicalIndentation = pythonState.getLastPhysicalLineIndentation(); int change = logicalIndentation - physicalIndentation; if( pythonState.isLogicalLineComplete() ){ if( endsLastLogicalLineWithColon() ){ change += pythonState.getTabSize(); }else if( isLastLogicalLineUnindentNextLineStatement() ){ change -= pythonState.getTabSize(); } }else if( pythonState.inLongString() ){ if( pythonState.getDepth() > 1 ){ // long string inside parenthesis change += (PARENTHESIS_INDENTATION_TABSTOPS + (pythonState .getDepth() - 2) * NESTED_PARENTHESIS_INDENTATION_TABSTOPS) * pythonState.getTabSize(); }else{ if( pythonState.getPhysicalLineNumber() == pythonState .getLogicalLinePhysicalStartLineNumber() ){ change = LONG_STRING_INDENTATION_COLUMNS; }else{ change = 0; } } }else if( pythonState.getDepth() > 0 ){ // only parenthesis, no string change += (PARENTHESIS_INDENTATION_TABSTOPS + (pythonState.getDepth() - 1) * NESTED_PARENTHESIS_INDENTATION_TABSTOPS) * pythonState.getTabSize(); }else if( pythonState.isExplicitLineJoining() ){ if( pythonState.getPhysicalLineNumber() == pythonState .getLogicalLinePhysicalStartLineNumber() ){ change = EXPLICIT_LINE_JOINING_INDENTATION_TABSTOPS * pythonState.getTabSize(); }else{ change = 0; } }else{ change = 0; } // avoid negative indentation if( physicalIndentation + change < 0 ){ change = -physicalIndentation; } return change; } /** * Returns a hint whether a colon should be added at the end of the last * logical line. This is the case if the last logical line satisfies all of * the following: *

    *
  • it is complete (i.e. no explicit or implicit joining with the next * physical line), *
  • it begins with a statement that starts a clause in a compound * statement, *
  • if the statement requires and argument, it is followed by another * token, *
  • it does not (logically) end with a punctuation symbol, *
  • it does not end with a comment. *
* For example, the following lines should have a colon appended: *
    * if x
    * else
    * elif x
    * while x
    * class C
    * class C(D)
    * def f()
    * try
    * except
    * except E
    * except E, e
    * finally
    * 
* The following lines should not have a colon appended: *
    * print x
    * 'if x'
    * if
    * if (x
    * if 's
    * if x:
    * if x,
    * if x ?
    * if x # ends with comment
    * elif
    * while
    * class
    * def
    * 
* * @return true if the last logical line should end with a colon but does not */ @Override public boolean shouldAddColon(){ if( !pythonState.isLogicalLineComplete() ) { return false; } return isLastLogicalLineCompoundHeaderStatement() && !hasLastLogicalLineColon(); } // @Override public String getLeadingWhitespace(String text) { int len = text.length(); int count = 0; while (count < len && isWhitespace(text.charAt(count))) { count++; } return text.substring(0, count); } @Override public String getLeadingWhitespace(StyledDocument doc, int head, int len) throws BadLocationException { String ret = ""; int pos = head; while (pos < head + len) { Element e = doc.getCharacterElement(pos); if (e.getName().equals(StyleConstants.ComponentElementName)) { break; } int eStart = e.getStartOffset(); int eEnd = e.getEndOffset(); String space = getLeadingWhitespace(doc.getText(eStart, eEnd - eStart)); ret += space; if (space.length() < eEnd - eStart) { break; } pos = eEnd; } return ret; } @Override public int atEndOfLine(StyledDocument doc, int cpos, int start, String s, int sLen) { for (int i = cpos - start; i < sLen; i++) { if (doc.getCharacterElement(cpos).getName().equals(StyleConstants.ComponentElementName) || !isWhitespace(s.charAt(i))) { return i + start; } cpos++; } return -1; } public static boolean isWhitespace(char ch) { return ch == ' ' || ch == '\t'; } // } sikulix-1.1.1/IDE/src/main/java/org/sikuli/idesupport/PythonState.java000066400000000000000000000722171315726130400256640ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.idesupport; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.sikuli.basics.Debug; /** * This class is used to determine the state at any given position in a python * document. Here, state means the following: *
    *
  • the nesting level of parentheses and string literals at the given * position, *
  • whether the given position is inside a comment, *
  • whether the given position terminates a physical line, *
  • whether the given position terminates a logical line, *
  • the indentation of the last physical line, *
  • the indentation of the last logical line. *
* See line * structure in the python * language reference for information about physical and logical lines. *

* To determine the state of a python document at a given position, you feed a * {@link PythonState} object with the prefix of the document ending at the * given position. The text can be fed in any number of consecutive chunks * (input chunks). Typically text is fed to the object in chunks that represent * (physical) lines, although this is not a requirement. *

* After each chunk of text, the state is updated and represents the state of * (the prefix of) the document given by the concatenation of all the chunks * seen so far. For example, if each chunk is one line of the python document, * you get information about the state of the document at the end of each line. * Possible applications are: *

    *
  • represent a python document as a sequence of logical lines, *
  • change the indentation level of the next line. *
*

* You can retrieve the last complete physical and logical line seen by the * state object. *

* You can retrieve a string that represents the structure of the last logical * line seen by the state object (see {@link #getLastLogicalLineStructure()}. * The line structure can be used as a hint for automatic code completion, e.g. * to automatically add a colon to certain python statements (if, * except). *

* This class does not perform syntactic analysis. You cannot use it to find * syntax errors in a python document. *

* This class does not perform full lexical analysis. It does not recognize * keywords, identifiers, or numbers. */ public class PythonState { public static final int DEFAULT_TABSIZE = 4; public static enum State{ DEFAULT, IN_SINGLE_QUOTED_STRING, IN_DOUBLE_QUOTED_STRING, IN_LONG_SINGLE_QUOTED_STRING, IN_LONG_DOUBLE_QUOTED_STRING, IN_PARENTHESIS, IN_COMMENT }; // Matchers to decide what to do next in each state. // Each matcher includes escaped EOL and escaped backslash // starts a string, parenthesis or comment private static final Pattern START_DELIMITER = Pattern.compile( "('''|\"\"\"|['\"(\\[{#]|\\\\?(?:\r|\n|\r\n)|\\\\.)", Pattern.MULTILINE); // starts or ends a string, parenthesis, or starts a comment private static final Pattern DELIMITER = Pattern .compile("('''|\"\"\"|['\"()\\[\\]{}#]|\\\\?(?:\r|\n|\r\n)|\\\\.)"); // ends a single quoted string private static final Pattern SINGLE_QUOTE_DELIMITER = Pattern .compile("('|\\\\?(?:\r|\n|\r\n)|\\\\.)"); // ends a double quoted string private static final Pattern DOUBLE_QUOTE_DELIMITER = Pattern .compile("(\"|\\\\?(?:\r|\n|\r\n)|\\\\.)"); // ends a single quoted long string private static final Pattern LONG_SINGLE_QUOTE_DELIMITER = Pattern .compile("('''|\\\\?(?:\r|\n|\r\n)|\\\\.)"); // ends a double quoted long string private static final Pattern LONG_DOUBLE_QUOTE_DELIMITER = Pattern .compile("(\"\"\"|\\\\?(?:\r|\n|\r\n)|\\\\.)"); // EOL private static final Pattern END_OF_LINE = Pattern.compile("(?:\n|\r\n?)"); private Matcher startDelimiterMatcher = START_DELIMITER.matcher("") .useAnchoringBounds(true); private Matcher delimiterMatcher = DELIMITER.matcher("").useAnchoringBounds( true); private Matcher singleQuoteMatcher = SINGLE_QUOTE_DELIMITER.matcher("") .useAnchoringBounds(true); private Matcher doubleQuoteMatcher = DOUBLE_QUOTE_DELIMITER.matcher("") .useAnchoringBounds(true); private Matcher longSingleQuoteMatcher = LONG_SINGLE_QUOTE_DELIMITER .matcher("").useAnchoringBounds(true); private Matcher longDoubleQuoteMatcher = LONG_DOUBLE_QUOTE_DELIMITER .matcher("").useAnchoringBounds(true); private Matcher endOfLineMatcher = END_OF_LINE.matcher("") .useAnchoringBounds(true); private StringBuilder physicalLine; private StringBuilder logicalLine; private StringBuilder unmatchedChunk; private boolean completePhysicalLine; private boolean completeLogicalLine; /** * Set to true to indicate that the next physical line is a continuation of * the previous physical line. */ private boolean explicitJoining; /** * If {@link #explicitJoining} is true, the length of the prefix of * {@link #unmatchedChunk} that has already been added to * {@link #logicalLine}. */ private int explicitJoinOffset; private int physicalLineNumber; private int logicalLineNumber; private int logicalLinePhysicalStartLineNumber; private int physicalLineIndentation; private int logicalLineIndentation; private int prevPhysicalLineIndentation; private int prevLogicalLineIndentation; private int tabsize = DEFAULT_TABSIZE; private Stack state; private StringBuilder logicalLineStructure; public PythonState(){ state = new Stack(); state.push(State.DEFAULT); physicalLine = new StringBuilder(); logicalLine = new StringBuilder(); unmatchedChunk = new StringBuilder(); logicalLineStructure = new StringBuilder(); reset(); } /** * Sets the number of whitespace columns that equals a single tab. This is * used to calculate the indentation of lines. * * @param tabsize * the number of whitespace columns that equals a single tab */ public void setTabSize(int tabsize){ this.tabsize = tabsize; } /** * Returns the number of whitespace columns equalling a single tab that is * used to calculate the indentation of lines. * * @return the number of whitespace columns that equals a single tab */ public int getTabSize(){ return tabsize; } /** * Resets the state of this object. The new state is equivalent to an empty * document. */ public void reset(){ state.setSize(1); physicalLine.setLength(0); logicalLine.setLength(0); unmatchedChunk.setLength(0); logicalLineStructure.setLength(0); completePhysicalLine = false; completeLogicalLine = false; explicitJoining = false; explicitJoinOffset = 0; physicalLineNumber = 0; logicalLineNumber = 0; logicalLinePhysicalStartLineNumber = 0; physicalLineIndentation = -1; logicalLineIndentation = -1; prevPhysicalLineIndentation = -1; prevLogicalLineIndentation = -1; } private boolean isEOL(String s){ return s.equals("\r") || s.equals("\n") || s.equals("\r\n"); } private boolean isEscapedEOL(String s){ return s.length() >= 2 && s.charAt(0) == '\\' && isEOL(s.substring(1)); } private boolean isEscapedChar(String s){ return s.length() == 2 && s.charAt(0) == '\\'; } /** * Feeds a chunk of text to this object. The text will be (virtually) * appended to any text that was fed to this object earlier since the last * reset. * * @param newChunk * a new chunk of text */ public void update(String newChunk){ unmatchedChunk.append(newChunk); // indexes in unmatchedChunk int searchStart; int matchEnd; int nextSearchStart = 0; String match = null; SCAN: while( nextSearchStart < unmatchedChunk.length() ){ searchStart = nextSearchStart; Debug.log(9, "%s: [%s]", state.peek().name(), unmatchedChunk.substring(searchStart)); // more input to match if( completePhysicalLine ){ physicalLine.setLength(0); completePhysicalLine = false; physicalLineNumber++; prevPhysicalLineIndentation = physicalLineIndentation; } if( completeLogicalLine ){ logicalLine.setLength(0); logicalLineStructure.setLength(0); completeLogicalLine = false; logicalLineNumber++; logicalLinePhysicalStartLineNumber = physicalLineNumber; prevLogicalLineIndentation = logicalLineIndentation; logicalLineIndentation = -1; } explicitJoining = false; // use different matchers, depending on current state switch( state.peek() ){ case DEFAULT: // start a string, parenthesis, comment, or EOL startDelimiterMatcher.reset(unmatchedChunk); startDelimiterMatcher.region(searchStart, unmatchedChunk.length()); if( startDelimiterMatcher.find() ){ match = startDelimiterMatcher.group(1); matchEnd = startDelimiterMatcher.end(1); if( isEscapedEOL(match) ){ completePhysicalLine = true; explicitJoining = true; }else if( isEOL(match) ){ completePhysicalLine = true; // append scanned input except EOL logicalLineStructure.append(unmatchedChunk.substring( searchStart, matchEnd - match.length())); }else{ if( match.equals("'") ){ state.push(State.IN_SINGLE_QUOTED_STRING); }else if( match.equals("\"") ){ state.push(State.IN_DOUBLE_QUOTED_STRING); }else if( match.equals("'''") ){ state.push(State.IN_LONG_SINGLE_QUOTED_STRING); }else if( match.equals("\"\"\"") ){ state.push(State.IN_LONG_DOUBLE_QUOTED_STRING); }else if( match.equals("(") || match.equals("[") || match.equals("{") ){ state.push(State.IN_PARENTHESIS); }else if( match.equals("#") ){ state.push(State.IN_COMMENT); }else if( isEscapedChar(match) ){ // skip }else{ throw new Error("unexpected match \"" + match + "\""); } logicalLineStructure.append(unmatchedChunk.substring( searchStart, matchEnd)); } }else{ break SCAN; } break; case IN_PARENTHESIS: // start string, start/end parenthesis, comment, EOL delimiterMatcher.reset(unmatchedChunk); delimiterMatcher.region(searchStart, unmatchedChunk.length()); if( delimiterMatcher.find() ){ match = delimiterMatcher.group(1); matchEnd = delimiterMatcher.end(1); if( match.equals("'") ){ state.push(State.IN_SINGLE_QUOTED_STRING); }else if( match.equals("\"") ){ state.push(State.IN_DOUBLE_QUOTED_STRING); }else if( match.equals("'''") ){ state.push(State.IN_LONG_SINGLE_QUOTED_STRING); }else if( match.equals("\"\"\"") ){ state.push(State.IN_LONG_DOUBLE_QUOTED_STRING); }else if( match.equals("(") || match.equals("[") || match.equals("{") ){ state.push(State.IN_PARENTHESIS); }else if( match.equals(")") || match.equals("]") || match.equals("}") ){ state.pop(); if( state.peek() == State.DEFAULT ){ logicalLineStructure.append(match); } }else if( match.equals("#") ){ state.push(State.IN_COMMENT); }else if( isEOL(match) ){ completePhysicalLine = true; }else if( isEscapedEOL(match) ){ completePhysicalLine = true; explicitJoining = true; }else if( isEscapedChar(match) ){ // skip }else{ throw new Error("unexpected match"); } }else{ break SCAN; } break; case IN_SINGLE_QUOTED_STRING: // end single quoted string, or EOL singleQuoteMatcher.reset(unmatchedChunk); singleQuoteMatcher.region(searchStart, unmatchedChunk.length()); if( singleQuoteMatcher.find() ){ match = singleQuoteMatcher.group(1); matchEnd = singleQuoteMatcher.end(1); if( match.equals("'") ){ state.pop(); if( state.peek() == State.DEFAULT ){ logicalLineStructure.append(match); } }else if( isEOL(match) ){ completePhysicalLine = true; }else if( isEscapedEOL(match) ){ completePhysicalLine = true; explicitJoining = true; }else if( isEscapedChar(match) ){ // skip }else{ throw new Error("unexpected match"); } }else{ break SCAN; } break; case IN_DOUBLE_QUOTED_STRING: // end double quoted string, or EOL doubleQuoteMatcher.reset(unmatchedChunk); doubleQuoteMatcher.region(searchStart, unmatchedChunk.length()); if( doubleQuoteMatcher.find() ){ match = doubleQuoteMatcher.group(1); matchEnd = doubleQuoteMatcher.end(1); if( match.equals("\"") ){ state.pop(); if( state.peek() == State.DEFAULT ){ logicalLineStructure.append(match); } }else if( isEOL(match) ){ completePhysicalLine = true; }else if( isEscapedEOL(match) ){ completePhysicalLine = true; explicitJoining = true; }else if( isEscapedChar(match) ){ // skip }else{ throw new Error("unexpected match"); } }else{ break SCAN; } break; case IN_LONG_SINGLE_QUOTED_STRING: // end single quoted long strong, or EOL longSingleQuoteMatcher.reset(unmatchedChunk); longSingleQuoteMatcher.region(searchStart, unmatchedChunk.length()); if( longSingleQuoteMatcher.find() ){ match = longSingleQuoteMatcher.group(1); matchEnd = longSingleQuoteMatcher.end(1); if( match.equals("'''") ){ state.pop(); if( state.peek() == State.DEFAULT ){ logicalLineStructure.append(match); } }else if( isEOL(match) ){ completePhysicalLine = true; }else if( isEscapedEOL(match) ){ completePhysicalLine = true; explicitJoining = true; }else if( isEscapedChar(match) ){ // skip }else{ throw new Error("unexpected match"); } }else{ break SCAN; } break; case IN_LONG_DOUBLE_QUOTED_STRING: // end double quoted long string, or EOL longDoubleQuoteMatcher.reset(unmatchedChunk); longDoubleQuoteMatcher.region(searchStart, unmatchedChunk.length()); if( longDoubleQuoteMatcher.find() ){ match = longDoubleQuoteMatcher.group(1); matchEnd = longDoubleQuoteMatcher.end(1); if( match.equals("\"\"\"") ){ state.pop(); if( state.peek() == State.DEFAULT ){ logicalLineStructure.append(match); } }else if( isEOL(match) ){ completePhysicalLine = true; }else if( isEscapedEOL(match) ){ completePhysicalLine = true; explicitJoining = true; }else if( isEscapedChar(match) ){ // skip }else{ throw new Error("unexpected match"); } }else{ break SCAN; } break; case IN_COMMENT: // search EOL endOfLineMatcher.reset(unmatchedChunk); endOfLineMatcher.region(searchStart, unmatchedChunk.length()); if( endOfLineMatcher.find() ){ match = endOfLineMatcher.group(); matchEnd = endOfLineMatcher.end(); state.pop(); completePhysicalLine = true; }else{ break SCAN; } break; default: throw new Error("This should never happen (probably a bug)"); } Debug.log(9, "matcher=[%s]", match); // add matched input to physical line physicalLine.append(unmatchedChunk .substring(searchStart + explicitJoinOffset, matchEnd)); if( completePhysicalLine ){ physicalLineIndentation = getPhysicalLineIndentation(); // if this is the first physical line of a logical line, set the // logical line indentation if( logicalLineIndentation < 0 ){ logicalLineIndentation = physicalLineIndentation; } } if( explicitJoining ){ // delete backslash-EOL unmatchedChunk.delete(matchEnd - match.length(), matchEnd); matchEnd -= match.length(); // add matched input to logical line (minus input that was already // added) logicalLine.append(unmatchedChunk.substring(searchStart + explicitJoinOffset, matchEnd)); explicitJoinOffset = matchEnd - searchStart; completeLogicalLine = false; // deleting the backslash-EOL effectively merges the current line // with the next line, and we attempt to match it again from the // start nextSearchStart = searchStart; if( matchEnd == unmatchedChunk.length() ){ // no further match is possible until there is new input break SCAN; } }else{ logicalLine.append(unmatchedChunk.substring(searchStart + explicitJoinOffset, matchEnd)); completeLogicalLine = completePhysicalLine && inDefaultState(); explicitJoinOffset = 0; nextSearchStart = matchEnd; } } // end SCAN loop unmatchedChunk.delete(0, nextSearchStart); Debug.log(9, "%s: unmatched: [%s]", state.peek().name(), unmatchedChunk); } /** * Returns the state of the python document seen so far. * * @return the current state */ public State getState(){ return state.peek(); } /** * Returns true if the state of the document seen by this object is not * inside any parenthesis, string or comment. * * @return true if the current state is the default state */ public boolean inDefaultState(){ return state.peek() == State.DEFAULT; } /** * Returns true if the state of the document seen by this object is inside a * parenthesis (including square brackets and curly braces). * * @return true if the current state is inside a parenthesis */ public boolean inParenthesis(){ return state.peek() == State.IN_PARENTHESIS; } /** * Returns true if the state of the document seen by this object is inside a * string (short string or long string). * * @return true if the current state is inside a string */ public boolean inString(){ switch( state.peek() ){ case IN_DOUBLE_QUOTED_STRING: case IN_SINGLE_QUOTED_STRING: case IN_LONG_SINGLE_QUOTED_STRING: case IN_LONG_DOUBLE_QUOTED_STRING: return true; } return false; } /** * Returns true if the state of the document seen by this object is inside a * long string. * * @return true if the current state is inside a long string */ public boolean inLongString(){ return state.peek() == State.IN_LONG_SINGLE_QUOTED_STRING || state.peek() == State.IN_LONG_DOUBLE_QUOTED_STRING; } /** * Returns true if the state of the document seen by this object is inside a * comment. * * @return true if the current state is inside a comment */ public boolean inComment(){ return state.peek() == State.IN_COMMENT; } /** * Returns the nesting level of parentheses and strings that the state of the * document seen by this object is in. The nesting level in the default state * is 0. *

* Note that parentheses can be nested at any depth, but only one level of * string can be nested inside the innermost parentheses because anything * inside a string is not interpreted. * * @return the nesting level of parentheses and strings of the current state */ public int getDepth(){ return state.size() - 1; } /** * Returns a string that represents the structure of the last logical line. * The returned string is identical to the last logical line except that the * contents of any strings or parenthesised expression, and any comment (i.e. * any input text with a nesting level greater than 0), and the trailing * end-of-line character, are deleted. * * * * * * * * * * * * * * * * * * * * * * * * * * *
Examples:
InputStructure
{@code print x}{@code print x}
{@code print 'x'}{@code print ''}
{@code print '%s=%d\n' % ('a', f(x[0]))}{@code print '' % ()}
{@code """a long comment"""}{@code """"""}
{@code if x: pass # case 1}{@code if x: pass #}
* */ public String getLastLogicalLineStructure(){ return logicalLineStructure.toString(); } /** * Returns the last physical line seen by this object, including the * terminating end-of-line sequence. If the last line seen by this object is * not a complete physical line, the return value is undefined. * * @return the last complete physical line seen by this object */ public String getLastPhysicalLine(){ return physicalLine.toString(); } /** * Returns the last logical line seen by this object, including the * terminating end-of-line sequence. If the input seen by this object does * not end with a complete logical line, the return value is guaranteed to * include all complete physical lines seen of which the logical line is * comprised. If explicit line joining has occurred, any escaped end-of-line * sequence is not included in the logical line. * * @return the last complete logical line seen seen by this instance */ public String getLastLogicalLine(){ return logicalLine.toString(); } /** * Returns the physical line number of the last physical line seen by this * object. * * @return the physical line number of the line returned by * {@link #getLastPhysicalLine()} (0-based) */ public int getPhysicalLineNumber(){ return physicalLineNumber; } /** * Returns the logical line number of the last logical line seen by this * object. * * @return the logical line number of the line returned by * {@link #getLastLogicalLine()} (0-based) */ public int getLogicalLineNumber(){ return logicalLineNumber; } /** * Returns the physical line number of the first physical line in the last * logical line seen by this object. * * @return the physical line number of the first physical line in the logical * line returned by {@link #getLastLogicalLine()} (0-based) */ public int getLogicalLinePhysicalStartLineNumber(){ return logicalLinePhysicalStartLineNumber; } /** * Returns whether the last physical line seen by this object is complete. A * physical line is complete if it is terminated by an end-of-line sequence. * * @return true if the line returned by {@link #getLastPhysicalLine()} is * complete */ public boolean isPhysicalLineComplete(){ return completePhysicalLine; } /** * Returns whether the last logical line seen by this object is complete. A * logical line is complete if all of the following are true: *

    *
  • the physical lines that it is comprised of are complete (i.e. it is * terminated by an end-of-line sequence) *
  • it does not end with a physical line that is explicitly joined with * the following line (i.e. the final end-of-line sequence is not preceded by * a backslash, unless the backslash is part of a comment) *
  • it does not contain any open parenthesis or string delimiter without * the matching closing parenthesis or string delimiter *
* * @return true if the line returned by {@link #getLastLogicalLine()} is * complete */ public boolean isLogicalLineComplete(){ return completeLogicalLine; } /** * Returns whether the last physical line seen by this object is explicitly * joined with the following line, i.e. whether its end-of-line sequence is * escaped with a backslash and the backslash is not inside a comment. If the * last physical line seen is not complete, the return value is undefined. * * @return true if the last complete physical line is explicitly joined with * the following line */ public boolean isExplicitLineJoining(){ return explicitJoining; } private int getPhysicalLineIndentation(){ int indentation = 0; for( int i = 0; i < physicalLine.length(); i++ ){ char c = physicalLine.charAt(i); if( c == ' ' ){ indentation++; }else if( c == '\t' ){ indentation += tabsize; }else{ break; } } return indentation; } /** * Returns the indentation (in columns of whitespace) of the last complete * physical line seen by this object. *

* Any tab characters in the leading whitespace of the line are counted as * the equivalent number of blank characters. * * @return the indentation of the last complete physical line * @throws IllegalStateException * if the last physical line is not complete */ public int getLastPhysicalLineIndentation() throws IllegalStateException{ if( !completePhysicalLine ){ throw new IllegalStateException("incomplete physical line"); } return physicalLineIndentation; } /** * Returns the indentation (in columns of whitespace) of the last logical * line seen by this object. This is the indentation of the physical line * which is the first line in the logical line. *

* Any tab characters in the leading whitespace of the line are counted as * the equivalent number of blank characters. * * @return the indentation of the last logical line * @throws IllegalStateException * if the first physical line in the last logical line is not * complete */ public int getLastLogicalLineIndentation() throws IllegalStateException{ if( logicalLineIndentation < 0 ){ throw new IllegalStateException("incomplete logical line"); } return logicalLineIndentation; } /** * Returns the indentation of the previous physical line. * * @return the indentation of the previous physical line * @throws IllegalStateException * if no complete physical line or only one complete physical line * has been seen by this object. */ public int getPrevPhysicalLineIndentation() throws IllegalStateException{ if( prevPhysicalLineIndentation < 0 ){ throw new IllegalStateException("not enough physical lines"); } return prevPhysicalLineIndentation; } /** * Returns the indentation of the previous logical line. * * @return the indentation of the previous logical line * @throws IllegalStateException * if no logical line or only one logical line has been seen by * this instance */ public int getPrevLogicalLineIndentation() throws IllegalStateException{ if( prevLogicalLineIndentation < 0 ){ throw new IllegalStateException("not enough logical lines"); } return prevLogicalLineIndentation; } } sikulix-1.1.1/IDE/src/main/java/org/sikuli/scriptrunner/000077500000000000000000000000001315726130400230665ustar00rootroot00000000000000sikulix-1.1.1/IDE/src/main/java/org/sikuli/scriptrunner/IScriptRunner.java000066400000000000000000000065661315726130400265150ustar00rootroot00000000000000/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.scriptrunner; import java.io.File; /** * Interface for ScriptRunners like Jython. */ public interface IScriptRunner { /** * Can be used to initialize the ScriptRunner. This method is called at the beginning of program * execution. The given parameters can be used to parse any ScriptRunner specific custom options. * * @param args All arguments that were passed to the main-method */ public void init(String[] args); /** * Executes the Script. * * @param scriptfile File containing the script * @param imagedirectory Directory containing the images (might be null: parent of script) * @param scriptArgs Arguments to be passed directly to the script with --args * @return exitcode for the script execution */ public int runScript(File scriptfile, File imagedirectory, String[] scriptArgs, String[] forIDE); /** * Executes the Script as Test. * * @param scriptfile File containing the script * @param imagedirectory Directory containing the images (might be null: parent of script) * @param scriptArgs Arguments to be passed directly to the script with --args * @param forIDE when called from Sikuli IDE additional info * @return exitcode for the script execution */ public int runTest(File scriptfile, File imagedirectory, String[] scriptArgs, String[] forIDE); /** * Starts an interactive session with the scriptrunner. * * @param scriptArgs Arguments to be passed directly to the script with --args * @return exitcode of the interactive session */ public int runInteractive(String[] scriptArgs); /** * Gets the scriptrunner specific help text to print on stdout. * * @return A helping description about how to use the scriptrunner */ public String getCommandLineHelp(); /** * Gets the help text that is shown if the user runs "shelp()" in interactive mode * * @return The helptext */ public String getInteractiveHelp(); /** * Gets the name of the ScriptRunner. Should be unique. This value is needed to distinguish * between different ScriptRunners. * * @return Name to identify the ScriptRunner or null if not available */ public String getName(); /** * returns the list of possible script file endings, first is the default * * @return array of strings */ public String[] getFileEndings(); /** * checks wether this ScriptRunner supports the given fileending * * @return the lowercase fileending */ public String hasFileEnding(String ending); /** * Is executed before Sikuli closes. Can be used to cleanup the ScriptRunner */ public void close(); /** * generic interface to a special runner action * @param action identifies what to do * @param args contains the needed parameters * @return true if successful, false otherwise */ public boolean doSomethingSpecial(String action, Object[] args); /** * add statements to be run after SCRIPT_HEADER, but before script is executed * * @param stmts string array of statements (null resets the statement buffer) */ public void execBefore(String[] stmts); /** * add statements to be run after script has ended * * @param stmts string array of statements (null resets the statement buffer) */ public void execAfter(String[] stmts); } sikulix-1.1.1/IDE/src/main/java/org/sikuli/scriptrunner/JRubyScriptRunner.java000066400000000000000000000442341315726130400273520ustar00rootroot00000000000000/* * Copyright 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * * WhoIsWho 2014 */ package org.sikuli.scriptrunner; //import java.io.File; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jruby.CompatVersion;import org.jruby.Ruby; import org.jruby.RubyInstanceConfig.CompileMode; import org.jruby.RubyProc; import org.jruby.embed.LocalContextScope; import org.jruby.embed.ScriptingContainer; import org.jruby.javasupport.JavaEmbedUtils.EvalUnit; import org.jruby.javasupport.JavaUtil; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.sikuli.basics.Debug; import org.sikuli.basics.FileManager; import org.sikuli.script.RunTime; import org.sikuli.script.Runner; public class JRubyScriptRunner implements IScriptRunner { static RunTime sxRunTime = RunTime.get(); // private static final String me = "JRubyScriptRunner: "; private int lvl = 3; private void log(int level, String message, Object... args) { Debug.logx(level, me + message, args); } // /** * The ScriptingContainer instance */ private static ScriptingContainer interpreter = null; private static int savedpathlen = 0; private static final String COMPILE_ONLY = "# COMPILE ONLY"; /** * sys.argv for the jruby script */ private static ArrayList sysargv = null; /** * The header commands, that are executed before every script */ private final static String SCRIPT_HEADER = "# coding: utf-8\n" + "require 'Lib/sikulix'\n" + "include Sikulix\n"; private static ArrayList codeBefore = null; private static ArrayList codeAfter = null; /** * CommandLine args */ private int errorLine; private int errorColumn; private String errorType; private String errorText; private int errorClass; private String errorTrace; private static final int PY_SYNTAX = 0; private static final int PY_RUNTIME = 1; private static final int PY_JAVA = 2; private static final int PY_UNKNOWN = -1; private static String sikuliLibPath; private boolean isFromIDE; private boolean isCompileOnly; private static Ruby runtime; private static ThreadContext context; @Override public void init(String[] args) { //TODO classpath and other path handlings sikuliLibPath = sxRunTime.fSikulixLib.getAbsolutePath(); } @Override public int runScript(File scriptfile, File imagedirectory, String[] scriptArgs, String[] forIDE) { if (null == scriptfile) { //run the Ruby statements from argv (special for setup functional test) fillSysArgv(null, null); createScriptingContainer(); executeScriptHeader(new String[0]); return runRuby(null, scriptArgs, null); } scriptfile = new File(scriptfile.getAbsolutePath()); fillSysArgv(scriptfile, scriptArgs); createScriptingContainer(); int exitCode = 0; isFromIDE = ! (forIDE == null); if (isFromIDE && forIDE.length > 1 && forIDE[0] != null ) { isCompileOnly = forIDE[0].toUpperCase().equals(COMPILE_ONLY); } if (forIDE == null) { executeScriptHeader(new String[]{ scriptfile.getParentFile().getAbsolutePath(), scriptfile.getParentFile().getParentFile().getAbsolutePath()}); exitCode = runRuby(scriptfile, null, new String[]{scriptfile.getParentFile().getAbsolutePath()}); } else { executeScriptHeader(new String[]{forIDE[0]}); exitCode = runRuby(scriptfile, null, forIDE); } log(lvl + 1, "runScript: at exit: path:"); for (Object p : interpreter.getLoadPaths()) { log(lvl + 1, "runScript: " + p.toString()); } log(lvl + 1, "runScript: at exit: --- end ---"); return exitCode; } @Override public int runTest(File scriptfile, File imagedirectory, String[] scriptArgs, String[] forIDE) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public int runInteractive(String[] scriptArgs) { fillSysArgv(null, scriptArgs); String[] args = null; String[] iargs = {/*"-i", "-c",*/ "require 'irb'\n" + "ScriptRunner.runningInteractive();\n" + "print \"Hello, this is your interactive Sikuli (rules for interactive Ruby apply)\\n" + "use the UP/DOWN arrow keys to walk through the input history\\n" + "help() will output some basic Ruby information\\n" + "... use ctrl-d to end the session\"\n" + "IRB.start(__FILE__)\n" }; if (scriptArgs != null && scriptArgs.length > 0) { args = new String[scriptArgs.length + iargs.length]; System.arraycopy(iargs, 0, args, 0, iargs.length); System.arraycopy(scriptArgs, 0, args, iargs.length, scriptArgs.length); } else { args = iargs; } StringBuilder buffer = new StringBuilder(); for (String e : args) { buffer.append(e); } createScriptingContainer(); executeScriptHeader(new String[0]); interpreter.runScriptlet(buffer.toString()); return 0; } @Override public String getCommandLineHelp() { return "You are using the JRuby ScriptRunner"; } @Override public String getInteractiveHelp() { return "**** this might be helpful ****\n" + "-- execute a line of code by pressing \n" + "-- separate more than one statement on a line using ;\n" + "-- Unlike the iDE, this command window will not vanish, when using a Sikuli feature\n" + " so take care, that all you need is visible on the screen\n" + "-- to create an image interactively:\n" + "img = capture()\n" + "-- use a captured image later:\n" + "click(img)"; } @Override public String getName() { try { Class.forName("org.jruby.embed.ScriptingContainer"); } catch (ClassNotFoundException ex) { return null; } return Runner.RRUBY; } @Override public String[] getFileEndings() { return new String[]{"rb"}; } @Override public String hasFileEnding(String ending) { for (String suf : getFileEndings()) { if (suf.equals(ending.toLowerCase())) { return suf; } } return null; } @Override public void close() { if (interpreter != null) { interpreter.terminate(); interpreter = null; } } @Override public boolean doSomethingSpecial(String action, Object[] args) { if ("redirect".equals(action)) { return doRedirect((PipedInputStream[]) args); } else if ("checkCallback".equals(action)) { return checkCallback(args); } else if ("runLoggerCallback".equals(action)) { return runLoggerCallback(args); } else if ("runObserveCallback".equals(action)) { return runObserveCallback(args); } else if ("runCallback".equals(action)) { return runCallback(args); } else { return false; } } private boolean checkCallback(Object[] args) { log(-1, "checkCallback: no implementation yet"); return false; } private boolean runLoggerCallback(Object[] args) { log(-1, "runLoggerCallback: no implementation yet"); return false; } private boolean runObserveCallback(Object[] args) { if (runtime == null) { runtime = ((RubyProc) args[0]).getRuntime(); context = runtime.getCurrentContext(); } IRubyObject[] pargs; try { pargs = new IRubyObject[] {JavaUtil.convertJavaToRuby(runtime, args[1])}; ((RubyProc) args[0]).call(context, pargs); } catch (Exception ex) { log(-1, "runObserveCallback: jruby invoke: %s\n%s\n%s", args[0].getClass(),args[0], ex.getMessage()); } return true; } private boolean runCallback(Object[] args) { log(-1, "runCallback: no implementation yet"); return false; } @Override public void execBefore(String[] stmts) { if (stmts == null) { codeBefore = null; return; } if (codeBefore == null) { codeBefore = new ArrayList(); } codeBefore.addAll(Arrays.asList(stmts)); } @Override public void execAfter(String[] stmts) { if (stmts == null) { codeAfter = null; return; } if (codeAfter == null) { codeAfter = new ArrayList(); } codeAfter.addAll(Arrays.asList(stmts)); } private int runRuby(File ruFile, String[] stmts, String[] scriptPaths) { int exitCode = 0; String stmt = ""; boolean fromIDE = false; String filename = "