blob: b40c2c09fffe5d1a32377a3f3e8183e8c9775019 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
using System;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace RGWConnector {
/// <summary>
/// Searches GUI Windows of R and send keys.
/// </summary>
public class RHandler {
[DllImport("USER32.DLL", SetLastError=true)]
private static extern bool IsIconic(IntPtr hWnd);
[DllImport("USER32.DLL", SetLastError=true)]
private static extern bool OpenIcon(IntPtr hWnd);
[DllImport("USER32.DLL", SetLastError=true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// [DllImport("USER32.DLL", SetLastError = true)]
// private static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildtAfter, string lpClassName, string lpWindowName);
[DllImport("USER32.DLL", SetLastError=true)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
// [DllImport("USER32.DLL", SetLastError=true)]
// public static extern bool SetActiveWindow(IntPtr hWnd);
// [DllImport("USER32.DLL", SetLastError=true)]
// private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("USER32.DLL", SetLastError = true)]
private static extern uint RealGetWindowClass(IntPtr hWnd, StringBuilder pszType, uint cchType);
[DllImport("USER32.DLL", SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("USER32.DLL", SetLastError = true)]
private static extern int InternalGetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
public delegate bool WndEnumProc(IntPtr hWdn, int lParam);
[DllImport("USER32.DLL", SetLastError = true)]
private static extern bool EnumWindows(WndEnumProc callback, int lParam);
[DllImport("USER32.DLL", SetLastError = true)]
private static extern bool EnumChildWindows(IntPtr hWndParent, WndEnumProc callback, int lParam);
/*
* MDI:
* Main Window Class = Rgui Workspace
* Child Window Class = Rgui Document
*
* SDI:
* Window Class = Rgui
*
* */
private const int M_MDI_CLIENT = 10;
private const int M_MDI_FALLBACK = 11;
private const int M_SDI = 20;
private const int M_PROCESS = 50;
private const int WAIT_MS = 10;
private char[] fEscapeChars;
private string[] fEscapeCharsReplacements;
private IntPtr fWindow = IntPtr.Zero;
private IntPtr fChildWindow = IntPtr.Zero;
private int fMode = -1;
public RHandler() {
// init replacements
fEscapeChars = new char[] {
'+', '^', '%', '~', '(', ')', '[', ']', '{', '}',
};
fEscapeCharsReplacements = new string[fEscapeChars.Length];
for (int i = 0; i < fEscapeChars.Length; i++) {
fEscapeCharsReplacements[i] = "{"+fEscapeChars[i]+"}";
}
}
public void connect() {
// MDI
if (fMode < 0) {
fWindow = FindWindow("Rgui Workspace", null);
if (!fWindow.Equals(IntPtr.Zero)) {
EnumChildWindows(fWindow, new WndEnumProc(checkMDIChildWindow), 0);
if (!fChildWindow.Equals(IntPtr.Zero)) {
fMode = M_MDI_CLIENT;
}
else {
fMode = M_MDI_FALLBACK;
}
}
}
// SDI
if (fMode < 0) {
EnumWindows(new WndEnumProc(checkSDIWindow), 0);
}
// Other UI? Search for processes
if (fMode < 0) {
Process process = getProcess("RGui");
if (process != null) {
fWindow = process.MainWindowHandle;
}
if (!fWindow.Equals(IntPtr.Zero)) {
fMode = M_PROCESS;
}
}
if (fMode > 0) {
focusWindow(fWindow);
if (fMode == M_MDI_CLIENT) {
Thread.Sleep(WAIT_MS);
focusWindow(fChildWindow);
}
}
else {
fMode = 0;
}
}
public bool checkMDIChildWindow(IntPtr hWdn, int lParam) {
StringBuilder text = new StringBuilder(255);
RealGetWindowClass(hWdn, text, 255);
if (!text.ToString().Equals("Rgui Document")) {
return true;
}
text.Remove(0, text.Length);
GetWindowText(hWdn, text, 255);
if (!text.ToString().StartsWith("R Console")) {
return true;
}
fChildWindow = hWdn;
return false;
}
public bool checkSDIWindow(IntPtr hWdn, int lParam) {
StringBuilder text = new StringBuilder(255);
RealGetWindowClass(hWdn, text, 255);
if (!text.ToString().Equals("Rgui")) {
return true;
}
text.Remove(0, text.Length);
GetWindowText(hWdn, text, 255);
if (!text.ToString().StartsWith("R Console")) {
return true;
}
fWindow = hWdn;
fMode = M_SDI;
return false;
}
private void debugWindowClass(IntPtr window) {
if (!IntPtr.Zero.Equals(window)) {
StringBuilder type = new StringBuilder(255);
RealGetWindowClass(window, type, 255);
// Filter
string testR = type.ToString();
if (!testR.StartsWith("R")) return;
StringBuilder text = new StringBuilder(255);
GetWindowText(window, text, 255);
StringBuilder iText = new StringBuilder(255);
InternalGetWindowText(window, iText, 255);
Console.WriteLine();
Console.WriteLine("WindowClass=" + type.ToString());
Console.WriteLine("InternalWindowText=" + iText.ToString());
Console.WriteLine("WindowText=" + text.ToString());
}
}
private void focusWindow(IntPtr window) {
if (IntPtr.Zero.Equals(window)) {
throw new Exception("Unable to Find R-Process/Window (Windows-Error: "+Marshal.GetLastWin32Error()+").");
}
if (IsIconic(window)) {
if (!OpenIcon(window)) {
throw new Exception("Unable to Restore minimized R-Window (Windows-Error: "+Marshal.GetLastWin32Error()+").");
}
}
if (!SetForegroundWindow(window))
throw new Exception("Unable to Activate R-Window (Windows-Error: "+Marshal.GetLastWin32Error()+").");
}
public void submit(string[] text) {
sendKeys(getPrefix());
for (int i = 0; i < text.Length; i++) {
sendKeys(prepareText(text[i]) + "{ENTER}");
}
}
public void sendPasteClipboard() {
string keys = getPrefix() + "^v";
sendKeys(keys);
}
private string getPrefix() {
switch (fMode) {
case M_MDI_FALLBACK:
// Activate Console-Window (Alt-w, 1)
return "%w1";
default:
return null;
}
}
private string prepareText(string text) {
StringBuilder s = new StringBuilder(text);
for (int i = 0; i < s.Length; i++) {
for (int j = 0; j < fEscapeChars.Length; j++) {
if (s[i] == fEscapeChars[j]) {
s.Remove(i, 1);
s.Insert(i, fEscapeCharsReplacements[j]);
i += 2;
break;
}
}
}
return s.ToString();
}
private void sendKeys(String keys) {
if (keys == null) {
return;
}
Thread.Sleep(WAIT_MS);
focusWindow(fWindow);
System.Windows.Forms.SendKeys.Flush();
focusWindow(fWindow);
System.Windows.Forms.SendKeys.SendWait(keys);
}
private Process getProcess(string name) {
name = name.ToLower();
Process[] processes = Process.GetProcesses();
for (int i = 0; i < processes.Length; i++) {
string curName = processes[i].ProcessName;
if (curName != null && curName.ToLower().StartsWith(name)
&& processes[i].MainWindowHandle != IntPtr.Zero)
return processes[i];
}
return null;
}
}
}