unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
type
TColouredMemo = class(TScrollingWinControl)
private
type
TExtStringList=class(TStringList)
FColouredMemo : TColouredMemo;
function Add(const S: string): Integer; override;
end;
var
FLines : TExtStringList;
FLineSpace: Cardinal;
FIndent : Cardinal;
FContent : TImage;
protected
procedure Paint; override;
procedure SetLines(const AValue: TExtStringList);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Indent : Cardinal read FIndent write FIndent;
property Lines : TExtStringList read FLines write SetLines;
property LineSpace : Cardinal read FLineSpace write FLineSpace;
end;
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
MyMemo: TColouredMemo;
Panel1: TPanel;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
private
public
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
MyMemo:=TColouredMemo.Create(Form1);
MyMemo.Parent:=Form1;
MyMemo.Align:=alClient;
MyMemo.Visible:=True;
MyMemo.AutoScroll:=True;
MyMemo.Font.Name:='Courier New';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
MyMemo.Lines.Add('Some more text');
end;
procedure TForm1.FormShow(Sender: TObject);
begin
MyMemo.Lines.Add('Some'+#$81#$00#$00#$FF+' red'+#$82#$01+' bold'+#$81#$00#$00#$00#$82#$00+' text');
MyMemo.Lines.Add('');
MyMemo.Lines.Add('Some'+#$81#$FF#$00#$00+' blue'+#$82#$02+' italic'+#$81#$00#$00#$00#$82#$00+' text');
MyMemo.Lines.Add('');
MyMemo.Lines.Add('Some'+#$81#$00#$77#$00+' green'+#$82#$03+' bold italic'+#$81#$00#$00#$00#$82#$00+' text');
end;
//--------------------- TColouredMemo definitions -----------------------
{ TColouredMemo.TExtStringList }
function TColouredMemo.TExtStringList.Add(const S: string): Integer;
begin
Result:=inherited Add(S);
if Assigned(FColouredMemo) then FColouredMemo.Invalidate;
end;
{ TColouredMemo }
constructor TColouredMemo.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
//Create the line container
FLines:=TExtStringList.Create;
FLines.FColouredMemo:=Self;
//Create the canvas
FContent:=TImage.Create(Self);
FContent.Parent:=Self;
FContent.Top :=0;
FContent.Left:=0;
FContent.Picture.Bitmap.Width :=ClientWidth;
FContent.Picture.Bitmap.Height:=ClientHeight;
FContent.Width :=ClientWidth;
FContent.Height:=ClientHeight;
FContent.Visible:=True;
//Defaults
FLineSpace:=4; //Space between lines, in pixels
FIndent:=4; //Indent in from the left, in pixels
Color:=$FFFFFF; //Default background colour
end;
destructor TColouredMemo.Destroy;
begin
FLines.Free;
FContent.Free;
inherited Destroy;
end;
procedure TColouredMemo.Paint;
var
LLine,
LIndex,
XPos,
YPos,
W,H : Integer;
LPart,
LText : String;
LRed,
LGreen,
LBlue : Byte;
begin
//First pass, calculate then set the canvas size
if FLines.Count>0 then
begin
//Starting size
W:=ClientWidth;
H:=FLineSpace;
for LLine:=0 to FLines.Count-1 do
begin
LText:='';
if FLines[LLine]='' then LText:=' ' //blank line will give 0 height
else //Remove control characters
for LIndex:=1 to Length(FLines[LLine]) do
if(ord(FLines[LLine][LIndex])>31)and(ord(FLines[LLine][LIndex])<127)then
LText:=LText+FLines[LLine][LIndex];
if FContent.Canvas.TextWidth(LText)+FIndent>W then
W:=FContent.Canvas.TextWidth(LText)+FIndent;
inc(H,FContent.Canvas.TextHeight(LText)+FLineSpace);
end;
//Can't be smaller than the scroll width
if H<ClientHeight then H:=ClientHeight;
//Set the canvas size
FContent.Picture.Bitmap.Width:=W;
FContent.Picture.Bitmap.Height:=H;
FContent.Width:=W;
FContent.Height:=H;
end;
//Clear the background
FContent.Canvas.Brush.Color:=Color;
FContent.Canvas.Brush.Style:=bsSolid;
FContent.Canvas.Pen.Color:=Color;
FContent.Canvas.Pen.Style:=psSolid;
FContent.Canvas.Rectangle(0,0,W,H);
//Are there any lines entered?
if FLines.Count>0 then
begin
//Start at the top
YPos:=FLineSpace;
//Work our way through the lines
for LLine:=0 to FLines.Count-1 do
begin
//Set the font
FContent.Canvas.Font:=Font;
//Default font colour
FContent.Canvas.Font.Color:=Font.Color;
//No styles
FContent.Canvas.Font.Style:=[];
//Transparent background
FContent.Canvas.Brush.Style:=bsClear;
//Get the current line
LText:=FLines[LLine];
//If it is empty, put a space in so we get a blank line
if LText='' then LText:=' ';
//Indent it
XPos:=FIndent;
//Clear the 'part of' string
LPart:='';
//Start at the beginning
LIndex:=1;
while LIndex<=Length(LText) do
begin
//Top bit set? Then this means a change of style or colour
if(ord(LText[LIndex])and$80)=$80 then
begin
//Output what we currently have
if LPart<>'' then
begin
FContent.Canvas.TextOut(XPos,YPos,LPart);
inc(XPos,FContent.Canvas.TextWidth(LPart));
end;
//New part of string
LPart:='';
//Change of colour
if(ord(LText[LIndex])AND$01)=$01 then
begin
//Get the Blue index
inc(LIndex);
if LIndex<=Length(LText) then LBlue :=ord(LText[LIndex]);
//Get the Green index
inc(LIndex);
if LIndex<=Length(LText) then LGreen:=ord(LText[LIndex]);
//Get the Red index
inc(LIndex);
if LIndex<=Length(LText) then LRed :=ord(LText[LIndex]);
//Set the colour
FContent.Canvas.Font.Color:=LRed+LGreen<<8+LBlue<<16;
end;
//Change of style
if(ord(LText[LIndex])AND$02)=$02 then
begin
inc(LIndex);
if LIndex<=Length(LText) then
begin
//Bold
if(ord(LText[LIndex])AND$01)=$01 then
FContent.Canvas.Font.Style:=FContent.Canvas.Font.Style+[fsBold]
else //No Bold
FContent.Canvas.Font.Style:=FContent.Canvas.Font.Style-[fsBold];
//Italic
if(ord(LText[LIndex])AND$02)=$02 then
FContent.Canvas.Font.Style:=FContent.Canvas.Font.Style+[fsItalic]
else //No Italic
FContent.Canvas.Font.Style:=FContent.Canvas.Font.Style-[fsItalic];
end;
end;
end;
//Valid ASCII character? add it to the 'part of' string
if(ord(LText[LIndex])>31)and(ord(LText[LIndex])<127)then
LPart:=LPart+LText[LIndex];
//Next character
inc(LIndex);
end;
//Anything left that hasn't been printed?
if LPart<>'' then FContent.Canvas.TextOut(XPos,YPos,LPart);
//Move the Y pointer downwards
inc(YPos,FContent.Canvas.TextHeight(LText)+FLineSpace);
end;
end;
end;
procedure TColouredMemo.SetLines(const AValue: TExtStringList);
begin
if AValue<>nil then FLines.Assign(AValue);
Invalidate;
end;
end.