mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 03:58:01 +02:00
352 lines
9.5 KiB
ObjectPascal
352 lines
9.5 KiB
ObjectPascal
{
|
|
*****************************************************************************
|
|
This file is part of the Lazarus Component Library (LCL)
|
|
|
|
See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
for details about the license.
|
|
*****************************************************************************
|
|
}
|
|
unit GtkFontCache;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, FPCAdds, LCLProc, LCLType, AVL_Tree, gtkdef,
|
|
{$IFDEF Gtk1}
|
|
gdk,
|
|
{$ELSE}
|
|
glib2, gdk2, pango,
|
|
{$ENDIF}
|
|
LCLResCache;
|
|
|
|
type
|
|
TGtkFontCacheDescriptor = class;
|
|
|
|
{ TGtkFontCacheItem }
|
|
|
|
TGtkFontCacheItem = class(TResourceCacheItem)
|
|
public
|
|
GtkFont: TGtkIntfFont;
|
|
|
|
// metrics
|
|
MetricsValid: boolean;
|
|
lBearing: LongInt;
|
|
rBearing: LongInt;
|
|
TextMetric: TTextMetric;
|
|
IsDoubleByteChar: boolean;
|
|
IsMonoSpace: boolean;
|
|
procedure WarnReferenceHigh; override;
|
|
end;
|
|
|
|
|
|
{ TGtkFontCacheDescriptor }
|
|
|
|
TGtkFontCacheDescriptor = class(TResourceCacheDescriptor)
|
|
public
|
|
LogFont: TLogFont;
|
|
LongFontName: string;
|
|
{$IFDEF Gtk1}
|
|
xlfd: string;
|
|
{$ELSE}
|
|
PangoFontDescription: PPangoFontDescription;
|
|
{$ENDIF}
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
|
|
{ TGtkFontCache
|
|
Notes:
|
|
Each font can be used by several Device Contexts.
|
|
Each font can have several font descriptors.
|
|
A font descriptor has one font.
|
|
}
|
|
|
|
TGtkFontCache = class(TResourceCache)
|
|
protected
|
|
procedure RemoveItem(Item: TResourceCacheItem); override;
|
|
public
|
|
constructor Create;
|
|
function CompareItems(Tree: TAvlTree; Item1, Item2: Pointer): integer; override;
|
|
function CompareDescriptors(Tree: TAvlTree; Desc1, Desc2: Pointer): integer; override;
|
|
function FindGtkFont(TheGtkFont: TGtkIntfFont): TGtkFontCacheItem;
|
|
function FindGtkFontDesc(const LogFont: TLogFont;
|
|
const LongFontName: string): TGtkFontCacheDescriptor;
|
|
function FindADescriptor(TheGtkFont: TGtkIntfFont): TGtkFontCacheDescriptor;
|
|
function Add(TheGtkFont: TGtkIntfFont; const LogFont: TLogFont;
|
|
const LongFontName: string): TGtkFontCacheDescriptor;
|
|
function AddWithoutName(TheGtkFont: TGtkIntfFont): TGtkFontCacheDescriptor;
|
|
procedure Reference(TheGtkFont: TGtkIntfFont);
|
|
procedure Unreference(TheGtkFont: TGtkIntfFont);
|
|
procedure DumpDescriptors;
|
|
end;
|
|
|
|
function LogFontToString(const LogFont: TLogFont): string;
|
|
|
|
procedure ReferenceGtkIntfFont(AFont: TGtkIntfFont);
|
|
procedure UnreferenceGtkIntfFont(AFont: TGtkIntfFont);
|
|
|
|
var
|
|
FontCache: TGtkFontCache;
|
|
|
|
implementation
|
|
|
|
uses
|
|
LazUtilities;
|
|
|
|
type
|
|
TLogFontAndName = record
|
|
LogFont: TLogFont;
|
|
LongFontName: string;
|
|
end;
|
|
PLogFontAndName = ^TLogFontAndName;
|
|
|
|
function LogFontToString(const LogFont: TLogFont): string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=''
|
|
+' lfFaceName="'+LogFont.lfFaceName+'" '
|
|
+' CharSet='+dbgs(LogFont.lfCharSet)
|
|
+' ClipPrecision='+dbgs(LogFont.lfClipPrecision)
|
|
+' Escapement='+dbgs(LogFont.lfEscapement)
|
|
+' Height='+dbgs(LogFont.lfHeight)
|
|
+' Italic='+dbgs(LogFont.lfItalic)
|
|
+' Orientation='+dbgs(LogFont.lfOrientation)
|
|
+' OutPrecision='+dbgs(LogFont.lfOutPrecision)
|
|
+' PitchAndFamily='+dbgs(LogFont.lfPitchAndFamily)
|
|
+' Quality='+dbgs(LogFont.lfQuality)
|
|
+' StrikeOut='+dbgs(LogFont.lfStrikeOut)
|
|
+' Underline='+dbgs(LogFont.lfUnderline)
|
|
+' Weight='+dbgs(LogFont.lfWeight)
|
|
+' Width='+dbgs(LogFont.lfWidth)
|
|
+#13#10;
|
|
for i:=0 to SizeOf(LogFont)-1 do
|
|
Result:=Result+HexStr(ord(PChar(@LogFont)[i]),2);
|
|
Result:=Result+#13#10;
|
|
end;
|
|
|
|
procedure ReferenceGtkIntfFont(AFont: TGtkIntfFont);
|
|
begin
|
|
{$IFDEF Gtk1}
|
|
gdk_font_ref(AFont);
|
|
{$ELSE}
|
|
//DebugLn(['ReferenceGtkIntfFont ',dbgs(AFont)]);
|
|
g_object_ref(AFont);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure UnreferenceGtkIntfFont(AFont: TGtkIntfFont);
|
|
begin
|
|
{$IFDEF Gtk1}
|
|
gdk_font_unref(AFont);
|
|
{$ELSE}
|
|
//DebugLn(['UnreferenceGtkIntfFont ',dbgs(AFont)]);
|
|
g_object_unref(AFont);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TGtkFontCache }
|
|
|
|
function CompareGtkFontWithResItem(Font: TGtkIntfFont;
|
|
Item: TGtkFontCacheItem): integer;
|
|
begin
|
|
Result := ComparePointers(Font, Item.GtkFont);
|
|
end;
|
|
|
|
function CompareLogFontAndNameWithResDesc(Key: PLogFontAndName;
|
|
Desc: TGtkFontCacheDescriptor): integer;
|
|
begin
|
|
Result:=CompareStr(Key^.LongFontName,Desc.LongFontName);
|
|
//debugln('CompareLogFontAndNameWithResDesc A ',Key^.LongFontName,' ',Desc.LongFontName,' ',DbgS(Desc),' Result=',Result);
|
|
if Result=0 then
|
|
Result:=CompareMemRange(@Key^.LogFont,@Desc.LogFont,SizeOf(Desc.LogFont));
|
|
//debugln('CompareLogFontAndNameWithResDesc END Result=',Result);
|
|
end;
|
|
|
|
procedure TGtkFontCache.RemoveItem(Item: TResourceCacheItem);
|
|
begin
|
|
UnreferenceGtkIntfFont(TGtkFontCacheItem(Item).GtkFont);
|
|
inherited RemoveItem(Item);
|
|
end;
|
|
|
|
constructor TGtkFontCache.Create;
|
|
begin
|
|
inherited Create;
|
|
FResourceCacheItemClass:=TGtkFontCacheItem;
|
|
FResourceCacheDescriptorClass:=TGtkFontCacheDescriptor;
|
|
end;
|
|
|
|
function TGtkFontCache.CompareItems(Tree: TAvlTree; Item1, Item2: Pointer): integer;
|
|
begin
|
|
Result:=ComparePointers(TGtkFontCacheItem(Item1).GtkFont,
|
|
TGtkFontCacheItem(Item2).GtkFont);
|
|
end;
|
|
|
|
function TGtkFontCache.CompareDescriptors(Tree: TAvlTree; Desc1, Desc2: Pointer): integer;
|
|
var
|
|
Descriptor1: TGtkFontCacheDescriptor;
|
|
Descriptor2: TGtkFontCacheDescriptor;
|
|
begin
|
|
Descriptor1:=TGtkFontCacheDescriptor(Desc1);
|
|
Descriptor2:=TGtkFontCacheDescriptor(Desc2);
|
|
Result:=CompareStr(Descriptor1.LongFontName,Descriptor2.LongFontName);
|
|
if Result<>0 then exit;
|
|
Result:=CompareMemRange(@Descriptor1.LogFont,@Descriptor2.LogFont,
|
|
SizeOf(Descriptor1.LogFont));
|
|
end;
|
|
|
|
function TGtkFontCache.FindGtkFont(TheGtkFont: TGtkIntfFont): TGtkFontCacheItem;
|
|
var
|
|
ANode: TAvlTreeNode;
|
|
begin
|
|
ANode:=FItems.Findkey(TheGtkFont,TListSortCompare(@CompareGtkFontWithResItem));
|
|
if ANode<>nil then
|
|
Result:=TGtkFontCacheItem(ANode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TGtkFontCache.FindGtkFontDesc(const LogFont: TLogFont;
|
|
const LongFontName: string): TGtkFontCacheDescriptor;
|
|
var
|
|
LogFontAndName: TLogFontAndName;
|
|
ANode: TAvlTreeNode;
|
|
begin
|
|
LogFontAndName.LogFont:=LogFont;
|
|
LogFontAndName.LongFontName:=LongFontName;
|
|
ANode:=FDescriptors.Findkey(@LogFontAndName,
|
|
TListSortCompare(@CompareLogFontAndNameWithResDesc));
|
|
if ANode<>nil then
|
|
Result:=TGtkFontCacheDescriptor(ANode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TGtkFontCache.FindADescriptor(TheGtkFont: TGtkIntfFont
|
|
): TGtkFontCacheDescriptor;
|
|
var
|
|
Item: TGtkFontCacheItem;
|
|
begin
|
|
Item:=FindGtkFont(TheGtkFont);
|
|
if Item=nil then
|
|
Result:=nil
|
|
else
|
|
Result:=TGtkFontCacheDescriptor(Item.FirstDescriptor);
|
|
end;
|
|
|
|
function TGtkFontCache.Add(TheGtkFont: TGtkIntfFont; const LogFont: TLogFont;
|
|
const LongFontName: string): TGtkFontCacheDescriptor;
|
|
var
|
|
Item: TGtkFontCacheItem;
|
|
begin
|
|
if TheGtkFont=nil then
|
|
RaiseGDBException('TGtkFontCache.Add TheGtkFont=nil');
|
|
if FindGtkFontDesc(LogFont,LongFontName)<>nil then
|
|
RaiseGDBException('TGtkFontCache.Add font desc added twice');
|
|
|
|
// find cache item with TheGtkFont
|
|
Item:=FindGtkFont(TheGtkFont);
|
|
if Item=nil then begin
|
|
// create new item
|
|
Item:=TGtkFontCacheItem.Create(Self,0);
|
|
Item.GtkFont:=TheGtkFont;
|
|
ReferenceGtkIntfFont(TheGtkFont);
|
|
FItems.Add(Item);
|
|
end;
|
|
|
|
// create descriptor
|
|
Result:=TGtkFontCacheDescriptor.Create(Self,Item);
|
|
Result.LongFontName:=LongFontName;
|
|
Result.LogFont:=LogFont;
|
|
FDescriptors.Add(Result);
|
|
if FindGtkFontDesc(LogFont,LongFontName)=nil then begin
|
|
DebugLn('TGtkFontCache.Add Added: %p LongFontName=%s LogFont=%s', [Pointer(Result), Result.LongFontName, LogFontToString(Result.LogFont)]);
|
|
DumpDescriptors;
|
|
RaiseGDBException('');
|
|
end;
|
|
end;
|
|
|
|
function TGtkFontCache.AddWithoutName(TheGtkFont: TGtkIntfFont
|
|
): TGtkFontCacheDescriptor;
|
|
var
|
|
LogFont: TLogFont;
|
|
LongFontName: string;
|
|
begin
|
|
FillChar(LogFont,SizeOf(LogFont),0);
|
|
LongFontName:=dbghex(PtrUInt(TheGtkFont));
|
|
Result:=Add(TheGtkFont,LogFont,LongFontName);
|
|
end;
|
|
|
|
procedure TGtkFontCache.Reference(TheGtkFont: TGtkIntfFont);
|
|
var
|
|
Item: TGtkFontCacheItem;
|
|
begin
|
|
Item:=FindGtkFont(TheGtkFont);
|
|
if Item=nil then
|
|
ReferenceGtkIntfFont(TheGtkFont)
|
|
else
|
|
Item.IncreaseRefCount;
|
|
end;
|
|
|
|
procedure TGtkFontCache.Unreference(TheGtkFont: TGtkIntfFont);
|
|
var
|
|
Item: TGtkFontCacheItem;
|
|
begin
|
|
Item:=FindGtkFont(TheGtkFont);
|
|
if Item=nil then
|
|
UnreferenceGtkIntfFont(TheGtkFont)
|
|
else
|
|
Item.DecreaseRefCount;
|
|
end;
|
|
|
|
procedure TGtkFontCache.DumpDescriptors;
|
|
var
|
|
ANode: TAvlTreeNode;
|
|
Desc: TGtkFontCacheDescriptor;
|
|
i: Integer;
|
|
begin
|
|
ANode:=FDescriptors.FindLowest;
|
|
i:=1;
|
|
while ANode<>nil do begin
|
|
Desc:=TGtkFontCacheDescriptor(ANode.Data);
|
|
DebugLn('TGtkFontCache.DumpDescriptors %d %p %s %s', [i, Pointer(Desc), Desc.LongFontName, LogFontToString(Desc.LogFont)]);
|
|
ANode:=FDescriptors.FindSuccessor(ANode);
|
|
inc(i);
|
|
end;
|
|
end;
|
|
|
|
{ TGtkFontCacheItem }
|
|
|
|
procedure TGtkFontCacheItem.WarnReferenceHigh;
|
|
begin
|
|
inherited WarnReferenceHigh;
|
|
debugln(' GtkFont='+DbgS(GtkFont));
|
|
if FirstDescriptor<>nil then
|
|
debugln(' '+TGtkFontCacheDescriptor(FirstDescriptor).LongFontName
|
|
+' '+LogFontToString(TGtkFontCacheDescriptor(FirstDescriptor).LogFont));
|
|
end;
|
|
|
|
{ TGtkFontCacheDescriptor }
|
|
|
|
destructor TGtkFontCacheDescriptor.Destroy;
|
|
begin
|
|
{$IFDEF Gtk2}
|
|
if PangoFontDescription<>nil then begin
|
|
pango_font_description_free(PangoFontDescription);
|
|
PangoFontDescription:=nil;
|
|
end;
|
|
{$ENDIF}
|
|
inherited Destroy;
|
|
end;
|
|
|
|
initialization
|
|
FontCache:=TGtkFontCache.Create;
|
|
|
|
finalization
|
|
FontCache.Free;
|
|
FontCache:=nil;
|
|
|
|
end.
|