views:

98

answers:

1

I am using the current code to highlight URLs on a TRichEdit:

procedure TForm1.WndProc(var Message: TMessage);
var
  p: TENLink;
  strURL: string;
begin
  if (Message.Msg = WM_NOTIFY) then
  begin
    if (PNMHDR(Message.lParam).code = EN_LINK) then
    begin
      p := TENLink(Pointer(TWMNotify(Message).NMHdr)^);
      if (p.Msg = WM_LBUTTONDOWN) then
      begin
        SendMessage(RichEdit1.Handle, EM_EXSETSEL, 0, Longint(@(p.chrg)));
        strURL := RichEdit1.SelText;
        ShellExecute(Handle, 'open', PChar(strURL), 0, 0, SW_SHOWNORMAL);
      end
    end;
  end;

  inherited;
end;

procedure TForm1.InitRichEditURLDetection;
var
      mask: Word;
begin
      mask := SendMessage(Handle, EM_GETEVENTMASK, 0, 0);
      SendMessage(RichEdit1.Handle, EM_SETEVENTMASK, 0, mask or ENM_LINK);
      SendMessage(RichEdit1.Handle, EM_AUTOURLDETECT, Integer(True), 0);
      form1.RichEdit1.OnChange := form1.RichEdit1Change;
end;

It highlights the URLs, however it prevent my RichEdit1.OnChange from being called. I trying setting again from within WndProc and other approaches but nothing works. The minute I enable the URL highlighter (by calling InitRichEditURLDetection on FormCreate) OnChange stops working.

This is on Delphi 7.

Any suggestions? thanks!

+2  A: 

There is a bug in your code. Replace

mask := SendMessage(Handle, EM_GETEVENTMASK, 0, 0);

with

mask := SendMessage(RichEdit1.Handle, EM_GETEVENTMASK, 0, 0);

Because of this bug, mask will not contain the default event bits of the Rich Edit control, so the Rich Edit control looses these event flags when you EM_SETEVENTMASK; in particular, it will lack the ENM_CHANGE bit.

Update

Sertac Akyuz found yet another show-stopping bug: mask needs to be an integer (which indeed is the result type of SendMessage).

Andreas Rejbrand
thanks, I tried it. I get a eRangeError exception on that line when I try to run it
Jessica
@Jessica: Are you sure? (Maybe a stupid question, but it really should work.)
Andreas Rejbrand
yes: read of address 0000000
Jessica
I tried also with: http://stackoverflow.com/questions/2480173/detect-click-on-url-in-richedit but I get a different exception.
Jessica
@Jessica: Create a new, empty, VCL application. Drop a `TRichEdit` on the form. Call `InitRichEditURLDetection` on `FormCreate` (with my correction, but without the `OnChange := ` part). Override the form's `WndProc` using your original code (which is fine). Set `RichEdit1.OnChange` to something silly like `Caption := IntToStr(length(RichEdit1.Text));` Does it work now?
Andreas Rejbrand
yes, but it doesn't allow me to click on the URL. Nothing happends
Jessica
if I add any other component or if I do point the OnChange to any other procedure then it throws an exception on the line you corrected. Try with Richedit1.OnChange := form1.RichEdit1Change; where RichEdit1Change has Caption := IntToStr(length(RichEdit1.Text));
Jessica
@Jessica: OK. I have created a minimal test project (a *.pas, a *.dfm, a *.dpr, and a compiled *.exe) here: http://privat.rejbrand.se/RichEditURLs.zip. Try to open this project, and see if it works (by doing so we'll find out if that there are any other bugs in your code, hidden somewhere). If you dare, you can even try the compiled *.exe. If it still doesn't work (not even the compiled *.exe), then I'll promise that I'll start believing you!
Andreas Rejbrand
I wish I could post a screenshot. It gives me an exception as well. On the same line. Could this be related only to delphi 7 or windows 7?
Jessica
OK. I run Windows 7, so there is not an issue with the operating system (which generally is very free from bugs). Most likely there is a bug in the implementation of the Rich Edit wrapper in Delphi 7 (or it is an older version of the Rich Edit control). Since I do not have Delphi 7 available, I am afraid that I cannot help you. Sorry. (I assume that the compiled *.exe did work if you tried it?) Nevertheless, the bugthat I pointed out in your original code is very real, so without my correction, it will not work with any Delphi/Windows version!
Andreas Rejbrand
I didn't try the compiled exe. Sorry, as much as I trust you on a good answer I don't trust anyone's exe but those I can compile.
Jessica
ok. I'll accept this answer since you are sure it works... however I am still getting the exception. thanks for the help, i really appreciate it
Jessica
Well, even if I am unable to help you, someone with Delphi 7 might be able to. I will add the tag "Delphi-7" to your question, so that it gets some more attention from other users.
Andreas Rejbrand
@Jessica - `SendMessage` returns a `LRESULT`. You should declare `mask` as such, or a `Longint`.
Sertac Akyuz
@Jessica: Indeed, Sertac has found a second show-stopping bug! `ENM_LINK = 67108864` cannot be stored in a 16-bit word; we need all 32 bits. This explains why you do not get the proper behaviour, but rather "random bugs". For some reason (luck), the bug did not cause any problems on my system. I should have seen it earlier, but better now than never. If you fix my bug **and** Sertac's bug, then it will probably work! (Btw: a `LRESULT`, or a `LongInt`, is simply a normal `integer`, 32-bit and signed.)
Andreas Rejbrand
As a sidenote, I suppose you originally got the code from http://www.scalabium.com/faq/dct0146.htm. Someone should inform them about these bugs.
Andreas Rejbrand
While shaving, I figured out why I did not detect this latter bug. Indeed, we `or ENM_LINK` directly in the call to `SendMessage`, so the high "link" bit need not be saved in `mask`. The effect of this bug, thus, is limited to the nullification of all *other* default bits > 16. In light of this, I find it slightly hard to believe that this is what is causing Jessica's issues, but since it probably is the only bug not yet corrected, I suspect that everything will work for her when this bug is fixed as well.
Andreas Rejbrand
@Andreas - It would also cause the 'eRangeError' Jessica mentions, if range checking is enabled that is.. But then she should be getting the error all the way from the beginning. Who knows?..
Sertac Akyuz
@Sertac: That might be it.
Andreas Rejbrand
@Andreas, @Sertac: great investigative work you guys!
Marjan Venema
Thanks, @Marjan!
Andreas Rejbrand
@Marjan - Yet one wonders if Jessica's code will work.. I know one sometimes wishes if one could see one's desktop.. :)
Sertac Akyuz
Thanks everyone for the tips. I tried setting mask as LRESULT and indeed it stops the exception from happening. however I get now random exceptions when I click on the highlighted URL, even to the point to freezing the app completely.
Jessica
hmmm, maybe I need to remove the range checks when I compile the app...
Jessica