tags:

views:

53

answers:

3

I'm writing a text entry app that sits in the System Tray and assists in text entry via keyboard hooks. The current character the user types is determined by previous letters.

So if you have "I want to ben|" where "|" represents the caret, when the user hits the keyboard the next letter is determined by doing calculation on the previous letters of the word, "ben". It could either turn out to be 'd' > "bend" or 't' > "bent", etc.

This works fine if I'm starting each word from scratch, I can just keep track of the word in a String in my program.

The problem arises when I set the caret down in the middle of a word. I need to detect what characters come before the caret position. The situation also occurs if I press the arrow keys, etc.

In a past AutoHotkey app I did this by sending a "Shift + Ctrl + Left Arrow" command to highlight the characters, a "Ctrl + C" command to copy it, and then saved the clipboard into my String. I'm hoping that in a more robust language like C# I can avoid the ugly highlighting artifacts of this technique. Is there a way to get the characters programatically?

Note that this must occur in any text field across apps, so I can't get the characters from the text field itself (unless there's a way to access the currently in-focus text field).

What do you think?

A: 

You will most likely have to programatically figure out the ID's of the text boxes that are being edited, and then send a Windows message (via PostMessage or SendMessage calls) to retrieve the text of the text field. I forget what message constant was needed to retrieve text but there is a way using the Windows API.

A C# enum of the messages you can try to send to windows apps can be found here.

Also, I would suggest debugging with a program like Spy++ or Winspector spy.

cmw
This won't work in general, because an application doesn't have to use Win32 TEXTBOX control for that. E.g. Qt, Gtk and WPF applications all use their own textbox controls. In fact, they don't even have HWNDs...
Pavel Minaev
+1  A: 

So with a traditional .NET TextBox, you can tell the caret position by using the SelectionStart property. So what I would do is detect when the text changes and see where the caret is. Here's some quick sample code you can adapt to your situation:

// first find the caret position
int caretPosition = MyTextBox.SelectionStart;

// now find all text that occurs before the caret
string textUpToCaret = MyTextBox.Text.Substring(0, caretPosition);

// now we can look for the last space character in the
// textUpToCaret to find the word
int lastSpaceIndex = textUpToCaret.LastIndexOf(" ");

if (lastSpaceIndex < 0)
    lastSpaceIndex = 0;

string lettersBeforeCaret = textUpToCaret.Substring(lastSpaceIndex);
Scott Anderson
This would be from a textbox in my own app's form, right? Is there a way to extend this to do the same in ANY app's text field, whatever currently has focus? That's the more difficult problem I'm facing.
cksubs
Sadly, no. The problem is that you don't know what technology the textbox you're interacting with was implemented in or if it even has hooks for you to get at the text. Suppose it was a GTK application, for example, that has it's own TextBox implementation.
Scott Anderson
+3  A: 

There is no foolproof way to get the "current textbox". Any application can roll out its own, so you don't know what to look for. And if an application won't cooperate on such things, you can't really force it. Well, I guess you can always try to capture a screenshot and try to do OCR on it, but that's not very realistic.

However, an application written using a widget toolkit of decent quality (or using stock controls) should support Microsoft Active Accessibility, which does provide a standardized way to access the UI of that application - or rather, some abstract view of that UI, which is optimized for extracting information from it by tools such as screen readers. Sounds like what you'd want, except that I do not know any way to retrieve information about text preceding the selection (a caret is just a 0-width selection) from a control using MSAA.

Pavel Minaev
Thanks I will look into this.
cksubs