| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251 | unit PasTools;interface{$WARN SYMBOL_PLATFORM OFF}uses  Windows, Types, Classes, ComCtrls, ExtCtrls, Controls, Dialogs, Forms, Messages, Graphics, SysUtils;function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;{$EXTERNALSYM IsWin7}function IsWin7: Boolean;{$EXTERNALSYM IsWin8}function IsWin8: Boolean;{$EXTERNALSYM CutToChar}function CutToChar(var Str: string; Ch: Char; Trim: Boolean): string;procedure FilterToFileTypes(Filter: string; FileTypes: TFileTypeItems);// Note that while we based our scaling on pixels-per-inch,// VCL actually scales based on font sizeconst  CM_DPICHANGED = WM_USER + $2000 + 10;function HasSystemParametersInfoForPixelsPerInch: Boolean;function SystemParametersInfoForPixelsPerInch(  uiAction, uiParam: UINT; pvParam: Pointer; fWinIni: UINT; dpi: UINT): BOOL;function GetMonitorFromControl(Control: TControl): TMonitor;function GetMonitorPixelsPerInch(Monitor: TMonitor): Integer;function GetControlPixelsPerInch(Control: TControl): Integer;function GetComponentPixelsPerInch(Component: TComponent): Integer;function LoadDimension(Dimension: Integer; PixelsPerInch: Integer; Control: TControl): Integer;function StrToDimensionDef(Str: string; PixelsPerInch: Integer; Control: TControl; Default: Integer): Integer;function SaveDimension(Dimension: Integer): Integer;function DimensionToDefaultPixelsPerInch(Dimension: Integer): Integer;function ScaleByPixelsPerInch(Dimension: Integer; Monitor: TMonitor): Integer; overload;function ScaleByPixelsPerInch(Dimension: Integer; Control: TControl): Integer; overload;function ScaleByPixelsPerInchFromSystem(Dimension: Integer; Control: TControl): Integer;function LoadPixelsPerInch(S: string; Control: TControl): Integer;function SavePixelsPerInch(Control: TControl): string;function SaveDefaultPixelsPerInch: string;function CalculateTextHeight(Canvas: TCanvas): Integer;function ScaleByTextHeight(Control: TControl; Dimension: Integer): Integer;function ScaleByTextHeightRunTime(Control: TControl; Dimension: Integer): Integer;function ScaleByControlTextHeightRunTime(Canvas: TCanvas; Dimension: Integer): Integer;function GetSystemMetricsForControl(Control: TControl; nIndex: Integer): Integer;type  TImageListSize = (ilsSmall, ilsLarge);procedure NeedShellImageLists;function ShellImageListForSize(Width: Integer): TImageList;function ShellImageListForControl(Control: TControl; Size: TImageListSize): TImageList;function ControlHasRecreationPersistenceData(Control: TControl): Boolean;function IsAppIconic: Boolean;procedure SetAppIconic(Value: Boolean);procedure SetAppMainForm(Value: TForm);procedure SetAppTerminated(Value: Boolean);procedure ForceColorChange(Control: TWinControl);function IsUncPath(Path: string): Boolean;function FileExistsFix(Path: string): Boolean;function DirectoryExistsFix(Path: string): Boolean;const  FIND_FIRST_EX_LARGE_FETCH_PAS = 2; // VCLCOPY (actually should be part of Winapi)function FindFirstEx(  const Path: string; Attr: Integer; var F: TSearchRec; AdditionalFlags: DWORD = 0;  SearchOp: _FINDEX_SEARCH_OPS = FindExSearchNameMatch): Integer;function SupportsDarkMode: Boolean;procedure AllowDarkModeForWindow(Control: TWinControl; Allow: Boolean); overload;procedure AllowDarkModeForWindow(Handle: THandle; Allow: Boolean); overload;procedure RefreshColorMode;procedure ResetSysDarkTheme;function GetSysDarkTheme: Boolean;type  TApiPathEvent = function(Path: string): string;var  OnApiPath: TApiPathEvent = nil;{$EXTERNALSYM ApiPath}function ApiPath(Path: string): string;type  TAppLogEvent = procedure(S: string);var  OnAppLog: TAppLogEvent = nil;{$EXTERNALSYM AppLog}procedure AppLog(S: string);type  TControlScrollBeforeUpdate = procedure(ObjectToValidate: TObject) of object;  TControlScrollAfterUpdate = procedure of object;  TCustomControlScrollOnDragOver = class  private    FOnBeforeUpdate: TControlScrollBeforeUpdate;    FOnAfterUpdate: TControlScrollAfterUpdate;    FDragOverTimer: TTimer;    FControl: TControl;    FDragOverTime: FILETIME;    FLastVScrollTime: FILETIME;    FVScrollCount: Integer;    procedure DragOverTimer(Sender: TObject);    procedure BeforeUpdate(ObjectToValidate: TObject);    procedure AfterUpdate;  public    constructor Create(Control: TControl; ScheduleDragOver: Boolean);    destructor Destroy; override;    procedure StartDrag; virtual;    procedure EndDrag; virtual;    procedure DragOver(Point: TPoint); virtual; abstract;    property OnBeforeUpdate: TControlScrollBeforeUpdate read FOnBeforeUpdate write FOnBeforeUpdate;    property OnAfterUpdate: TControlScrollAfterUpdate read FOnAfterUpdate write FOnAfterUpdate;  end;  TTreeViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)  private    FLastDragNode: TTreeNode;    FLastHScrollTime: FILETIME;  public    procedure StartDrag; override;    procedure DragOver(Point: TPoint); override;  end;  TListViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)  public    procedure DragOver(Point: TPoint); override;  end;  TListBoxScrollOnDragOver = class(TCustomControlScrollOnDragOver)  public    procedure DragOver(Point: TPoint); override;  end;implementationuses  StdCtrls, MultiMon, ShellAPI, Generics.Collections, CommCtrl, ImgList, Registry;const  DDExpandDelay = 15000000;  DDMaxSlowCount = 3;  DDVScrollDelay   = 2000000;  DDHScrollDelay   = 2000000;  DDDragStartDelay = 500000;function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;begin  Result := ComponentClass.Create(Owner);end;function IsWin7: Boolean;begin  Result := CheckWin32Version(6, 1);end;function IsWin8: Boolean;begin  Result := CheckWin32Version(6, 2);end;function CutToChar(var Str: string; Ch: Char; Trim: Boolean): string;var  P: Integer;begin  P := Pos(Ch, Str);  if P > 0 then  begin    Result := Copy(Str, 1, P-1);    Delete(Str, 1, P);  end    else  begin    Result := Str;    Str := '';  end;  if Trim then Result := SysUtils.Trim(Result);end;procedure FilterToFileTypes(Filter: string; FileTypes: TFileTypeItems);var  Item: TFileTypeItem;begin  while Filter <> '' do  begin    Item := FileTypes.Add();    Item.DisplayName := CutToChar(Filter, '|', True);    Item.FileMask := CutToChar(Filter, '|', True);  end;end;type  TGetDpiForMonitorFunc =    function (hMonitor: HMONITOR; MonitorType: Integer; out DpiX, DpiY: Cardinal): HRESULT; stdcall;  TGetSystemMetricsForDpiFunc =    function (nIndex: Integer; Dpi: Cardinal): Integer; stdcall;  TSystemParametersInfoForDpiFunc =    function (uiAction, uiParam: UINT; pvParam: Pointer; fWinIni: UINT; dpi: UINT): BOOL; stdcall;const  MDT_EFFECTIVE_DPI = 0;var  GetDpiForMonitor: TGetDpiForMonitorFunc = nil;  GetSystemMetricsForDpi: TGetSystemMetricsForDpiFunc = nil;  SystemParametersInfoForDpi: TSystemParametersInfoForDpiFunc = nil;function HasSystemParametersInfoForPixelsPerInch: Boolean;begin  Result := Assigned(SystemParametersInfoForDpi);end;function SystemParametersInfoForPixelsPerInch(  uiAction, uiParam: UINT; pvParam: Pointer; fWinIni: UINT; dpi: UINT): BOOL;begin  if HasSystemParametersInfoForPixelsPerInch then  begin    Result := SystemParametersInfoForDpi(uiAction, uiParam, pvParam, fWinIni, dpi);  end    else  begin    Result := SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni);  end;end;function GetMonitorPixelsPerInch(Monitor: TMonitor): Integer;var  DpiX, DpiY: Cardinal;begin  if Assigned(GetDpiForMonitor) and     (GetDpiForMonitor(Monitor.Handle, MDT_EFFECTIVE_DPI, DpiX, DpiY) = S_OK) then  begin    Result := DpiX;  end    else  begin    Result := Screen.PixelsPerInch;  end;end;function GetMonitorFromControl(Control: TControl): TMonitor;begin  if Control.Parent <> nil then  begin    Result := GetMonitorFromControl(Control.Parent);  end    else  if Control is TCustomForm then  begin    Result := TCustomForm(Control).Monitor;  end    else  if (Control is TWinControl) and TWinControl(Control).HandleAllocated then  begin    Result := Screen.MonitorFromWindow(TWinControl(Control).Handle);  end    else  begin    Result := nil;  end;end;function GetControlPixelsPerInch(Control: TControl): Integer;var  Form: TCustomForm;  Monitor: TMonitor;begin  if Assigned(GetDpiForMonitor) then // optimization  begin    Form := GetParentForm(Control);    if Assigned(Form) then    begin      // By default, scale according to what the form is so far rendered on.      // If the monitor perceived DPI does not match its monitor DPI, it's because the WM_DPICHANGED is still pending.      Result := TForm(Form).PixelsPerInch;    end      else    begin      Monitor := GetMonitorFromControl(Control);      if Monitor = nil then      begin        Assert(False);        Monitor := Screen.PrimaryMonitor;      end;      Result := GetMonitorPixelsPerInch(Monitor);    end;  end    else  begin    Result := Screen.PixelsPerInch;  end;end;function GetComponentPixelsPerInch(Component: TComponent): Integer;begin  Result := GetControlPixelsPerInch(TControl(Component.Owner));end;function LoadDimension(Dimension: Integer; PixelsPerInch: Integer; Control: TControl): Integer;begin  Result := MulDiv(Dimension, GetControlPixelsPerInch(Control), PixelsPerInch);end;function StrToDimensionDef(Str: string; PixelsPerInch: Integer; Control: TControl; Default: Integer): Integer;begin  if TryStrToInt(Str, Result) then  begin    Result := LoadDimension(Result, PixelsPerInch, Control);  end    else  begin    Result := Default;  end;end;function SaveDimension(Dimension: Integer): Integer;begin  // noop  Result := Dimension;end;function DimensionToDefaultPixelsPerInch(Dimension: Integer): Integer;begin  Result := MulDiv(Dimension, USER_DEFAULT_SCREEN_DPI, Screen.PixelsPerInch);end;function ScaleByPixelsPerInch(Dimension: Integer; Monitor: TMonitor): Integer;begin  Result := MulDiv(Dimension, GetMonitorPixelsPerInch(Monitor), USER_DEFAULT_SCREEN_DPI);end;function ScaleByPixelsPerInch(Dimension: Integer; Control: TControl): Integer;begin  Result := MulDiv(Dimension, GetControlPixelsPerInch(Control), USER_DEFAULT_SCREEN_DPI);end;function ScaleByPixelsPerInchFromSystem(Dimension: Integer; Control: TControl): Integer;begin  Result := MulDiv(Dimension, GetControlPixelsPerInch(Control), Screen.PixelsPerInch);end;function LoadPixelsPerInch(S: string; Control: TControl): Integer;begin  // for backward compatibility with version that did not save the DPI,  // make reasonable assumption that the configuration was saved with  // the same DPI as we run now  Result := StrToIntDef(S, GetControlPixelsPerInch(Control));end;function SavePixelsPerInch(Control: TControl): string;begin  Result := IntToStr(GetControlPixelsPerInch(Control));end;function SaveDefaultPixelsPerInch: string;begin  Result := IntToStr(USER_DEFAULT_SCREEN_DPI);end;// WORKAROUND// https://stackoverflow.com/q/9410485/850848type  TFormHelper = class helper for TCustomForm  public    function RetrieveTextHeight: Integer;  end;function TFormHelper.RetrieveTextHeight: Integer;begin  Result := Self.GetInternalTextHeight;end;function CalculateTextHeight(Canvas: TCanvas): Integer;begin  // RTL_COPY (TCustomForm.GetTextHeight)  Result := Canvas.TextHeight('0');end;function ScaleByTextHeightImpl(Canvas: TCanvas; Dimension: Integer; TextHeight: Integer): Integer; overload;var  NewTextHeight: Integer;begin  // RTL_COPY (TCustomForm.ReadState)  NewTextHeight := CalculateTextHeight(Canvas);  if TextHeight <> NewTextHeight then  begin    Dimension := MulDiv(Dimension, NewTextHeight, TextHeight);  end;  Result := Dimension;end;function ScaleByTextHeightImpl(Control: TControl; Dimension: Integer; TextHeight: Integer): Integer; overload;var  Form: TCustomForm;begin  // RTL_COPY (TCustomForm.ReadState)  Form := ValidParentForm(Control);  Result := ScaleByTextHeightImpl(Form.Canvas, Dimension, TextHeight);end;const  OurDesignTimeTextHeight = 13;function ScaleByTextHeight(Control: TControl; Dimension: Integer): Integer;var  Form: TCustomForm;  TextHeight: Integer;begin  // RTL_COPY (TCustomForm.ReadState)  Form := GetParentForm(Control);  if Form = nil then  begin    // This should happen only for screen tip over dropped down menu.    // The other condition is a temporary fix is for TCustomComboEdit on TCopyParamsFrame.    Assert((Control.ClassName = 'TTBXPopupWindow') or (Control.ClassName = 'TTBXChevronPopupWindow') or ((Control.Parent <> nil) and (Control.Parent.ClassName = 'TCopyParamsFrame')) or ((Control.Parent <> nil) and (Control.Parent.Parent <> nil) and (Control.Parent.Parent.ClassName = 'TCopyParamsFrame')));    Result := ScaleByPixelsPerInch(Dimension, Control);  end    else  begin    TextHeight := Form.RetrieveTextHeight;    // runtime form (such as TTBFloatingWindowParent)    if TextHeight = 0 then    begin      Result := ScaleByTextHeightRunTime(Control, Dimension);    end      else    begin      // that's our design text-size, we do not expect any other value      Assert(TextHeight = OurDesignTimeTextHeight);      Result := ScaleByTextHeightImpl(Control, Dimension, TextHeight);    end;  end;end;// this differs from ScaleByTextHeight only by enforcing// constant design-time text heightfunction ScaleByTextHeightRunTime(Control: TControl; Dimension: Integer): Integer;begin  Result := ScaleByTextHeightImpl(Control, Dimension, OurDesignTimeTextHeight);end;function ScaleByControlTextHeightRunTime(Canvas: TCanvas; Dimension: Integer): Integer;begin  Result := ScaleByTextHeightImpl(Canvas, Dimension, OurDesignTimeTextHeight);end;function GetSystemMetricsForControl(Control: TControl; nIndex: Integer): Integer;begin  if Assigned(GetSystemMetricsForDpi) then  begin    Result := GetSystemMetricsForDpi(nIndex, GetControlPixelsPerInch(Control))  end    else  begin    Result := GetSystemMetrics(nIndex);  end;end;var  ShellImageLists: TDictionary<Integer, TImageList> = nil;// This should be replaced with IShellItemImageFactory, as already used for thumbnailsprocedure InitializeShellImageLists;type  TSHGetImageList = function (iImageList: integer; const riid: TGUID; var ppv: Pointer): hResult; stdcall;const  IID_IImageList: TGUID = '{46EB5926-582E-4017-9FDF-E8998DAA0950}';var  Lib: THandle;  ImageList: Integer;  Handle: THandle;  Height, Width: Integer;  ShellImageList: TImageList;  SHGetImageList: TSHGetImageList;  HR: HRESULT;begin  Lib := LoadLibrary('shell32');  SHGetImageList := GetProcAddress(Lib, 'SHGetImageList');  ShellImageLists := TDictionary<Integer, TImageList>.Create;  for ImageList := 0 to SHIL_LAST do  begin    // VCL have declaration for SHGetImageList in ShellAPI, but it does not link    HR := SHGetImageList(ImageList, IID_IImageList, Pointer(Handle));    if (HR = S_OK) and       ImageList_GetIconSize(Handle, Width, Height) then    begin      // We could use AddOrSetValue instead, but to be on a safe side, we prefer e.g. SHIL_SMALL over SHIL_SYSSMALL,      // while they actually can be the same      if not ShellImageLists.ContainsKey(Width) then      begin        ShellImageList := TImageList.Create(Application);        ShellImageList.Handle := Handle;        ShellImageList.ShareImages := True;        ShellImageList.DrawingStyle := dsTransparent;        ShellImageLists.Add(Width, ShellImageList);      end;    end;  end;end;procedure NeedShellImageLists;begin  if ShellImageLists = nil then  begin    InitializeShellImageLists;  end;end;function ShellImageListForSize(Width: Integer): TImageList;var  ImageListPair: TPair<Integer, TImageList>;  ImageListWidth: Integer;  Diff, BestDiff: Integer;begin  // Delay load image lists, not to waste resources in console/scripting mode  NeedShellImageLists;  Result := nil;  BestDiff := -1;  for ImageListPair in ShellImageLists do  begin    ImageListWidth := ImageListPair.Key;    if ImageListWidth <= Width then    begin      Diff := Width - ImageListWidth;    end      else    begin      // Prefer smaller images over larger, so for 150%, we use 100% images, not 200%      // (a larger icon would make the item row higher)      Diff := ImageListWidth - Width + 1;    end;    if (BestDiff < 0) or (BestDiff > Diff) then    begin      BestDiff := Diff;      Result := ImageListPair.Value;    end;  end;end;function ShellImageListForControl(Control: TControl; Size: TImageListSize): TImageList;var  Width: Integer;begin  case Size of    ilsSmall: Width := 16;    ilsLarge: Width := 32;    else Width := 0; Assert(False);  end;  Width := ScaleByPixelsPerInch(Width, Control);  Result := ShellImageListForSize(Width);end;type  TListViewHelper = class helper for TCustomListView  public    function HasMemStream: Boolean;  end;function TListViewHelper.HasMemStream: Boolean;begin  with Self do    Result := Assigned(FMemStream);end;type  TTreeViewHelper = class helper for TCustomTreeView  public    function HasMemStream: Boolean;  end;function TTreeViewHelper.HasMemStream: Boolean;begin  with Self do    Result := Assigned(FMemStream);end;type  TRichEditHelper = class helper for TCustomRichEdit  public    function HasMemStream: Boolean;  end;function TRichEditHelper.HasMemStream: Boolean;begin  with Self do    Result := Assigned(FMemStream);end;function ControlHasRecreationPersistenceData(Control: TControl): Boolean;begin  // not implemented for this class as we do not use it as of now  Assert(not (Control is TCustomComboBoxEx));  Result :=    ((Control is TCustomListView) and (Control as TCustomListView).HasMemStream) or    ((Control is TCustomTreeView) and (Control as TCustomTreeView).HasMemStream) or    ((Control is TCustomRichEdit) and (Control as TCustomRichEdit).HasMemStream);end;type  TApplicationHelper = class helper for TApplication  public    function IsAppIconic: Boolean;    procedure SetAppIconic(Value: Boolean);    procedure SetMainForm(Value: TForm);    procedure SetTerminated(Value: Boolean);  end;function TApplicationHelper.IsAppIconic: Boolean;begin  with Self do    Result := FAppIconic;end;procedure TApplicationHelper.SetAppIconic(Value: Boolean);begin  with Self do    FAppIconic := Value;end;procedure TApplicationHelper.SetMainForm(Value: TForm);begin  with Self do    FMainForm := Value;end;procedure TApplicationHelper.SetTerminated(Value: Boolean);begin  with Self do    FTerminate := Value;end;function IsAppIconic: Boolean;begin  Result := Application.IsAppIconic;end;procedure SetAppIconic(Value: Boolean);begin  Application.SetAppIconic(Value);end;procedure SetAppMainForm(Value: TForm);begin  Application.SetMainForm(Value);end;procedure SetAppTerminated(Value: Boolean);begin  Application.SetTerminated(Value);end;function ApiPath(Path: string): string;begin  Result := Path;  if Assigned(OnApiPath) then  begin    Result := OnApiPath(Result);  end;end;procedure ForceColorChange(Control: TWinControl);begin  // particularly when changing color back to default (clWindow),  // non-client area (border line) is not redrawn,  // keeping previous color. force redraw here  if Control.HandleAllocated then  begin    RedrawWindow(Control.Handle, nil, 0, RDW_INVALIDATE or RDW_FRAME);  end;end;procedure AppLog(S: string);begin  if Assigned(OnAppLog) then  begin    OnAppLog(S);  end;end;  { TCustomControlScrollOnDragOver }constructor TCustomControlScrollOnDragOver.Create(Control: TControl;  ScheduleDragOver: Boolean);begin  FControl := Control;  FOnBeforeUpdate := nil;  FOnAfterUpdate := nil;  if ScheduleDragOver then  begin    FDragOverTimer := TTimer.Create(Control);    FDragOverTimer.Enabled := False;    FDragOverTimer.Interval := 50;    FDragOverTimer.OnTimer := DragOverTimer;  end    else FDragOverTimer := nil;end;destructor TCustomControlScrollOnDragOver.Destroy;begin  FreeAndNil(FDragOverTimer);end;procedure TCustomControlScrollOnDragOver.DragOverTimer(Sender: TObject);var  P: TPoint;begin  P := FControl.ScreenToClient(Mouse.CursorPos);  if (P.X >= 0) and (P.X < FControl.Width) and     (P.Y >= 0) and (P.Y < FControl.Height) then  begin    DragOver(P);  end;end;procedure TCustomControlScrollOnDragOver.StartDrag;begin  GetSystemTimeAsFileTime(FDragOverTime);  GetSystemTimeAsFileTime(FLastVScrollTime);  FVScrollCount := 0;  if Assigned(FDragOverTimer) then    FDragOverTimer.Enabled := True;end;procedure TCustomControlScrollOnDragOver.EndDrag;begin  if Assigned(FDragOverTimer) then    FDragOverTimer.Enabled := False;end;type  TPublicControl = class(TControl);procedure TCustomControlScrollOnDragOver.BeforeUpdate(ObjectToValidate: TObject);var  DragImages: TDragImageList;begin  if Assigned(FOnBeforeUpdate) then    FOnBeforeUpdate(ObjectToValidate);  DragImages := TPublicControl(FControl).GetDragImages;  if Assigned(DragImages) then    DragImages.HideDragImage;end;procedure TCustomControlScrollOnDragOver.AfterUpdate;var  DragImages: TDragImageList;begin  if Assigned(FOnAfterUpdate) then    FOnAfterUpdate;  DragImages := TPublicControl(FControl).GetDragImages;  if Assigned(DragImages) then    DragImages.ShowDragImage;end;procedure TTreeViewScrollOnDragOver.StartDrag;var  KeyBoardState : TKeyBoardState;begin  inherited;  FLastDragNode := nil;  if (GetKeyState(VK_SPACE) <> 0) and GetKeyboardState(KeyBoardState) then  begin    KeyBoardState[VK_SPACE] := 0;    SetKeyBoardState(KeyBoardState);  end;  GetSystemTimeAsFileTime(FLastHScrollTime);end;  { TTreeViewScrollOnDragOver }procedure TTreeViewScrollOnDragOver.DragOver(Point: TPoint);var  TreeView: TCustomTreeView;  NbPixels: Integer;  KnowTime: TFileTime;  Node: TTreeNode;  TempTopItem: TTreeNode;  ScrollInfo: TScrollInfo;  KeyBoardState : TKeyBoardState;begin  TreeView := (FControl as TCustomTreeView);  Node := TreeView.GetNodeAt(Point.X, Point.Y);  if Assigned(Node) then  begin    GetSystemTimeAsFileTime(KnowTime);    if GetKeyState(VK_SPACE) = 0 then    begin      {Expand node after 2.5 seconds: }      if not Assigned(FLastDragNode) or (FLastDragNode <> Node) then      begin        {not previous droptarget: start timer}        GetSystemTimeAsFileTime(FDragOverTime);        FLastDragNode := Node      end        else      begin        if ((Int64(KnowTime) - Int64(FDragOverTime)) > DDExpandDelay) then        begin          TempTopItem := TreeView.TopItem;          BeforeUpdate(nil);          Node.Expand(False);          TreeView.TopItem := TempTopItem;          TreeView.Update;          AfterUpdate;          FDragOverTime := KnowTime;        end;      end;    end      else    begin      {restart timer}      GetSystemTimeAsFileTime(FDragOverTime);      if GetKeyboardState(KeyBoardState) then      begin        KeyBoardState[VK_Space] := 0;        SetKeyBoardState(KeyBoardState);      end;      TempTopItem := TreeView.TopItem;      BeforeUpdate(Node);      if Node.Expanded then      begin        if not TreeView.Selected.HasAsParent(Node) then          Node.Collapse(False);      end        else Node.Expand(False);      TreeView.TopItem := TempTopItem;      TreeView.Update;      AfterUpdate;    end;    NbPixels := Abs(TTreeView(FControl).Font.Height);    {Vertical treescrolling:}    if ((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or       ((FVScrollCount > 3) and        ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay Div 4))) then    begin      {Scroll tree up, if droptarget is topitem:}      if Node = TreeView.TopItem then      begin        BeforeUpdate(nil);        TreeView.Perform(WM_VSCROLL, SB_LINEUP, 0);        AfterUpdate;        GetSystemTimeAsFileTime(FLastVScrollTime);        Inc(FVScrollCount);      end        else      {Scroll tree down, if next visible item of droptarget is not visible:}      begin        if Point.Y + 3 * nbPixels > TreeView.Height then        begin          BeforeUpdate(nil);          TreeView.Perform(WM_VSCROLL, SB_LINEDOWN, 0);          AfterUpdate;          GetSystemTimeAsFileTime(FLastVScrollTime);          Inc(FVScrollCount);        end          else        begin          FVScrollCount := 0;        end;      end;    end; {VScrollDelay}    {Horizontal treescrolling:}    {Scroll tree Left}    if ((Int64(KnowTime) - Int64(FLastHScrollTime)) > DDHScrollDelay) then    begin      GetSystemTimeAsFileTime(FLastHScrollTime);      ScrollInfo.cbSize := SizeOf(ScrollInfo);      ScrollInfo.FMask := SIF_ALL;      GetScrollInfo(TreeView.Handle, SB_HORZ, ScrollInfo);      if ScrollInfo.nMin <> ScrollInfo.nMax then      begin        if Point.X < 50 then        begin          if Node.DisplayRect(True).Right + 50 < TreeView.Width then          begin            BeforeUpdate(nil);            TreeView.Perform(WM_HSCROLL, SB_LINELEFT, 0);            AfterUpdate;          end;        end          else        if Point.X > (TreeView.Width - 50) then        begin          if Node.DisplayRect(True).Left > 50 then          begin            BeforeUpdate(nil);            TreeView.Perform(WM_HSCROLL, SB_LINERIGHT, 0);            AfterUpdate;          end;        end;      end;    end;  end;end;  { TListViewScrollOnDragOver }procedure TListViewScrollOnDragOver.DragOver(Point: TPoint);var  ListView: TCustomListView;  KnowTime: TFileTime;  NbPixels: Integer;  WParam: LongInt;begin  ListView := (FControl as TCustomListView);  GetSystemTimeAsFileTime(KnowTime);  NbPixels := Abs(TListView(ListView).Font.Height);  {Vertical scrolling, if viewstyle = vsReport:}  if (TListView(ListView).ViewStyle = vsReport) and Assigned(ListView.TopItem) and     (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or      ((FVScrollCount > DDMaxSlowCount) and        ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then  begin    if (Point.Y - 3 * nbPixels <= 0) and (ListView.TopItem.Index > 0) then WParam := SB_LINEUP      else    if (Point.Y + 3 * nbPixels > ListView.Height) then WParam := SB_LINEDOWN      else WParam := -1;    if WParam >= 0 then    begin      BeforeUpdate(nil);      ListView.Perform(WM_VSCROLL, WParam, 0);      if FVScrollCount > DDMaxSlowCount then        ListView.Perform(WM_VSCROLL, WParam, 0);      if FVScrollCount > DDMaxSlowCount * 3 then        ListView.Perform(WM_VSCROLL, WParam, 0);      ListView.Update;      AfterUpdate;      GetSystemTimeAsFileTime(FLastVScrollTime);      Inc(FVScrollCount);    end      else FVScrollCount := 0;  end;end;  { TListBoxScrollOnDragOver }procedure TListBoxScrollOnDragOver.DragOver(Point: TPoint);var  ListBox: TListBox;  KnowTime: TFileTime;  NbPixels: Integer;  WParam: LongInt;begin  ListBox := (FControl as TListBox);  GetSystemTimeAsFileTime(KnowTime);  NbPixels := Abs(ListBox.Font.Height);  {Vertical scrolling, if viewstyle = vsReport:}  if (ListBox.Items.Count > 0) and     (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or      ((FVScrollCount > DDMaxSlowCount) and        ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then  begin    if (Point.Y - 3 * nbPixels <= 0) and (ListBox.TopIndex > 0) then WParam := SB_LINEUP      else    if (Point.Y + 3 * nbPixels > ListBox.Height) then WParam := SB_LINEDOWN      else WParam := -1;    if WParam >= 0 then    begin      BeforeUpdate(nil);      ListBox.Perform(WM_VSCROLL, WParam, 0);      if FVScrollCount > DDMaxSlowCount then        ListBox.Perform(WM_VSCROLL, WParam, 0);      if FVScrollCount > DDMaxSlowCount * 3 then        ListBox.Perform(WM_VSCROLL, WParam, 0);      ListBox.Update;      AfterUpdate;      GetSystemTimeAsFileTime(FLastVScrollTime);      Inc(FVScrollCount);    end      else FVScrollCount := 0;  end;end;function IsUncPath(Path: string): Boolean;begin  Result := (Copy(Path, 1, 2) = '\\') or (Copy(Path, 1, 2) = '//');end;const ERROR_CANT_ACCESS_FILE = 1920;function DoExists(R: Boolean; Path: string): Boolean;var  Error: Integer;begin  Result := R;  if not Result then  begin    Error := GetLastError();    if (Error = ERROR_CANT_ACCESS_FILE) or // returned when resolving symlinks in %LOCALAPPDATA%\Microsoft\WindowsApps       (Error = ERROR_ACCESS_DENIED) then // returned for %USERPROFILE%\Application Data symlink    begin      Result := DirectoryExists(ApiPath(ExtractFileDir(Path)));    end;  end;end;function FileExistsFix(Path: string): Boolean;begin  // WORKAROUND  SetLastError(ERROR_SUCCESS);  Result := DoExists(FileExists(ApiPath(Path)), Path);end;function DirectoryExistsFix(Path: string): Boolean;begin  // WORKAROUND  SetLastError(ERROR_SUCCESS);  Result := DoExists(DirectoryExists(ApiPath(Path)), Path);end;// VCLCOPYfunction FindMatchingFileEx(var F: TSearchRec): Integer;var  LocalFileTime: TFileTime;begin  while F.FindData.dwFileAttributes and F.ExcludeAttr <> 0 do    if not FindNextFile(F.FindHandle, F.FindData) then    begin      Result := GetLastError;      Exit;    end;  FileTimeToLocalFileTime(F.FindData.ftLastWriteTime, LocalFileTime);{$WARN SYMBOL_DEPRECATED OFF}  FileTimeToDosDateTime(LocalFileTime, LongRec(F.Time).Hi,    LongRec(F.Time).Lo);{$WARN SYMBOL_DEPRECATED ON}  F.Size := F.FindData.nFileSizeLow or Int64(F.FindData.nFileSizeHigh) shl 32;  F.Attr := F.FindData.dwFileAttributes;  F.Name := F.FindData.cFileName;  Result := 0;end;var  FindexAdvancedSupport: Boolean = False;// VCLCOPY (with FindFirstFile replaced by FindFirstFileEx)function FindFirstEx(  const Path: string; Attr: Integer; var F: TSearchRec; AdditionalFlags: DWORD; SearchOp: _FINDEX_SEARCH_OPS): Integer;const  faSpecial = faHidden or faSysFile or faDirectory;var  FindexInfoLevel: TFindexInfoLevels;begin  F.ExcludeAttr := not Attr and faSpecial;  // FindExInfoBasic = do not retrieve cAlternateFileName, which we do not use  if FindexAdvancedSupport then FindexInfoLevel := FindExInfoBasic    else  begin    FindexInfoLevel := FindExInfoStandard;    AdditionalFlags := AdditionalFlags and (not FIND_FIRST_EX_LARGE_FETCH_PAS);  end;  F.FindHandle := FindFirstFileEx(PChar(Path), FindexInfoLevel, @F.FindData, SearchOp, nil, AdditionalFlags);  if F.FindHandle <> INVALID_HANDLE_VALUE then  begin    Result := FindMatchingFileEx(F);    if Result <> 0 then FindClose(F);  end  else    Result := GetLastError;end;type TPreferredAppMode = (pamDefault, pamAllowDark, pamForceDark, pamForceLight, pamMax);var  AAllowDarkModeForWindow: function(hWnd: HWND; Allow: BOOL): BOOL; stdcall;  ARefreshImmersiveColorPolicyState: procedure; stdcall;  ASetPreferredAppMode: function(AppMode: TPreferredAppMode): TPreferredAppMode; stdcall;function SupportsDarkMode: Boolean;begin  Result := Assigned(AAllowDarkModeForWindow) and Assigned(ARefreshImmersiveColorPolicyState);end;procedure AllowDarkModeForWindow(Control: TWinControl; Allow: Boolean);begin  Assert(Control.HandleAllocated);  if SupportsDarkMode and Control.HandleAllocated then  begin    AAllowDarkModeForWindow(Control.Handle, Allow);  end;end;procedure AllowDarkModeForWindow(Handle: THandle; Allow: Boolean);begin  if SupportsDarkMode then  begin    AAllowDarkModeForWindow(Handle, Allow);  end;end;procedure RefreshColorMode;begin  if SupportsDarkMode then  begin    ARefreshImmersiveColorPolicyState;  end;end;var  SysDarkTheme: Integer;procedure ResetSysDarkTheme;begin  SysDarkTheme := -1;end;function DoGetSysDarkTheme(RootKey: HKEY): Integer;const  ThemesPersonalizeKey = 'Software\Microsoft\Windows\CurrentVersion\Themes\Personalize';  AppsUseLightThemeValue = 'AppsUseLightTheme';var  Registry: TRegistry;begin  Registry := TRegistry.Create;  try    Registry.RootKey := RootKey;    Result := -1;    if Registry.OpenKeyReadOnly(ThemesPersonalizeKey) and       Registry.ValueExists(AppsUseLightThemeValue) then    begin      if Registry.ReadBool(AppsUseLightThemeValue) then Result := 0        else Result := 1;    end;  finally    Registry.Free;  end;end;function GetSysDarkTheme: Boolean;begin  if SysDarkTheme < 0 then  begin    SysDarkTheme := DoGetSysDarkTheme(HKEY_CURRENT_USER);    if SysDarkTheme < 0 then    begin      SysDarkTheme := DoGetSysDarkTheme(HKEY_LOCAL_MACHINE);      if SysDarkTheme < 0 then      begin        SysDarkTheme := 0;      end;    end;  end;  Result := (SysDarkTheme > 0);end;const  LOAD_LIBRARY_SEARCH_SYSTEM32 = $00000800;  LOAD_LIBRARY_SEARCH_USER_DIRS = $00000400;var  Lib: THandle;  OSVersionInfo: TOSVersionInfoEx;  SetDefaultDllDirectories: function(DirectoryFlags: DWORD): BOOL; stdcall;initialization  FindexAdvancedSupport := IsWin7;  // Translated from PuTTY's dll_hijacking_protection().  // Inno Setup does not use LOAD_LIBRARY_SEARCH_USER_DIRS and falls back to SetDllDirectory.  Lib := LoadLibrary(kernel32);  SetDefaultDllDirectories := GetProcAddress(Lib, 'SetDefaultDllDirectories');  if Assigned(SetDefaultDllDirectories) then  begin    SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 or LOAD_LIBRARY_SEARCH_USER_DIRS);  end;  Lib := LoadLibrary('shcore');  if Lib <> 0 then  begin    GetDpiForMonitor := GetProcAddress(Lib, 'GetDpiForMonitor');  end;  Lib := LoadLibrary('user32');  if Lib <> 0 then  begin    GetSystemMetricsForDpi := GetProcAddress(Lib, 'GetSystemMetricsForDpi');    SystemParametersInfoForDpi := GetProcAddress(Lib, 'SystemParametersInfoForDpi');  end;  AAllowDarkModeForWindow := nil;  ARefreshImmersiveColorPolicyState := nil;  ASetPreferredAppMode := nil;  OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);  if GetVersionEx(OSVersionInfo) and (OSVersionInfo.dwBuildNumber >= 17763) then  begin    Lib := GetModuleHandle('uxtheme');    if Lib <> 0 then    begin      AAllowDarkModeForWindow := GetProcAddress(Lib, MakeIntResource(133));      ARefreshImmersiveColorPolicyState := GetProcAddress(Lib, MakeIntResource(104));      if OSVersionInfo.dwBuildNumber >= 18334 then      begin        ASetPreferredAppMode := GetProcAddress(Lib, MakeIntResource(135));      end;      if SupportsDarkMode then      begin        // Both SetPreferredAppMode and RefreshImmersiveColorPolicyState is needed for        // dark list view headers and dark list view and tree view scrollbars        if Assigned(ASetPreferredAppMode) then        begin          ASetPreferredAppMode(pamAllowDark);        end;        ARefreshImmersiveColorPolicyState;      end;    end;  end;  ResetSysDarkTheme;finalization  // No need to release individual image lists as they are owned by Application object.  FreeAndNil(ShellImageLists);end.
 |