Link to home
Start Free TrialLog in
Avatar of Ron_de_Weijze
Ron_de_Weijze

asked on

component for printing header

Hello expert,

I am trying to create a procedure for printing headers above RichEdit text. Found out I could use the code of TCustomRichEdit.Print (overload) in a component based on TRichEdit and add a call to a function that prints the header above the PageRect that RichEdit1 in my unit uses on the Canvas.

When I use this call: ...

    RichEdit1.Print('test');

... then the original TCustomRichEdit.Print method is called from unit ComCtrls and not the adapted copy in the newly installed and used component in my unit.

I cannot understand why the component based on TRichEdit is bypassed toward its ancestor TCustomRichEdit when the code from the latter is copied and changed in (to) the component.

Hopefully the question is stated clear enough.
Help much appreciated!!

Thank you,
Ron dW
Avatar of arnismit
arnismit

Hi ron,

can you put your code of your customized TRichEdit in here ? so i can fix your problem ?

Greetings,
arni
Avatar of Ron_de_Weijze

ASKER

I used code from Zarko Gajic, who deserves lots of credit.

{
Article:

TRichEditURL - hyperlink aware RichEdit

http://delphi.about.com/library/weekly/aa051804a.htm

Full source code of the TRichEditURL Delphi component,
an extension to the standard RichEdit component. The
TRichEditURL automatically recognizes URLs. Whenever the
text in a RichEditURL matches the format of a URL, the
control will display it as a hyperlink - when the link is
clicked an event is raised enabling you to, for example,
open a browser or send an email. The TRichEditURL works
correctly event when placed on a Panel or any other container control.
}

unit RichEditURL;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Controls,
  ComCtrls, ExtCtrls, RichEdit, PropertyForm;

type
  TURLClickEvent = procedure(Sender :TObject; const URL: string) of object;

  TRichEditURL = class(TRichEdit)
  private
    FOnURLClick: TURLClickEvent;
    procedure CNNotify(var Msg: TWMNotify); message CN_NOTIFY;
  protected
    procedure DoURLClick (const URL : string);
    procedure CreateWnd; override;
  public
    procedure Print(const Caption: string);
  published
    property OnURLClick : TURLClickEvent read FOnURLClick write FOnURLClick;
  end;

procedure Register;
 
 
implementation

procedure Register;
begin
  RegisterComponents('delphi.about.com', [TRichEditURL]);
end;


{ TRichEditURL }
procedure TRichEditURL.DoURLClick(const URL : string);
begin
  if Assigned(FOnURLClick) then OnURLClick(Self, URL);
end; (*DoURLClick*)

procedure TRichEditURL.CNNotify(var Msg: TWMNotify);
var
  p: TENLink;
  sURL: string;
begin
  if (Msg.NMHdr^.code = EN_LINK) then
  begin
   p := TENLink(Pointer(Msg.NMHdr)^);
   if (p.Msg = WM_LBUTTONDOWN) then
   begin
    try
     SendMessage(Handle, EM_EXSETSEL, 0, Longint(@(p.chrg)));
     sURL := SelText;
     DoURLClick(sURL);
    except
    end;
   end;
  end;

 inherited;
end; (*CNNotify*)

procedure TRichEditURL.CreateWnd;
var
  mask: Word;
begin
  inherited CreateWnd;

  SendMessage(Handle, EM_AUTOURLDETECT,1, 0);
  mask := SendMessage(Handle, EM_GETEVENTMASK, 0, 0);
  SendMessage(Handle, EM_SETEVENTMASK, 0, mask or ENM_LINK);
end; (*CreateWnd*)

procedure TCustomRichEdit.Print(const Caption: string); overload;
var
  Range: TFormatRange;
  LastChar, MaxLen, LogX, LogY, OldMap: Integer;
  SaveRect, TheRect: TRect;
begin
  FillChar(Range, SizeOf(TFormatRange), 0);
  with Printer, Range do
  begin
    Title := Caption;
    BeginDoc;
    hdc := Handle;
    hdcTarget := hdc;
    LogX := GetDeviceCaps(Handle, LOGPIXELSX);
    LogY := GetDeviceCaps(Handle, LOGPIXELSY);
    if IsRectEmpty(PageRect) then
    begin
      rc.right := PageWidth * 1440 div LogX;
      rc.bottom := PageHeight * 1440 div LogY;
    end
    else begin
      rc.left := PageRect.Left * 1440 div LogX;
      rc.top := PageRect.Top * 1440 div LogY;
      rc.right := PageRect.Right * 1440 div LogX;
      rc.bottom := PageRect.Bottom * 1440 div LogY;
    end;
    rcPage := rc;
    SaveRect := rc;
    LastChar := 0;
    MaxLen := GetTextLen;
    chrg.cpMax := -1;
    // ensure printer DC is in text map mode
    OldMap := SetMapMode(hdc, MM_TEXT);
    SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0);    // flush buffer
    try
      Canvas.Textout(10, 10, ED_Caption.text);
      repeat
        rc := SaveRect;
        chrg.cpMin := LastChar;
        LastChar := SendMessage(Self.Handle, EM_FORMATRANGE, 1, Longint(@Range));
        if (LastChar < MaxLen) and (LastChar <> -1) then
        begin
        NewPage;
        end;
      until (LastChar >= MaxLen) or (LastChar = -1);
      EndDoc;
    finally
      SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0);  // flush buffer
      SetMapMode(hdc, OldMap);       // restore previous map mode
    end;
  end;
end;

end. (* RichEditURL.pas *)


{
********************************************
Zarko Gajic
About.com Guide to Delphi Programming
http://delphi.about.com
email: delphi.guide@about.com
free newsletter: http://delphi.about.com/library/blnewsletter.htm
forum: http://forums.about.com/ab-delphi/start/
********************************************
}
do you use this component only for the printing ???
or also for the URL options ?

greetings,
arni
Hello Arni,

I use it for handling URL references, but I thought why not also use it for the problem at hand. Is that where the error lies?

Thanks,
Ron dW
Update: found out that although the component-unit compiled, the declaration of the added procedure: ...

  public
    procedure Print(const Caption: string); overload;

... missed the 'overload' statement. Don't see why it would compile anyway, using 'install component'. However, now that 'overload' is added, the problem remains.

I have little experience with components and I wonder whether it could be right to have the procedure named ...

procedure TCustomRichEdit.Print(const Caption: string); overload;

... even though it is in the RichEditURL unit. Should "TCustomRichEdit" not be "TRichEditURL"? Anyway, the original in RichEdit, from which the component-code is a copy, is the same and does not specify its printcommand with "TRichEdit" either.

Questions questions..
Points increased.
Points increased to 400.
Update: leaving out the "TCustomRichEdit" out of the procedure name: ...

procedure TCustomRichEdit.Print(const Caption: string); overload;

... does not make a difference. Still, the compiler jumps to procedure TCustomRichEdit.Print(const Caption: string); in unit ComCtrls and not to procedure Print(const Caption: string); overload; in unit RichEditURL.

Still unresolved.


Points increased to 500
Ron, i will check it when i come home! I promise!
you'll have an answer tonight. sorry for the delay, but i was very busy lately.

greetings,
arni
can you try this ?

unit RichEditURL;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Controls,
  ComCtrls, ExtCtrls, RichEdit, printers;

type
  TURLClickEvent = procedure(Sender :TObject; const URL: string) of object;

  TRichEditURL = class(TRichEdit)
  private
    FOnURLClick: TURLClickEvent;
    procedure CNNotify(var Msg: TWMNotify); message CN_NOTIFY;
  protected
    procedure DoURLClick (const URL : string);
    procedure CreateWnd; override;
  public
    procedure Print(const Caption: string); Override;
  published
    property OnURLClick : TURLClickEvent read FOnURLClick write FOnURLClick;
  end;

procedure Register;
 
 
implementation

procedure Register;
begin
  RegisterComponents('delphi.about.com', [TRichEditURL]);
end;


{ TRichEditURL }
procedure TRichEditURL.DoURLClick(const URL : string);
begin
  if Assigned(FOnURLClick) then OnURLClick(Self, URL);
end; (*DoURLClick*)

procedure TRichEditURL.CNNotify(var Msg: TWMNotify);
var
  p: TENLink;
  sURL: string;
begin
  if (Msg.NMHdr^.code = EN_LINK) then
  begin
   p := TENLink(Pointer(Msg.NMHdr)^);
   if (p.Msg = WM_LBUTTONDOWN) then
   begin
    try
     SendMessage(Handle, EM_EXSETSEL, 0, Longint(@(p.chrg)));
     sURL := SelText;
     DoURLClick(sURL);
    except
    end;
   end;
  end;

 inherited;
end; (*CNNotify*)

procedure TRichEditURL.CreateWnd;
var
  mask: Word;
begin
  inherited CreateWnd;

  SendMessage(Handle, EM_AUTOURLDETECT,1, 0);
  mask := SendMessage(Handle, EM_GETEVENTMASK, 0, 0);
  SendMessage(Handle, EM_SETEVENTMASK, 0, mask or ENM_LINK);
end; (*CreateWnd*)

procedure TRichEditURL.Print(const Caption: string);
var
  Range: TFormatRange;
  LastChar, MaxLen, LogX, LogY, OldMap: Integer;
  SaveRect: TRect;
begin
  FillChar(Range, SizeOf(TFormatRange), 0);
  with Printer, Range do
  begin
    Title := Caption;
    BeginDoc;
    hdc := Handle;
    hdcTarget := hdc;
    LogX := GetDeviceCaps(Handle, LOGPIXELSX);
    LogY := GetDeviceCaps(Handle, LOGPIXELSY);
    if IsRectEmpty(PageRect) then
    begin
      rc.right := PageWidth * 1440 div LogX;
      rc.bottom := PageHeight * 1440 div LogY;
    end
    else begin
      rc.left := PageRect.Left * 1440 div LogX;
      rc.top := PageRect.Top * 1440 div LogY;
      rc.right := PageRect.Right * 1440 div LogX;
      rc.bottom := PageRect.Bottom * 1440 div LogY;
    end;
    rcPage := rc;
    SaveRect := rc;
    LastChar := 0;
    MaxLen := GetTextLen;
    chrg.cpMax := -1;
    // ensure printer DC is in text map mode
    OldMap := SetMapMode(hdc, MM_TEXT);
    SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0);    // flush buffer
    try
      Canvas.Textout(10, 10, Caption);
      repeat
        rc := SaveRect;
        chrg.cpMin := LastChar;
        LastChar := SendMessage(Self.Handle, EM_FORMATRANGE, 1, Longint(@Range));
        if (LastChar < MaxLen) and (LastChar <> -1) then
        begin
        NewPage;
        end;
      until (LastChar >= MaxLen) or (LastChar = -1);
      EndDoc;
    finally
      SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0);  // flush buffer
      SetMapMode(hdc, OldMap);       // restore previous map mode
    end;
  end;
end;

end.

good luck
Hi Arni,

I noticed that you added Printers to the Uses. Unfortunately, no luck.

Perhaps my logic is rambling?:

From the running program, by clicking the printbutton, I call the RichEditURL1 control that is on the unit's form and that is the compiled component I just changed using your code.

This is the procedure that the buttonclick calls ...

procedure TUnitForm.FilePrint(Sender: TObject);
var
  presX, presY: Integer;
begin
  inherited;
  with RichEditURL1 do
  begin
  plaintext := true;
  presX := GetDeviceCaps( printer.handle, LOGPIXELSX );
  presY := GetDeviceCaps( printer.handle, LOGPIXELSY );
  end;
  with RichEditURL1.PageRect do
  begin
    left := presX;  // 1 inch left margin
    top  := 3 * presY div 2;  //  1.5 inch top margin
    right := Printer.PageWidth - 3 * presX div 4; // 0.75 inch right margin
    bottom := Printer.PageHeight - presY; // 1 inch bottom margin
  end;
  if PrintDialog.Execute then
    RichEditURL1.Print('test');
end;

... to position the RichEdit text below a location for the header. The header...

Canvas.Textout(10, 10, ED_Caption.text);

... is called from the RichEditURL component itself. Now, the clause in the code above...

RichEditURL1.Print('test')

... is pointing at ...

proc TCustomRichEdit.Print: Procedure(const Caption:string) - ComCtrl.pas (11654)

... while I expect ...

procedure TRichEditURL.Print: Procedure(const Caption: string) - RichEditURL.pas (75)

well thats because i changed more then only the uses clause.

please try the code above.. there was also an override statement missing.

i tried it at home and it worked!!!!

good luck,
arni
Forgot to mention that, sorry.
I have replaced the component completely with your code (just changing the code for the header to its former state) and the outcome is as above.
Like before, the RichEdit text is printed, leaving room for a header, however the header is not printed.

ASKER CERTIFIED SOLUTION
Avatar of arnismit
arnismit

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Arni, still no luck. Do you see a reason why the wrong code could be run? I am still a newbee so expect the impossibly stupid.
Why would the code handling the URL references (underline, turn blue, make clickable) work and the print header code not work?
Could it be the relation between the calling procedure and the compiled component (heredity, encapsulation, inclusive compiling)?
Could it be a path that, just in this case and not in case of URL specifics, finds the ComCtrl sooner than the RichEditURL component?
this part:

 public
    procedure Print(const Caption: string); Override;
  published

and this

procedure TRichEditURL.Print(const Caption: string);
var

should do the trick, please compare the lines with you first code. note the override and the proper classname.
well im wondering too if you have installed this component on the component pallette, let me know, because that is vital for a good use

greetings
arni
it works all fine here :)
so you must do something wrong.

the code i provided last time is fully working
what version of delphi are you using anyways ?, not that that should make a difference, but just asking
Hi Arni,

I use Delphi 7.

You made me doubt whether I had installed the component correctly so I deleted it from the palette page and started all over. Then I found out that in the Install Component dialog the Search path had directories containing old versions of my program. (Perhaps I should not put updated versions in new directories?) Clicking ok opened a window "Package - dclusr.dpr" showing RichEditURL, also with a wrong path. So I corrected this. Now, compiling shows no error.

If there is no error in compiling RichEditURL, should it not be registered and show up again on the component palette? I am now trying to figure out why it doesn't and why it did previously.

Will keep you posted.

Thanks,
Ron.
It turns out that deleting probably just hid the component palette label for RichEditURL. Palette properties allowed to make it visible again. And... it works!

So thank you Arni, for helping me out.
im glad we finally got there,

good luk exploring delphi

if you post any new questions and you want my assistance please be so kind to drop a message in this question
Will do so Arni.
By the way, could I use Dutch?
(You have a Dutch name.)
yes you can speak dutch but thats not allowed in here

greetings,
arni
Hi Arni,

I just put up another question. You asked me to drop a message if I would like your assistance.
https://www.experts-exchange.com/questions/21362610/autocommit-text-in-RichEdit.html

thanks,
Ron