How to autodetect urls in RichEdit 2.0? - c++

When we have a RichEdit control and send it an EM_AUTOURLDETECT message with WPARAM set to TRUE, it nicely hightlights the detected URLs and sends the EN_LINK notifications.
But it does this only for text that is entered into the control. I haven't found the way to do it for text that's loaded into the control with SetWindowText or EM_STREAMIN.
Please help! Thanks
Upd:
I've created a test application from scratch and it works fine there. I think the problem might be that I have superclassed the control, that is, created a new window class and just use the window procedure of the original class. I'm gonna try subclassing the control instead..

I just knocked up a basic WTL dialog based app containing a riched20 control and the following works fine:
CRichEditCtrl richedit = GetDlgItem(IDC_RICHEDIT);
richedit.SetAutoURLDetect(TRUE);
richedit.SetWindowText(_T("http://www.stackoverflow.com"));
I have some old MFC code that does something similar, albeit with ES_STREAM, and it works OK too.
FWIW the WTL CRichEditCtrl wrapper is pretty thin. SetAutoURLDetect simply calls SendMessage passing it EM_AUTOURLDETECT.
I am compiling with _RICHEDIT_VER set to 0x0200 FWIW.

Without knowing the format of the text you are trying to add to the control with SetWindowText and EM_STREAMIN I'm going to take a guess and say this might have something to do with the control's text mode. After setting the contents of the control try sending it a EM_GETTEXTMODE message and see if the TM_PLAINTEXT bit is set. If this is the case then try sending a EM_SETTEXTMODE message followed by EM_AUTOURLDETECT. Your code should look something like this:
UINT textmode = (UINT)::SendMessage(handle_to_control, EM_GETTEXTMODE, 0, 0);
if(textmode & TM_PLAINTEXT) {
textmode &= ~TM_PLAINTEXT; // Clear the TM_PLAINTEXT bit
textmode |= TM_RICHTEXT; // Set the TM_RICHTEXT bit
if(::SendMessage(handle_to_control, EM_SETTEXTMODE, textmode, 0) != 0) {
// Failed to set the text mode
}
}
::SendMessage(handle_to_control, EM_AUTOURLDETECT, TRUE, 0);

You might just have to rewrite the text to the control to get it to re-parse.

Related

Creating HyperLink in Notepad(textEdit)[MFC]

I am building a textEdit application with MFC. Is there a way to create a hyperlink automatically when a user write web address? It's like when you write a web address "www.google.com" the application detects web address and create a hyperlink right away. I have searched documents that explains about this, but couldn't find it..
and i couldn't make it..
i already have made notepad but i couldn't add the function of hyperlink on the notepad.
the following sentences are functions of hyperlink.
Clicking the text needs to open a browser window to the location specified by the text.
The cursor needs to change from the standard arrow cursor to a pointing index finger when it moves over the control.
The text in the control needs to be underlined when the cursor moves over the control.
A hyperlink control needs to display text in a different color—black just won't do.
The features that I added are:
5.A hyperlink control once visited needs to change color.
6.The hyperlink control should be accessible from the keyboard.
7.It should install some kind of hooks to allow the programmer to perform some actions when the control has the focus or when the cursor is hovering over the control.
Among the functions, What I mostly want to complete is the first one.
If I click a Hyperlink text, it should be linked to a browser window on the Internet.
Please answer and help me. Thanks.
Just use a CRichEditCtrl control (remember to call AfxInitRichEdit2 in your InitInstance). Call SetAutoURLDetect. Done.
Unfortunately this is not enough to make it work. It will display text that resembles URL as blue underlined but it will not invoke the link.
This will have to be handled by additional code. This will set needed event mask:
long lMask = m_RichEditCtrl.GetEventMask();
m_RichEditCtrl.SetEventMask(lMask | ENM_LINK);
m_RichEditCtrl.SetAutoURLDetect();
Also reflected EN_LINK will has to be handled to follow the link. For example:
void CHyperLinkInEditView::OnEnLink(NMHDR *pNMHDR, LRESULT *pResult)
{
ENLINK *p_Link = reinterpret_cast<ENLINK *>(pNMHDR);
if(p_Link && p_Link->msg == WM_LBUTTONDOWN)
{
//int iRange = m_RichEditCtrl.GetTextRange(p_enLinkInfo->chrg.cpMin, p_enLinkInfo->chrg.cpMax);
m_RichEditCtrl.SetSel(p_Link->chrg);
CString szLinkString = m_RichEditCtrl.GetSelText ();
ShellExecute(m_hWnd, L"Open", szLinkString, NULL, NULL, SW_MAXIMIZE);
}
*pResult = 0;
}
All of the above will solve requirement 1, 2, 3 (partially –text is underlined always), and 4.
I do not quite understand 5, 6 and 7.
Could you elaborate?

How to implement the mouse click for URLs at rich edit control

I added a read-only rich edit 2.0 control to my dialog (code is using C windows API, the dialog is created by using function DialogBox)
At the dialog call back, at the WM_INITDIALOG, I add the following code to enable url detection and also enable the event ENM_LINK is sent to the parent dialog instead of the rich edit control itself:
LRESULT mask = SendMessage(hWndText, EM_GETEVENTMASK, 0, 0); //hWndText is rich edit control
SendMessage(hWndText, EM_SETEVENTMASK, 0, mask | ENM_LINK);
::SendMessage(hWndText, EM_AUTOURLDETECT, TRUE, NULL);
I had a little trouble to enable the url detection when dialog is initially launched (which seems a known issue or behavior since rich edit control would only enable url detection of modified text). However I worked around this issue by setting the dialog text again on every WM_PAINT event.
The code is generally working. I also implemented the following code to launch the URL at the browser when mouse is hovering over the url:
case WM_NOTIFY:
plink = (ENLINK *) lParam;
switch(LOWORD(wParam))
{
case IDC_DISPLAY_TEXT_2: //this is ID for my rich edit control
szURL =m_strDisplay.Mid(plink->chrg.cpMin, plink->chrg.cpMax - plink->chrg.cpMin);
LaunchURL(szURL); //function to launch the url with default browser
break;
default:
break;
}
It seems that I would get WM_NOTIFY event every time when I hovered the mouse over the url. However when I clicked on it, I always get same event as the mouse hover over.
Based on the structure of ENLINK, I should get more detailed NM event at the NMHDR structure, however the value plink->nmhdr.code is always 1803 which is not even NM_HOVER (its defined value is (NM_FIRST-13) and NM_FIRST is (0U- 0U), so NM_HOVER value is 4294967283 on my 64 bit machine). I know that I am missing something here. Could someone shed some lights here? How can I get the mouse click event for the rich edit control?
I think you should capture the EN_LINK notification. I implemented the following code. It enables a url link in a richedit control placed into the parent window, not into a dialog. You could adapt it for your dialog, as well.
Consider beginning with the code:
case WM_NOTIFY: {
switch (((LPNMHDR)lParam)->code) { //NMHDR structure contains information about a notification message.
case EN_LINK: {
ENLINK *enLinkInfo = (ENLINK *)lParam; // pointer to a ENLINK structure
then, if you choose to launch url on LBUTTONUP, you have to check the value contained in enLinkInfo->msg (remember to adapt it for your dialog, though)
if (enLinkInfo->msg == WM_LBUTTONUP) {
// select all the text from enLinkInfo->chrg.cpMin to enLinkInfo->chrg.cpMax
// lauch the url
}
Besides, you can intercept WM_MOUSEMOVE:
if(enLinkInfo->msg == WM_MOUSEMOVE) {
; // do nothing
}
Hope it helps.
As the answer by #A_nto2 shows, to intercept a mouse click do:
case WM_NOTIFY: {
//NMHDR structure contains information about a notification message.
switch (((LPNMHDR)lParam)->code) {
case EN_LINK: {
ENLINK *enLinkInfo = (ENLINK *)lParam; // pointer to a ENLINK structure
if (enLinkInfo->msg == WM_LBUTTONUP) {
But the tricky part is to get the link that was clicked on.
One gets a "range" that was clicked on in the enLinkInfo->chrg of the type CHARRANGE.
An answer to Detect click on URL in RichEdit suggests using the EM_EXSETSEL with the enLinkInfo->chrg. And then using the EM_GETSELTEXT to retrieve the text.
That works with auto-detected plain-text URLs (EM_AUTOURLDETECT).
A problem is with friendly name hyperlinks (i.e. those that have an anchor text different than the URL itself):
{\rtf1{\field{\*\fldinst{ HYPERLINK "https://www.example.com"}}{\fldrslt{Example}}}}
(Note that these are supported in Rich Edit 4.1 and newer only)
For these, the CHARRANGE points to the HYPERLINK "https://www.example.com" part, which is hidden and cannot be selected using the EM_EXSETSEL. Actually it can be selected on Windows 10. But it cannot be selected on Windows 7, Vista and XP. Sending the EM_EXSETSEL to these systems results in selecting a zero-length block just after the hidden part.
So either you have to go back in the rich edit buffer and scan for the link; or use another method to retrieve the clicked text.
In my case, as I have small texts only in the rich edit, I've used the WM_GETTEXT. It returns a plain-text version of the rich edit document, but with the friendly name hyperlinks preserved in this form:
HYPERLINK "https://www.example.com" Example
The CHARRANGE points to the URL, strangely including the leading quote: ("https://www.example.com).
But the indexes correspond to a text with a single-character (LF) line separators. While the WM_GETTEXT returns the CRLF separators. So you have to convert the text to the LF before extracting the URL using the CHARRANGE.
According to the documentation of EM_AUTOURLDETECT, you are supposed to get an EN_LINK notification, which should be reflected in the nmhdr.code. According to Google,
#define EN_LINK 0x70B
which is 7 * 256 + 11 = 1750 + 42 + 11 = 1803.
Please note that your code misses a check for nmhdr.code == EN_LINK.
I'm not sure if the control sends NM_HOVER messages at all.

Setting wxTE_PASSWORD later

I created two wxTextCtrl. One for log in (loginTxt) and another for password (pwdTxt) and both have readable default message.
I also installed wxEVT_LEFT_DOWN event so that when user click on either loginTxt or pwdTxt the default message will be set to empty string
Is it possible to set wxTE_PASSWORD style to the pwdTxt later? If it's possible, how can I do that?
I read wx.chm and it say,
"Note that alignment styles (wxTE_LEFT, wxTE_CENTRE and wxTE_RIGHT) can be changed dynamically after control creation on wxMSW and wxGTK. wxTE_READONLY, wxTE_PASSWORD and wrapping styles can be dynamically changed under wxGTK but not wxMSW. The other styles can be only set during control creation.".
I am writing my application on MS Windows with wxWidgets 2.9.3
You cannot change it later on Windows, since Microsoft's control does not support that. If you really need to, I suggest creating 2 different controls and show/hide the appropriate one.
Windows-only solution, probably will be useful:
void Sample::OnBUTTONClick( wxCommandEvent& event )
{
#if defined(__WXMSW__)
HWND hWnd = (HWND)m_Text->GetHandle();
SendMessage(hWnd, EM_SETPASSWORDCHAR, 0x25cf, 0); // 0x25cf is ● character
m_Text->Update();
#endif
}

Maximized Window Restores to Full Screen

Using CWnd::ShowWindow(SW_SHOWMAXIMIZED) maximizes my app window as expected.
However, when clicking the restore button on the app (or double clicking the title-bar), the restored size is the same size as the maximized window, which is confusing for the user.
Using this alternative code has the same problem:
WINDOWPLACEMENT wndpl;
GetWindowPlacement(&wndpl);
wndpl.showCmd = SW_SHOWMAXIMIZED;
SetWindowPlacement(&wndpl);
How can I keep the default un-maximized size when restoring.
I've solved my problem, and the solution might solve yours too. My problem was that even though I called SetWindowPlacement(&wndpl) within CMainFrame::OnCreate the window was not properly restored if it was maximized. I added two lines of code before SetWindowPlacement, and now it works as expected.
CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
// Obtain wndpl, maybe from registry
AfxGetApp()->m_nCmdShow = wndpl.showCmd;
wndpl.showCmd = SW_SHOW;
SetWindowPlacement(&wndpl);
}
These two lines helps underlying code not to mess things up when calling ActivateFrame, which calls ShowWindow with parameter obtained from CWinApp::m_nCmdShow.
All information are in the file with extension .RC. I never used a Maximize/Restore procedures though you should look for a 'DIALOGEX' for the same window. You can change it using any editor (notepad, ultraedit etc.)

Reading from a text field in another application's window

Is there a way for a Windows application to access another applications data, more specifically a text input field in the GUI, and grab the text there for processing in our own application?
If it is possible, is there a way to "shield" your application to prevent it?
EDIT: The three first answers seem to be about getting the another applications window title, not a specific text input field in that window.
I'm no Windows API expert, so could you be more exact how do I find a certain text field in that window, what are the prerequisites for it (seems like knowing a window handle something is required, does it require knowing the text field handle as well? How do I get that? etc...)
Code snippets in C++ really would be really appreciated. MSDN help is hard to browse since Win32-API has such horrible naming conventions.
Completed! See my answer below for a how-to in C++.
For reading text content from another application's text box you will need to get that text box control's window handle somehow. Depending on how your application UI is designed (if it has a UI that is) there are a couple of different ways that you can use to get this handle. You might use "FindWindow"/"FindWindowEx" to locate your control or use "WindowFromPoint" if that makes sense. Either way, once you have the handle to the text control you can send a "WM_GETTEXT" message to it to retrieve its contents (assuming it is a standard text box control). Here's a concocted sample (sans error checks):
HWND hwnd = (HWND)0x00310E3A;
char szBuf[2048];
LONG lResult;
lResult = SendMessage( hwnd, WM_GETTEXT, sizeof( szBuf ) / sizeof( szBuf[0] ), (LPARAM)szBuf );
printf( "Copied %d characters. Contents: %s\n", lResult, szBuf );
I used "Spy++" to get the handle to a text box window that happened to be lying around.
As for protecting your own text boxes from being inspected like this, you could always sub-class your text box (see "SetWindowLong" with "GWL_WNDPROC" for the "nIndex" parameter) and do some special processing of the "WM_GETTEXT" message to ensure that only requests from the same process are serviced.
OK, I have somewhat figured this out.
The starting point is now knowing the window handle exactly, we only know partial window title, so first thing to do is find that main window:
...
EnumWindows((WNDENUMPROC)on_enumwindow_cb, 0);
...
which enumerates through all the windows on desktop. It makes a callback with each of these window handles:
BOOL CALLBACK on_enumwindow_cb(HWND hwndWindow, LPARAM lParam) {
TCHAR wsTitle[2048];
LRESULT result;
result = SendMessage(hwndWindow, WM_GETTEXT, (WPARAM) 2048, (LPARAM) wsTitle);
...
and by using the wsTitle and little regex magic, we can find the window we want.
By using the before mentioned Spy++ I could figure out the text edit field class name and use it to find wanted field in the hwndWindow:
hwndEdit = FindWindowEx(hwndWindow, NULL, L"RichEdit20W", NULL);
and then we can read the text from that field:
result = SendMessage(hwndEdit, WM_GETTEXT, (WPARAM) 4096, (LPARAM) wsText);
I hope this helps anyone fighting with the same problem!
Look at AutoHotkey. If you need an API for your application, look at their sources.
To prevent it, use a custom widget instead of WinForms, MFC or Win32 API. That is not foolproof, but helps.
Yes it is possible in many ways (one way is to use WINAPI GetWindow and GetWindowText).
First, get a handle to the textbox you want to retrieve text from (using FindWindow, EnumChildWindows and other APIs), then:
Old VB6-codeexample, declaration of API:
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Code to extract text:
Dim MyStr As String
MyStr = String(GetWindowTextLength(TextBoxHandle) + 1, Chr$(0))
GetWindowText TextBoxHandle, MyStr, Len(MyStr)
MsgBox MyStr
About how to shield the application to prevent it, you could do many things.
One way would be to have a own control to handle text input that build up the text from lets say a couple of labels placed where the text would be, or that draws the text graphically.
You can also get text from a richedit control with EM_GETTEXTRANGE message, but it works only in the same process in which the control was created.