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 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
cyprus106Asked:
Who is Participating?

Improve company productivity with a Business Account.Sign Up

x
 
jemaxConnect With a Mentor Commented:
Hi,

I am not familiar to BCB too much. I checked the issue out on my coworker's computer, it seems you should write something like this:

in your unit1.h, add to the class (TForm1, assuming it is the form where you richedit resides) declaration
protected:
void __fastcall OnNotify(TMessage &Message);

        BEGIN_MESSAGE_MAP
        MESSAGE_HANDLER(WM_NOTIFY, TMessage, OnNotify)
        END_MESSAGE_MAP(TControl)

in your unit1.cpp
void __fastcall TForm1::OnNotify(TMessage &Message)
{
        NMHDR *pNMHDR;
        pNMHDR = (NMHDR *)Message.LParam;

        switch (pNMHDR->code)
        {
                case EN_LINK :
                        OnLink (pNMHDR, (LRESULT *)&Message.Result);
                        break;

                case EN_PROTECTED :
                        OnProtected (pNMHDR, (LRESULT *)&Message.Result);
                        break;

                default :
                        Message.Result = DefWindowProc (this->Handle, WM_NOTIFY, Message.WParam, Message.LParam);
        }
}

if you have more than 1 richedits, and you need different EN_LINK processing, check pNMHDR->idFrom, that is control id of the rich which sent the WM_NOTIFY.

Best regards,
jemax
0
 
jemaxCommented:
Hi,

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)notification when user clicks on it, all the rest is on your own.
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
0
 
cyprus106Author Commented:
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
0
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

 
jemaxCommented:
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", "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[0018www.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.chrg.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



0
 
cyprus106Author Commented:
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
0
 
jemaxCommented:
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
0
 
cyprus106Author Commented:
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?
0
 
jemaxCommented:
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
0
 
cyprus106Author Commented:
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...
0
 
jemaxCommented:
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
0
 
cyprus106Author Commented:
That did it. That was what I was looking for. Your a lifesaver. Thanks for stickin' with it.
0
 
cyprus106Author Commented:
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
0
 
jemaxCommented:
Hi,

ok. I will send it, when I reach the BCB:) computer (several hours, sorry)

Best regards,
jemax
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.