Solved

Add Hyperlink(s) to a RichEdit

Posted on 2002-07-20
13
1,591 Views
Last Modified: 2007-11-27
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
0
Comment
Question by:cyprus106
  • 7
  • 6
13 Comments
 
LVL 1

Expert Comment

by:jemax
ID: 7167813
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
 

Author Comment

by:cyprus106
ID: 7167974
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
 
LVL 1

Expert Comment

by:jemax
ID: 7168575
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
Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 

Author Comment

by:cyprus106
ID: 7169490
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
 
LVL 1

Expert Comment

by:jemax
ID: 7171233
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
 

Author Comment

by:cyprus106
ID: 7171873
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
 
LVL 1

Expert Comment

by:jemax
ID: 7172401
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
 

Author Comment

by:cyprus106
ID: 7172511
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
 
LVL 1

Accepted Solution

by:
jemax earned 200 total points
ID: 7172809
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
 
LVL 1

Expert Comment

by:jemax
ID: 7172829
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
 

Author Comment

by:cyprus106
ID: 7172839
That did it. That was what I was looking for. Your a lifesaver. Thanks for stickin' with it.
0
 

Author Comment

by:cyprus106
ID: 7172856
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
 
LVL 1

Expert Comment

by:jemax
ID: 7173643
Hi,

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

Best regards,
jemax
0

Featured Post

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Fully specialized class template function 21 142
C++ Language error 28 245
computer science syllabus 3 88
boost::uuid crashes 17 11
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

839 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question