4
{$include customdrawndefines.inc}
10
Types, Classes, SysUtils,
11
fpimage, fpcanvas, Math,
14
{$ifndef CD_UseNativeText}
16
TTTypes, LazFreeTypeIntfDrawer, LazFreeType, EasyLazFreeType, IniFiles,
18
// Custom Drawn Canvas
19
IntfGraphics, lazcanvas, lazregions, customdrawndrawers, customdrawncontrols,
21
GraphType, Controls, LCLMessageGlue, WSControls, LCLType, LCLProc,
22
StdCtrls, ExtCtrls, Forms, Graphics, ComCtrls,
23
InterfaceBase, LCLIntf;
26
TUpdateLazImageFormat = (
28
clfRGB24, clfRGB24UpsideDown, clfBGR24,
29
clfBGRA32, clfRGBA32, clfARGB32);
33
TCDBaseControl = class
36
function GetProps(AnIndex: String): pointer;
37
procedure SetProps(AnIndex: String; AValue: pointer);
39
FWinControl: TWinControl;
41
Children: TFPList; // of TCDWinControl;
42
// For scrolling a control
43
// The initial values are x=0, y=0 After scrolling downwards (by dragging upwards)
44
// it will be for example x=0, y=+27
45
ScrollX, ScrollY: Integer;
48
// Counter to keep track of when we requested Invalidate
49
// Some systems like X11 and Win32 will keep sending unnecessary paint messages
50
// so for them we just throw the previously painted image
51
InvalidateCount: Integer;
53
ControlImage: TLazIntfImage;
54
ControlCanvas: TLazCanvas;
55
constructor Create; virtual;
56
destructor Destroy; override;
57
procedure IncInvalidateCount;
58
function AdjustCoordinatesForScrolling(AX, AY: Integer): TPoint;
59
procedure UpdateImageAndCanvas; virtual;
60
function IsControlBackgroundVisible: Boolean; virtual;
61
property Props[AnIndex:String]:pointer read GetProps write SetProps;
66
TCDWinControl = class(TCDBaseControl)
68
Region: TLazRegionWithChilds;
69
WinControl: TWinControl;
70
CDControl: TCDControl;
71
CDControlInjected: Boolean;
72
procedure UpdateImageAndCanvas; override;
73
function IsControlBackgroundVisible: Boolean; override;
78
TCDForm = class(TCDBaseControl)
83
LastMouseDownControl: TWinControl; // Stores the control which should receive the next MouseUp
84
FocusedControl: TWinControl; // The control focused in the form
85
FocusedIntfControl: TWinControl; // The intf control focused in the form
86
LayoutAutoAdjusted: Boolean; // Indicates if the form layout was already auto-adjusted once
87
// For merging invalidate requests, currently utilized in X11
88
InvalidateRequestedInAnyControl: Boolean;
89
// painting objects which represent the composed form image, don't confuse with ControlImage/ControlCanvas
92
constructor Create; virtual;
93
function GetFocusedControl: TWinControl;
94
function GetFormVirtualHeight(AScreenHeight: Integer): Integer;
95
procedure SanityCheckScrollPos();
96
procedure UpdateImageAndCanvas; override;
97
function IsControlBackgroundVisible: Boolean; override;
100
TCDNonNativeForm = class(TCDForm)
109
Image: TLazIntfImage;
110
destructor Destroy; override;
115
NativeHandle: PtrInt; // The X11 timer uses this to store the current time which is summed up to the next interval
117
TimerFunc: TWSTimerProc;
120
// Routines for form managing (both native and non-native)
122
procedure AddCDWinControlToForm(const AForm: TCustomForm; ACDWinControl: TCDWinControl);
123
function GetCDWinControlList(const AForm: TCustomForm): TFPList;
125
// Routines for non-native form managing
126
procedure InitNonNativeForms();
127
function GetCurrentForm(): TCDNonNativeForm;
128
function GetForm(AIndex: Integer): TCDNonNativeForm;
129
function GetFormCount(): Integer;
130
function AddNewForm(AForm: TCustomForm): TCDNonNativeForm;
131
procedure AddFormWithCDHandle(AHandle: TCDForm);
132
function FindFormWithNativeHandle(AHandle: HWND): TCDForm;
133
procedure ShowForm(ACDForm: TCDNonNativeForm);
134
procedure HideForm(ACDForm: TCDNonNativeForm);
135
procedure BringFormToFront(ACDForm: TCDNonNativeForm);
136
procedure SendFormToBack(ACDForm: TCDNonNativeForm);
137
function FindTopMostVisibleForm: TCDNonNativeForm;
139
// Routines for non-native wincontrol
141
procedure UpdateControlLazImageAndCanvas(var AImage: TLazIntfImage;
142
var ACanvas: TLazCanvas; AWidth, AHeight: Integer; AFormat: TUpdateLazImageFormat;
143
AData: Pointer = nil; AForceUpdate: Boolean = False;
144
AFreeImageOnUpdate: Boolean = True; ADataOwner: Boolean = True);
145
procedure DrawFormBackground(var AImage: TLazIntfImage; var ACanvas: TLazCanvas);
146
procedure RenderChildWinControls(var AImage: TLazIntfImage;
147
var ACanvas: TLazCanvas; ACDControlsList: TFPList; ACDForm: TCDForm);
148
function RenderWinControl(var AImage: TLazIntfImage;
149
var ACanvas: TLazCanvas; ACDWinControl: TCDWinControl; ACDForm: TCDForm): Boolean;
150
procedure RenderWinControlAndChildren(var AImage: TLazIntfImage;
151
var ACanvas: TLazCanvas; ACDWinControl: TCDWinControl; ACDForm: TCDForm);
152
procedure RenderForm(var AImage: TLazIntfImage;
153
var ACanvas: TLazCanvas; AForm: TCustomForm);
154
function FindControlWhichReceivedEvent(AForm: TCustomForm;
155
AControlsList: TFPList; AX, AY: Integer): TWinControl;
156
function FindControlPositionRelativeToTheForm(ALCLControl: TWinControl; AConsiderScrolling: Boolean = False): TPoint;
157
function FormPosToControlPos(ALCLControl: TWinControl; AX, AY: Integer): TPoint;
161
function DateTimeToMilliseconds(aDateTime: TDateTime): Int64;
162
function IsValidDC(ADC: HDC): Boolean;
163
function IsValidGDIObject(AGDIObj: HGDIOBJ): Boolean;
164
function IsValidBitmap(ABitmap: HBITMAP): Boolean;
165
function RemoveAccelChars(AStr: string): string;
167
// Timers list management (for platforms that need it)
169
procedure InitTimersList();
170
procedure AddTimer(ATimer: TCDTimer);
171
function GetTimer(AIndex: Integer): TCDTimer;
172
function GetTimerCount(): Integer;
173
function GetSmallestTimerInterval(): Integer;
174
procedure RemoveTimer(ATimer: TCDTimer);
175
function FindTimerWithNativeHandle(ANativeHandle: PtrInt): TCDTimer;
177
// Font choosing routines
179
{$ifndef CD_UseNativeText}
180
procedure VerifyAndCleanUpFontDirectories(AFontDirectories: TStringList);
181
procedure FontsScanForTTF(APath: string; var AFontTable: THashedStringList);
182
procedure FontsScanDir(APath: string; var AFontPaths: TStringList; var AFontList: THashedStringList);
188
// List with the Z-order of non-native forms, index=0 is the bottom-most form
189
NonNativeForms: TFPList = nil;
190
lCurrentForm: TCDNonNativeForm = nil;
193
TimersList: TFPList = nil;
195
procedure AddCDWinControlToForm(const AForm: TCustomForm; ACDWinControl: TCDWinControl);
197
lWindowInfo: TCDForm;
199
lWindowInfo := TCDForm(AForm.Handle);
200
if lWindowInfo.Children = nil then lWindowInfo.Children := TFPList.Create;
201
lWindowInfo.Children.Add(ACDWinControl);
204
function GetCDWinControlList(const AForm: TCustomForm): TFPList;
206
lWindowInfo: TCDForm;
208
lWindowInfo := TCDForm(AForm.Handle);
209
if lWindowInfo.Children = nil then lWindowInfo.Children := TFPList.Create;
210
Result := lWindowInfo.Children;
213
procedure InitNonNativeForms();
215
if NonNativeForms <> nil then Exit;
216
NonNativeForms := TFPList.Create;
219
function GetCurrentForm(): TCDNonNativeForm;
221
{$IFDEF VerboseCDForms}
222
DebugLn('GetCurrentForm');
224
Result := lCurrentForm;
227
function GetForm(AIndex: Integer): TCDNonNativeForm;
229
InitNonNativeForms();
230
Result := TCDNonNativeForm(NonNativeForms.Items[AIndex]);
233
function GetFormCount: Integer;
235
InitNonNativeForms();
236
Result := NonNativeForms.Count;
239
function AddNewForm(AForm: TCustomForm): TCDNonNativeForm;
241
lFormInfo: TCDNonNativeForm;
243
{$IFDEF VerboseCDForms}
244
DebugLn('AddNewForm');
246
lFormInfo := TCDNonNativeForm.Create;
247
lFormInfo.LCLForm := AForm;
248
AddFormWithCDHandle(lFormInfo);
252
procedure AddFormWithCDHandle(AHandle: TCDForm);
254
InitNonNativeForms();
255
NonNativeForms.Insert(0, AHandle);
258
function FindFormWithNativeHandle(AHandle: HWND): TCDForm;
264
InitNonNativeForms();
265
for i := 0 to NonNativeForms.Count - 1 do
267
lCDForm := TCDForm(NonNativeForms.Items[i]);
268
if lCDForm.NativeHandle = AHandle then
276
procedure ShowForm(ACDForm: TCDNonNativeForm);
278
{$IFDEF VerboseCDForms}
279
DebugLn(Format('ShowForm LCLForm=%s:%s', [ACDForm.LCLForm.Name, ACDForm.LCLForm.ClassName]));
281
ACDForm.Visible := True;
282
BringFormToFront(ACDForm);
283
lCurrentForm := ACDForm;
286
procedure HideForm(ACDForm: TCDNonNativeForm);
288
ACDForm.Visible := False;
289
// update the Current Form if required, and invalidate too
290
if lCurrentForm = ACDForm then
292
lCurrentForm := FindTopMostVisibleForm();
293
LCLIntf.InvalidateRect(HWND(lCurrentForm), nil, True);
295
// Warn the LCL that the form was hidden
296
LCLSendCloseQueryMsg(ACDForm.LCLForm);
299
procedure BringFormToFront(ACDForm: TCDNonNativeForm);
301
lCount, lCurIndex: Integer;
303
InitNonNativeForms();
304
lCount := NonNativeForms.Count;
305
lCurIndex := NonNativeForms.IndexOf(ACDForm);
306
{$IFDEF VerboseCDForms}
307
DebugLn(Format('BringFormToFront lOldIndex=%d lNewIndex=%d', [lCurIndex, lCount-1]));
309
NonNativeForms.Move(lCurIndex, lCount-1);
312
procedure SendFormToBack(ACDForm: TCDNonNativeForm);
314
lCount, lCurIndex: Integer;
317
ACDForm.Visible := False;
319
InitNonNativeForms();
320
lCount := NonNativeForms.Count;
321
lCurIndex := NonNativeForms.IndexOf(ACDForm);
322
{$IFDEF VerboseCDForms}
323
DebugLn(Format('SendFormToBack lOldIndex=%d lNewIndex=0', [lCurIndex]));
325
NonNativeForms.Move(lCurIndex, 0);
328
function FindTopMostVisibleForm: TCDNonNativeForm;
331
lForm: TCDNonNativeForm;
335
InitNonNativeForms();
336
// Iterate starting from Count to zero until we find a visible form
337
lCount := NonNativeForms.Count;
339
for i := lCount-1 downto 0 do
341
lForm := TCDNonNativeForm(NonNativeForms.Items[i]);
342
if lForm.Visible then
348
{$IFDEF VerboseCDForms}
349
DebugLn(Format('FindTopMostVisibleForm FoundIndex=%d FoundForm=%s:%s',
350
[i, Result.LCLForm.Name, Result.LCLForm.ClassName]));
354
// If AForceUpdate=True then it will update even if the width and height remain the same
355
procedure UpdateControlLazImageAndCanvas(var AImage: TLazIntfImage;
356
var ACanvas: TLazCanvas; AWidth, AHeight: Integer; AFormat: TUpdateLazImageFormat;
357
AData: Pointer = nil; AForceUpdate: Boolean = False;
358
AFreeImageOnUpdate: Boolean = True; ADataOwner: Boolean = True);
360
lRawImage: TRawImage;
363
{$IFDEF VerboseCDLazCanvas}
364
DebugLn(Format(':>[UpdateControlLazImageAndCanvas] Input Image: %x Canvas: %x',
365
[PtrInt(AImage), PtrInt(ACanvas)]));
367
// Check if the image needs update
368
if (AImage = nil) or (AWidth <> AImage.Width) or (AHeight <> AImage.Height)
371
if (AImage <> nil) and AFreeImageOnUpdate then AImage.Free;
372
// Free the canvas and create a new one if it is a dummy Canvas created for text metrics reading by GetDC(control)
373
if (ACanvas <> nil) and ACanvas.HasNoImage then
381
clfRGB16_R5G6B5: lRawImage.Description.Init_BPP16_R5G6B5(AWidth, AHeight);
382
clfRGB24: lRawImage.Description.Init_BPP24_R8G8B8_BIO_TTB(AWidth, AHeight);
383
clfRGB24UpsideDown: lRawImage.Description.Init_BPP24_R8G8B8_BIO_TTB_UpsideDown(AWidth, AHeight);
384
clfBGR24: lRawImage.Description.Init_BPP24_B8G8R8_BIO_TTB(AWidth, AHeight);
385
clfBGRA32: lRawImage.Description.Init_BPP32_B8G8R8A8_BIO_TTB(AWidth, AHeight);
386
clfRGBA32: lRawImage.Description.Init_BPP32_R8G8B8A8_BIO_TTB(AWidth, AHeight);
387
clfARGB32: lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight);
390
// Now connect the pixel buffer or create one
391
if AData = nil then lRawImage.CreateData(True)
397
clfRGB24, clfRGB24UpsideDown, clfBGR24:
399
clfBGRA32, clfRGBA32, clfARGB32:
403
lRawImage.Data := AData;
404
lRawImage.DataSize := AWidth * lPixelSize * AHeight;
407
AImage := TLazIntfImage.Create(AWidth, AHeight);
408
AImage.SetRawImage(lRawImage, ADataOwner);
410
if (ACanvas <> nil) then ACanvas.Free;
411
ACanvas := TLazCanvas.Create(AImage);
413
{$IFDEF VerboseCDLazCanvas}
414
DebugLn(Format(':<[UpdateControlLazImageAndCanvas] Output Image: %x Canvas: %x',
415
[PtrInt(AImage), PtrInt(ACanvas)]));
419
procedure DrawFormBackground(var AImage: TLazIntfImage; var ACanvas: TLazCanvas);
422
ACanvas.ResetCanvasState;
423
ACanvas.Brush.FPColor := TColorToFPColor(ColorToRGB(clForm));
424
ACanvas.Pen.FPColor := TColorToFPColor(ColorToRGB(clForm));
425
ACanvas.Rectangle(0, 0, AImage.Width, AImage.Height);
426
ACanvas.RestoreState(-1);
429
// This does not render the win control itself, only it's children
430
// The WinControls themselves will render child TControls not descending from TWinControl
431
procedure RenderChildWinControls(var AImage: TLazIntfImage;
432
var ACanvas: TLazCanvas; ACDControlsList: TFPList; ACDForm: TCDForm);
434
i, lChildrenCount: Integer;
435
lCDWinControl: TCDWinControl;
437
lChildrenCount := ACDControlsList.Count;
438
{$ifdef VerboseCDWinControl}
439
DebugLn(Format('[RenderChildWinControls] ACanvas=%x ACDControlsList=%x lChildrenCount=%d',
440
[PtrInt(ACanvas), PtrInt(ACDControlsList), lChildrenCount]));
443
for i := 0 to lChildrenCount-1 do
445
{$ifdef VerboseCDWinControl}
446
DebugLn(Format('[RenderChildWinControls] i=%d', [i]));
449
lCDWinControl := TCDWinControl(ACDControlsList.Items[i]);
451
RenderWinControlAndChildren(AImage, ACanvas, lCDWinControl, ACDForm);
455
// Renders a WinControl, but not it's children
456
// Returns if the control is visible and therefore if its children should be rendered
457
function RenderWinControl(var AImage: TLazIntfImage; var ACanvas: TLazCanvas;
458
ACDWinControl: TCDWinControl; ACDForm: TCDForm): Boolean;
460
lWinControl, lParentControl: TWinControl;
461
struct : TPaintStruct;
463
lControlCanvas: TLazCanvas;
464
lBaseWindowOrg: TPoint;
465
lControlStateEx: TCDControlStateEx;
466
lDrawControl: Boolean;
470
lWinControl := ACDWinControl.WinControl;
472
{$ifdef VerboseCDWinControl}
473
DebugLn(Format('[RenderWinControl] lWinControl=%x Name=%s:%s Left=%d'
474
+ ' Top=%d Width=%d Height=%d', [PtrInt(lWinControl), lWinControl.Name, lWinControl.ClassName,
475
lWinControl.Left, lWinControl.Top, lWinControl.Width, lWinControl.Height]));
478
if lWinControl.Visible = False then Exit;
480
// Disable the drawing itself, but keep the window org and region operations
481
// or else clicking and other things are broken
482
lDrawControl := ACDWinControl.IsControlBackgroundVisible();
484
// Save the Canvas state
486
ACanvas.ResetCanvasState;
488
// lBaseWindowOrg makes debugging easier
489
// Iterate to find the appropriate BaseWindowOrg relative to the parent control
490
lBaseWindowOrg := FindControlPositionRelativeToTheForm(lWinControl);
491
ACanvas.BaseWindowOrg := Point(lBaseWindowOrg.X, lBaseWindowOrg.Y - ACDForm.ScrollY);
492
ACanvas.WindowOrg := Point(0, 0);
494
// Prepare the clippping relative to the form
495
ACanvas.Clipping := True;
496
ACDWinControl.Region.Rect := Bounds(lBaseWindowOrg.X, lBaseWindowOrg.Y - ACDForm.ScrollY,
497
lWinControl.Width, lWinControl.Height);
498
ACanvas.ClipRegion := ACDWinControl.Region;
500
lControlCanvas := ACanvas;
502
if (ACDWinControl.InvalidateCount > 0) and lDrawControl then
504
ACDWinControl.UpdateImageAndCanvas();
505
lControlCanvas := ACDWinControl.ControlCanvas;
506
ACDWinControl.InvalidateCount := 0;
508
// Special drawing for some native controls
509
if (lWinControl is TCustomPanel) or (lWinControl is TTabSheet)
510
or (lWinControl is TCustomPage) or (lWinControl is TNotebook) then
512
// Erase the background of TPanel controls, since it can draw it's own border, but fails to draw it's own background
513
// and also erase the background for TTabSheet (children of TPageControl) and TCustomPage (children of TNotebook)
514
lControlCanvas.SaveState;
515
lControlCanvas.Brush.FPColor := TColorToFPColor(lWinControl.GetRGBColorResolvingParent());
516
lControlCanvas.Pen.FPColor := lControlCanvas.Brush.FPColor;
517
lControlCanvas.Rectangle(Bounds(0, 0, lWinControl.Width, lWinControl.Height));
518
lControlCanvas.RestoreState(-1);
520
else if lWinControl is TCustomGroupBox then
522
lControlCanvas.SaveState;
523
lControlStateEx := TCDControlStateEx.Create;
525
lControlStateEx.Font := lWinControl.Font;
526
lControlStateEx.Caption := lWinControl.Caption;
527
lControlStateEx.ParentRGBColor := lWinControl.GetRGBColorResolvingParent();
528
GetDefaultDrawer().DrawGroupBox(lControlCanvas, Size(lWinControl.Width, lWinControl.Height),
529
[], lControlStateEx);
531
lControlStateEx.Free;
532
lControlCanvas.RestoreState(-1);
536
// Send the drawing message
537
{$ifdef VerboseCDWinControl}
538
DebugLn('[RenderWinControl] before LCLSendPaintMsg');
540
FillChar(struct, SizeOf(TPaintStruct), 0);
541
struct.hdc := HDC(lControlCanvas);
542
LCLSendEraseBackgroundMsg(lWinControl, struct.hdc);
543
LCLSendPaintMsg(lWinControl, struct.hdc, @struct);
544
{$ifdef VerboseCDWinControl}
545
DebugLn('[RenderWinControl] after LCLSendPaintMsg');
549
// Here we actually blit the control to the form canvas
551
ACanvas.CanvasCopyRect(ACDWinControl.ControlCanvas, 0, 0, 0, 0,
552
lWinControl.Width, lWinControl.Height);
555
ACanvas.RestoreState(-1);
560
// Render a WinControl and all it's children
561
procedure RenderWinControlAndChildren(var AImage: TLazIntfImage;
562
var ACanvas: TLazCanvas; ACDWinControl: TCDWinControl; ACDForm: TCDForm);
564
if not RenderWinControl(AImage, ACanvas, ACDWinControl, ACDForm) then Exit;
566
// Now Draw all sub-controls
567
if ACDWinControl.Children <> nil then
568
RenderChildWinControls(AImage, ACanvas, ACDWinControl.Children, ACDForm);
571
// Draws a form and all of its child controls
572
procedure RenderForm(var AImage: TLazIntfImage; var ACanvas: TLazCanvas;
575
struct : TPaintStruct;
576
lWindowHandle: TCDForm;
577
lFormCanvas: TLazCanvas;
578
lDrawControl: Boolean;
580
lWindowHandle := TCDForm(AForm.Handle);
582
// Disable the drawing itself, but keep the window org and region operations
583
// or else clicking and other things are broken, specially in Android
584
lDrawControl := lWindowHandle.IsControlBackgroundVisible();
587
DrawFormBackground(AImage, ACanvas);
589
// Consider the form scrolling
590
// ToDo: Figure out why this "div 2" factor is necessary for drawing non-windows controls and remove this factor
591
// If you remove this factor then the wincontrols are fine,
592
// but graphiccontrols scroll with a different speed which is a huge problem
593
ACanvas.BaseWindowOrg := Point(0, - lWindowHandle.ScrollY div 2);
594
ACanvas.WindowOrg := Point(0, 0);
596
lFormCanvas := ACanvas;
600
// Send the paint message to the LCL
601
{$IFDEF VerboseCDForms}
602
DebugLn(Format('[RenderForm] OnPaint event started context: %x', [struct.hdc]));
604
FillChar(struct, SizeOf(TPaintStruct), 0);
605
struct.hdc := HDC(lFormCanvas);
606
LCLSendPaintMsg(AForm, struct.hdc, @struct);
607
{$IFDEF VerboseCDForms}
608
DebugLn('[RenderForm] OnPaint event ended');
612
// Now paint all child win controls
613
RenderChildWinControls(AImage, ACanvas, GetCDWinControlList(AForm), lWindowHandle);
616
function FindControlWhichReceivedEvent(AForm: TCustomForm;
617
AControlsList: TFPList; AX, AY: Integer): TWinControl;
620
lRegionOfEvent: TLazRegionWithChilds;
621
lCurCDControl: TCDWinControl;
622
lEventPos: TPoint; // local, already adjusted for the scrolling
625
lEventPos := Point(AX, AY); // Don't adjust for the scrolling because the regions are scrolled too
627
// The order of this loop is important to respect the Z-order of controls
628
for i := AControlsList.Count-1 downto 0 do
630
lCurCDControl := TCDWinControl(AControlsList.Items[i]);
631
if lCurCDControl.Region = nil then Continue;
632
if not lCurCDControl.WinControl.HandleObjectShouldBeVisible then Continue;
633
lRegionOfEvent := lCurCDControl.Region.IsPointInRegion(lEventPos.X, lEventPos.Y);
634
if lRegionOfEvent <> nil then
636
if lRegionOfEvent.UserData = nil then
637
raise Exception.Create('[FindControlWhichReceivedEvent] Malformed tree of regions');
638
Result := TWinControl(lRegionOfEvent.UserData);
640
// If it is a native LCL control, redirect to the CDControl
641
if lCurCDControl.CDControl <> nil then
642
Result := lCurCDControl.CDControl;
649
function FindControlPositionRelativeToTheForm(ALCLControl: TWinControl; AConsiderScrolling: Boolean = False): TPoint;
651
lParentControl: TWinControl;
652
lParentHandle: TCDBaseControl;
653
lScroll, lParentPos: TPoint;
655
// Iterate to find the appropriate BaseWindowOrg relative to the parent control
656
Result := Point(ALCLControl.Left, ALCLControl.Top);
657
lParentControl := ALCLControl.Parent;
658
while (lParentControl <> nil) do
660
if AConsiderScrolling and lParentControl.HandleAllocated then
662
lParentHandle := TCDBaseControl(lParentControl.Handle);
663
lScroll := Point(lParentHandle.ScrollX, lParentHandle.ScrollY);
665
else lScroll := Point(0, 0);
667
if (lParentControl is TCustomForm) then lParentPos := Point(0, 0)
668
else lParentPos := Point(lParentControl.Left, lParentControl.Top);
670
Result.X := Result.X + lParentPos.X - lScroll.X;
671
Result.Y := Result.Y + lParentPos.Y - lScroll.Y;
672
lParentControl := lParentControl.Parent;
676
function FormPosToControlPos(ALCLControl: TWinControl; AX, AY: Integer): TPoint;
680
lControlPos := FindControlPositionRelativeToTheForm(ALCLControl, True);
681
Result.X := AX - lControlPos.X;
682
Result.Y := AY - lControlPos.Y;
685
function DateTimeToMilliseconds(aDateTime: TDateTime): Int64;
687
TimeStamp: TTimeStamp;
689
{Call DateTimeToTimeStamp to convert DateTime to TimeStamp:}
690
TimeStamp:= DateTimeToTimeStamp (aDateTime);
691
{Multiply and add to complete the conversion:}
692
Result:= TimeStamp.Time;
695
function IsValidDC(ADC: HDC): Boolean;
700
function IsValidGDIObject(AGDIObj: HGDIOBJ): Boolean;
702
Result := AGDIObj <> 0;
705
function IsValidBitmap(ABitmap: HBITMAP): Boolean;
707
Result := ABitmap <> 0;
710
function RemoveAccelChars(AStr: string): string;
712
// ToDo convert && to & and keep it
713
Result := StringReplace(AStr, '&', '', [rfReplaceAll]);
716
procedure InitTimersList;
718
if TimersList = nil then TimersList := TFPList.Create;
721
procedure AddTimer(ATimer: TCDTimer);
724
TimersList.Add(ATimer);
727
function GetTimer(AIndex: Integer): TCDTimer;
730
Result := TCDTimer(TimersList.Items[AIndex]);
733
function GetTimerCount: Integer;
736
Result := TimersList.Count;
739
function GetSmallestTimerInterval: Integer;
744
Result := High(Integer);
745
for i := 0 to GetTimerCount()-1 do
747
lTimer := GetTimer(i);
748
Result := Min(Result, lTimer.Interval);
750
if Result = High(Integer) then Result := -1;
753
procedure RemoveTimer(ATimer: TCDTimer);
756
TimersList.Remove(ATimer);
759
function FindTimerWithNativeHandle(ANativeHandle: PtrInt): TCDTimer;
766
for i := 0 to TimersList.Count - 1 do
768
lTimer := TCDTimer(TimersList.Items[i]);
769
if lTimer.NativeHandle = ANativeHandle then
774
{$ifndef CD_UseNativeText}
775
procedure VerifyAndCleanUpFontDirectories(AFontDirectories: TStringList);
779
// Add path delimitiers to the end of all paths
780
for i := 0 to AFontDirectories.Count -1 do
782
AFontDirectories.Strings[i] := IncludeTrailingPathDelimiter(AFontDirectories.Strings[i]);
785
// remove all duplicates
787
while i < AFontDirectories.Count do
790
while j < AFontDirectories.Count do
792
if AFontDirectories.Strings[i] = AFontDirectories.Strings[j] then
793
AFontDirectories.Delete(j);
799
// Now remove all directories which don't exist
801
while i < AFontDirectories.Count do
803
if not DirectoryExistsUTF8(AFontDirectories.Strings[i]) then
804
AFontDirectories.Delete(i);
808
// Raise an exception if there are no font directories
809
if AFontDirectories.Count = 0 then
810
raise Exception.Create('[VerifyAndCleanUpFontDirectories] After cleaning up no font directories were found.');
813
{------------------------------------------------------------------------------
814
Procedure: BackendScanForTTF - Scope=local
815
Params: APath - path for a font directory
816
AFontTable - Font name to Font path Hashed List
818
Scan a directory for ttf fonts and updates the FontTable
819
------------------------------------------------------------------------------}
820
procedure FontsScanForTTF(APath: string; var AFontTable: THashedStringList);
825
SearchResult, J: Integer;
830
Platform,Encoding,Language: Integer;
833
{$ifdef CD_Debug_TTF}
834
DebugList: TstringList;
837
SearchResult := FindFirstUTF8(APath+'*.ttf', faAnyFile, Rslt);
838
{$ifdef CD_Debug_TTF}
839
DebugList:= TStringList.Create;
841
while SearchResult = 0 do
843
FontPath:= APath+Rslt.Name;
845
{$ifdef CD_Debug_TTF}
846
DebugLn(Format('[FontsScanForTTF] font=', [FontPath]));
849
// Work around for fonts which cause errors. See bug 21456
850
if Rslt.Name = 'tunga.ttf' then
852
SearchResult := FindNextUTF8(Rslt);
856
ErrNum:= TT_Open_Face(FontPath, AFace);
857
if ErrNum = TT_Err_Ok then
859
NameCount:= TT_Get_Name_Count(AFace);
860
for J:= 0 to NameCount-1 do
862
ErrNum:= TT_Get_Name_ID(AFace, J, Platform, Encoding, Language, NameID);
863
{ -------------------------------------------------------------------
865
1= Font Family (e.g. Arial, Times, Liberation )
866
2= Font Subfamily (e.g. Bold, Italic, Condensed)
867
3= Unique Font Identifier
868
4= Full Name - Human readable - the one used by the IDE
869
-----------------------------------------------------------------------}
870
{$ifdef CD_Debug_TTF}
871
if ErrNum = TT_Err_Ok then
873
ErrNum:= TT_Get_Name_String(AFace,J,NameString,NameLen);
875
if NameString <> '' then //DBG
877
SetLength(AName,NameLen);
878
DebugList.Add('ID='+IntToStr(NameID)+' Path='+FontPath+' Name='+AName);
880
else DebugList.Add('ID='+IntToStr(NameID)+' Path='+FontPath+' Name=<Empty String>');
883
if (ErrNum = TT_Err_Ok) and (NameID = 4) then begin
884
ErrNum:= TT_Get_Name_String(AFace,J,NameString,NameLen);
886
// Skip empty entries
887
if NameString <> '' then begin
888
SetLength(AName,NameLen);
889
AFontTable.Add(AName+'='+FontPath);
894
{$ifdef CD_Debug_TTF}
895
DebugList.Add('------');
897
ErrNum:= TT_Close_Face(AFace);
898
SearchResult := FindNextUTF8(Rslt);
901
{$ifdef CD_Debug_TTF}
902
AName:= ExtractFileDir(Apath);
903
AName:= ExtractFileName(AName) + '.txt';
904
DebugList.SaveToFile({$ifdef UNIX}'/tmp/'+{$endif}{$ifdef Windows}'C:\'+{$endif}AName);
909
{------------------------------------------------------------------------------
910
Procedure: BackendScanDir - Scope=Local
911
Params: APath - path for a font directory
912
AFontPaths - Font path List
914
Recursively scans font directories to find the ones populated only
916
------------------------------------------------------------------------------}
917
procedure FontsScanDir(APath: string; var AFontPaths: TStringList; var AFontList: THashedStringList);
922
DirFound,DirEmpty: Boolean;
923
TmpList: THashedStringList;
927
I:= FindFirstUTF8(APath+'*',faAnyFile,Rslt);
928
while I >= 0 do begin
929
if (Rslt.Name <> '.') and (Rslt.Name <> '..') then
932
if (Rslt.Attr and faDirectory) <> 0 then
934
NextPath:= APath + Rslt.Name + PathDelim;
936
FontsScanDir(NextPath,AFontPaths,AFontList);
939
I:= FindNextUTF8(Rslt);
942
if (not DirFound) and (not DirEmpty) then
943
AFontPaths.Add(APath);
950
procedure TCDWinControl.UpdateImageAndCanvas;
952
UpdateControlLazImageAndCanvas(ControlImage, ControlCanvas,
953
WinControl.Width, WinControl.Height, clfARGB32);
956
function TCDWinControl.IsControlBackgroundVisible: Boolean;
958
FWinControl := WinControl;
959
Result:=inherited IsControlBackgroundVisible;
964
destructor TCDBitmap.Destroy;
966
if Image <> nil then Image.Free;
972
function TCDBaseControl.GetProps(AnIndex: String): pointer;
976
i:=Fprops.IndexOf(AnIndex);
979
result:=Fprops.Objects[i];
985
procedure TCDBaseControl.SetProps(AnIndex: String; AValue: pointer);
989
i := Fprops.IndexOf(AnIndex);
991
i := FProps.Add(AnIndex);
992
Fprops.Objects[i] := TObject(AValue);
995
constructor TCDBaseControl.Create;
998
FProps := TStringList.Create;
999
//FProps.CaseSensitive:=false; commented as in the qt widgetset
1000
FProps.Sorted:=true;
1001
IncInvalidateCount(); // Always starts needing an invalidate
1003
Children := TFPList.Create;
1006
destructor TCDBaseControl.Destroy;
1011
// Free the Canvas and Image if required
1012
// Dont free for the Form because elsewhere this is taken care of
1013
if ControlCanvas <> nil then ControlCanvas.Free;
1014
if ControlImage <> nil then ControlImage.Free;
1019
procedure TCDBaseControl.IncInvalidateCount;
1021
Inc(InvalidateCount);
1024
function TCDBaseControl.AdjustCoordinatesForScrolling(AX, AY: Integer): TPoint;
1026
DebugLn(Format('AX=%d AY=%d ScrollX=%d ScrollY=%d', [AX, AY, ScrollX, ScrollY]));
1027
Result := Point(AX + ScrollX, AY + ScrollY);
1030
procedure TCDBaseControl.UpdateImageAndCanvas;
1035
// This is utilized for optimizing the painting. If we figure out that there is
1036
// nothing visible from a control, just give up drawing it completely
1038
// What usually happens is that child controls might completely cover their
1041
// We should watch out for alpha-blending, however
1042
function TCDBaseControl.IsControlBackgroundVisible: Boolean;
1046
lWinChild: TWinControl;
1049
if FWinControl = nil then Exit;
1050
for i := 0 to FWinControl.ControlCount-1 do
1052
lChild := FWinControl.Controls[i];
1053
if not (lChild is TWinControl) then Continue;
1054
lWinChild := TWinControl(lChild);
1056
// ToDo: Ignore alpha blended controls
1058
// Basic case: alClient
1059
if lWinChild.Align = alClient then Exit(False);
1061
// Another case: coordinates match
1062
if (lWinChild.Left = 0) and (lWinChild.Top = 0) and
1063
(lWinChild.Width = FWinControl.Width) and (lWinChild.Height = FWinControl.Height) then
1070
constructor TCDForm.Create;
1073
InvalidateCount := 1;
1076
function TCDForm.GetFocusedControl: TWinControl;
1078
if FocusedIntfControl <> nil then Result := FocusedIntfControl
1079
else if FocusedControl <> nil then Result := FocusedControl
1080
else Result := LCLForm;
1083
function TCDForm.GetFormVirtualHeight(AScreenHeight: Integer): Integer;
1085
i, lControlRequiredHeight: Integer;
1088
Result := AScreenHeight;
1089
for i := 0 to LCLForm.ControlCount-1 do
1091
lControl := LCLForm.Controls[i];
1092
lControlRequiredHeight := lControl.Top + lControl.Height;
1093
Result := Max(lControlRequiredHeight, Result);
1097
procedure TCDForm.SanityCheckScrollPos;
1099
ScrollY := Max(ScrollY, 0);
1100
ScrollY := Min(ScrollY, GetFormVirtualHeight(Image.Height) - Image.Height);
1103
procedure TCDForm.UpdateImageAndCanvas;
1105
UpdateControlLazImageAndCanvas(ControlImage, ControlCanvas,
1106
LCLForm.ClientWIdth, LCLForm.ClientHeight, clfARGB32);
1109
function TCDForm.IsControlBackgroundVisible: Boolean;
1111
FWinControl := LCLForm;
1112
Result:=inherited IsControlBackgroundVisible;