PageRenderTime 36ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/Rusty/Common/Keyboard/InputCommand.cs

http://github.com/polyethene/IronAHK
C# | 428 lines | 286 code | 65 blank | 77 comment | 36 complexity | c3e6e9a3ab2398c48b42ce4257238127 MD5 | raw file
  1. using System;
  2. using System.Threading;
  3. using System.Collections.Generic;
  4. using System.Windows.Forms;
  5. namespace IronAHK.Rusty.Common
  6. {
  7. partial class Keyboard
  8. {
  9. // ToDo: Handle IgnoreIAGeneratedInput
  10. // ToDo: Test TimeOut
  11. // ToDo: Test Multithreaded Access to this Singleton
  12. /// <summary>
  13. /// Input Command Handler (Singleton)
  14. /// </summary>
  15. internal class IAInputCommand : Singleton<IAInputCommand>
  16. {
  17. #region Fields
  18. KeyboardHook _keyboardHook;
  19. bool _visible = false;
  20. int _keyLimit = 0;
  21. bool _ignoreBackSpace = false;
  22. bool _recognizeModifiedKeystrockes = false;
  23. string catchedText = "";
  24. Keys endKeyReason = Keys.None;
  25. AbortReason abortReason = AbortReason.Fail;
  26. bool isCatching = false;
  27. object catchingLock = new object();
  28. int? _timeout = null;
  29. bool _ignoreIAGeneratedInput = false;
  30. List<Keys> _endkeys = new List<Keys>();
  31. List<string> _endMatchings = new List<string>();
  32. bool _caseSensitive = false;
  33. bool _findAnywhere = false;
  34. System.Threading.Timer _timeoutTimer;
  35. #endregion
  36. #region Singleton Config Properties
  37. // as long as this Class depends on other non Singleton Objects,
  38. // it must be configured else where
  39. /// <summary>
  40. /// Set the KeyboardHook to use to catch userinput
  41. /// </summary>
  42. public KeyboardHook Hook
  43. {
  44. set
  45. {
  46. if (value == null)
  47. throw new ArgumentException("Hook");
  48. _keyboardHook = value;
  49. }
  50. private get { return _keyboardHook; }
  51. }
  52. #endregion
  53. #region Public Methods
  54. /// <summary>
  55. /// Start Logging Text. Blocks calling Thread until user input meets an abort condition.
  56. /// </summary>
  57. /// <returns>Returns the catched Userinput</returns>
  58. public AbortInformation StartCatching()
  59. {
  60. lock (catchingLock)
  61. {
  62. if (isCatching)
  63. {
  64. throw new NotImplementedException("Pending Input interruption not implemented yet!");
  65. }
  66. Hook.IAKeyEvent += OnKeyPressedEvent;
  67. isCatching = true;
  68. if (TimeOutVal.HasValue)
  69. {
  70. if (_timeoutTimer != null)
  71. _timeoutTimer.Dispose();
  72. _timeoutTimer = new System.Threading.Timer(new System.Threading.TimerCallback(OnTimoutTick));
  73. _timeoutTimer.Change(this.TimeOutVal.Value, Timeout.Infinite);
  74. }
  75. }
  76. while (true)
  77. {
  78. lock (catchingLock)
  79. {
  80. if (!isCatching)
  81. break;
  82. }
  83. Application.DoEvents(); // This is necessary if the StartCatching Method gets called on the Main GUI Thread
  84. Thread.Sleep(2);
  85. }
  86. Hook.IAKeyEvent -= OnKeyPressedEvent; // we no longer need to get notified about keys...
  87. var ret = new AbortInformation(abortReason, endKeyReason, catchedText);
  88. return ret;
  89. }
  90. public void Reset()
  91. {
  92. _visible = false;
  93. _keyLimit = 0;
  94. _ignoreBackSpace = false;
  95. _recognizeModifiedKeystrockes = false;
  96. catchedText = "";
  97. endKeyReason = Keys.None;
  98. abortReason = AbortReason.Fail;
  99. isCatching = false;
  100. catchingLock = new object();
  101. _timeout = 0;
  102. _ignoreIAGeneratedInput = false;
  103. _endkeys.Clear();
  104. _endMatchings.Clear();
  105. _caseSensitive = false;
  106. _findAnywhere = false;
  107. if (_timeoutTimer != null)
  108. _timeoutTimer.Dispose();
  109. }
  110. #endregion
  111. #region Properties
  112. #region Behaviour Properties
  113. /// <summary>
  114. /// A list of Keys which terminates the key catching
  115. /// </summary>
  116. public List<Keys> Endkeys
  117. {
  118. get { return _endkeys; }
  119. }
  120. /// <summary>
  121. /// Normally, the user's input is blocked (hidden from the system).
  122. /// Use this option to have the user's keystrokes sent to the active window.
  123. /// </summary>
  124. public bool Visible
  125. {
  126. get { return _visible; }
  127. set { _visible = value; }
  128. }
  129. /// <summary>
  130. /// Max Keys which are catched. 0 Means no Limit.
  131. /// </summary>
  132. public int KeyLimit
  133. {
  134. get { return _keyLimit; }
  135. set { _keyLimit = value; }
  136. }
  137. /// <summary>
  138. /// Modified keystrokes such as Control-A through Control-Z are recognized and
  139. /// transcribed if they correspond to real ASCII characters.
  140. /// </summary>
  141. public bool RecognizeModifiedKeystrockes
  142. {
  143. get { return _recognizeModifiedKeystrockes; }
  144. set { _recognizeModifiedKeystrockes = value; }
  145. }
  146. /// <summary>
  147. /// Backspace is ignored. Normally, pressing backspace during an Input will remove
  148. /// the most recently pressed character from the end of the string.
  149. ///
  150. /// Note: If the input text is visible (such as in an editor) and the arrow keys or other
  151. /// means are used to navigate within it, backspace will still remove the last character
  152. /// rather than the one behind the caret (insertion point).
  153. /// </summary>
  154. public bool IgnoreBackSpace
  155. {
  156. get { return _ignoreBackSpace; }
  157. set { _ignoreBackSpace = value; }
  158. }
  159. /// <summary>
  160. /// The number of milliseconds to wait before terminating the Input and setting ErrorLevel to the word Timeout.
  161. /// If the Input times out, OutputVar will be set to whatever text the user had time to enter.
  162. ///
  163. /// Null means that there is no TimeOut. (default)
  164. /// </summary>
  165. public int? TimeOutVal
  166. {
  167. get { return _timeout; }
  168. set { _timeout = value; }
  169. }
  170. /// <summary>
  171. /// Strings which force to abort Logging
  172. /// </summary>
  173. public List<string> EndMatches
  174. {
  175. get { return _endMatchings; }
  176. }
  177. /// <summary>
  178. /// Normally, what the user types must exactly match one of the MatchList phrases for a match to occur.
  179. /// Use this option to find a match more often by searching the entire length of the input text.
  180. /// </summary>
  181. public bool FindAnyWhere
  182. {
  183. get { return _findAnywhere; }
  184. set { _findAnywhere = value; }
  185. }
  186. /// <summary>
  187. /// Is MatchList case sensitive?
  188. /// </summary>
  189. public bool CaseSensitive
  190. {
  191. get { return _caseSensitive; }
  192. set { _caseSensitive = value; }
  193. }
  194. /// <summary>
  195. /// Ignore Scripts own Send Input
  196. /// </summary>
  197. public bool IgnoreIAGeneratedInput
  198. {
  199. get { return _ignoreIAGeneratedInput; }
  200. set { _ignoreIAGeneratedInput = value; }
  201. }
  202. #endregion
  203. /// <summary>
  204. /// Is User Input catching running?
  205. /// </summary>
  206. public bool IsBusy
  207. {
  208. get
  209. {
  210. lock (catchingLock)
  211. {
  212. return isCatching;
  213. }
  214. }
  215. }
  216. #endregion
  217. #region Event Handlers
  218. void OnKeyPressedEvent(object sender, IAKeyEventArgs e)
  219. {
  220. if (e.Block || e.Handeled || !e.Down)
  221. return;
  222. lock (catchingLock)
  223. {
  224. if (!isCatching)
  225. return;
  226. }
  227. // Tell how to proceed whit this key
  228. if (!Visible)
  229. e.Block = true;
  230. // Check for Post Abort Conditions
  231. if (PostAbortCondition(e))
  232. {
  233. CatchingDone();
  234. return;
  235. }
  236. // Handle Input
  237. if (e.Keys == System.Windows.Forms.Keys.Back)
  238. {
  239. if (!_ignoreBackSpace)
  240. {
  241. catchedText = catchedText.Substring(0, catchedText.Length - 2);
  242. }
  243. }
  244. else
  245. {
  246. catchedText += e.Typed;
  247. }
  248. // Check for Past Abort Conditions
  249. if (PastAbortCondition(e))
  250. {
  251. CatchingDone();
  252. }
  253. }
  254. public void AbortCatching()
  255. {
  256. abortReason = AbortReason.NewInput;
  257. CatchingDone();
  258. }
  259. void CatchingDone()
  260. {
  261. lock (catchingLock)
  262. {
  263. isCatching = false;
  264. }
  265. }
  266. void OnTimoutTick(object state)
  267. {
  268. var timeoutTimer = (System.Threading.Timer)state;
  269. timeoutTimer.Dispose();
  270. CatchingDone();
  271. }
  272. #endregion
  273. #region Abort Conditions
  274. /// <summary>
  275. /// Checks if we are done with logging.
  276. /// </summary>
  277. /// <param name="e"></param>
  278. /// <returns></returns>
  279. bool PostAbortCondition(IAKeyEventArgs e)
  280. {
  281. if (Endkeys.Contains(e.Keys))
  282. {
  283. endKeyReason = e.Keys;
  284. abortReason = AbortReason.EndKey;
  285. return true;
  286. }
  287. return false;
  288. }
  289. /// <summary>
  290. /// Checks if we are done with logging.
  291. /// </summary>
  292. /// <returns></returns>
  293. bool PastAbortCondition(IAKeyEventArgs e)
  294. {
  295. // Past Condition: Key Limit
  296. if (KeyLimit != 0)
  297. {
  298. if (catchedText.Length >= KeyLimit)
  299. {
  300. abortReason = AbortReason.Max;
  301. return true;
  302. }
  303. }
  304. bool abort = false;
  305. // Past Condition Matchlist
  306. foreach (var match in _endMatchings)
  307. {
  308. if (CaseSensitive)
  309. {
  310. if (!FindAnyWhere && match == catchedText)
  311. abort = true;
  312. if (FindAnyWhere && catchedText.Contains(match))
  313. abort = true;
  314. }
  315. else
  316. {
  317. if (!FindAnyWhere && match.Equals(catchedText, StringComparison.OrdinalIgnoreCase))
  318. abort = true;
  319. if (FindAnyWhere && catchedText.ToLowerInvariant().Contains(match.ToLowerInvariant()))
  320. abort = true;
  321. }
  322. if (abort)
  323. break;
  324. }
  325. if (abort)
  326. abortReason = AbortReason.Match;
  327. return abort;
  328. }
  329. #endregion
  330. }
  331. internal class AbortInformation
  332. {
  333. public AbortReason Reason;
  334. public Keys EndKey;
  335. public string CatchedText;
  336. public AbortInformation()
  337. {
  338. Reason = AbortReason.Fail;
  339. EndKey = Keys.None;
  340. CatchedText = "";
  341. }
  342. public AbortInformation(AbortReason reason, Keys endkey, string catchedText)
  343. {
  344. Reason = reason;
  345. EndKey = endkey;
  346. CatchedText = catchedText;
  347. }
  348. }
  349. /// <summary>
  350. /// Reason why Catching of Userinput was stopped
  351. /// </summary>
  352. internal enum AbortReason : int
  353. {
  354. Success = 0,
  355. Fail = 1,
  356. NewInput = 2,
  357. Max = 3,
  358. Timeout = 4,
  359. Match = 5,
  360. EndKey = 6,
  361. }
  362. }
  363. }