cyprus106
asked on
Add Hyperlink(s) to a RichEdit
First off, I'm using BCB5.
I can add URL autodetection functionality to a RichEdit easily, but I want to be able to add a hyperlink, (i.e. when the user clicks on something that says:
Go to experts exchange
then it will go to the url https://www.experts-exchange.com)
How would I go about setting this up?
I've spent the entire day trying and guessing (and failing...) I hope somebody on here has a better idea.
Thanks,
Cyprus
I can add URL autodetection functionality to a RichEdit easily, but I want to be able to add a hyperlink, (i.e. when the user clicks on something that says:
Go to experts exchange
then it will go to the url https://www.experts-exchange.com)
How would I go about setting this up?
I've spent the entire day trying and guessing (and failing...) I hope somebody on here has a better idea.
Thanks,
Cyprus
ASKER
Actually, a sample would be nice. I tried implementing the code based on your comment, but it didn't really like how i did things and I'm pretty sure I didn't do it right at all. If you do have sample code I would appreciate it greatly.
Thanks,
Cyprus
Thanks,
Cyprus
Hi,
here is the sample. I use VC6, but there is no MFC or VC spec. code, I hope. I make the link as [PROT.][HIDDEN][PROT.], because rich edit has a bug which allows deleting protected chars if you make it [P][H]. It is just a sample, so making it safer, faster, unicode etc is up to you.
#define URL_BUF_LEN 512
#define URL_HDR_LEN 4
void CDlg::OnButton1()
{
::SendMessage (m_hRichWnd, EM_SETEVENTMASK, 0 , ENM_LINK|ENM_PROTECTED);
InsertLink (2, "Go to Experts-Exchange", "https://www.experts-exchange.com");
}
BOOL CDlg::InsertLink(int nPos, const char *pszTitle, const char *pszURL)
{
CHARFORMAT2 cf2;;
int nTitleLen, nURLen;
char buf[URL_BUF_LEN];
if (!pszTitle || !pszTitle[0] || !pszTitle[1])
return FALSE; //title too short
memset (&cf2, 0, sizeof(cf2));
cf2.cbSize = sizeof(cf2);
nTitleLen = strlen (pszTitle);
nURLen = strlen (pszURL) + URL_HDR_LEN;
//1st letter+url len+url text+rest of the title
// "G[0018https://www.experts-exchange.com]o to Experts-Exchange"
sprintf (buf, "%c%04X%s%s", *pszTitle, nURLen, pszURL, pszTitle + 1);
//set title text
::SendMessage (m_hRichWnd, EM_SETSEL, nPos, nPos);
::SendMessage (m_hRichWnd, EM_REPLACESEL, 0, (LPARAM)buf);
//apply link style on'G'
::SendMessage (m_hRichWnd, EM_SETSEL, nPos, nPos+1);
cf2.dwMask = CFE_LINK|CFE_PROTECTED;
cf2.dwEffects = CFM_LINK|CFM_PROTECTED;
::SendMessage (m_hRichWnd, EM_SETCHARFORMAT, SCF_SELECTION , (LPARAM)&cf2);
//set url text and hide it
nPos++;
::SendMessage (m_hRichWnd, EM_SETSEL, nPos, nPos+nURLen);
cf2.dwMask = CFE_LINK|CFE_PROTECTED|CFE _HIDDEN;
cf2.dwEffects = CFM_LINK|CFM_PROTECTED|CFM _HIDDEN;
::SendMessage (m_hRichWnd, EM_SETCHARFORMAT, SCF_SELECTION , (LPARAM)&cf2);
//apply link style on the rest of the title
nPos += nURLen;
::SendMessage (m_hRichWnd, EM_SETSEL, nPos, nPos+nTitleLen-1);
cf2.dwMask = CFE_LINK|CFE_PROTECTED;
cf2.dwEffects = CFM_LINK|CFM_PROTECTED;
::SendMessage (m_hRichWnd, EM_SETCHARFORMAT, SCF_SELECTION , (LPARAM)&cf2);
return TRUE;
}
//EN_LINK message handler
void CDlg::OnLink(NMHDR* pNMHDR, LRESULT* pResult)
{
ENLINK *pEnLink = (ENLINK *)pNMHDR;
if (pEnLink->msg == WM_LBUTTONDOWN)
{
TEXTRANGE tr;
int nURLen, nRange;
tr.chrg = pEnLink->chrg;
tr.lpstrText = new TCHAR[tr.chrg.cpMax-tr.chr g.cpMin + 1];
if (!tr.lpstrText)
return;
//get the link text
nRange = ::SendMessage (m_rich.m_hWnd, EM_GETTEXTRANGE, 0 , (LPARAM)&tr);
//get URL length
if (!sscanf (tr.lpstrText + 1, "%04X", &nURLen) ||
nRange < nURLen + 1)
return;
//place zero at the URL's end
tr.lpstrText[nURLen + 1] = 0;
//here is your URL
printf (tr.lpstrText + 1 + URL_HDR_LEN);
delete[] tr.lpstrText;
}
*pResult = 0;
}
//EN_PROTECTED message handler
void CDlg::OnProtected(NMHDR* pNMHDR, LRESULT* pResult)
{
ENPROTECTED *pEnProtected = (ENPROTECTED *)pNMHDR;
//allow copy, but deny link modification
*pResult = pEnProtected->msg != WM_COPY;
}
Best regards,
jemax
here is the sample. I use VC6, but there is no MFC or VC spec. code, I hope. I make the link as [PROT.][HIDDEN][PROT.], because rich edit has a bug which allows deleting protected chars if you make it [P][H]. It is just a sample, so making it safer, faster, unicode etc is up to you.
#define URL_BUF_LEN 512
#define URL_HDR_LEN 4
void CDlg::OnButton1()
{
::SendMessage (m_hRichWnd, EM_SETEVENTMASK, 0 , ENM_LINK|ENM_PROTECTED);
InsertLink (2, "Go to Experts-Exchange", "https://www.experts-exchange.com");
}
BOOL CDlg::InsertLink(int nPos, const char *pszTitle, const char *pszURL)
{
CHARFORMAT2 cf2;;
int nTitleLen, nURLen;
char buf[URL_BUF_LEN];
if (!pszTitle || !pszTitle[0] || !pszTitle[1])
return FALSE; //title too short
memset (&cf2, 0, sizeof(cf2));
cf2.cbSize = sizeof(cf2);
nTitleLen = strlen (pszTitle);
nURLen = strlen (pszURL) + URL_HDR_LEN;
//1st letter+url len+url text+rest of the title
// "G[0018https://www.experts-exchange.com]o to Experts-Exchange"
sprintf (buf, "%c%04X%s%s", *pszTitle, nURLen, pszURL, pszTitle + 1);
//set title text
::SendMessage (m_hRichWnd, EM_SETSEL, nPos, nPos);
::SendMessage (m_hRichWnd, EM_REPLACESEL, 0, (LPARAM)buf);
//apply link style on'G'
::SendMessage (m_hRichWnd, EM_SETSEL, nPos, nPos+1);
cf2.dwMask = CFE_LINK|CFE_PROTECTED;
cf2.dwEffects = CFM_LINK|CFM_PROTECTED;
::SendMessage (m_hRichWnd, EM_SETCHARFORMAT, SCF_SELECTION , (LPARAM)&cf2);
//set url text and hide it
nPos++;
::SendMessage (m_hRichWnd, EM_SETSEL, nPos, nPos+nURLen);
cf2.dwMask = CFE_LINK|CFE_PROTECTED|CFE
cf2.dwEffects = CFM_LINK|CFM_PROTECTED|CFM
::SendMessage (m_hRichWnd, EM_SETCHARFORMAT, SCF_SELECTION , (LPARAM)&cf2);
//apply link style on the rest of the title
nPos += nURLen;
::SendMessage (m_hRichWnd, EM_SETSEL, nPos, nPos+nTitleLen-1);
cf2.dwMask = CFE_LINK|CFE_PROTECTED;
cf2.dwEffects = CFM_LINK|CFM_PROTECTED;
::SendMessage (m_hRichWnd, EM_SETCHARFORMAT, SCF_SELECTION , (LPARAM)&cf2);
return TRUE;
}
//EN_LINK message handler
void CDlg::OnLink(NMHDR* pNMHDR, LRESULT* pResult)
{
ENLINK *pEnLink = (ENLINK *)pNMHDR;
if (pEnLink->msg == WM_LBUTTONDOWN)
{
TEXTRANGE tr;
int nURLen, nRange;
tr.chrg = pEnLink->chrg;
tr.lpstrText = new TCHAR[tr.chrg.cpMax-tr.chr
if (!tr.lpstrText)
return;
//get the link text
nRange = ::SendMessage (m_rich.m_hWnd, EM_GETTEXTRANGE, 0 , (LPARAM)&tr);
//get URL length
if (!sscanf (tr.lpstrText + 1, "%04X", &nURLen) ||
nRange < nURLen + 1)
return;
//place zero at the URL's end
tr.lpstrText[nURLen + 1] = 0;
//here is your URL
printf (tr.lpstrText + 1 + URL_HDR_LEN);
delete[] tr.lpstrText;
}
*pResult = 0;
}
//EN_PROTECTED message handler
void CDlg::OnProtected(NMHDR* pNMHDR, LRESULT* pResult)
{
ENPROTECTED *pEnProtected = (ENPROTECTED *)pNMHDR;
//allow copy, but deny link modification
*pResult = pEnProtected->msg != WM_COPY;
}
Best regards,
jemax
ASKER
OK, so I stuck in your code and did a few very quick conversions to BCB5, checked the code out: works like a charm, It highlights the hyperlink, changes the cursor when necessary... There's just one problem:
It doesn't launch the hyperlink when it's clicked on. In fact, it doesn't do anything when it's clicked on. We're getting there! Am I supposed to stick in a message handler or something in the header to catch the clicking on the URL?
Much Thanks,
>>Cyprus
It doesn't launch the hyperlink when it's clicked on. In fact, it doesn't do anything when it's clicked on. We're getting there! Am I supposed to stick in a message handler or something in the header to catch the clicking on the URL?
Much Thanks,
>>Cyprus
Hi,
in void CDlg::OnLink(...) func, in the the sample, you can see the following 2 lines:
//here is your URL
printf (tr.lpstrText + 1 + URL_HDR_LEN);
(tr.lpstrText + 1 + URL_HDR_LEN) is a ptr to the real URL.
You can replace the 'printf' by
ShellExecute (NULL, "open", tr.lpstrText + 1 + URL_HDR_LEN, NULL, NULL, SW_SHOW); to launch the default browser.
Best regards,
jemax
in void CDlg::OnLink(...) func, in the the sample, you can see the following 2 lines:
//here is your URL
printf (tr.lpstrText + 1 + URL_HDR_LEN);
(tr.lpstrText + 1 + URL_HDR_LEN) is a ptr to the real URL.
You can replace the 'printf' by
ShellExecute (NULL, "open", tr.lpstrText + 1 + URL_HDR_LEN, NULL, NULL, SW_SHOW); to launch the default browser.
Best regards,
jemax
ASKER
OnLink(...) is never called anywhere within the functions, which is probably the reason it's not opening up a browser or even thinking when the link is clicked on. Should I be putting this somewhere or something? Where do I put a call to OnLink?
Hi,
I can think of 3 possible reasons for that:
1. ::SendMessage (m_hRichWnd, EM_SETEVENTMASK, 0 , ENM_LINK|ENM_PROTECTED); has not been invoked before inserting a link.
2. Somewhat is incorrect in the message map.
3. Rich Edit control sends EN_LINK & EN_PROTECTED messages to its parent window, so the handles should be there.
HTH,
jemax
I can think of 3 possible reasons for that:
1. ::SendMessage (m_hRichWnd, EM_SETEVENTMASK, 0 , ENM_LINK|ENM_PROTECTED); has not been invoked before inserting a link.
2. Somewhat is incorrect in the message map.
3. Rich Edit control sends EN_LINK & EN_PROTECTED messages to its parent window, so the handles should be there.
HTH,
jemax
ASKER
but just having OnLink in there wouldn't automatically called when the RichEdit does EN_LINK. Shouldn't there be a message handler, or an actual call to OnLink?
I would think it needs to know that there is an event handler for when it calls EN_LINK...
Do you have the code for the message map somewhere?
Thanks alot, I know this has to be kind of annoying by now. I'll up the points before I accept the answer.
I've got alot of extra I don't need laying around...
I would think it needs to know that there is an event handler for when it calls EN_LINK...
Do you have the code for the message map somewhere?
Thanks alot, I know this has to be kind of annoying by now. I'll up the points before I accept the answer.
I've got alot of extra I don't need laying around...
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Sorry, I forgot to say; do not rely too much on my BCB code, it would be better to check/ask it is good, especially usage of 'DefWindowProc' in BCB.
BTW, now I have working sample in BCB. If you need it I can send it to you.
Best regards,
jemax
BTW, now I have working sample in BCB. If you need it I can send it to you.
Best regards,
jemax
ASKER
That did it. That was what I was looking for. Your a lifesaver. Thanks for stickin' with it.
ASKER
actually, yea if you could send it to me that would be great. this works but theres a few conversion things I did that weren't exactly "proper". my e-mail address is hackcyprus@hotmail.com if you could send me a copy I would be much appreciative
Hi,
ok. I will send it, when I reach the BCB:) computer (several hours, sorry)
Best regards,
jemax
ok. I will send it, when I reach the BCB:) computer (several hours, sorry)
Best regards,
jemax
there is no simple way, in my opinion. RichEdit ctrl provides you drawing of blue underlined text+hand cursor(CFM_LINK) and EN_LINK (EM_SETEVENTMASK : ENM_LINK|ENM_PROTECTED)not
If you want your URL to appear in rich edit, as it is ("www.w.com') just set selection for this piece of text, apply CFM_LINK style, and handle EN_LINK.
If you want to have a "hidden" URL, you could do the following:
1 Insert text like "G[ww.e-e.com]o to experts exchange"
2 Apply CFM_HIDDEN style on 'www.e-e.com' part of your text
3 When handling EN_LINK, you get the CHARRANGE of your entire link text, so you can extract the [] part, and launch the browser.
You can use CFM_PROTECTED style for your URL, to prevent deleting or splitting it.
Caution: do not place the hidden text at the beginning or end of the URL, rich edit has prepared some bugs for this case:)
If you need more info or a sample..., I'll be glad to provide it.
HTH,
jemax