Files
jlibwinapi/src/org/synthuse/SynthuseDlg.java
Edward Jakubowski acf215fe00 Update native bridge to UI Automation for speed and reliability
Native UI Automation bridge redesigned for speed and reliability.  The
bridge library has been renamed to UiaBridge.  Native components use VS
2010 C++ and .Net 4.
2014-04-29 23:33:10 -04:00

473 lines
16 KiB
Java

/*
* Copyright 2014, Synthuse.org
* Released under the Apache Version 2.0 License.
*
* last modified by ejakubowski
*/
package org.synthuse;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JToolBar;
import javax.swing.JSplitPane;
import javax.swing.JRadioButton;
import javax.swing.WindowConstants;
import java.awt.Component;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import java.awt.Dimension;
import javax.swing.JComboBox;
import javax.swing.JScrollPane;
/*
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.RowSpec;
import com.jgoodies.forms.factories.FormFactory;
*/
import com.sun.jna.platform.win32.WinDef.HWND;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JTextPane;
import org.synthuse.Api.User32;
import org.synthuse.DragTarget.dragEvents;
//public class SynthuseDlg extends JDialog {
public class SynthuseDlg extends JFrame {
/**
*
*/
public static String VERSION_STR = "1.1.2";
public static String RES_STR_MAIN_ICON = "/org/synthuse/img/gnome-robots.png";
public static String RES_STR_REFRESH_IMG = "/org/synthuse/img/rapidsvn.png";
public static String RES_STR_TESTIDE_IMG = "/org/synthuse/img/applications-education.png";
public static String RES_STR_HELP_IMG = "/org/synthuse/img/help-3.png";
public static String RES_STR_TARGET_IMG = "/org/synthuse/img/bullseye-logo-th18x18.png";
public static String RES_STR_FIND_IMG = "/org/synthuse/img/edit-find-3.png";
public static String RES_STR_SETACTION_IMG = "/org/synthuse/img/document-new-5.png";
public static String RES_STR_CANCEL_IMG = "/org/synthuse/img/document-close-2.png";
public static List<String> actionListQueue = new ArrayList<String>();
private static final long serialVersionUID = 1L;
private XpathManager.Events xpathEvents = null;
private JPanel contentPane;
private JLabel lblStatus;
private JButton btnRefresh;
private JTextPane textPane;
private JButton btnSetAction;
private JButton btnCancel;
private JButton btnFind;
public static Config config = new Config(Config.DEFAULT_PROP_FILENAME);
private String dialogResult = "";
private String lastDragHwnd = "";
private String lastRuntimeId ="";
private JComboBox<String> cmbXpath;
private JButton btnTestIde;
private TestIdeFrame testIde = null;
private int targetX;
private int targetY;
private UiaBridge uiabridge = new UiaBridge();
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SynthuseDlg frame = new SynthuseDlg();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public SynthuseDlg() {
setIconImage(Toolkit.getDefaultToolkit().getImage(SynthuseDlg.class.getResource(RES_STR_MAIN_ICON)));
//setModal(true);
setTitle("Synthuse");
setBounds(100, 100, 827, 420);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JToolBar toolBar = new JToolBar();
contentPane.add(toolBar, BorderLayout.NORTH);
JRadioButton rdbtnWindows = new JRadioButton("Windows Enumerated Xml");
rdbtnWindows.setSelected(true);
toolBar.add(rdbtnWindows);
Component horizontalStrut = Box.createHorizontalStrut(20);
toolBar.add(horizontalStrut);
JRadioButton rdbtnUrl = new JRadioButton("Url: ");
rdbtnUrl.setEnabled(false);
toolBar.add(rdbtnUrl);
// Group the radio buttons.
ButtonGroup group = new ButtonGroup();
group.add(rdbtnWindows);
group.add(rdbtnUrl);
JComboBox<String> cmbUrl = new JComboBox<String>();
cmbUrl.setEnabled(false);
cmbUrl.setEditable(true);
toolBar.add(cmbUrl);
Component horizontalStrut_1 = Box.createHorizontalStrut(20);
toolBar.add(horizontalStrut_1);
btnRefresh = new JButton(" Refresh ");
btnRefresh.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//WindowsEnumeratedXml wex = new WindowsEnumeratedXml(textPane, lblStatus);
//wex.run();
WindowsEnumeratedXml.getXmlThreaded(textPane, lblStatus);
}
});
btnRefresh.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_REFRESH_IMG)));
toolBar.add(btnRefresh);
Component horizontalStrut_3 = Box.createHorizontalStrut(20);
toolBar.add(horizontalStrut_3);
btnTestIde = new JButton("Test IDE");
btnTestIde.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (testIde == null) {
testIde = new TestIdeFrame();
if (SynthuseDlg.actionListQueue.size() > 0) { // if stuff is already in the queue add it to the test ide
for (String action : SynthuseDlg.actionListQueue) {
testIde.txtTest.append(action + "\n");
}
}
}
testIde.setVisible(true);
}
});
btnTestIde.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_TESTIDE_IMG)));
toolBar.add(btnTestIde);
Component horizontalStrut_2 = Box.createHorizontalStrut(20);
toolBar.add(horizontalStrut_2);
JButton helpBtn = new JButton("Help");
helpBtn.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_HELP_IMG)));
helpBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
String about = "";
about += "Synthuse Version " + VERSION_STR + " create by Edward Jakubowski ejakubowski7@gmail.com\n\n";
about += "System information: \n";
about += " Java version - " + System.getProperty("java.version") + "\n";
about += " Java home - " + System.getProperty("java.home") + "\n";
about += " OS Name - " + System.getProperty("os.name") + "\n";
about += " OS Arch - " + System.getProperty("os.arch") + "\n";
String jclasspath = System.getProperty("java.class.path");
jclasspath = jclasspath.replaceAll(";", ";\n ");
if (!System.getProperty("os.name").startsWith("Windows"))
jclasspath = jclasspath.replaceAll(":", ":\n ");
about += " Java class path - " + jclasspath + "\n\n";
JOptionPane.showMessageDialog(null, about , "About", JOptionPane.QUESTION_MESSAGE);
}
});
toolBar.add(helpBtn);
JSplitPane splitPane = new JSplitPane();
splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
contentPane.add(splitPane, BorderLayout.CENTER);
JPanel panel = new JPanel();
panel.setMinimumSize(new Dimension(20, 35));
splitPane.setLeftComponent(panel);
/*
panel.setLayout(new FormLayout(new ColumnSpec[] {
FormFactory.RELATED_GAP_COLSPEC,
ColumnSpec.decode("left:max(11dlu;default)"),
FormFactory.RELATED_GAP_COLSPEC,
ColumnSpec.decode("left:default"),
FormFactory.RELATED_GAP_COLSPEC,
FormFactory.DEFAULT_COLSPEC,
FormFactory.RELATED_GAP_COLSPEC,
FormFactory.DEFAULT_COLSPEC,
FormFactory.RELATED_GAP_COLSPEC,
FormFactory.DEFAULT_COLSPEC,},
new RowSpec[] {
FormFactory.RELATED_GAP_ROWSPEC,
RowSpec.decode("default:grow"),}));
*/
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.gridwidth = 1;
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(3,3,3,3); // add padding around objects
DragTarget lblTarget = new DragTarget();
lblTarget.setHorizontalAlignment(SwingConstants.CENTER);
lblTarget.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_TARGET_IMG)));
panel.add(lblTarget, c);
btnFind = new JButton("Find");
btnFind.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
String xpathItem = cmbXpath.getSelectedItem().toString();
int matches = XpathManager.nextXpathMatch(xpathItem, textPane, lblStatus, false);
if (matches < 0) //check for an error
return; //don't save bad xpath to combobox
if (config.xpathList == null)
config.xpathList = "";
if (!config.xpathList.contains(xpathItem + "\u00ba")){
config.xpathList += xpathItem + "\u00ba";
refreshDatabinding();
cmbXpath.setSelectedItem(xpathItem);
}
}
});
cmbXpath = new JComboBox<String>();
cmbXpath.setPreferredSize(new Dimension(440, 20));//fix the width of the combobox
cmbXpath.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");//fix the width of the combobox
cmbXpath.setEditable(true);
cmbXpath.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ENTER) {
btnFind.doClick();
}
}
});
c.gridwidth = 3;
c.gridx = 1;
panel.add(cmbXpath, c);
btnFind.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_FIND_IMG)));
c.gridwidth = 1;
c.gridx = 4;
panel.add(btnFind, c);
btnSetAction = new JButton("Set Action");
btnSetAction.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
CommandPopupMenu menu = new CommandPopupMenu();
final String xpathItem = cmbXpath.getSelectedItem().toString();
menu.events = new CommandPopupMenu.menuEvents() {
@Override
public void menuItemClicked(String command, int paramCount, boolean useXpath, ActionEvent e) {
String actionStr = CommandPopupMenu.buildSkeletonCommand(command, paramCount, xpathItem, useXpath);
SynthuseDlg.actionListQueue.add(actionStr);
lblStatus.setText("Setting Action: " + actionStr);
if (testIde != null)
testIde.txtTest.append(actionStr + "\n");
}
};
Component c = (Component) e.getSource();
menu.show(c, -1, c.getHeight());
}
});
btnSetAction.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_SETACTION_IMG)));
c.gridwidth = 1;
c.gridx = 5;
panel.add(btnSetAction, c);
btnCancel = new JButton("Cancel");
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
dialogResult = "";
SynthuseDlg.this.disposeWindow();
}
});
btnCancel.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_CANCEL_IMG)));
c.gridwidth = 1;
c.gridx = 6;
panel.add(btnCancel, c);
JScrollPane scrollPane = new JScrollPane();
splitPane.setRightComponent(scrollPane);
textPane = new JTextPane();
textPane.setEditable(false);
textPane.setEditorKitForContentType("text/xml", new XmlEditorKit());
textPane.setContentType("text/xml");
scrollPane.setViewportView(textPane);
JPanel panel_1 = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel_1.getLayout();
flowLayout.setAlignment(FlowLayout.LEFT);
panel_1.setMinimumSize(new Dimension(20, 20));
contentPane.add(panel_1, BorderLayout.SOUTH);
lblStatus = new JLabel("Status:");
lblStatus.setHorizontalAlignment(SwingConstants.LEFT);
lblStatus.setVerticalAlignment(SwingConstants.TOP);
panel_1.add(lblStatus);
xpathEvents = new XpathManager.Events() {
@Override
public void statusChanged(String status) {
lblStatus.setText(status);
}
@Override
public void executionCompleted(Object input, String results) { //buildXpathStatementThreaded finished, with results being the xpath statement
if (input instanceof HWND) { // in case thread takes long time to process
lastDragHwnd = Api.GetHandleAsString((HWND)input);
}
XpathManager.nextXpathMatch(results, textPane, lblStatus, true);
cmbXpath.setSelectedItem(results);
}
};
lblTarget.events = new dragEvents() {
public void dragStarted(JComponent c) {
//might be nice to minimize this window, if we weren't displaying realtime information about each window
}
public void dragMouseMoved(int x, int y) {
targetX = x;
targetY = y;
targetDragged();
}
public void dropped(JComponent c) {
lastDragHwnd = ""; //sometimes with multithreaded the order becomes incorrect, so we may want to refresh this on dropped.
targetDragged();
}
};
this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent arg0) {
KeyboardHook.stopGlobalKeyboardHook(); //stop keyboard hook
config.save();
SynthuseDlg.this.dispose(); // force app to close
}
});
KeyboardHook.addKeyEvent(KeyEvent.VK_3, true, true, false);// refresh xml when CTRL+SHIFT+3 is pressed
//add global hook and event
KeyboardHook.StartGlobalKeyboardHookThreaded(new KeyboardHook.KeyboardEvents() {
@Override
public void keyPressed(KeyboardHook.TargetKeyPress target) {
//System.out.println("target key pressed " + target.targetKeyCode);
if (target.targetKeyCode == KeyEvent.VK_3){
btnRefresh.doClick();
}
}
});
btnRefresh.doClick();
refreshDatabinding();
}
/*
@Override
public void setVisible(final boolean visible) {
// make sure that frame is marked as not disposed if it is asked to be visible
if (visible) {
//setDisposed(false);
}
// let's handle visibility...
if (!visible || !isVisible()) { // have to check this condition simply because super.setVisible(true) invokes toFront if frame was already visible
super.setVisible(visible);
}
// ...and bring frame to the front.. in a strange and weird way
if (visible) {
int state = super.getExtendedState();
state &= ~JFrame.ICONIFIED;
super.setExtendedState(state);
super.setAlwaysOnTop(true);
super.toFront();
super.requestFocus();
super.setAlwaysOnTop(false);
}
}
*/
public String showDialog() {
//this.setModal(true);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation(dim.width/2-this.getSize().width/2, dim.height/2-this.getSize().height/2);
this.setVisible(true);
return dialogResult;
}
public void refreshDatabinding() {
if (config.xpathList != null)
cmbXpath.setModel(new DefaultComboBoxModel<String>(config.xpathList.split("\u00ba")));
if (config.xpathHightlight != null)
XmlEditorKit.TAG_HIGHLIGHTED = config.xpathHightlight;
}
public void targetDragged() {
HWND hwnd = Api.GetWindowFromPoint(new Point(targetX,targetY));
String handleStr = Api.GetHandleAsString(hwnd);
String classStr = WindowsEnumeratedXml.escapeXmlAttributeValue(Api.GetWindowClassName(hwnd));
String parentStr = Api.GetHandleAsString(User32.instance.GetParent(hwnd));
String enumProperties = uiabridge.getWindowInfo(targetX, targetY, WindowInfo.UIA_PROPERTY_LIST);
String runtimeId = WindowInfo.getRuntimeIdFromProperties(enumProperties);
lblStatus.setText("rid:" + runtimeId + " class: " + classStr + " hWnd: " + handleStr + " parent: " + parentStr + " X,Y: " + targetX + ", " + targetY);
if (!lastDragHwnd.equals(handleStr) || !lastRuntimeId.equals(runtimeId)) {
if (!lastDragHwnd.isEmpty()) {
Api.refreshWindow(Api.GetHandleFromString(lastDragHwnd));
}
lastDragHwnd = handleStr;
lastRuntimeId = runtimeId;
//lastDragHwnd = (hwnd + "");
Api.highlightWindow(hwnd);
XpathManager.buildXpathStatementThreaded(hwnd, enumProperties, textPane, xpathEvents);
}
}
public void disposeWindow()
{
WindowEvent closingEvent = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(closingEvent);
}
}