• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1822
  • Last Modified:

hyperlink from a CRichEditCtrl

I like to enable the hyperlink functionality from within a CRichEditCtrl.

Problem is, that the CRichEditView class isn't notified in CRichEditView::OnNotify(..) function

Do you know what's missing, to receive the notification ?

i figured out that there's no message handler needed, because, OnNotify() is virtual...?

Is there a difference using provided editctrl of view class (using GetRichEditCtrl())
or having a member CRichEditCtrl myRichEdit ?

Please see also code below. Thanks!
protected members:
virtual void					OnInitialUpdate (void);
virtual BOOL					OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
 
void CMyRichEditView::OnInitialUpdate ()
{
	CRichEditView::OnInitialUpdate ();
 
	GetRichEditCtrl().SetEventMask(GetRichEditCtrl().GetEventMask() | ENM_SELCHANGE | ENM_LINK);
	GetRichEditCtrl().SetAutoURLDetect(TRUE);
	GetRichEditCtrl().SetReadOnly();
}
 
BOOL CMyRichEditView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// have a breakpoint here, but never stepped in !!
	ENLINK* link = (ENLINK*)lParam;
 
    if(link && link->msg == WM_LBUTTONUP)
    {
		CString s;
		GetRichEditCtrl().GetTextRange(link->chrg.cpMin, link->chrg.cpMax, s);
		ShellExecute(m_hWnd, "Open", s.GetBuffer(0), NULL, NULL, SW_SHOW);
    }
    return CRichEditView::OnNotify(wParam, lParam, pResult);
}

Open in new window

0
stev75
Asked:
stev75
  • 6
  • 5
1 Solution
 
ZoppoCommented:
Hi stev75,

to handle this within the control's class you need to use notify-reflection (since in general notification messages are sent from a control to it's parent - to handle a notification within a control itself there's a technique called notify-reflection).

To do so you can add a '=EN_LINK' event handler via ClassManager/ClassProperties or you create it manually by adding code like this:

// in header
class CMyRichEditView : ...
{
 ...
public:
      afx_msg void OnEnLink(NMHDR *pNMHDR, LRESULT *pResult);
};

// in .cpp
BEGIN_MESSAGE_MAP(CMyRichEditView, CRichEditView)
...
      ON_NOTIFY_REFLECT(EN_LINK, &CMyRichEditView::OnEnLink)
END_MESSAGE_MAP()

void CMyRichEditView::OnEnLink(NMHDR *pNMHDR, LRESULT *pResult)
{
      ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);

// Just a sample showing a message box with the name of the clicked link
      if ( pEnLink->msg == WM_LBUTTONDOWN )
      {
            CString strLink, strMsg;
            GetRichEditCtrl().GetTextRange( pEnLink->chrg.cpMin, pEnLink->chrg.cpMax, strLink );

            strMsg.Format( "Clicked link: '%s'.", strLink );

            AfxMessageBox( strMsg );
      }

      *pResult = 0;
}

Hope that helps,

ZOPPO
0
 
stev75Author Commented:
Hi, message reflection works. But somehow the GetTextRange gives me just one single letter ('h') but the range of chrg.cpMin to chrg.cpMax is always correct. Then, proceeding, i get a heap damage. I used exactly your code from above.
So the output in Messagebox is
 Clicked link: h
This is probably another issue of GetRichEditCtrl().GetTextRange()
Do you have an idea why it fails ? Besides, the message reflection itself works fine.
 
0
 
ZoppoCommented:
Hi stev75,

that's strange - I tested my sample (I generated a default MFC MDI application with a CRichEditCtrl in VS 2008) and wasn't able to reproduce this.

I guess it could be a ANSI/MBCS/UNICODE related problem - which VS-version do you use? It maybe i.e. in VC++ 6.0 it might be a problem.

I found one hint in the web that it might be the _RICHEDIT_VER macro is declared wrong - you could try to define it before you include anything else in 'stdafx.h' as

#define _RICHEDIT_VER 0x0210

Hope that helps,

ZOPPO
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
stev75Author Commented:

Hi,
i get the same error (only one letter)  (which seems the 'h' of 'http', using this broken down code see below. It's weird and has maybe nothing to do with notification, so I think of raising another question.
- i get valid ranges of cpMin and Max, and a size of 51
- calling Sendmessage fills the string with only 'h'
- deleting the  tr.lpstrText causes heap damage !
I'm using the CRichEditView in a splitter pane.

if ( pEnLink->msg == WM_LBUTTONDOWN )
	{		
                  TEXTRANGE tr;
		memset(&tr, 0, sizeof(tr));
		tr.chrg.cpMin = pEnLink->chrg.cpMin;
		tr.chrg.cpMax = pEnLink->chrg.cpMax;
		const int size = tr.chrg.cpMax - tr.chrg.cpMin;
		if(size > 0)
		{
			tr.lpstrText = new char[size];
 
			GetRichEditCtrl().SendMessage( EM_GETTEXTRANGE, 0, (LPARAM)&tr);
 
			AfxMessageBox(tr.lpstrText);
 
			if(tr.lpstrText) delete tr.lpstrText;
 
			//CString strLink, strMsg;
			//GetRichEditCtrl().GetTextRange( pEnLink->chrg.cpMin, pEnLink->chrg.cpMax, strLink );
 
			//strMsg.Format( "Clicked link: '%s'.", strLink );
 
			//AfxMessageBox( strMsg );
		}
	}

Open in new window

0
 
ZoppoCommented:
ok, I still guess it's a UNICODE issue - you could try to set a breakpoint in the line with 'AfxMessageBox' and then check the memory pointed to by 'tr.lpstrText' with a 'memory'-window - I guess you'll find something like 'h', 0x0, 't', 0x0 a.s.o.

If so it seems as if your application doesn't use UNICODE but the RichEditCtrl does (which would be strange at all, don't know if this is possible ...).

ZOPPO
0
 
stev75Author Commented:
Thanks, that's it. I'm using RICHEDIT50W to have the error-free table display (20A is really bad!)
I use it for viewing, so text is put in by StreamIn function. And it works in a non-UNICODE app.
Is it possible to use a "50A" version , or do I need to change back to "20A" ?
Or can I read the string by
What is the best version generally to use a richeditctrl ? I don't want to use the really ugly "20A" control, which has lots of functions, that simply do not work.
i use this in PreCreateWindow

if(bIsWindowsXPorLater)
             cs.lpszClass = TEXT("RICHEDIT50W");
else
             cs.lpszClass = TEXT("RICHEDIT20A");
 
0
 
ZoppoCommented:
Hm - I'm not sure how exactly this can be done since in my test app I can't for any reason use RICHEDIT50W.

BTW, I think you should be able to retrieve the string as UNICODE and convert it to ANSI/ASCII somehow like this:

TEXTRANGEW tr;
memset(&tr, 0, sizeof(tr));
tr.chrg.cpMin = pEnLink->chrg.cpMin;
tr.chrg.cpMax = pEnLink->chrg.cpMax;
const int size = tr.chrg.cpMax - tr.chrg.cpMin;
if(size > 0)
{
 tr.lpstrText = new WCHAR[size];
 
 GetRichEditCtrl().SendMessage( EM_GETTEXTRANGE, 0, (LPARAM)&tr);

 CString strText;
 strText.GetBufferSetLength( size ) = CW2A( tr.lpstrText );
 strText.ReleaseBuffer();
 
 AfxMessageBox( strText );
 
 if(tr.lpstrText) delete tr.lpstrText;
...

For the 'CW2A' macro it may be you need to include <atlconv.>.

Hope that works, as told it's untested ...

ZOPPO
0
 
stev75Author Commented:
thanks, now the linking works - I used your code in a bit different way, because the GetBufferSetLength() seems to work different from your code.
but the deleteion of the WCHAR fails for some reason. If I don't need to delete it, why ?
Thanks,
stev

if (pEnLink->msg == WM_LBUTTONDOWN)
	{
		int size = pEnLink->chrg.cpMax - pEnLink->chrg.cpMin;
		if(size > 0)
		{
			TEXTRANGEW tr;
			tr.chrg.cpMin = pEnLink->chrg.cpMin;
			tr.chrg.cpMax = pEnLink->chrg.cpMax;
			
			WCHAR* lpszWChar = new WCHAR[size];
			
			tr.lpstrText = lpszWChar;
 
			GetRichEditCtrl().SendMessage(EM_GETTEXTRANGE, 0, (LPARAM)&tr);
 
			CString sLink = CW2A(tr.lpstrText);
 
			ShellExecute(m_hWnd, "Open", sLink.GetBuffer(0), NULL, NULL, SW_SHOW);
 
			delete []lpszWChar;
		}
	}

Open in new window

0
 
stev75Author Commented:
btw RICHEDIT50W is supported when loading "msftedit.dll". a description is in microsoft richedit.h header. I'm using VS2008.
0
 
stev75Author Commented:
found out, had to adjust memory allocation..
below is the working code.
Thanks very much!
Stev

ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);
 
	if (pEnLink->msg == WM_LBUTTONUP)
	{
		int size = pEnLink->chrg.cpMax - pEnLink->chrg.cpMin;
		if(size > 0)
		{
			// get text from unicode control!
			TEXTRANGEW tr;
			tr.chrg.cpMin = pEnLink->chrg.cpMin;
			tr.chrg.cpMax = pEnLink->chrg.cpMax;
			
			WCHAR* lpszWChar = new WCHAR[size + 1]; // + 1 for null character
			tr.lpstrText = lpszWChar;
 
			GetRichEditCtrl().SendMessage(EM_GETTEXTRANGE, 0, (LPARAM)&tr);
			ShellExecute(m_hWnd, "Open", CW2A(tr.lpstrText), NULL, NULL, SW_SHOW);
 
			delete lpszWChar;
		}
	}
	*pResult = 0;

Open in new window

0
 
ZoppoCommented:
Sorry, I was in a longer meeting - I'm glad you found the solution yourself :o)

Thanks,

have a nice day,

best regards,

ZOPPO
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 6
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now