delphieventsurlmessagerichedit

Detect click on URL in RichEdit


I am trying to update RichEdit so that it detects URL and enables clicking on it to open in the browser. Detecting URL is easy, I just use the following code from http://www.scalabium.com/faq/dct0146.htm

mask := SendMessage(MNote.Handle, EM_GETEVENTMASK, 0, 0);
  SendMessage(MNote.Handle, EM_SETEVENTMASK, 0, mask or ENM_LINK);
  SendMessage(MNote.Handle, EM_AUTOURLDETECT, Integer(True), 0); 

but the second part doesn't work for me. They give the following code to capture EN_LINK message and processing it:

type
  TForm1 = class(TForm)
  protected
    procedure WndProc(var Message: TMessage); override;
  end;
...

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;

When I run the program, URL is detected, but clicking on it doesn't do anything. Using debug I found out that Message.Msg = WM_NOTIFY is not true when I click on URL. I then tried to override TRichEdit's WndProc, but result is the same. Any suggestions?


Solution

  • Subclass the RichEdit's WindowProc property and look for the CN_NOTIFY message, for example:

    type
      TForm1 = class(TForm)
        RichEdit1: TRichEdit;
        procedure FormCreate(Sender: TObject);
      private
        PrevRichEditWndProc: TWndMethod;
        procedure RichEditWndProc(var Message: TMessage);
        procedure SetRichEditMasks;
      end; 
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      PrevRichEditWndProc := RichEdit1.WindowProc;
      RichEdit1.WindowProc := RichEditWndProc;
      SetRichEditMasks;
    end;
    
    procedure TForm1.SetRichEditMasks;
    var
      mask: Longint;
    begin
      mask := SendMessage(RichEdit1.Handle, EM_GETEVENTMASK, 0, 0); 
      SendMessage(RichEdit1.Handle, EM_SETEVENTMASK, 0, mask or ENM_LINK); 
      SendMessage(RichEdit1.Handle, EM_AUTOURLDETECT, 1, 0);  
    end;
    
    procedure TForm1.RichEditWndProc(var Message: TMessage); 
    begin 
      PrevRichEditWndProc(Message);
      case Message.Msg of
        CN_NOTIFY:
          begin
            if (TWMNotify(Message).NMHdr^.code = EN_LINK) then
            begin
              with PENLink(Message.LParam)^ do
              begin
                if (msg = WM_LBUTTONDOWN) then
                begin 
                  SendMessage(RichEdit1.Handle, EM_EXSETSEL, 0, LongInt(@chrg)); 
                  ShellExecute(Handle, 'open', PChar(RichEdit1.SelText), 0, 0, SW_SHOWNORMAL); 
                end;
              end;
            end;
          end;
        CM_RECREATEWND:
          begin
            SetRichEditMasks;
          end;
      end; 
    end;